From 727186f20820f9f5b12376f89df1803aa57884d8 Mon Sep 17 00:00:00 2001 From: Istiak Tridip <13367189+istiak-tridip@users.noreply.github.com> Date: Wed, 28 Jan 2026 02:22:00 +0600 Subject: [PATCH] feat: ButtonNavigator class --- src/main.cpp | 2 + src/util/ButtonNavigator.cpp | 85 ++++++++++++++++++++++++++++++++++++ src/util/ButtonNavigator.h | 47 ++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/util/ButtonNavigator.cpp create mode 100644 src/util/ButtonNavigator.h diff --git a/src/main.cpp b/src/main.cpp index 2308f0a2..a726e78a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ #include "activities/settings/SettingsActivity.h" #include "activities/util/FullScreenMessageActivity.h" #include "fontIds.h" +#include "util/ButtonNavigator.h" HalDisplay display; HalGPIO gpio; @@ -293,6 +294,7 @@ void setup() { SETTINGS.loadFromFile(); KOREADER_STORE.loadFromFile(); + ButtonNavigator::setMappedInputManager(mappedInputManager); if (gpio.isWakeupByPowerButton()) { // For normal wakeups, verify power button press duration diff --git a/src/util/ButtonNavigator.cpp b/src/util/ButtonNavigator.cpp new file mode 100644 index 00000000..f7006ca5 --- /dev/null +++ b/src/util/ButtonNavigator.cpp @@ -0,0 +1,85 @@ +#include "ButtonNavigator.h" + +const MappedInputManager* ButtonNavigator::mappedInput = nullptr; + +void ButtonNavigator::onNext(const Callback& callback) { + onNextPress(callback); + onNextContinuous(callback); +} +void ButtonNavigator::onPrevious(const Callback& callback) { + onPreviousPress(callback); + onPreviousContinuous(callback); +} + +void ButtonNavigator::onNextPress(const Callback& callback) { onPress(getNextButtons(), callback); } + +void ButtonNavigator::onPreviousPress(const Callback& callback) { onPress(getPreviousButtons(), callback); } + +void ButtonNavigator::onNextContinuous(const Callback& callback) { onContinuous(getNextButtons(), callback); } + +void ButtonNavigator::onPreviousContinuous(const Callback& callback) { onContinuous(getPreviousButtons(), callback); } + +void ButtonNavigator::onPress(const Buttons& buttons, const Callback& callback) { + if (!mappedInput) return; + + bool buttonPressed = false; + for (const MappedInputManager::Button button : buttons) { + if (mappedInput->wasPressed(button)) { + buttonPressed = true; + break; + } + } + + if (buttonPressed && !recentlyNavigatedContinuously()) { + callback(); + } +} + +void ButtonNavigator::onContinuous(const Buttons& buttons, const Callback& callback) { + if (!mappedInput) return; + + bool buttonPressed = false; + for (const MappedInputManager::Button button : buttons) { + if (mappedInput->isPressed(button)) { + buttonPressed = true; + break; + } + } + + if (buttonPressed && shouldNavigateContinuously()) { + callback(); + lastContinuousNavTime = millis(); + } +} + +bool ButtonNavigator::shouldNavigateContinuously() const { + if (!mappedInput) return false; + + const bool buttonHeldLongEnough = mappedInput->getHeldTime() > continuousStartMs; + const bool navigationIntervalElapsed = (millis() - lastContinuousNavTime) > continuousIntervalMs; + + return buttonHeldLongEnough && navigationIntervalElapsed; +} + +bool ButtonNavigator::recentlyNavigatedContinuously() const { + const int elapsedTime = millis() - lastContinuousNavTime; + if (elapsedTime < 50) { + return true; + } + + return false; +} + +int ButtonNavigator::nextIndex(const int currentIndex, const int totalItems) { + if (totalItems <= 0) return 0; + + // Calculate the next index with wrap-around + return (currentIndex + 1) % totalItems; +} + +int ButtonNavigator::previousIndex(const int currentIndex, const int totalItems) { + if (totalItems <= 0) return 0; + + // Calculate the previous index with wrap-around + return (currentIndex + totalItems - 1) % totalItems; +} \ No newline at end of file diff --git a/src/util/ButtonNavigator.h b/src/util/ButtonNavigator.h new file mode 100644 index 00000000..14a1d928 --- /dev/null +++ b/src/util/ButtonNavigator.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include "MappedInputManager.h" + +class ButtonNavigator final { + using Callback = std::function; + using Buttons = std::vector; + + const uint16_t continuousStartMs; + const uint16_t continuousIntervalMs; + uint32_t lastContinuousNavTime = 0; + + static const MappedInputManager* mappedInput; + + [[nodiscard]] bool shouldNavigateContinuously() const; + [[nodiscard]] bool recentlyNavigatedContinuously() const; + + [[nodiscard]] static Buttons getNextButtons() { + return {MappedInputManager::Button::Down, MappedInputManager::Button::Right}; + } + [[nodiscard]] static Buttons getPreviousButtons() { + return {MappedInputManager::Button::Up, MappedInputManager::Button::Left}; + } + + public: + explicit ButtonNavigator(const uint16_t continuousIntervalMs = 500, const uint16_t continuousStartMs = 500) + : continuousStartMs(continuousStartMs), continuousIntervalMs(continuousIntervalMs) {} + + static void setMappedInputManager(const MappedInputManager& mappedInputManager) { mappedInput = &mappedInputManager; } + + void onNext(const Callback& callback); + void onPrevious(const Callback& callback); + + void onNextPress(const Callback& callback); + void onPreviousPress(const Callback& callback); + void onPress(const Buttons& buttons, const Callback& callback); + + void onNextContinuous(const Callback& callback); + void onPreviousContinuous(const Callback& callback); + void onContinuous(const Buttons& buttons, const Callback& callback); + + [[nodiscard]] static int nextIndex(int currentIndex, int totalItems); + [[nodiscard]] static int previousIndex(int currentIndex, int totalItems); +}; \ No newline at end of file