Add double and long clicks in lists
This commit is contained in:
@@ -27,14 +27,9 @@ void MenuListActivity::onEnter() {
|
||||
}
|
||||
|
||||
void MenuListActivity::handleNavigation() {
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = buttonNavigator.nextIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = buttonNavigator.previousIndex(selectedIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
const int count = static_cast<int>(menuItems.size());
|
||||
buttonNavigator.onNextList(selectedIndex, count, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, count, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void MenuListActivity::toggleCurrentItem() {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "ButtonEventManager.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OpdsFormatLabel.h"
|
||||
#include "activities/network/WifiSelectionActivity.h"
|
||||
@@ -113,9 +112,6 @@ OpdsEntry OpdsBookBrowserActivity::getEntry(size_t index) const {
|
||||
void OpdsBookBrowserActivity::onEnter() {
|
||||
Activity::onEnter();
|
||||
|
||||
globalButtonEvents().forceDoubleAction(ButtonEventManager::Button::Up, true);
|
||||
globalButtonEvents().forceDoubleAction(ButtonEventManager::Button::Down, true);
|
||||
|
||||
state = BrowserState::CHECK_WIFI;
|
||||
entryOffsets.clear();
|
||||
navigationHistory.clear();
|
||||
@@ -137,9 +133,6 @@ void OpdsBookBrowserActivity::onEnter() {
|
||||
void OpdsBookBrowserActivity::onExit() {
|
||||
Activity::onExit();
|
||||
|
||||
globalButtonEvents().forceDoubleAction(ButtonEventManager::Button::Up, false);
|
||||
globalButtonEvents().forceDoubleAction(ButtonEventManager::Button::Down, false);
|
||||
|
||||
HalClock::wifiOff();
|
||||
|
||||
entryOffsets.clear();
|
||||
@@ -216,14 +209,10 @@ void OpdsBookBrowserActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, entry] {
|
||||
formatSelectorIndex = ButtonNavigator::nextIndex(formatSelectorIndex, entry.acquisitionLinks.size());
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onPreviousRelease([this, entry] {
|
||||
formatSelectorIndex = ButtonNavigator::previousIndex(formatSelectorIndex, entry.acquisitionLinks.size());
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(formatSelectorIndex, static_cast<int>(entry.acquisitionLinks.size()),
|
||||
[this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(formatSelectorIndex, static_cast<int>(entry.acquisitionLinks.size()),
|
||||
[this] { requestUpdate(); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -240,37 +229,11 @@ void OpdsBookBrowserActivity::loop() {
|
||||
}
|
||||
|
||||
if (!entryOffsets.empty()) {
|
||||
ButtonEventManager::ButtonEvent extEvent;
|
||||
while (globalButtonEvents().consumeEvent(extEvent)) {
|
||||
if (extEvent.type == ButtonEventManager::PressType::Double) {
|
||||
if (extEvent.button == ButtonEventManager::Button::Down) {
|
||||
selectorIndex = (selectorIndex + 9) % entryOffsets.size();
|
||||
requestUpdate();
|
||||
} else if (extEvent.button == ButtonEventManager::Button::Up) {
|
||||
int size = entryOffsets.size();
|
||||
selectorIndex = (selectorIndex - 9 + size) % size;
|
||||
requestUpdate();
|
||||
}
|
||||
} else if (extEvent.type == ButtonEventManager::PressType::Short ||
|
||||
extEvent.type == ButtonEventManager::PressType::Long) {
|
||||
if (extEvent.button == ButtonEventManager::Button::Down) {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, entryOffsets.size());
|
||||
requestUpdate();
|
||||
} else if (extEvent.button == ButtonEventManager::Button::Up) {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, entryOffsets.size());
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Down}, [this] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, entryOffsets.size(), PAGE_ITEMS);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Up}, [this] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, entryOffsets.size(), PAGE_ITEMS);
|
||||
requestUpdate();
|
||||
});
|
||||
// Left/Right are reserved for Back and Search, so restrict to Up/Down only.
|
||||
buttonNavigator.onNextList({MappedInputManager::Button::Down}, selectorIndex,
|
||||
static_cast<int>(entryOffsets.size()), [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList({MappedInputManager::Button::Up}, selectorIndex,
|
||||
static_cast<int>(entryOffsets.size()), [this] { requestUpdate(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ void FileBrowserActivity::onEnter() {
|
||||
if (!focusName.empty()) {
|
||||
const size_t idx = findEntry(focusName);
|
||||
if (idx < files.size()) {
|
||||
selectorIndex = idx;
|
||||
selectorIndex = static_cast<int>(idx);
|
||||
}
|
||||
focusName.clear();
|
||||
}
|
||||
@@ -141,8 +141,6 @@ void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
|
||||
}
|
||||
|
||||
void FileBrowserActivity::loop() {
|
||||
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
|
||||
|
||||
ButtonEventManager::ButtonEvent ev;
|
||||
while (buttonEvents.consumeEvent(ev)) {
|
||||
if (ev.button == MappedInputManager::Button::Back) {
|
||||
@@ -162,7 +160,7 @@ void FileBrowserActivity::loop() {
|
||||
const auto pos = oldPath.find_last_of('/');
|
||||
const std::string dirName = oldPath.substr(pos + 1) + "/";
|
||||
const size_t idx = findEntry(dirName);
|
||||
selectorIndex = (idx < files.size()) ? idx : 0;
|
||||
selectorIndex = (idx < files.size()) ? static_cast<int>(idx) : 0;
|
||||
requestUpdate();
|
||||
} else if (mode == Mode::PickFirmware) {
|
||||
// At root in PickFirmware: cancel back to caller.
|
||||
@@ -249,8 +247,8 @@ void FileBrowserActivity::loop() {
|
||||
loadFiles();
|
||||
if (files.empty()) {
|
||||
selectorIndex = 0;
|
||||
} else if (selectorIndex >= files.size()) {
|
||||
selectorIndex = files.size() - 1;
|
||||
} else if (selectorIndex >= static_cast<int>(files.size())) {
|
||||
selectorIndex = static_cast<int>(files.size()) - 1;
|
||||
}
|
||||
requestUpdate(true);
|
||||
} else {
|
||||
@@ -280,27 +278,11 @@ void FileBrowserActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Up/Down side buttons navigate the list
|
||||
// Up/Down side buttons navigate the list (Left/Right are reserved for Back/Info actions)
|
||||
const int listSize = static_cast<int>(files.size());
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Down}, [this, listSize] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(static_cast<int>(selectorIndex), listSize);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Up}, [this, listSize] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(static_cast<int>(selectorIndex), listSize);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Down}, [this, listSize, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Up}, [this, listSize, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList({MappedInputManager::Button::Down}, selectorIndex, listSize, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList({MappedInputManager::Button::Up}, selectorIndex, listSize,
|
||||
[this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
std::string getFileName(std::string filename) {
|
||||
|
||||
@@ -19,7 +19,7 @@ class FileBrowserActivity final : public Activity {
|
||||
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
size_t selectorIndex = 0;
|
||||
int selectorIndex = 0;
|
||||
|
||||
Mode mode = Mode::Books;
|
||||
|
||||
|
||||
@@ -250,39 +250,10 @@ void GlobalBookmarksActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
|
||||
|
||||
// Navigator is restricted to Up/Down so the Left (rename) and Right (delete)
|
||||
// handlers above cannot race default cursor movement on the same release tick.
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Down}, [this] {
|
||||
selectorIndex = buttonNavigator.nextIndex(selectorIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Up}, [this] {
|
||||
selectorIndex = buttonNavigator.previousIndex(selectorIndex);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Down}, [this, total, pageItems] {
|
||||
int next = ButtonNavigator::nextPageIndex(selectorIndex, total, pageItems);
|
||||
if (isSeparatorRow(next)) {
|
||||
const int adj = ButtonNavigator::nextIndex(next, total, [this](int i) { return !isSeparatorRow(i); });
|
||||
if (adj >= 0) next = adj;
|
||||
}
|
||||
selectorIndex = next;
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Up}, [this, total, pageItems] {
|
||||
int prev = ButtonNavigator::previousPageIndex(selectorIndex, total, pageItems);
|
||||
if (isSeparatorRow(prev)) {
|
||||
const int adj = ButtonNavigator::previousIndex(prev, total, [this](int i) { return !isSeparatorRow(i); });
|
||||
if (adj >= 0) prev = adj;
|
||||
}
|
||||
selectorIndex = prev;
|
||||
requestUpdate();
|
||||
});
|
||||
// handlers above cannot race default cursor movement on the same press tick.
|
||||
buttonNavigator.onNextList({MappedInputManager::Button::Down}, selectorIndex, total, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList({MappedInputManager::Button::Up}, selectorIndex, total, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void GlobalBookmarksActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -38,8 +38,8 @@ void RecentBooksActivity::onEnter() {
|
||||
loadRecentBooks();
|
||||
|
||||
selectorIndex = 0;
|
||||
if (initialFocusIndex >= 0 && static_cast<size_t>(initialFocusIndex) < recentBooks.size()) {
|
||||
selectorIndex = static_cast<size_t>(initialFocusIndex);
|
||||
if (initialFocusIndex >= 0 && initialFocusIndex < static_cast<int>(recentBooks.size())) {
|
||||
selectorIndex = initialFocusIndex;
|
||||
}
|
||||
initialFocusIndex = -1;
|
||||
requestUpdate();
|
||||
@@ -51,13 +51,11 @@ void RecentBooksActivity::onExit() {
|
||||
}
|
||||
|
||||
void RecentBooksActivity::loop() {
|
||||
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, true);
|
||||
|
||||
ButtonEventManager::ButtonEvent ev;
|
||||
while (buttonEvents.consumeEvent(ev)) {
|
||||
if (ev.button == MappedInputManager::Button::Confirm &&
|
||||
(ev.type == ButtonEventManager::PressType::Short || ev.type == ButtonEventManager::PressType::Long)) {
|
||||
if (recentBooks.empty() || selectorIndex >= recentBooks.size()) {
|
||||
if (recentBooks.empty() || selectorIndex >= static_cast<int>(recentBooks.size())) {
|
||||
return;
|
||||
}
|
||||
// Long-press Confirm signals "open with KOReader sync" only for EPUBs.
|
||||
@@ -75,7 +73,7 @@ void RecentBooksActivity::loop() {
|
||||
}
|
||||
ReturnHint hint;
|
||||
hint.target = ReturnTo::RecentBooks;
|
||||
hint.selectIndex = static_cast<int>(selectorIndex);
|
||||
hint.selectIndex = selectorIndex;
|
||||
activityManager.replaceWithReader(recentBooks[selectorIndex].path, std::move(hint));
|
||||
return;
|
||||
}
|
||||
@@ -86,7 +84,7 @@ void RecentBooksActivity::loop() {
|
||||
}
|
||||
|
||||
if (ev.button == MappedInputManager::Button::Left && ev.type == ButtonEventManager::PressType::Short) {
|
||||
if (recentBooks.empty() || selectorIndex >= recentBooks.size()) return;
|
||||
if (recentBooks.empty() || selectorIndex >= static_cast<int>(recentBooks.size())) return;
|
||||
const std::string bookPath = recentBooks[selectorIndex].path;
|
||||
const std::string bookTitle = recentBooks[selectorIndex].title;
|
||||
|
||||
@@ -97,8 +95,8 @@ void RecentBooksActivity::loop() {
|
||||
loadRecentBooks();
|
||||
if (recentBooks.empty()) {
|
||||
selectorIndex = 0;
|
||||
} else if (selectorIndex >= recentBooks.size()) {
|
||||
selectorIndex = recentBooks.size() - 1;
|
||||
} else if (selectorIndex >= static_cast<int>(recentBooks.size())) {
|
||||
selectorIndex = static_cast<int>(recentBooks.size()) - 1;
|
||||
}
|
||||
requestUpdate(true);
|
||||
} else {
|
||||
@@ -113,7 +111,7 @@ void RecentBooksActivity::loop() {
|
||||
}
|
||||
|
||||
if (ev.button == MappedInputManager::Button::Right && ev.type == ButtonEventManager::PressType::Short) {
|
||||
if (recentBooks.empty() || selectorIndex >= recentBooks.size()) return;
|
||||
if (recentBooks.empty() || selectorIndex >= static_cast<int>(recentBooks.size())) return;
|
||||
const std::string& path = recentBooks[selectorIndex].path;
|
||||
if (FsHelpers::hasEpubExtension(path) || FsHelpers::hasXtcExtension(path)) {
|
||||
startActivityForResult(std::make_unique<BookInfoActivity>(renderer, mappedInput, path),
|
||||
@@ -123,31 +121,12 @@ void RecentBooksActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
int listSize = static_cast<int>(recentBooks.size());
|
||||
|
||||
// Navigator is restricted to Up/Down so it cannot race the Left/Right Short
|
||||
// handlers above: with a double-click action configured, Short events are
|
||||
// deferred 300ms while wasReleased() is immediate, which would otherwise let
|
||||
// the cursor move before the custom action runs on the wrong entry.
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Down}, [this, listSize] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(static_cast<int>(selectorIndex), listSize);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onRelease({MappedInputManager::Button::Up}, [this, listSize] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(static_cast<int>(selectorIndex), listSize);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Down}, [this, listSize, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onContinuous({MappedInputManager::Button::Up}, [this, listSize, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
// handlers above (Left/Right actions are not in the onNextList/onPreviousList button sets).
|
||||
const int listSize = static_cast<int>(recentBooks.size());
|
||||
buttonNavigator.onNextList({MappedInputManager::Button::Down}, selectorIndex, listSize, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList({MappedInputManager::Button::Up}, selectorIndex, listSize,
|
||||
[this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void RecentBooksActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class RecentBooksActivity final : public Activity {
|
||||
private:
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
size_t selectorIndex = 0;
|
||||
int selectorIndex = 0;
|
||||
int initialFocusIndex = -1; // applied once in onEnter(), then cleared
|
||||
|
||||
// Recent tab state
|
||||
|
||||
@@ -43,15 +43,8 @@ void NetworkModeSelectionActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, MENU_ITEM_COUNT);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, MENU_ITEM_COUNT);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedIndex, MENU_ITEM_COUNT, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, MENU_ITEM_COUNT, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void NetworkModeSelectionActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -636,15 +636,9 @@ void WifiSelectionActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNext([this] {
|
||||
selectedNetworkIndex = ButtonNavigator::nextIndex(selectedNetworkIndex, networks.size());
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
selectedNetworkIndex = ButtonNavigator::previousIndex(selectedNetworkIndex, networks.size());
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedNetworkIndex, static_cast<int>(networks.size()), [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedNetworkIndex, static_cast<int>(networks.size()),
|
||||
[this] { requestUpdate(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class WifiSelectionActivity final : public Activity {
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
WifiSelectionState state = WifiSelectionState::SCANNING;
|
||||
size_t selectedNetworkIndex = 0;
|
||||
int selectedNetworkIndex = 0;
|
||||
std::vector<WifiNetworkInfo> networks;
|
||||
|
||||
// Selected network for connection
|
||||
|
||||
@@ -78,25 +78,8 @@ void EpubReaderChapterSelectionActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void EpubReaderChapterSelectionActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -71,25 +71,8 @@ void MdReaderTocSelectionActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void MdReaderTocSelectionActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -113,27 +113,8 @@ void StarredPagesActivity::loop() {
|
||||
|
||||
if (totalItems == 0) return;
|
||||
|
||||
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, false);
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void StarredPagesActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -85,25 +85,8 @@ void XtcReaderChapterSelectionActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this, totalItems] {
|
||||
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::nextPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this, totalItems, pageItems] {
|
||||
selectorIndex = ButtonNavigator::previousPageIndex(selectorIndex, totalItems, pageItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectorIndex, totalItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void XtcReaderChapterSelectionActivity::render(RenderLock&&) {
|
||||
|
||||
@@ -392,19 +392,8 @@ void FontDownloadActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
buttonNavigator_.onNextRelease([this] {
|
||||
if (selectedIndex_ < listItemCount() - 1) {
|
||||
selectedIndex_++;
|
||||
requestUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
buttonNavigator_.onPreviousRelease([this] {
|
||||
if (selectedIndex_ > 0) {
|
||||
selectedIndex_--;
|
||||
requestUpdate();
|
||||
}
|
||||
});
|
||||
buttonNavigator_.onNextList(selectedIndex_, listItemCount(), [this] { requestUpdate(); });
|
||||
buttonNavigator_.onPreviousList(selectedIndex_, listItemCount(), [this] { requestUpdate(); });
|
||||
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||
if (!families_.empty()) {
|
||||
|
||||
@@ -45,15 +45,8 @@ void FontSelectionActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, fontCount);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, fontCount);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedIndex, fontCount, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, fontCount, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void FontSelectionActivity::handleSelection() {
|
||||
|
||||
@@ -37,15 +37,8 @@ void LanguageSelectActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(static_cast<int>(selectedIndex), totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
selectedIndex = ButtonNavigator::previousIndex(static_cast<int>(selectedIndex), totalItems);
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedIndex, totalItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, totalItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void LanguageSelectActivity::handleSelection() {
|
||||
|
||||
@@ -49,17 +49,8 @@ void OpdsServerListActivity::loop() {
|
||||
}
|
||||
|
||||
const int itemCount = getItemCount();
|
||||
if (itemCount > 0) {
|
||||
buttonNavigator.onNext([this, itemCount] {
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, itemCount);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this, itemCount] {
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, itemCount);
|
||||
requestUpdate();
|
||||
});
|
||||
}
|
||||
buttonNavigator.onNextList(selectedIndex, itemCount, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, itemCount, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void OpdsServerListActivity::handleSelection() {
|
||||
|
||||
@@ -61,15 +61,8 @@ void OpdsSettingsActivity::loop() {
|
||||
}
|
||||
|
||||
const int menuItems = getMenuItemCount();
|
||||
buttonNavigator.onNext([this, menuItems] {
|
||||
selectedIndex = (selectedIndex + 1) % menuItems;
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this, menuItems] {
|
||||
selectedIndex = (selectedIndex + menuItems - 1) % menuItems;
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedIndex, menuItems, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, menuItems, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
bool OpdsSettingsActivity::saveServer() {
|
||||
@@ -206,7 +199,7 @@ void OpdsSettingsActivity::render(RenderLock&&) {
|
||||
StrId::STR_PASSWORD};
|
||||
|
||||
GUI.drawList(
|
||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, menuItems, static_cast<int>(selectedIndex),
|
||||
renderer, Rect{0, contentTop, pageWidth, contentHeight}, menuItems, selectedIndex,
|
||||
[this, &fieldNames](int index) {
|
||||
if (index < BASE_ITEMS) {
|
||||
return std::string(I18N.get(fieldNames[index]));
|
||||
|
||||
@@ -25,7 +25,7 @@ class OpdsSettingsActivity final : public Activity {
|
||||
private:
|
||||
ButtonNavigator buttonNavigator;
|
||||
|
||||
size_t selectedIndex = 0;
|
||||
int selectedIndex = 0;
|
||||
int serverIndex;
|
||||
OpdsServer editServer;
|
||||
bool isNewServer = false;
|
||||
|
||||
@@ -197,29 +197,9 @@ void StatusBarSettingsActivity::loop() {
|
||||
}
|
||||
|
||||
// Handle navigation
|
||||
buttonNavigator.onNextRelease([this] {
|
||||
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, menuCount);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousRelease([this] {
|
||||
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, menuCount);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onNextContinuous([this] {
|
||||
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
|
||||
selectedIndex = ButtonNavigator::nextIndex(selectedIndex, menuCount);
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPreviousContinuous([this] {
|
||||
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
|
||||
selectedIndex = ButtonNavigator::previousIndex(selectedIndex, menuCount);
|
||||
requestUpdate();
|
||||
});
|
||||
const int menuCount = SETTINGS.useClock ? MENU_ITEMS_WITH_CLOCK : MENU_ITEMS_NO_CLOCK;
|
||||
buttonNavigator.onNextList(selectedIndex, menuCount, [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, menuCount, [this] { requestUpdate(); });
|
||||
}
|
||||
|
||||
void StatusBarSettingsActivity::handleSelection() {
|
||||
|
||||
@@ -55,18 +55,8 @@ void WeatherSettingsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
buttonNavigator.onNext([this] {
|
||||
if (searchResults.empty()) return;
|
||||
selectedIndex = (selectedIndex + 1) % static_cast<int>(searchResults.size());
|
||||
requestUpdate();
|
||||
});
|
||||
|
||||
buttonNavigator.onPrevious([this] {
|
||||
if (searchResults.empty()) return;
|
||||
const int size = static_cast<int>(searchResults.size());
|
||||
selectedIndex = (selectedIndex + size - 1) % size;
|
||||
requestUpdate();
|
||||
});
|
||||
buttonNavigator.onNextList(selectedIndex, static_cast<int>(searchResults.size()), [this] { requestUpdate(); });
|
||||
buttonNavigator.onPreviousList(selectedIndex, static_cast<int>(searchResults.size()), [this] { requestUpdate(); });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -224,3 +224,84 @@ int ButtonNavigator::previousPageIndex(const int currentIndex, const int totalIt
|
||||
|
||||
return lastPageIndex * itemsPerPage;
|
||||
}
|
||||
|
||||
void ButtonNavigator::onNextList(int& selectedIndex, const int totalItems, const Callback& onChange) {
|
||||
onListNav(getNextButtons(), true, selectedIndex, totalItems, lastNextPressMs, longPressNextFired, onChange);
|
||||
}
|
||||
|
||||
void ButtonNavigator::onNextList(const Buttons& buttons, int& selectedIndex, const int totalItems,
|
||||
const Callback& onChange) {
|
||||
onListNav(buttons, true, selectedIndex, totalItems, lastNextPressMs, longPressNextFired, onChange);
|
||||
}
|
||||
|
||||
void ButtonNavigator::onPreviousList(int& selectedIndex, const int totalItems, const Callback& onChange) {
|
||||
onListNav(getPreviousButtons(), false, selectedIndex, totalItems, lastPreviousPressMs, longPressPreviousFired,
|
||||
onChange);
|
||||
}
|
||||
|
||||
void ButtonNavigator::onPreviousList(const Buttons& buttons, int& selectedIndex, const int totalItems,
|
||||
const Callback& onChange) {
|
||||
onListNav(buttons, false, selectedIndex, totalItems, lastPreviousPressMs, longPressPreviousFired, onChange);
|
||||
}
|
||||
|
||||
void ButtonNavigator::onListNav(const Buttons& buttons, const bool forward, int& selectedIndex, const int totalItems,
|
||||
uint32_t& lastPressMs, bool& longPressFired, const Callback& onChange) {
|
||||
if (!mappedInput || totalItems <= 0) return;
|
||||
|
||||
const bool anyHeld = std::any_of(buttons.begin(), buttons.end(),
|
||||
[](const MappedInputManager::Button b) { return mappedInput->isPressed(b); });
|
||||
|
||||
if (anyHeld && mappedInput->getHeldTime() > listLongPressMs) {
|
||||
if (!longPressFired) {
|
||||
longPressFired = true;
|
||||
if (forward) {
|
||||
selectedIndex = totalItems - 1;
|
||||
if (selectablePredicate) {
|
||||
while (selectedIndex > 0 && !selectablePredicate(selectedIndex)) --selectedIndex;
|
||||
}
|
||||
} else {
|
||||
selectedIndex = 0;
|
||||
if (selectablePredicate) {
|
||||
while (selectedIndex < totalItems - 1 && !selectablePredicate(selectedIndex)) ++selectedIndex;
|
||||
}
|
||||
}
|
||||
onChange();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const bool wasReleased = std::any_of(buttons.begin(), buttons.end(),
|
||||
[](const MappedInputManager::Button b) { return mappedInput->wasReleased(b); });
|
||||
if (wasReleased) {
|
||||
longPressFired = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool wasPressed = std::any_of(buttons.begin(), buttons.end(),
|
||||
[](const MappedInputManager::Button b) { return mappedInput->wasPressed(b); });
|
||||
if (!wasPressed) return;
|
||||
|
||||
const uint32_t now = millis();
|
||||
const bool isDouble = (now - lastPressMs) < listDoubleClickMs;
|
||||
lastPressMs = now;
|
||||
|
||||
if (isDouble) {
|
||||
// Restore to position before the first press so the total movement is exactly listJumpCount.
|
||||
selectedIndex = indexBeforePress;
|
||||
for (int i = 0; i < listJumpCount; ++i) {
|
||||
const int next =
|
||||
forward ? (selectablePredicate ? nextIndex(selectedIndex) : nextIndex(selectedIndex, totalItems))
|
||||
: (selectablePredicate ? previousIndex(selectedIndex) : previousIndex(selectedIndex, totalItems));
|
||||
// Stop before wrapping: forward movement decreases index only on wrap; backward vice versa.
|
||||
if (forward && next <= selectedIndex) break;
|
||||
if (!forward && next >= selectedIndex) break;
|
||||
selectedIndex = next;
|
||||
}
|
||||
} else {
|
||||
indexBeforePress = selectedIndex;
|
||||
selectedIndex =
|
||||
forward ? (selectablePredicate ? nextIndex(selectedIndex) : nextIndex(selectedIndex, totalItems))
|
||||
: (selectablePredicate ? previousIndex(selectedIndex) : previousIndex(selectedIndex, totalItems));
|
||||
}
|
||||
onChange();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,19 @@ class ButtonNavigator final {
|
||||
std::function<bool(int)> selectablePredicate;
|
||||
int selectableTotalItems = 0;
|
||||
|
||||
uint32_t lastNextPressMs = 0;
|
||||
uint32_t lastPreviousPressMs = 0;
|
||||
bool longPressNextFired = false;
|
||||
bool longPressPreviousFired = false;
|
||||
int indexBeforePress = 0;
|
||||
|
||||
static constexpr uint16_t listDoubleClickMs = 350;
|
||||
static constexpr uint32_t listLongPressMs = 1500;
|
||||
static constexpr int listJumpCount = 10;
|
||||
|
||||
[[nodiscard]] bool shouldNavigateContinuously() const;
|
||||
void onListNav(const Buttons& buttons, bool forward, int& selectedIndex, int totalItems, uint32_t& lastPressMs,
|
||||
bool& longPressFired, const Callback& onChange);
|
||||
|
||||
public:
|
||||
explicit ButtonNavigator(const uint16_t continuousIntervalMs = 500, const uint16_t continuousStartMs = 500)
|
||||
@@ -57,6 +69,13 @@ class ButtonNavigator final {
|
||||
[[nodiscard]] static int nextPageIndex(int currentIndex, int totalItems, int itemsPerPage);
|
||||
[[nodiscard]] static int previousPageIndex(int currentIndex, int totalItems, int itemsPerPage);
|
||||
|
||||
// List navigation with double-click (skip 10) and long-press (jump to edge).
|
||||
// Replaces the typical onNext/onPrevious + onContinuous pattern for drawList consumers.
|
||||
void onNextList(int& selectedIndex, int totalItems, const Callback& onChange);
|
||||
void onNextList(const Buttons& buttons, int& selectedIndex, int totalItems, const Callback& onChange);
|
||||
void onPreviousList(int& selectedIndex, int totalItems, const Callback& onChange);
|
||||
void onPreviousList(const Buttons& buttons, int& selectedIndex, int totalItems, const Callback& onChange);
|
||||
|
||||
[[nodiscard]] static Buttons getNextButtons() {
|
||||
return {MappedInputManager::Button::Down, MappedInputManager::Button::Right};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user