From 02bab00be6ca63d980921975e04e9a2aeb839fff Mon Sep 17 00:00:00 2001 From: Ronin <6971304+TheCyberRonin@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:19:34 -0400 Subject: [PATCH] fix: swap reader menu navigation direction in CCW/inverted (#2321) (#2341) Co-authored-by: Uri Tauber --- src/MappedInputManager.cpp | 24 +++++++++++++++++++++--- src/MappedInputManager.h | 18 ++++++++++++++++-- src/activities/reader/ReaderUtils.h | 7 ++----- src/main.cpp | 2 +- src/util/ButtonNavigator.h | 12 +++++------- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/MappedInputManager.cpp b/src/MappedInputManager.cpp index 81bc5d87..f8b5f0cc 100644 --- a/src/MappedInputManager.cpp +++ b/src/MappedInputManager.cpp @@ -1,7 +1,18 @@ #include "MappedInputManager.h" +#include + #include "CrossPointSettings.h" +bool MappedInputManager::isNavDirectionSwapped() const { + // Key the swap on the orientation the screen is *actually* rendered at, not the persisted reader + // setting. The reader (and its modal menus) render rotated, so navigation/labels flip there; the + // home and settings UI render in portrait, so they never flip even when a rotated reader is configured. + const auto orientation = renderer.getOrientation(); + return SETTINGS.frontButtonFollowOrientation && + (orientation == GfxRenderer::PortraitInverted || orientation == GfxRenderer::LandscapeCounterClockwise); +} + bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint8_t) const) const { const auto sideLayout = SETTINGS.sideButtonLayout; @@ -49,6 +60,15 @@ bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint default: return false; } + case Button::NavNext: + // Logical "next item" navigation: side Down + front Right, with the control axis flipped in + // INVERTED / LANDSCAPE_CCW (frontButtonFollowOrientation) so it matches the rotated hint labels. + return isNavDirectionSwapped() ? (mapButton(Button::Up, fn) || mapButton(Button::Left, fn)) + : (mapButton(Button::Down, fn) || mapButton(Button::Right, fn)); + case Button::NavPrevious: + // Logical "previous item" navigation: side Up + front Left, axis-flipped in the same orientations. + return isNavDirectionSwapped() ? (mapButton(Button::Down, fn) || mapButton(Button::Right, fn)) + : (mapButton(Button::Up, fn) || mapButton(Button::Left, fn)); } return false; @@ -69,9 +89,7 @@ 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 { // Swap previous/next labels to match the page turn direction swap in INVERTED and LANDSCAPE_CCW. - const bool swapLabels = - SETTINGS.frontButtonFollowOrientation && (SETTINGS.orientation == CrossPointSettings::INVERTED || - SETTINGS.orientation == CrossPointSettings::LANDSCAPE_CCW); + const bool swapLabels = isNavDirectionSwapped(); const char* leftLabel = swapLabels ? next : previous; const char* rightLabel = swapLabels ? previous : next; diff --git a/src/MappedInputManager.h b/src/MappedInputManager.h index 67f094cc..89e6b862 100644 --- a/src/MappedInputManager.h +++ b/src/MappedInputManager.h @@ -2,9 +2,11 @@ #include +class GfxRenderer; + class MappedInputManager { public: - enum class Button { Back, Confirm, Left, Right, Up, Down, Power, PageBack, PageForward }; + enum class Button { Back, Confirm, Left, Right, Up, Down, Power, PageBack, PageForward, NavNext, NavPrevious }; struct Labels { const char* btn1; @@ -13,7 +15,7 @@ class MappedInputManager { const char* btn4; }; - explicit MappedInputManager(HalGPIO& gpio) : gpio(gpio) {} + MappedInputManager(HalGPIO& gpio, const GfxRenderer& renderer) : gpio(gpio), renderer(renderer) {} void update() const { gpio.update(); } bool wasPressed(Button button) const; @@ -26,8 +28,20 @@ class MappedInputManager { // Returns the raw front button index that was pressed this frame (or -1 if none). int getPressedFrontButton() const; + // True when the control axis is flipped relative to the physical buttons: the user opted into + // orientation-following front buttons AND the screen is *currently rendered* rotated (INVERTED / + // LANDSCAPE_CCW). Keyed on the live renderer orientation rather than the persisted reader setting, + // so portrait UI (home, settings) never swaps while the reader and its menus do. + [[nodiscard]] bool isNavDirectionSwapped() const; + private: HalGPIO& gpio; + // Logical-to-physical button mapping depends on what the user is actually looking at: when the + // screen is rendered rotated, the directional buttons must flip to match. The renderer is the only + // authority on the *live* orientation (the reader rotates it and restores portrait on exit), so we + // read it here instead of CrossPointSettings.orientation, which is just the persisted reader + // preference and stays "rotated" even while portrait UI like home/settings is on screen. + const GfxRenderer& renderer; bool mapButton(Button button, bool (HalGPIO::*fn)(uint8_t) const) const; }; diff --git a/src/activities/reader/ReaderUtils.h b/src/activities/reader/ReaderUtils.h index 9d0a4aba..10b4cbe5 100644 --- a/src/activities/reader/ReaderUtils.h +++ b/src/activities/reader/ReaderUtils.h @@ -43,11 +43,8 @@ inline PageTurnResult detectPageTurn(const MappedInputManager& input) { const bool usePress = SETTINGS.longPressButtonBehavior == SETTINGS.OFF; const bool tiltNext = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedForward(); const bool tiltPrev = SETTINGS.tiltPageTurn && halTiltSensor.wasTiltedBack(); - const bool swapFront = - SETTINGS.frontButtonFollowOrientation && (SETTINGS.orientation == CrossPointSettings::INVERTED || - SETTINGS.orientation == CrossPointSettings::LANDSCAPE_CCW); - const auto prevButton = swapFront ? MappedInputManager::Button::Right : MappedInputManager::Button::Left; - const auto nextButton = swapFront ? MappedInputManager::Button::Left : MappedInputManager::Button::Right; + const auto prevButton = MappedInputManager::Button::NavPrevious; + const auto nextButton = MappedInputManager::Button::NavNext; const bool prev = tiltPrev || (usePress ? (input.wasPressed(MappedInputManager::Button::PageBack) || input.wasPressed(prevButton)) diff --git a/src/main.cpp b/src/main.cpp index cb1d9b23..13ba129b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,8 +34,8 @@ #include "util/ButtonNavigator.h" #include "util/ScreenshotUtil.h" -MappedInputManager mappedInputManager(gpio); GfxRenderer renderer(display); +MappedInputManager mappedInputManager(gpio, renderer); ActivityManager activityManager(renderer, mappedInputManager); FontDecompressor fontDecompressor; SdCardFontSystem sdFontSystem; diff --git a/src/util/ButtonNavigator.h b/src/util/ButtonNavigator.h index 2f9afbc1..12b36c79 100644 --- a/src/util/ButtonNavigator.h +++ b/src/util/ButtonNavigator.h @@ -44,10 +44,8 @@ class ButtonNavigator final { [[nodiscard]] static int nextPageIndex(int currentIndex, int totalItems, int itemsPerPage); [[nodiscard]] static int previousPageIndex(int currentIndex, int totalItems, int itemsPerPage); - [[nodiscard]] static Buttons getNextButtons() { - return {MappedInputManager::Button::Down, MappedInputManager::Button::Right}; - } - [[nodiscard]] static Buttons getPreviousButtons() { - return {MappedInputManager::Button::Up, MappedInputManager::Button::Left}; - } -}; \ No newline at end of file + // Navigation uses the logical NavNext / NavPrevious buttons; MappedInputManager::mapButton resolves + // them to physical buttons and applies any orientation-based direction swap, so this stays settings-free. + [[nodiscard]] static Buttons getNextButtons() { return {MappedInputManager::Button::NavNext}; } + [[nodiscard]] static Buttons getPreviousButtons() { return {MappedInputManager::Button::NavPrevious}; } +};