This commit is contained in:
Arthur Tazhitdinov 2026-02-03 12:38:15 +03:00 committed by GitHub
commit f2dfb62319
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 431 additions and 42 deletions

View File

@ -22,8 +22,59 @@ void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
namespace {
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
// Increment this when adding new persisted settings fields
constexpr uint8_t SETTINGS_COUNT = 23;
constexpr uint8_t SETTINGS_COUNT = 27;
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
// Validate front button mapping to ensure each hardware button is unique.
// If duplicates are detected, reset to the default physical order to prevent invalid mappings.
void validateFrontButtonMapping(CrossPointSettings& settings) {
// Snapshot the logical->hardware mapping so we can compare for duplicates.
const uint8_t mapping[] = {settings.frontButtonBack, settings.frontButtonConfirm, settings.frontButtonLeft,
settings.frontButtonRight};
for (size_t i = 0; i < 4; i++) {
for (size_t j = i + 1; j < 4; j++) {
if (mapping[i] == mapping[j]) {
// Duplicate detected: restore the default physical order (Back, Confirm, Left, Right).
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
return;
}
}
}
}
// Convert legacy front button layout into explicit logical->hardware mapping.
void applyLegacyFrontButtonLayout(CrossPointSettings& settings) {
switch (static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(settings.frontButtonLayout)) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
settings.frontButtonBack = CrossPointSettings::FRONT_HW_LEFT;
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_RIGHT;
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
settings.frontButtonRight = CrossPointSettings::FRONT_HW_CONFIRM;
break;
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
settings.frontButtonBack = CrossPointSettings::FRONT_HW_CONFIRM;
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_LEFT;
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
break;
case CrossPointSettings::BACK_CONFIRM_RIGHT_LEFT:
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_RIGHT;
settings.frontButtonRight = CrossPointSettings::FRONT_HW_LEFT;
break;
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
break;
}
}
} // namespace
bool CrossPointSettings::saveToFile() const {
@ -42,7 +93,7 @@ bool CrossPointSettings::saveToFile() const {
serialization::writePod(outputFile, shortPwrBtn);
serialization::writePod(outputFile, statusBar);
serialization::writePod(outputFile, orientation);
serialization::writePod(outputFile, frontButtonLayout);
serialization::writePod(outputFile, frontButtonLayout); // legacy
serialization::writePod(outputFile, sideButtonLayout);
serialization::writePod(outputFile, fontFamily);
serialization::writePod(outputFile, fontSize);
@ -60,6 +111,10 @@ bool CrossPointSettings::saveToFile() const {
serialization::writeString(outputFile, std::string(opdsUsername));
serialization::writeString(outputFile, std::string(opdsPassword));
serialization::writePod(outputFile, sleepScreenCoverFilter);
serialization::writePod(outputFile, frontButtonBack);
serialization::writePod(outputFile, frontButtonConfirm);
serialization::writePod(outputFile, frontButtonLeft);
serialization::writePod(outputFile, frontButtonRight);
// New fields added at end for backward compatibility
outputFile.close();
@ -86,6 +141,8 @@ bool CrossPointSettings::loadFromFile() {
// load settings that exist (support older files with fewer fields)
uint8_t settingsRead = 0;
// Track whether remap fields were present in the settings file.
bool frontButtonMappingRead = false;
do {
readAndValidate(inputFile, sleepScreen, SLEEP_SCREEN_MODE_COUNT);
if (++settingsRead >= fileSettingsCount) break;
@ -97,7 +154,7 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, orientation, ORIENTATION_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT);
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT); // legacy
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, sideButtonLayout, SIDE_BUTTON_LAYOUT_COUNT);
if (++settingsRead >= fileSettingsCount) break;
@ -148,9 +205,23 @@ bool CrossPointSettings::loadFromFile() {
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, frontButtonBack, FRONT_BUTTON_HARDWARE_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, frontButtonConfirm, FRONT_BUTTON_HARDWARE_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, frontButtonLeft, FRONT_BUTTON_HARDWARE_COUNT);
if (++settingsRead >= fileSettingsCount) break;
readAndValidate(inputFile, frontButtonRight, FRONT_BUTTON_HARDWARE_COUNT);
frontButtonMappingRead = true;
// New fields added at end for backward compatibility
} while (false);
if (frontButtonMappingRead) {
validateFrontButtonMapping(*this);
} else {
applyLegacyFrontButtonLayout(*this);
}
inputFile.close();
Serial.printf("[%lu] [CPS] Settings loaded from file\n", millis());
return true;

View File

@ -42,7 +42,7 @@ class CrossPointSettings {
ORIENTATION_COUNT
};
// Front button layout options
// Front button layout options (legacy)
// Default: Back, Confirm, Left, Right
// Swapped: Left, Right, Back, Confirm
enum FRONT_BUTTON_LAYOUT {
@ -53,6 +53,15 @@ class CrossPointSettings {
FRONT_BUTTON_LAYOUT_COUNT
};
// Front button hardware identifiers (for remapping)
enum FRONT_BUTTON_HARDWARE {
FRONT_HW_BACK = 0,
FRONT_HW_CONFIRM = 1,
FRONT_HW_LEFT = 2,
FRONT_HW_RIGHT = 3,
FRONT_BUTTON_HARDWARE_COUNT
};
// Side button layout options
// Default: Previous, Next
// Swapped: Next, Previous
@ -113,9 +122,15 @@ class CrossPointSettings {
// EPUB reading orientation settings
// 0 = portrait (default), 1 = landscape clockwise, 2 = inverted, 3 = landscape counter-clockwise
uint8_t orientation = PORTRAIT;
// Button layouts
// Button layouts (front layout retained for migration only)
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
uint8_t sideButtonLayout = PREV_NEXT;
// Front button remap (logical -> hardware)
// Used by MappedInputManager to translate logical buttons into physical front buttons.
uint8_t frontButtonBack = FRONT_HW_BACK;
uint8_t frontButtonConfirm = FRONT_HW_CONFIRM;
uint8_t frontButtonLeft = FRONT_HW_LEFT;
uint8_t frontButtonRight = FRONT_HW_RIGHT;
// Reader font settings
uint8_t fontFamily = BOOKERLY;
uint8_t fontSize = MEDIUM;

View File

@ -5,26 +5,11 @@
namespace {
using ButtonIndex = uint8_t;
struct FrontLayoutMap {
ButtonIndex back;
ButtonIndex confirm;
ButtonIndex left;
ButtonIndex right;
};
struct SideLayoutMap {
ButtonIndex pageBack;
ButtonIndex pageForward;
};
// Order matches CrossPointSettings::FRONT_BUTTON_LAYOUT.
constexpr FrontLayoutMap kFrontLayouts[] = {
{HalGPIO::BTN_BACK, HalGPIO::BTN_CONFIRM, HalGPIO::BTN_LEFT, HalGPIO::BTN_RIGHT},
{HalGPIO::BTN_LEFT, HalGPIO::BTN_RIGHT, HalGPIO::BTN_BACK, HalGPIO::BTN_CONFIRM},
{HalGPIO::BTN_CONFIRM, HalGPIO::BTN_LEFT, HalGPIO::BTN_BACK, HalGPIO::BTN_RIGHT},
{HalGPIO::BTN_BACK, HalGPIO::BTN_CONFIRM, HalGPIO::BTN_RIGHT, HalGPIO::BTN_LEFT},
};
// Order matches CrossPointSettings::SIDE_BUTTON_LAYOUT.
constexpr SideLayoutMap kSideLayouts[] = {
{HalGPIO::BTN_UP, HalGPIO::BTN_DOWN},
@ -33,29 +18,36 @@ constexpr SideLayoutMap kSideLayouts[] = {
} // namespace
bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint8_t) const) const {
const auto frontLayout = static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
const auto sideLayout = static_cast<CrossPointSettings::SIDE_BUTTON_LAYOUT>(SETTINGS.sideButtonLayout);
const auto& front = kFrontLayouts[frontLayout];
const auto& side = kSideLayouts[sideLayout];
switch (button) {
case Button::Back:
return (gpio.*fn)(front.back);
// Logical Back maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonBack);
case Button::Confirm:
return (gpio.*fn)(front.confirm);
// Logical Confirm maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonConfirm);
case Button::Left:
return (gpio.*fn)(front.left);
// Logical Left maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonLeft);
case Button::Right:
return (gpio.*fn)(front.right);
// Logical Right maps to user-configured front button.
return (gpio.*fn)(SETTINGS.frontButtonRight);
case Button::Up:
// Side buttons remain fixed for Up/Down.
return (gpio.*fn)(HalGPIO::BTN_UP);
case Button::Down:
// Side buttons remain fixed for Up/Down.
return (gpio.*fn)(HalGPIO::BTN_DOWN);
case Button::Power:
// Power button bypasses remapping.
return (gpio.*fn)(HalGPIO::BTN_POWER);
case Button::PageBack:
// Reader page navigation uses side buttons and can be swapped via settings.
return (gpio.*fn)(side.pageBack);
case Button::PageForward:
// Reader page navigation uses side buttons and can be swapped via settings.
return (gpio.*fn)(side.pageForward);
}
@ -76,17 +68,42 @@ unsigned long MappedInputManager::getHeldTime() const { return gpio.getHeldTime(
MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous,
const char* next) const {
const auto layout = static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
switch (layout) {
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
return {previous, next, back, confirm};
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
return {previous, back, confirm, next};
case CrossPointSettings::BACK_CONFIRM_RIGHT_LEFT:
return {back, confirm, next, previous};
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
default:
return {back, confirm, previous, next};
// Build the label order based on the configured hardware mapping.
auto labelForHardware = [&](uint8_t hw) -> const char* {
// Compare against configured logical roles and return the matching label.
if (hw == SETTINGS.frontButtonBack) {
return back;
}
if (hw == SETTINGS.frontButtonConfirm) {
return confirm;
}
if (hw == SETTINGS.frontButtonLeft) {
return previous;
}
if (hw == SETTINGS.frontButtonRight) {
return next;
}
return "";
};
return {labelForHardware(HalGPIO::BTN_BACK), labelForHardware(HalGPIO::BTN_CONFIRM),
labelForHardware(HalGPIO::BTN_LEFT), labelForHardware(HalGPIO::BTN_RIGHT)};
}
int MappedInputManager::getPressedFrontButton() const {
// Scan the raw front buttons in hardware order.
// This bypasses remapping so the remap activity can capture physical presses.
if (gpio.wasPressed(HalGPIO::BTN_BACK)) {
return HalGPIO::BTN_BACK;
}
if (gpio.wasPressed(HalGPIO::BTN_CONFIRM)) {
return HalGPIO::BTN_CONFIRM;
}
if (gpio.wasPressed(HalGPIO::BTN_LEFT)) {
return HalGPIO::BTN_LEFT;
}
if (gpio.wasPressed(HalGPIO::BTN_RIGHT)) {
return HalGPIO::BTN_RIGHT;
}
return -1;
}

View File

@ -22,6 +22,8 @@ class MappedInputManager {
bool wasAnyReleased() const;
unsigned long getHeldTime() const;
Labels mapLabels(const char* back, const char* confirm, const char* previous, const char* next) const;
// Returns the raw front button index that was pressed this frame (or -1 if none).
int getPressedFrontButton() const;
private:
HalGPIO& gpio;

View File

@ -0,0 +1,226 @@
#include "ButtonRemapActivity.h"
#include <GfxRenderer.h>
#include "CrossPointSettings.h"
#include "MappedInputManager.h"
#include "fontIds.h"
namespace {
// UI steps correspond to logical roles in order: Back, Confirm, Left, Right.
constexpr uint8_t kRoleCount = 4;
// Marker used when a role has not been assigned yet.
constexpr uint8_t kUnassigned = 0xFF;
// Duration to show temporary error text when reassigning a button.
constexpr unsigned long kErrorDisplayMs = 1500;
} // namespace
void ButtonRemapActivity::taskTrampoline(void* param) {
auto* self = static_cast<ButtonRemapActivity*>(param);
self->displayTaskLoop();
}
void ButtonRemapActivity::onEnter() {
Activity::onEnter();
renderingMutex = xSemaphoreCreateMutex();
// Start with all roles unassigned to avoid duplicate blocking.
currentStep = 0;
tempMapping[0] = kUnassigned;
tempMapping[1] = kUnassigned;
tempMapping[2] = kUnassigned;
tempMapping[3] = kUnassigned;
errorMessage.clear();
errorUntil = 0;
updateRequired = true;
xTaskCreate(&ButtonRemapActivity::taskTrampoline, "ButtonRemapTask", 4096, this, 1, &displayTaskHandle);
}
void ButtonRemapActivity::onExit() {
Activity::onExit();
// Ensure display task is stopped outside of active rendering.
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
}
void ButtonRemapActivity::loop() {
// Side buttons:
// - Up: reset mapping to defaults and exit.
// - Down: cancel without saving.
if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
// Persist default mapping immediately so the user can recover quickly.
SETTINGS.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
SETTINGS.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
SETTINGS.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
SETTINGS.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
SETTINGS.saveToFile();
onBack();
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
// Exit without changing settings.
onBack();
return;
}
// Wait for the UI to refresh before accepting another assignment.
// This avoids rapid double-presses that can advance the step without a visible redraw.
if (updateRequired) {
return;
}
// Wait for a front button press to assign to the current role.
const int pressedButton = mappedInput.getPressedFrontButton();
if (pressedButton < 0) {
return;
}
// Update temporary mapping and advance the remap step.
// Only accept the press if this hardware button isn't already assigned elsewhere.
if (!validateUnassigned(static_cast<uint8_t>(pressedButton))) {
updateRequired = true;
return;
}
tempMapping[currentStep] = static_cast<uint8_t>(pressedButton);
currentStep++;
if (currentStep >= kRoleCount) {
// All roles assigned; save to settings and exit.
applyTempMapping();
SETTINGS.saveToFile();
onBack();
return;
}
updateRequired = true;
}
[[noreturn]] void ButtonRemapActivity::displayTaskLoop() {
while (true) {
if (updateRequired) {
// Ensure render calls are serialized with UI thread changes.
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
updateRequired = false;
xSemaphoreGive(renderingMutex);
}
// Clear any temporary warning after its timeout.
if (errorUntil > 0 && millis() > errorUntil) {
errorMessage.clear();
errorUntil = 0;
updateRequired = true;
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
void ButtonRemapActivity::render() {
renderer.clearScreen();
const auto pageWidth = renderer.getScreenWidth();
const auto labelForHardware = [&](uint8_t hardwareIndex) -> const char* {
for (uint8_t i = 0; i < kRoleCount; i++) {
if (tempMapping[i] == hardwareIndex) {
return getRoleName(i);
}
}
return "-";
};
renderer.drawCenteredText(UI_12_FONT_ID, 15, "Remap Front Buttons", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(UI_10_FONT_ID, 40, "Press a front button for each role");
for (uint8_t i = 0; i < kRoleCount; i++) {
const int y = 70 + i * 30;
const bool isSelected = (i == currentStep);
// Highlight the role that is currently being assigned.
if (isSelected) {
renderer.fillRect(0, y - 2, pageWidth - 1, 30);
}
const char* roleName = getRoleName(i);
renderer.drawText(UI_10_FONT_ID, 20, y, roleName, !isSelected);
// Show currently assigned hardware button (or unassigned).
const char* assigned = (tempMapping[i] == kUnassigned) ? "Unassigned" : getHardwareName(tempMapping[i]);
const auto width = renderer.getTextWidth(UI_10_FONT_ID, assigned);
renderer.drawText(UI_10_FONT_ID, pageWidth - 20 - width, y, assigned, !isSelected);
}
// Temporary warning banner for duplicates.
if (!errorMessage.empty()) {
renderer.drawCenteredText(UI_10_FONT_ID, 210, errorMessage.c_str(), true);
}
// Provide side button actions at the bottom of the screen (split across two lines).
renderer.drawCenteredText(SMALL_FONT_ID, 250, "Side button Up: Reset to default layout", true);
renderer.drawCenteredText(SMALL_FONT_ID, 280, "Side button Down: Cancel remapping", true);
// Live preview of logical labels under front buttons.
// This mirrors the on-device front button order: Back, Confirm, Left, Right.
renderer.drawButtonHints(UI_10_FONT_ID, labelForHardware(CrossPointSettings::FRONT_HW_BACK),
labelForHardware(CrossPointSettings::FRONT_HW_CONFIRM),
labelForHardware(CrossPointSettings::FRONT_HW_LEFT),
labelForHardware(CrossPointSettings::FRONT_HW_RIGHT));
renderer.displayBuffer();
}
void ButtonRemapActivity::applyTempMapping() {
// Commit temporary mapping into settings (logical role -> hardware).
SETTINGS.frontButtonBack = tempMapping[0];
SETTINGS.frontButtonConfirm = tempMapping[1];
SETTINGS.frontButtonLeft = tempMapping[2];
SETTINGS.frontButtonRight = tempMapping[3];
}
bool ButtonRemapActivity::validateUnassigned(const uint8_t pressedButton) {
// Block reusing a hardware button already assigned to another role.
for (uint8_t i = 0; i < kRoleCount; i++) {
if (tempMapping[i] == pressedButton && i != currentStep) {
errorMessage = "Already assigned";
errorUntil = millis() + kErrorDisplayMs;
return false;
}
}
return true;
}
const char* ButtonRemapActivity::getRoleName(const uint8_t roleIndex) const {
switch (roleIndex) {
case 0:
return "Back";
case 1:
return "Confirm";
case 2:
return "Left";
case 3:
default:
return "Right";
}
}
const char* ButtonRemapActivity::getHardwareName(const uint8_t buttonIndex) const {
switch (buttonIndex) {
case CrossPointSettings::FRONT_HW_BACK:
return "Back (1st button)";
case CrossPointSettings::FRONT_HW_CONFIRM:
return "Confirm (2nd button)";
case CrossPointSettings::FRONT_HW_LEFT:
return "Left (3rd button)";
case CrossPointSettings::FRONT_HW_RIGHT:
return "Right (4th button)";
default:
return "Unknown";
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include "activities/Activity.h"
class ButtonRemapActivity final : public Activity {
public:
explicit ButtonRemapActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onBack)
: Activity("ButtonRemap", renderer, mappedInput), onBack(onBack) {}
void onEnter() override;
void onExit() override;
void loop() override;
private:
// Rendering task state.
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
bool updateRequired = false;
// Callback used to exit the remap flow back to the settings list.
const std::function<void()> onBack;
// Index of the logical role currently awaiting input.
uint8_t currentStep = 0;
// Temporary mapping from logical role -> hardware button index.
uint8_t tempMapping[4] = {0xFF, 0xFF, 0xFF, 0xFF};
// Error banner timing (used when reassigning duplicate buttons).
unsigned long errorUntil = 0;
std::string errorMessage;
// FreeRTOS task helpers.
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render();
// Commit temporary mapping to settings.
void applyTempMapping();
// Returns false if a hardware button is already assigned to a different role.
bool validateUnassigned(uint8_t pressedButton);
// Labels for UI display.
const char* getRoleName(uint8_t roleIndex) const;
const char* getHardwareName(uint8_t buttonIndex) const;
};

View File

@ -5,6 +5,7 @@
#include <cstring>
#include "ButtonRemapActivity.h"
#include "CalibreSettingsActivity.h"
#include "ClearCacheActivity.h"
#include "CrossPointSettings.h"
@ -127,6 +128,15 @@ void CategorySettingsActivity::toggleCurrentSetting() {
updateRequired = true;
}));
xSemaphoreGive(renderingMutex);
} else if (strcmp(setting.name, "Remap Front Buttons") == 0) {
// Start the button remap flow.
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new ButtonRemapActivity(renderer, mappedInput, [this] {
exitActivity();
updateRequired = true;
}));
xSemaphoreGive(renderingMutex);
}
} else {
return;
@ -186,7 +196,7 @@ void CategorySettingsActivity::render() const {
renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION),
pageHeight - 60, CROSSPOINT_VERSION);
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "", "");
const auto labels = mappedInput.mapLabels("« Back", "Toggle", "Up", "Down");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
renderer.displayBuffer();

View File

@ -40,9 +40,8 @@ const SettingInfo readerSettings[readerSettingsCount] = {
constexpr int controlsSettingsCount = 4;
const SettingInfo controlsSettings[controlsSettingsCount] = {
SettingInfo::Enum(
"Front Button Layout", &CrossPointSettings::frontButtonLayout,
{"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", "Lft, Bck, Cnfrm, Rght", "Bck, Cnfrm, Rght, Lft"}),
// Launches the remap wizard for front buttons.
SettingInfo::Action("Remap Front Buttons"),
SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout,
{"Prev, Next", "Next, Prev"}),
SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip),
@ -199,7 +198,7 @@ void SettingsActivity::render() const {
pageHeight - 60, CROSSPOINT_VERSION);
// Draw help text
const auto labels = mappedInput.mapLabels("« Back", "Select", "", "");
const auto labels = mappedInput.mapLabels("« Back", "Select", "Up", "Down");
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
// Always use standard refresh for settings screen