diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index a7a51350..9ca0b16f 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -615,3 +615,4 @@ STR_KB_HINT_LOWER_SECONDARY: "Hold SELECT for lowercase or secondary char" STR_KB_HINT_URL_SNIPPETS: "Press URL for snippets" STR_BIONIC_READING: "Bionic Reading" STR_BTN_ACT_TOGGLE_BIONIC_READING: "Toggle Bionic Reading" +STR_BTN_ACT_CYCLE_FONT_SIZE: "Cycle Font Size" diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 74fe6915..d7da625b 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -287,8 +287,9 @@ class CrossPointSettings { BTN_PREV_SECTION, BTN_EXIT_READER, BTN_READER_MENU, - BTN_KOREADER_SYNC, BTN_TOGGLE_BIONIC_READING, + BTN_KOREADER_SYNC, + BTN_CYCLE_FONT_SIZE, BUTTON_ACTION_COUNT }; diff --git a/src/SettingsList.h b/src/SettingsList.h index a4639368..903fcda0 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -140,13 +140,14 @@ inline const std::vector list = { // All entries share the same ordered action-label list; the submenu groups them behind // a single placeholder row in the device UI. // Shared action options (everything except the first "default" entry). -#define BTN_ACT_OPTIONS \ - StrId::STR_BTN_ACT_PAGE_FORWARD, StrId::STR_BTN_ACT_PAGE_BACK, StrId::STR_BTN_ACT_PAGE_FORWARD_10, \ - StrId::STR_BTN_ACT_PAGE_BACK_10, StrId::STR_BTN_ACT_GO_HOME, StrId::STR_BTN_ACT_SLEEP, \ - StrId::STR_BTN_ACT_FORCE_REFRESH, StrId::STR_BTN_ACT_FORCE_FAST_REFRESH, StrId::STR_BTN_ACT_OPEN_TOC, \ - StrId::STR_BTN_ACT_OPEN_BOOKMARKS, StrId::STR_BTN_ACT_STAR_PAGE, StrId::STR_BTN_ACT_FOOTNOTES, \ - StrId::STR_BTN_ACT_NEXT_SECTION, StrId::STR_BTN_ACT_PREV_SECTION, StrId::STR_BTN_ACT_EXIT_READER, \ - StrId::STR_BTN_ACT_READER_MENU, StrId::STR_BTN_ACT_TOGGLE_BIONIC_READING, StrId::STR_BTN_ACT_KOREADER_SYNC +#define BTN_ACT_OPTIONS \ + StrId::STR_BTN_ACT_PAGE_FORWARD, StrId::STR_BTN_ACT_PAGE_BACK, StrId::STR_BTN_ACT_PAGE_FORWARD_10, \ + StrId::STR_BTN_ACT_PAGE_BACK_10, StrId::STR_BTN_ACT_GO_HOME, StrId::STR_BTN_ACT_SLEEP, \ + StrId::STR_BTN_ACT_FORCE_REFRESH, StrId::STR_BTN_ACT_FORCE_FAST_REFRESH, StrId::STR_BTN_ACT_OPEN_TOC, \ + StrId::STR_BTN_ACT_OPEN_BOOKMARKS, StrId::STR_BTN_ACT_STAR_PAGE, StrId::STR_BTN_ACT_FOOTNOTES, \ + StrId::STR_BTN_ACT_NEXT_SECTION, StrId::STR_BTN_ACT_PREV_SECTION, StrId::STR_BTN_ACT_EXIT_READER, \ + StrId::STR_BTN_ACT_READER_MENU, StrId::STR_BTN_ACT_TOGGLE_BIONIC_READING, StrId::STR_BTN_ACT_KOREADER_SYNC, \ + StrId::STR_BTN_ACT_CYCLE_FONT_SIZE // Back button: short=exit reader, double=ignore, long=go home SettingInfo::Enum(StrId::STR_BTN_SHORT_PRESS, &CrossPointSettings::btnShortBack, {StrId::STR_BTN_DEF_EXIT_READER}, diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index e68ac22b..4d2f14c3 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -2006,6 +2006,16 @@ void EpubReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION requestUpdate(); } break; + case BA::BTN_CYCLE_FONT_SIZE: + if (epub) { + const uint8_t current = + (bookFontSizeOverride >= 0) ? static_cast(bookFontSizeOverride) : SETTINGS.fontSize; + const int8_t next = static_cast((current + 1) % CrossPointSettings::FONT_SIZE_COUNT); + applyBookReaderOverrides(bookEmbeddedStyleOverride, bookImageRenderingOverride, bookFontFamilyOverride, + bookSdFontFamilyOverride, next, bookBionicReadingOverride); + requestUpdate(); + } + break; case BA::BTN_KOREADER_SYNC: launchKOReaderSync(SyncLaunchMode::COMPARE); break; diff --git a/src/main.cpp b/src/main.cpp index bdb0f666..59af5978 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -539,6 +539,12 @@ void loop() { case BA::BTN_KOREADER_SYNC: activityManager.dispatchButtonAction(BA::BTN_KOREADER_SYNC); break; + case BA::BTN_TOGGLE_BIONIC_READING: + activityManager.dispatchButtonAction(BA::BTN_TOGGLE_BIONIC_READING); + break; + case BA::BTN_CYCLE_FONT_SIZE: + activityManager.dispatchButtonAction(BA::BTN_CYCLE_FONT_SIZE); + break; default: break; } diff --git a/src/util/ButtonNavigator.cpp b/src/util/ButtonNavigator.cpp index ab24130d..ede0c06b 100644 --- a/src/util/ButtonNavigator.cpp +++ b/src/util/ButtonNavigator.cpp @@ -1,6 +1,7 @@ #include "ButtonNavigator.h" #include "ButtonEventManager.h" +#include "activities/ActivityManager.h" const MappedInputManager* ButtonNavigator::mappedInput = nullptr; @@ -42,15 +43,25 @@ void ButtonNavigator::onPress(const Buttons& buttons, const Callback& callback) } void ButtonNavigator::onRelease(const Buttons& buttons, const Callback& callback) { - const bool wasReleased = std::any_of(buttons.begin(), buttons.end(), [](const MappedInputManager::Button button) { - if (mappedInput == nullptr || !mappedInput->wasReleased(button)) { - return false; - } - - // If a button Short is still pending while we wait for a possible double, - // avoid firing release-based navigation first. - return !globalButtonEvents().isShortPending(button); - }); + // The double-click FSM in ButtonEventManager delays Short events by DOUBLE_WINDOW_MS + // (300ms) when a double-press action is configured for that button, so the configured + // Short and Double actions can be disambiguated. In a reader activity that gating must + // suppress release-based navigation during the wait, otherwise Left/Right would both + // turn the page AND fire the configured short action. + // + // In non-reader UIs (settings, file browser, etc.), only navigation reacts to release — + // the configured per-button actions are not dispatched there (see + // ActivityManager::dispatchButtonAction, which is reader-only). Gating on isShortPending + // there just makes Left/Right navigation feel sluggish (300ms lag) compared to Up/Down + // (which have no FSM at all). So skip the gate outside reader activities. + const bool inReader = activityManager.isReaderActivity(); + const bool wasReleased = + std::any_of(buttons.begin(), buttons.end(), [inReader](const MappedInputManager::Button button) { + if (mappedInput == nullptr || !mappedInput->wasReleased(button)) { + return false; + } + return !(inReader && globalButtonEvents().isShortPending(button)); + }); if (wasReleased) { if (lastContinuousNavTime == 0) {