mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
Compare commits
16 Commits
4f41ff9642
...
5f7be312d6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f7be312d6 | ||
|
|
d403044f76 | ||
|
|
66e69e750e | ||
|
|
45bba9a822 | ||
|
|
cebfc54266 | ||
|
|
fe119239ad | ||
|
|
a6b4ed5cf9 | ||
|
|
91313d8505 | ||
|
|
3c727dd8f9 | ||
|
|
bde92c288f | ||
|
|
3d9c3bf101 | ||
|
|
b7b36f02e2 | ||
|
|
73c5f05843 | ||
|
|
178c826b52 | ||
|
|
7aa21f2386 | ||
|
|
727186f208 |
@ -17,7 +17,6 @@
|
||||
|
||||
namespace {
|
||||
constexpr int PAGE_ITEMS = 23;
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
} // namespace
|
||||
|
||||
void OpdsBookBrowserActivity::taskTrampoline(void* param) {
|
||||
@ -118,12 +117,6 @@ void OpdsBookBrowserActivity::loop() {
|
||||
|
||||
// Handle browsing state
|
||||
if (state == BrowserState::BROWSING) {
|
||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (!entries.empty()) {
|
||||
const auto& entry = entries[selectorIndex];
|
||||
@ -135,20 +128,29 @@ void OpdsBookBrowserActivity::loop() {
|
||||
}
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
navigateBack();
|
||||
} else if (prevReleased && !entries.empty()) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / PAGE_ITEMS - 1) * PAGE_ITEMS + entries.size()) % entries.size();
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + entries.size() - 1) % entries.size();
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
if (!entries.empty()) {
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, entries.size());
|
||||
updateRequired = true;
|
||||
} else if (nextReleased && !entries.empty()) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / PAGE_ITEMS + 1) * PAGE_ITEMS) % entries.size();
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % entries.size();
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, entries.size());
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, entries.size(), PAGE_ITEMS);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, entries.size(), PAGE_ITEMS);
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
/**
|
||||
* Activity for browsing and downloading books from an OPDS server.
|
||||
@ -37,6 +38,7 @@ class OpdsBookBrowserActivity final : public ActivityWithSubactivity {
|
||||
private:
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
|
||||
BrowserState state = BrowserState::LOADING;
|
||||
|
||||
@ -162,13 +162,18 @@ void HomeActivity::freeCoverBuffer() {
|
||||
}
|
||||
|
||||
void HomeActivity::loop() {
|
||||
const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left);
|
||||
const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right);
|
||||
|
||||
const int menuCount = getMenuItemCount();
|
||||
|
||||
buttonNavigator.onNext([this, menuCount] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, menuCount);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this, menuCount] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, menuCount);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
// Calculate dynamic indices based on which options are available
|
||||
int idx = 0;
|
||||
@ -189,12 +194,6 @@ void HomeActivity::loop() {
|
||||
} else if (selectorIndex == settingsIdx) {
|
||||
onSettingsOpen();
|
||||
}
|
||||
} else if (prevPressed) {
|
||||
selectorIndex = (selectorIndex + menuCount - 1) % menuCount;
|
||||
updateRequired = true;
|
||||
} else if (nextPressed) {
|
||||
selectorIndex = (selectorIndex + 1) % menuCount;
|
||||
updateRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,10 +6,12 @@
|
||||
#include <functional>
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class HomeActivity final : public Activity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
int selectorIndex = 0;
|
||||
bool updateRequired = false;
|
||||
bool hasContinueReading = false;
|
||||
|
||||
@ -21,7 +21,6 @@ constexpr int LEFT_MARGIN = 20;
|
||||
constexpr int RIGHT_MARGIN = 40; // Extra space for scroll indicator
|
||||
|
||||
// Timing thresholds
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
constexpr unsigned long GO_HOME_MS = 1000;
|
||||
|
||||
void sortFileList(std::vector<std::string>& strs) {
|
||||
@ -178,13 +177,9 @@ void MyLibraryActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool upReleased = mappedInput.wasReleased(MappedInputManager::Button::Up);
|
||||
const bool downReleased = mappedInput.wasReleased(MappedInputManager::Button::Down);
|
||||
const bool leftReleased = mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||
const bool rightReleased = mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
|
||||
// Confirm button - open selected item
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (currentTab == Tab::Recent) {
|
||||
@ -249,24 +244,28 @@ void MyLibraryActivity::loop() {
|
||||
}
|
||||
|
||||
// Navigation: Up/Down moves through items only
|
||||
const bool prevReleased = upReleased;
|
||||
const bool nextReleased = downReleased;
|
||||
constexpr auto upButton = MappedInputManager::Button::Up;
|
||||
constexpr auto downButton = MappedInputManager::Button::Down;
|
||||
|
||||
if (prevReleased && itemCount > 0) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + itemCount) % itemCount;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + itemCount - 1) % itemCount;
|
||||
}
|
||||
buttonNavigator.onRelease({downButton}, [this, itemCount] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, itemCount);
|
||||
updateRequired = true;
|
||||
} else if (nextReleased && itemCount > 0) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % itemCount;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % itemCount;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onRelease({upButton}, [this, itemCount] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, itemCount);
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({downButton}, [this, itemCount, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, itemCount, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({upButton}, [this, itemCount, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, itemCount, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
|
||||
void MyLibraryActivity::displayTaskLoop() {
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "RecentBooksStore.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class MyLibraryActivity final : public Activity {
|
||||
public:
|
||||
@ -17,6 +18,7 @@ class MyLibraryActivity final : public Activity {
|
||||
private:
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
Tab currentTab = Tab::Recent;
|
||||
int selectorIndex = 0;
|
||||
|
||||
@ -72,18 +72,15 @@ void NetworkModeSelectionActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
const bool prevPressed = mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left);
|
||||
const bool nextPressed = mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right);
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, MENU_ITEM_COUNT);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
if (prevPressed) {
|
||||
selectedIndex = (selectedIndex + MENU_ITEM_COUNT - 1) % MENU_ITEM_COUNT;
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, MENU_ITEM_COUNT);
|
||||
updateRequired = true;
|
||||
} else if (nextPressed) {
|
||||
selectedIndex = (selectedIndex + 1) % MENU_ITEM_COUNT;
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkModeSelectionActivity::displayTaskLoop() {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
// Enum for network mode selection
|
||||
enum class NetworkMode { JOIN_NETWORK, CONNECT_CALIBRE, CREATE_HOTSPOT };
|
||||
@ -22,6 +23,8 @@ enum class NetworkMode { JOIN_NETWORK, CONNECT_CALIBRE, CREATE_HOTSPOT };
|
||||
class NetworkModeSelectionActivity final : public Activity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
int selectedIndex = 0;
|
||||
bool updateRequired = false;
|
||||
const std::function<void(NetworkMode)> onModeSelected;
|
||||
|
||||
@ -419,20 +419,16 @@ void WifiSelectionActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle UP/DOWN navigation
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
if (selectedNetworkIndex > 0) {
|
||||
selectedNetworkIndex--;
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedNetworkIndex = ButtonNavigator::nextIndex(selectedNetworkIndex, networks.size());
|
||||
updateRequired = true;
|
||||
}
|
||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
if (!networks.empty() && selectedNetworkIndex < static_cast<int>(networks.size()) - 1) {
|
||||
selectedNetworkIndex++;
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedNetworkIndex = ButtonNavigator::previousIndex(selectedNetworkIndex, networks.size());
|
||||
updateRequired = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,8 +542,8 @@ void WifiSelectionActivity::renderNetworkList() const {
|
||||
|
||||
// Draw network name (truncate if too long)
|
||||
std::string displayName = network.ssid;
|
||||
if (displayName.length() > 16) {
|
||||
displayName.replace(13, displayName.length() - 13, "...");
|
||||
if (displayName.length() > 33) {
|
||||
displayName.replace(30, displayName.length() - 30, "...");
|
||||
}
|
||||
renderer.drawText(UI_10_FONT_ID, 20, networkY, displayName.c_str());
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
// Structure to hold WiFi network information
|
||||
struct WifiNetworkInfo {
|
||||
@ -45,6 +46,7 @@ enum class WifiSelectionState {
|
||||
class WifiSelectionActivity final : public ActivityWithSubactivity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
WifiSelectionState state = WifiSelectionState::SCANNING;
|
||||
int selectedNetworkIndex = 0;
|
||||
|
||||
@ -7,11 +7,6 @@
|
||||
#include "MappedInputManager.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
// Time threshold for treating a long press as a page-up/page-down
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
} // namespace
|
||||
|
||||
bool EpubReaderChapterSelectionActivity::hasSyncOption() const { return KOREADER_STORE.hasCredentials(); }
|
||||
|
||||
int EpubReaderChapterSelectionActivity::getTotalItems() const {
|
||||
@ -119,12 +114,6 @@ void EpubReaderChapterSelectionActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
const int pageItems = getPageItems();
|
||||
const int totalItems = getTotalItems();
|
||||
|
||||
@ -145,21 +134,27 @@ void EpubReaderChapterSelectionActivity::loop() {
|
||||
}
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoBack();
|
||||
} else if (prevReleased) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + totalItems) % totalItems;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + totalItems - 1) % totalItems;
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
updateRequired = true;
|
||||
} else if (nextReleased) {
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % totalItems;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % totalItems;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
|
||||
void EpubReaderChapterSelectionActivity::displayTaskLoop() {
|
||||
|
||||
@ -7,12 +7,14 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class EpubReaderChapterSelectionActivity final : public ActivityWithSubactivity {
|
||||
std::shared_ptr<Epub> epub;
|
||||
std::string epubPath;
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
int currentSpineIndex = 0;
|
||||
int currentPage = 0;
|
||||
int totalPagesInSpine = 0;
|
||||
|
||||
@ -5,10 +5,6 @@
|
||||
#include "MappedInputManager.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
constexpr int SKIP_PAGE_MS = 700;
|
||||
} // namespace
|
||||
|
||||
int XtcReaderChapterSelectionActivity::getPageItems() const {
|
||||
constexpr int startY = 60;
|
||||
constexpr int lineHeight = 30;
|
||||
@ -75,13 +71,8 @@ void XtcReaderChapterSelectionActivity::onExit() {
|
||||
}
|
||||
|
||||
void XtcReaderChapterSelectionActivity::loop() {
|
||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right);
|
||||
|
||||
const bool skipPage = mappedInput.getHeldTime() > SKIP_PAGE_MS;
|
||||
const int pageItems = getPageItems();
|
||||
const int totalItems = static_cast<int>(xtc->getChapters().size());
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
const auto& chapters = xtc->getChapters();
|
||||
@ -90,29 +81,27 @@ void XtcReaderChapterSelectionActivity::loop() {
|
||||
}
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoBack();
|
||||
} else if (prevReleased) {
|
||||
const int total = static_cast<int>(xtc->getChapters().size());
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems - 1) * pageItems + total) % total;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + total - 1) % total;
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
updateRequired = true;
|
||||
} else if (nextReleased) {
|
||||
const int total = static_cast<int>(xtc->getChapters().size());
|
||||
if (total == 0) {
|
||||
return;
|
||||
}
|
||||
if (skipPage) {
|
||||
selectorIndex = ((selectorIndex / pageItems + 1) * pageItems) % total;
|
||||
} else {
|
||||
selectorIndex = (selectorIndex + 1) % total;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
|
||||
void XtcReaderChapterSelectionActivity::displayTaskLoop() {
|
||||
|
||||
@ -7,11 +7,13 @@
|
||||
#include <memory>
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class XtcReaderChapterSelectionActivity final : public Activity {
|
||||
std::shared_ptr<Xtc> xtc;
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
uint32_t currentPage = 0;
|
||||
int selectorIndex = 0;
|
||||
bool updateRequired = false;
|
||||
|
||||
@ -62,15 +62,16 @@ void CalibreSettingsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
|
||||
void CalibreSettingsActivity::handleSelection() {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
/**
|
||||
* Submenu for OPDS Browser settings.
|
||||
@ -24,6 +25,7 @@ class CalibreSettingsActivity final : public ActivityWithSubactivity {
|
||||
private:
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
|
||||
int selectedIndex = 0;
|
||||
|
||||
@ -62,15 +62,15 @@ void CategorySettingsActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
selectedSettingIndex = (selectedSettingIndex > 0) ? (selectedSettingIndex - 1) : (settingsCount - 1);
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedSettingIndex = (selectedSettingIndex + 1) % settingsCount;
|
||||
updateRequired = true;
|
||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
selectedSettingIndex = (selectedSettingIndex < settingsCount - 1) ? (selectedSettingIndex + 1) : 0;
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedSettingIndex = (selectedSettingIndex + settingsCount - 1) % settingsCount;
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CategorySettingsActivity::toggleCurrentSetting() {
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class CrossPointSettings;
|
||||
|
||||
@ -44,6 +45,7 @@ struct SettingInfo {
|
||||
class CategorySettingsActivity final : public ActivityWithSubactivity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
int selectedSettingIndex = 0;
|
||||
const char* categoryName;
|
||||
|
||||
@ -63,15 +63,16 @@ void KOReaderSettingsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
} else if (mappedInput.wasPressed(MappedInputManager::Button::Down) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = (selectedIndex + 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = (selectedIndex + MENU_ITEMS - 1) % MENU_ITEMS;
|
||||
updateRequired = true;
|
||||
});
|
||||
}
|
||||
|
||||
void KOReaderSettingsActivity::handleSelection() {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
/**
|
||||
* Submenu for KOReader Sync settings.
|
||||
@ -24,6 +25,7 @@ class KOReaderSettingsActivity final : public ActivityWithSubactivity {
|
||||
private:
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
|
||||
int selectedIndex = 0;
|
||||
|
||||
@ -111,17 +111,15 @@ void SettingsActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up) ||
|
||||
mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
// Move selection up (with wrap-around)
|
||||
selectedCategoryIndex = (selectedCategoryIndex > 0) ? (selectedCategoryIndex - 1) : (categoryCount - 1);
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedCategoryIndex = (selectedCategoryIndex + 1) % categoryCount;
|
||||
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;
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedCategoryIndex = (selectedCategoryIndex + categoryCount - 1) % categoryCount;
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SettingsActivity::enterCategory(int categoryIndex) {
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "activities/ActivityWithSubactivity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
class CrossPointSettings;
|
||||
struct SettingInfo;
|
||||
@ -15,6 +16,7 @@ struct SettingInfo;
|
||||
class SettingsActivity final : public ActivityWithSubactivity {
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
int selectedCategoryIndex = 0; // Currently selected category
|
||||
const std::function<void()> onGoHome;
|
||||
|
||||
@ -138,37 +138,24 @@ void KeyboardEntryActivity::handleKeyPress() {
|
||||
}
|
||||
|
||||
void KeyboardEntryActivity::loop() {
|
||||
// Navigation
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
|
||||
if (selectedRow > 0) {
|
||||
selectedRow--;
|
||||
// Clamp column to valid range for new row
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
} else {
|
||||
// Wrap to bottom row
|
||||
selectedRow = NUM_ROWS - 1;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
// Handle navigation
|
||||
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Up}, [this] {
|
||||
selectedRow = ButtonNavigator::previousIndex(selectedRow, NUM_ROWS);
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
|
||||
if (selectedRow < NUM_ROWS - 1) {
|
||||
selectedRow++;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
} else {
|
||||
// Wrap to top row
|
||||
selectedRow = 0;
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
|
||||
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Down}, [this] {
|
||||
selectedRow = ButtonNavigator::nextIndex(selectedRow, NUM_ROWS);
|
||||
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
if (selectedCol > maxCol) selectedCol = maxCol;
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Left}, [this] {
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
|
||||
// Special bottom row case
|
||||
@ -187,20 +174,14 @@ void KeyboardEntryActivity::loop() {
|
||||
// At done button, move to backspace
|
||||
selectedCol = BACKSPACE_COL;
|
||||
}
|
||||
updateRequired = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCol > 0) {
|
||||
selectedCol--;
|
||||
} else {
|
||||
// Wrap to end of current row
|
||||
selectedCol = maxCol;
|
||||
}
|
||||
updateRequired = true;
|
||||
selectedCol = ButtonNavigator::previousIndex(selectedCol, maxCol + 1);
|
||||
}
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
|
||||
updateRequired = true;
|
||||
});
|
||||
|
||||
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Right}, [this] {
|
||||
const int maxCol = getRowLength(selectedRow) - 1;
|
||||
|
||||
// Special bottom row case
|
||||
@ -219,18 +200,11 @@ void KeyboardEntryActivity::loop() {
|
||||
// At done button, wrap to beginning of row
|
||||
selectedCol = SHIFT_COL;
|
||||
}
|
||||
updateRequired = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCol < maxCol) {
|
||||
selectedCol++;
|
||||
} else {
|
||||
// Wrap to beginning of current row
|
||||
selectedCol = 0;
|
||||
selectedCol = ButtonNavigator::nextIndex(selectedCol, maxCol + 1);
|
||||
}
|
||||
updateRequired = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Selection
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "../Activity.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
|
||||
/**
|
||||
* Reusable keyboard entry activity for text input.
|
||||
@ -65,6 +66,7 @@ class KeyboardEntryActivity : public Activity {
|
||||
bool isPassword;
|
||||
TaskHandle_t displayTaskHandle = nullptr;
|
||||
SemaphoreHandle_t renderingMutex = nullptr;
|
||||
ButtonNavigator buttonNavigator;
|
||||
bool updateRequired = false;
|
||||
|
||||
// Keyboard state
|
||||
|
||||
@ -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);
|
||||
|
||||
switch (gpio.getWakeupReason()) {
|
||||
case HalGPIO::WakeupReason::PowerButton:
|
||||
|
||||
124
src/util/ButtonNavigator.cpp
Normal file
124
src/util/ButtonNavigator.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
#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::onPressAndContinuous(const Buttons& buttons, const Callback& callback) {
|
||||
onPress(buttons, callback);
|
||||
onContinuous(buttons, callback);
|
||||
}
|
||||
|
||||
void ButtonNavigator::onNextPress(const Callback& callback) { onPress(getNextButtons(), callback); }
|
||||
|
||||
void ButtonNavigator::onPreviousPress(const Callback& callback) { onPress(getPreviousButtons(), callback); }
|
||||
|
||||
void ButtonNavigator::onNextRelease(const Callback& callback) { onRelease(getNextButtons(), callback); }
|
||||
|
||||
void ButtonNavigator::onPreviousRelease(const Callback& callback) { onRelease(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) {
|
||||
const bool wasPressed = std::any_of(buttons.begin(), buttons.end(), [](const MappedInputManager::Button button) {
|
||||
return mappedInput != nullptr && mappedInput->wasPressed(button);
|
||||
});
|
||||
|
||||
if (wasPressed) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonNavigator::onRelease(const Buttons& buttons, const Callback& callback) {
|
||||
const bool wasReleased = std::any_of(buttons.begin(), buttons.end(), [](const MappedInputManager::Button button) {
|
||||
return mappedInput != nullptr && mappedInput->wasReleased(button);
|
||||
});
|
||||
|
||||
if (wasReleased) {
|
||||
if (lastContinuousNavTime == 0) {
|
||||
callback();
|
||||
}
|
||||
|
||||
lastContinuousNavTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonNavigator::onContinuous(const Buttons& buttons, const Callback& callback) {
|
||||
const bool isPressed = std::any_of(buttons.begin(), buttons.end(), [this](const MappedInputManager::Button button) {
|
||||
return mappedInput != nullptr && mappedInput->isPressed(button) && shouldNavigateContinuously();
|
||||
});
|
||||
|
||||
if (isPressed) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int ButtonNavigator::nextPageIndex(const int currentIndex, const int totalItems, const int itemsPerPage) {
|
||||
if (totalItems <= 0 || itemsPerPage <= 0) return 0;
|
||||
|
||||
// When items fit on one page, use index navigation instead
|
||||
if (totalItems <= itemsPerPage) {
|
||||
return nextIndex(currentIndex, totalItems);
|
||||
}
|
||||
|
||||
const int lastPageIndex = (totalItems - 1) / itemsPerPage;
|
||||
const int currentPageIndex = currentIndex / itemsPerPage;
|
||||
|
||||
if (currentPageIndex < lastPageIndex) {
|
||||
return (currentPageIndex + 1) * itemsPerPage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ButtonNavigator::previousPageIndex(const int currentIndex, const int totalItems, const int itemsPerPage) {
|
||||
if (totalItems <= 0 || itemsPerPage <= 0) return 0;
|
||||
|
||||
// When items fit on one page, use index navigation instead
|
||||
if (totalItems <= itemsPerPage) {
|
||||
return previousIndex(currentIndex, totalItems);
|
||||
}
|
||||
|
||||
const int lastPageIndex = (totalItems - 1) / itemsPerPage;
|
||||
const int currentPageIndex = currentIndex / itemsPerPage;
|
||||
|
||||
if (currentPageIndex > 0) {
|
||||
return (currentPageIndex - 1) * itemsPerPage;
|
||||
}
|
||||
|
||||
return lastPageIndex * itemsPerPage;
|
||||
}
|
||||
53
src/util/ButtonNavigator.h
Normal file
53
src/util/ButtonNavigator.h
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
|
||||
class ButtonNavigator final {
|
||||
using Callback = std::function<void()>;
|
||||
using Buttons = std::vector<MappedInputManager::Button>;
|
||||
|
||||
const uint16_t continuousStartMs;
|
||||
const uint16_t continuousIntervalMs;
|
||||
uint32_t lastContinuousNavTime = 0;
|
||||
static const MappedInputManager* mappedInput;
|
||||
|
||||
[[nodiscard]] bool shouldNavigateContinuously() const;
|
||||
|
||||
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 onPressAndContinuous(const Buttons& buttons, const Callback& callback);
|
||||
|
||||
void onNextPress(const Callback& callback);
|
||||
void onPreviousPress(const Callback& callback);
|
||||
void onPress(const Buttons& buttons, const Callback& callback);
|
||||
|
||||
void onNextRelease(const Callback& callback);
|
||||
void onPreviousRelease(const Callback& callback);
|
||||
void onRelease(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);
|
||||
|
||||
[[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};
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user