diff --git a/USER_GUIDE.md b/USER_GUIDE.md index d670abb7..6c6ebd90 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -116,6 +116,7 @@ The Settings screen allows you to configure the device's behavior. There are a f - Back, Confirm, Left, Right (default) - Left, Right, Back, Confirm - Left, Back, Confirm, Right + - Back, Left, Right, Confirm - **Side Button Layout (reader)**: Swap the order of the up and down volume buttons from Previous/Next to Next/Previous. This change is only in effect when reading. - **Long-press Chapter Skip**: Set whether long-pressing page turn buttons skip to the next/previous chapter. - "Chapter Skip" (default) - Long-pressing skips to next/previous chapter diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 8ce32a2c..64fe1ce1 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -32,7 +32,13 @@ class CrossPointSettings { // Front button layout options // Default: Back, Confirm, Left, Right // Swapped: Left, Right, Back, Confirm - enum FRONT_BUTTON_LAYOUT { BACK_CONFIRM_LEFT_RIGHT = 0, LEFT_RIGHT_BACK_CONFIRM = 1, LEFT_BACK_CONFIRM_RIGHT = 2 }; + // Additional: Back, Left, Right, Confirm (navigation buttons centered) + enum FRONT_BUTTON_LAYOUT { + BACK_CONFIRM_LEFT_RIGHT = 0, + LEFT_RIGHT_BACK_CONFIRM = 1, + LEFT_BACK_CONFIRM_RIGHT = 2, + BACK_LEFT_RIGHT_CONFIRM = 3 + }; // Side button layout options // Default: Previous, Next diff --git a/src/MappedInputManager.cpp b/src/MappedInputManager.cpp index 1b038446..f628027e 100644 --- a/src/MappedInputManager.cpp +++ b/src/MappedInputManager.cpp @@ -2,99 +2,124 @@ #include "CrossPointSettings.h" -decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button button) const { - const auto frontLayout = static_cast(SETTINGS.frontButtonLayout); - const auto sideLayout = static_cast(SETTINGS.sideButtonLayout); +decltype(InputManager::BTN_BACK) +MappedInputManager::mapButton(const Button button) const { + const auto frontLayout = static_cast( + SETTINGS.frontButtonLayout); + const auto sideLayout = static_cast( + SETTINGS.sideButtonLayout); switch (button) { - case Button::Back: - switch (frontLayout) { - case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_LEFT; - case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_CONFIRM; - case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: - default: - return InputManager::BTN_BACK; - } - case Button::Confirm: - switch (frontLayout) { - case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_RIGHT; - case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_LEFT; - case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: - default: - return InputManager::BTN_CONFIRM; - } - case Button::Left: - switch (frontLayout) { - case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - return InputManager::BTN_BACK; - case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: - default: - return InputManager::BTN_LEFT; - } - case Button::Right: - switch (frontLayout) { - case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: - return InputManager::BTN_CONFIRM; - case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: - case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: - default: - return InputManager::BTN_RIGHT; - } - case Button::Up: - return InputManager::BTN_UP; - case Button::Down: + case Button::Back: + switch (frontLayout) { + case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: + return InputManager::BTN_LEFT; + case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: + return InputManager::BTN_CONFIRM; + case CrossPointSettings::BACK_LEFT_RIGHT_CONFIRM: + case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: + default: + return InputManager::BTN_BACK; + } + case Button::Confirm: + switch (frontLayout) { + case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: + case CrossPointSettings::BACK_LEFT_RIGHT_CONFIRM: + return InputManager::BTN_RIGHT; + case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: + return InputManager::BTN_LEFT; + case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: + default: + return InputManager::BTN_CONFIRM; + } + case Button::Left: + switch (frontLayout) { + case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: + case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: + return InputManager::BTN_BACK; + case CrossPointSettings::BACK_LEFT_RIGHT_CONFIRM: + return InputManager::BTN_CONFIRM; + case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: + default: + return InputManager::BTN_LEFT; + } + case Button::Right: + switch (frontLayout) { + case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM: + return InputManager::BTN_CONFIRM; + case CrossPointSettings::BACK_LEFT_RIGHT_CONFIRM: + return InputManager::BTN_LEFT; + case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: + case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT: + default: + return InputManager::BTN_RIGHT; + } + case Button::Up: + return InputManager::BTN_UP; + case Button::Down: + return InputManager::BTN_DOWN; + case Button::Power: + return InputManager::BTN_POWER; + case Button::PageBack: + switch (sideLayout) { + case CrossPointSettings::NEXT_PREV: return InputManager::BTN_DOWN; - case Button::Power: - return InputManager::BTN_POWER; - case Button::PageBack: - switch (sideLayout) { - case CrossPointSettings::NEXT_PREV: - return InputManager::BTN_DOWN; - case CrossPointSettings::PREV_NEXT: - default: - return InputManager::BTN_UP; - } - case Button::PageForward: - switch (sideLayout) { - case CrossPointSettings::NEXT_PREV: - return InputManager::BTN_UP; - case CrossPointSettings::PREV_NEXT: - default: - return InputManager::BTN_DOWN; - } + case CrossPointSettings::PREV_NEXT: + default: + return InputManager::BTN_UP; + } + case Button::PageForward: + switch (sideLayout) { + case CrossPointSettings::NEXT_PREV: + return InputManager::BTN_UP; + case CrossPointSettings::PREV_NEXT: + default: + return InputManager::BTN_DOWN; + } } return InputManager::BTN_BACK; } -bool MappedInputManager::wasPressed(const Button button) const { return inputManager.wasPressed(mapButton(button)); } +bool MappedInputManager::wasPressed(const Button button) const { + return inputManager.wasPressed(mapButton(button)); +} -bool MappedInputManager::wasReleased(const Button button) const { return inputManager.wasReleased(mapButton(button)); } +bool MappedInputManager::wasReleased(const Button button) const { + return inputManager.wasReleased(mapButton(button)); +} -bool MappedInputManager::isPressed(const Button button) const { return inputManager.isPressed(mapButton(button)); } +bool MappedInputManager::isPressed(const Button button) const { + return inputManager.isPressed(mapButton(button)); +} -bool MappedInputManager::wasAnyPressed() const { return inputManager.wasAnyPressed(); } +bool MappedInputManager::wasAnyPressed() const { + return inputManager.wasAnyPressed(); +} -bool MappedInputManager::wasAnyReleased() const { return inputManager.wasAnyReleased(); } +bool MappedInputManager::wasAnyReleased() const { + return inputManager.wasAnyReleased(); +} -unsigned long MappedInputManager::getHeldTime() const { return inputManager.getHeldTime(); } +unsigned long MappedInputManager::getHeldTime() const { + return inputManager.getHeldTime(); +} -MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous, - const char* next) const { - const auto layout = static_cast(SETTINGS.frontButtonLayout); +MappedInputManager::Labels +MappedInputManager::mapLabels(const char *back, const char *confirm, + const char *previous, const char *next) const { + const auto layout = static_cast( + 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_LEFT_RIGHT: - default: - return {back, confirm, previous, next}; + 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_LEFT_RIGHT_CONFIRM: + return {back, previous, next, confirm}; + case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT: + default: + return {back, confirm, previous, next}; } } diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 943fdb4c..786389a5 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -8,52 +8,75 @@ #include "MappedInputManager.h" #include "fontIds.h" -const char* SettingsActivity::categoryNames[categoryCount] = {"Display", "Reader", "Controls", "System"}; +const char *SettingsActivity::categoryNames[categoryCount] = { + "Display", "Reader", "Controls", "System"}; namespace { constexpr int displaySettingsCount = 5; const SettingInfo displaySettings[displaySettingsCount] = { // Should match with SLEEP_SCREEN_MODE - SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}), - SettingInfo::Enum("Sleep Screen Cover Mode", &CrossPointSettings::sleepScreenCoverMode, {"Fit", "Crop"}), - SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, {"None", "No Progress", "Full"}), - SettingInfo::Enum("Hide Battery %", &CrossPointSettings::hideBatteryPercentage, {"Never", "In Reader", "Always"}), - SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency, - {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})}; + SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, + {"Dark", "Light", "Custom", "Cover", "None"}), + SettingInfo::Enum("Sleep Screen Cover Mode", + &CrossPointSettings::sleepScreenCoverMode, + {"Fit", "Crop"}), + SettingInfo::Enum("Status Bar", &CrossPointSettings::statusBar, + {"None", "No Progress", "Full"}), + SettingInfo::Enum("Hide Battery %", + &CrossPointSettings::hideBatteryPercentage, + {"Never", "In Reader", "Always"}), + SettingInfo::Enum( + "Refresh Frequency", &CrossPointSettings::refreshFrequency, + {"1 page", "5 pages", "10 pages", "15 pages", "30 pages"})}; constexpr int readerSettingsCount = 9; const SettingInfo readerSettings[readerSettingsCount] = { - SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, {"Bookerly", "Noto Sans", "Open Dyslexic"}), - SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}), - SettingInfo::Enum("Line Spacing", &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}), - SettingInfo::Value("Screen Margin", &CrossPointSettings::screenMargin, {5, 40, 5}), - SettingInfo::Enum("Paragraph Alignment", &CrossPointSettings::paragraphAlignment, + SettingInfo::Enum("Font Family", &CrossPointSettings::fontFamily, + {"Bookerly", "Noto Sans", "Open Dyslexic"}), + SettingInfo::Enum("Font Size", &CrossPointSettings::fontSize, + {"Small", "Medium", "Large", "X Large"}), + SettingInfo::Enum("Line Spacing", &CrossPointSettings::lineSpacing, + {"Tight", "Normal", "Wide"}), + SettingInfo::Value("Screen Margin", &CrossPointSettings::screenMargin, + {5, 40, 5}), + SettingInfo::Enum("Paragraph Alignment", + &CrossPointSettings::paragraphAlignment, {"Justify", "Left", "Center", "Right"}), SettingInfo::Toggle("Hyphenation", &CrossPointSettings::hyphenationEnabled), - SettingInfo::Enum("Reading Orientation", &CrossPointSettings::orientation, - {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}), - SettingInfo::Toggle("Extra Paragraph Spacing", &CrossPointSettings::extraParagraphSpacing), - SettingInfo::Toggle("Text Anti-Aliasing", &CrossPointSettings::textAntiAliasing)}; + SettingInfo::Enum( + "Reading Orientation", &CrossPointSettings::orientation, + {"Portrait", "Landscape CW", "Inverted", "Landscape CCW"}), + SettingInfo::Toggle("Extra Paragraph Spacing", + &CrossPointSettings::extraParagraphSpacing), + SettingInfo::Toggle("Text Anti-Aliasing", + &CrossPointSettings::textAntiAliasing)}; 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"}), - SettingInfo::Enum("Side Button Layout (reader)", &CrossPointSettings::sideButtonLayout, + SettingInfo::Enum("Front Button Layout", + &CrossPointSettings::frontButtonLayout, + {"Bck, Cnfrm, Lft, Rght", "Lft, Rght, Bck, Cnfrm", + "Lft, Bck, Cnfrm, Rght", "Bck, Lft, Rght, Cnfrm"}), + SettingInfo::Enum("Side Button Layout (reader)", + &CrossPointSettings::sideButtonLayout, {"Prev, Next", "Next, Prev"}), - SettingInfo::Toggle("Long-press Chapter Skip", &CrossPointSettings::longPressChapterSkip), - SettingInfo::Enum("Short Power Button Click", &CrossPointSettings::shortPwrBtn, {"Ignore", "Sleep", "Page Turn"})}; + SettingInfo::Toggle("Long-press Chapter Skip", + &CrossPointSettings::longPressChapterSkip), + SettingInfo::Enum("Short Power Button Click", + &CrossPointSettings::shortPwrBtn, + {"Ignore", "Sleep", "Page Turn"})}; constexpr int systemSettingsCount = 5; const SettingInfo systemSettings[systemSettingsCount] = { SettingInfo::Enum("Time to Sleep", &CrossPointSettings::sleepTimeout, {"1 min", "5 min", "10 min", "15 min", "30 min"}), - SettingInfo::Action("KOReader Sync"), SettingInfo::Action("Calibre Settings"), SettingInfo::Action("Clear Cache"), + SettingInfo::Action("KOReader Sync"), + SettingInfo::Action("Calibre Settings"), SettingInfo::Action("Clear Cache"), SettingInfo::Action("Check for updates")}; -} // namespace +} // namespace -void SettingsActivity::taskTrampoline(void* param) { - auto* self = static_cast(param); +void SettingsActivity::taskTrampoline(void *param) { + auto *self = static_cast(param); self->displayTaskLoop(); } @@ -68,17 +91,18 @@ void SettingsActivity::onEnter() { updateRequired = true; xTaskCreate(&SettingsActivity::taskTrampoline, "SettingsActivityTask", - 4096, // Stack size - this, // Parameters - 1, // Priority - &displayTaskHandle // Task handle + 4096, // Stack size + this, // Parameters + 1, // Priority + &displayTaskHandle // Task handle ); } void SettingsActivity::onExit() { ActivityWithSubactivity::onExit(); - // Wait until not rendering to delete task to avoid killing mid-instruction to EPD + // Wait until not rendering to delete task to avoid killing mid-instruction to + // EPD xSemaphoreTake(renderingMutex, portMAX_DELAY); if (displayTaskHandle) { vTaskDelete(displayTaskHandle); @@ -110,12 +134,16 @@ void SettingsActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Up) || mappedInput.wasPressed(MappedInputManager::Button::Left)) { // Move selection up (with wrap-around) - selectedCategoryIndex = (selectedCategoryIndex > 0) ? (selectedCategoryIndex - 1) : (categoryCount - 1); + selectedCategoryIndex = (selectedCategoryIndex > 0) + ? (selectedCategoryIndex - 1) + : (categoryCount - 1); updateRequired = true; } else if (mappedInput.wasPressed(MappedInputManager::Button::Down) || mappedInput.wasPressed(MappedInputManager::Button::Right)) { // Move selection down (with wrap around) - selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) ? (selectedCategoryIndex + 1) : 0; + selectedCategoryIndex = (selectedCategoryIndex < categoryCount - 1) + ? (selectedCategoryIndex + 1) + : 0; updateRequired = true; } } @@ -128,33 +156,34 @@ void SettingsActivity::enterCategory(int categoryIndex) { xSemaphoreTake(renderingMutex, portMAX_DELAY); exitActivity(); - const SettingInfo* settingsList = nullptr; + const SettingInfo *settingsList = nullptr; int settingsCount = 0; switch (categoryIndex) { - case 0: // Display - settingsList = displaySettings; - settingsCount = displaySettingsCount; - break; - case 1: // Reader - settingsList = readerSettings; - settingsCount = readerSettingsCount; - break; - case 2: // Controls - settingsList = controlsSettings; - settingsCount = controlsSettingsCount; - break; - case 3: // System - settingsList = systemSettings; - settingsCount = systemSettingsCount; - break; + case 0: // Display + settingsList = displaySettings; + settingsCount = displaySettingsCount; + break; + case 1: // Reader + settingsList = readerSettings; + settingsCount = readerSettingsCount; + break; + case 2: // Controls + settingsList = controlsSettings; + settingsCount = controlsSettingsCount; + break; + case 3: // System + settingsList = systemSettings; + settingsCount = systemSettingsCount; + break; } - enterNewActivity(new CategorySettingsActivity(renderer, mappedInput, categoryNames[categoryIndex], settingsList, - settingsCount, [this] { - exitActivity(); - updateRequired = true; - })); + enterNewActivity(new CategorySettingsActivity( + renderer, mappedInput, categoryNames[categoryIndex], settingsList, + settingsCount, [this] { + exitActivity(); + updateRequired = true; + })); xSemaphoreGive(renderingMutex); } @@ -177,26 +206,31 @@ void SettingsActivity::render() const { const auto pageHeight = renderer.getScreenHeight(); // Draw header - renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, EpdFontFamily::BOLD); + renderer.drawCenteredText(UI_12_FONT_ID, 15, "Settings", true, + EpdFontFamily::BOLD); // Draw selection renderer.fillRect(0, 60 + selectedCategoryIndex * 30 - 2, pageWidth - 1, 30); // Draw all categories for (int i = 0; i < categoryCount; i++) { - const int categoryY = 60 + i * 30; // 30 pixels between categories + const int categoryY = 60 + i * 30; // 30 pixels between categories // Draw category name - renderer.drawText(UI_10_FONT_ID, 20, categoryY, categoryNames[i], i != selectedCategoryIndex); + renderer.drawText(UI_10_FONT_ID, 20, categoryY, categoryNames[i], + i != selectedCategoryIndex); } // Draw version text above button hints - renderer.drawText(SMALL_FONT_ID, pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), - pageHeight - 60, CROSSPOINT_VERSION); + renderer.drawText( + SMALL_FONT_ID, + pageWidth - 20 - renderer.getTextWidth(SMALL_FONT_ID, CROSSPOINT_VERSION), + pageHeight - 60, CROSSPOINT_VERSION); // Draw help text const auto labels = mappedInput.mapLabels("« Back", "Select", "", ""); - renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, + labels.btn4); // Always use standard refresh for settings screen renderer.displayBuffer();