diff --git a/.gitmodules b/.gitmodules index 7c0ed75c..ceeb9c15 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "freeink-sdk"] path = freeink-sdk url = https://github.com/Free-Ink/freeink-sdk.git - branch = main + branch = sticky diff --git a/freeink-sdk b/freeink-sdk index e016d51e..5ea178c6 160000 --- a/freeink-sdk +++ b/freeink-sdk @@ -1 +1 @@ -Subproject commit e016d51e51ccb858dbbc8e25fca9894b4ed0913b +Subproject commit 5ea178c69b5df5fd5338514486c13d19b8e28ed4 diff --git a/platformio.ini b/platformio.ini index 34339a5e..ff8accda 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,6 +68,7 @@ lib_deps = PowerManager=symlink://freeink-sdk/libs/hardware/PowerManager Rtc=symlink://freeink-sdk/libs/hardware/Rtc Imu=symlink://freeink-sdk/libs/hardware/Imu + FreeInkUI=symlink://freeink-sdk/libs/ui/FreeInkUI Icons=symlink://freeink-sdk/libs/assets/Icons bblanchon/ArduinoJson @ 7.4.2 ricmoo/QRCode @ 0.0.1 diff --git a/src/MappedInputManager.cpp b/src/MappedInputManager.cpp index 5049a8aa..b8f76abd 100644 --- a/src/MappedInputManager.cpp +++ b/src/MappedInputManager.cpp @@ -1,5 +1,6 @@ #include "MappedInputManager.h" +#include #include #include @@ -162,18 +163,23 @@ bool MappedInputManager::wasCoverTapped(int& id) const { return TouchRegistry::getInstance().hitTest(lx, ly, TouchRegistry::Cover, id); } +bool MappedInputManager::wasScreenTapped(int& x, int& y) const { + float nx = 0.0f, ny = 0.0f; + if (!gpio.wasTouchTap(nx, ny)) return false; + renderer.tapToLogical(nx, ny, x, y); + return true; +} + bool MappedInputManager::wasListScroll(int& index, int count, int pageItems) const { if (count <= 0) return false; if (pageItems < 1) pageItems = 1; if (wasBottomEdgeSwipeUp()) return false; const SwipeDir swipe = wasSwipe(); if (swipe == SwipeDir::Up) { - index = std::min(index + pageItems, count - 1); - return true; + return freeink::ui::listPageIndex(index, +1, count, pageItems); } if (swipe == SwipeDir::Down) { - index = std::max(index - pageItems, 0); - return true; + return freeink::ui::listPageIndex(index, -1, count, pageItems); } return false; } diff --git a/src/MappedInputManager.h b/src/MappedInputManager.h index d623a371..d435efe0 100644 --- a/src/MappedInputManager.h +++ b/src/MappedInputManager.h @@ -39,6 +39,8 @@ class MappedInputManager { // (id = item index). Distinct kinds so a screen with both doesn't confuse them. bool wasTabTapped(int& id) const; bool wasCoverTapped(int& id) const; + // True on a touch release anywhere on screen, with logical/oriented coords. + bool wasScreenTapped(int& x, int& y) const; // Swipe direction in the current logical (oriented) frame, or None. A swipe also // raises the tap helpers above, so check this first and consume it. SwipeDir wasSwipe() const; diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index 6ff5b719..8333a4f2 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -1,6 +1,7 @@ #include "OpdsBookBrowserActivity.h" #include +#include #include #include #include @@ -10,6 +11,7 @@ #include "SilentRestart.h" #include "activities/network/WifiSelectionActivity.h" #include "activities/util/KeyboardEntryActivity.h" +#include "components/TouchRegistry.h" #include "components/UITheme.h" #include "fontIds.h" #include "network/HttpDownloader.h" @@ -91,11 +93,28 @@ void OpdsBookBrowserActivity::loop() { if (state == BrowserState::DOWNLOADING) return; if (state == BrowserState::BROWSING) { - if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { - if (!entries.empty()) { - const auto& entry = entries[selectorIndex]; - entry.type == OpdsEntryType::BOOK ? downloadBook(entry) : navigateToEntry(entry); + if (!entries.empty()) { + if (mappedInput.wasListScroll(selectorIndex, static_cast(entries.size()), PAGE_ITEMS)) { + requestUpdate(); + return; } + + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && + freeink::ui::listSelectIndex(selectorIndex, downId, static_cast(entries.size()))) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < static_cast(entries.size())) { + selectorIndex = tappedId; + activateSelectedEntry(); + return; + } + } + + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { + activateSelectedEntry(); } else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { navigateBack(); } else if (mappedInput.wasReleased(MappedInputManager::Button::Left)) { @@ -178,13 +197,20 @@ void OpdsBookBrowserActivity::render(RenderLock&&) { std::string displayText = (entry.type == OpdsEntryType::NAVIGATION) ? "> " + entry.title : entry.title; if (entry.type == OpdsEntryType::BOOK && !entry.author.empty()) displayText += " - " + entry.author; auto item = renderer.truncatedText(UI_10_FONT_ID, displayText.c_str(), pageWidth - 40); - renderer.drawText(UI_10_FONT_ID, 20, 60 + (i % PAGE_ITEMS) * 30, item.c_str(), - i != static_cast(selectorIndex)); + const int itemY = 60 + (i % PAGE_ITEMS) * 30; + renderer.drawText(UI_10_FONT_ID, 20, itemY, item.c_str(), i != static_cast(selectorIndex)); + TouchRegistry::getInstance().add(Rect{0, itemY - 2, pageWidth, 30}, static_cast(i), TouchRegistry::Item); } } renderer.displayBuffer(); } +void OpdsBookBrowserActivity::activateSelectedEntry() { + if (entries.empty() || selectorIndex < 0 || selectorIndex >= static_cast(entries.size())) return; + const auto& entry = entries[selectorIndex]; + entry.type == OpdsEntryType::BOOK ? downloadBook(entry) : navigateToEntry(entry); +} + void OpdsBookBrowserActivity::fetchFeed(const std::string& path) { if (server.url.empty()) { state = BrowserState::ERROR; diff --git a/src/activities/browser/OpdsBookBrowserActivity.h b/src/activities/browser/OpdsBookBrowserActivity.h index 437ba58b..bf8642b0 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.h +++ b/src/activities/browser/OpdsBookBrowserActivity.h @@ -51,5 +51,6 @@ class OpdsBookBrowserActivity final : public Activity { void downloadBook(const OpdsEntry& book); void launchSearch(); void performSearch(const std::string& query); + void activateSelectedEntry(); bool preventAutoSleep() override { return true; } }; diff --git a/src/activities/home/CrashActivity.cpp b/src/activities/home/CrashActivity.cpp index 4f5a392b..6ef4b71d 100644 --- a/src/activities/home/CrashActivity.cpp +++ b/src/activities/home/CrashActivity.cpp @@ -20,7 +20,9 @@ void CrashActivity::onEnter() { } void CrashActivity::loop() { - if (mappedInput.isPressed(MappedInputManager::Button::Back)) { + int tapX = 0; + int tapY = 0; + if (mappedInput.isPressed(MappedInputManager::Button::Back) || mappedInput.wasScreenTapped(tapX, tapY)) { finish(); } } diff --git a/src/activities/reader/EpubReaderFootnotesActivity.cpp b/src/activities/reader/EpubReaderFootnotesActivity.cpp index 390dd3c5..a6d4ea59 100644 --- a/src/activities/reader/EpubReaderFootnotesActivity.cpp +++ b/src/activities/reader/EpubReaderFootnotesActivity.cpp @@ -1,11 +1,13 @@ #include "EpubReaderFootnotesActivity.h" +#include #include #include #include #include "MappedInputManager.h" +#include "components/TouchRegistry.h" #include "components/UITheme.h" #include "fontIds.h" @@ -18,6 +20,26 @@ void EpubReaderFootnotesActivity::onEnter() { void EpubReaderFootnotesActivity::onExit() { Activity::onExit(); } void EpubReaderFootnotesActivity::loop() { + const int visibleCount = std::max(1, renderer.getScreenHeight() / 36); + if (mappedInput.wasListScroll(selectedIndex, static_cast(footnotes.size()), visibleCount)) { + requestUpdate(); + return; + } + + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && + freeink::ui::listSelectIndex(selectedIndex, downId, static_cast(footnotes.size()))) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < static_cast(footnotes.size())) { + selectedIndex = tappedId; + setResult(FootnoteResult{footnotes[selectedIndex].href}); + finish(); + return; + } + if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { ActivityResult result; result.isCancelled = true; @@ -102,6 +124,7 @@ void EpubReaderFootnotesActivity::render(RenderLock&&) { label = tr(STR_LINK); } renderer.drawText(UI_10_FONT_ID, marginLeft, y + 4, label.c_str(), !isSelected); + TouchRegistry::getInstance().add(Rect{contentX, y, contentWidth, lineHeight}, i, TouchRegistry::Item); } const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "", ""); diff --git a/src/activities/reader/EpubReaderPercentSelectionActivity.cpp b/src/activities/reader/EpubReaderPercentSelectionActivity.cpp index 78cb2683..0adc3c8e 100644 --- a/src/activities/reader/EpubReaderPercentSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderPercentSelectionActivity.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "MappedInputManager.h" #include "components/UITheme.h" #include "fontIds.h" @@ -32,6 +34,22 @@ void EpubReaderPercentSelectionActivity::adjustPercent(const int delta) { requestUpdate(); } +bool EpubReaderPercentSelectionActivity::setPercentFromTouch(const int x, const int y) { + auto& theme = UITheme::getInstance(); + const auto metrics = theme.getMetrics(); + const Rect screen = theme.getScreenSafeArea(renderer, true, false); + const int contentTop = screen.y + metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing * 4; + constexpr int barWidth = 360; + constexpr int barHeight = 16; + const int barX = screen.x + (screen.width - barWidth) / 2; + const int barY = contentTop + metrics.verticalSpacing * 2; + if (y < barY - 28 || y > barY + barHeight + 36 || x < barX - 20 || x > barX + barWidth + 20) return false; + + const int clampedX = std::max(barX, std::min(x, barX + barWidth)); + percent = (clampedX - barX) * 100 / barWidth; + return true; +} + void EpubReaderPercentSelectionActivity::loop() { // Back cancels, confirm selects, arrows adjust the percent. if (mappedInput.wasReleased(MappedInputManager::Button::Back)) { @@ -48,6 +66,31 @@ void EpubReaderPercentSelectionActivity::loop() { return; } + switch (mappedInput.wasSwipe()) { + case MappedInputManager::SwipeDir::Left: + adjustPercent(-kSmallStep); + return; + case MappedInputManager::SwipeDir::Right: + adjustPercent(kSmallStep); + return; + case MappedInputManager::SwipeDir::Up: + adjustPercent(kLargeStep); + return; + case MappedInputManager::SwipeDir::Down: + adjustPercent(-kLargeStep); + return; + case MappedInputManager::SwipeDir::None: + break; + } + + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY) && setPercentFromTouch(tapX, tapY)) { + setResult(PercentResult{percent}); + finish(); + return; + } + buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Left}, [this] { adjustPercent(-kSmallStep); }); buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Right}, [this] { adjustPercent(kSmallStep); }); diff --git a/src/activities/reader/EpubReaderPercentSelectionActivity.h b/src/activities/reader/EpubReaderPercentSelectionActivity.h index 8cba8664..1bbd7707 100644 --- a/src/activities/reader/EpubReaderPercentSelectionActivity.h +++ b/src/activities/reader/EpubReaderPercentSelectionActivity.h @@ -24,4 +24,5 @@ class EpubReaderPercentSelectionActivity final : public Activity { // Change the current percent by a delta and clamp within bounds. void adjustPercent(int delta); + bool setPercentFromTouch(int x, int y); }; diff --git a/src/activities/reader/QrDisplayActivity.cpp b/src/activities/reader/QrDisplayActivity.cpp index 2bcf330a..6bef36cb 100644 --- a/src/activities/reader/QrDisplayActivity.cpp +++ b/src/activities/reader/QrDisplayActivity.cpp @@ -21,6 +21,13 @@ void QrDisplayActivity::loop() { finish(); return; } + + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY)) { + finish(); + return; + } } void QrDisplayActivity::render(RenderLock&&) { diff --git a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp index b95ac2b1..06ed7d0b 100644 --- a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp @@ -1,11 +1,13 @@ #include "XtcReaderChapterSelectionActivity.h" +#include #include #include #include #include "MappedInputManager.h" +#include "components/TouchRegistry.h" #include "components/UITheme.h" #include "fontIds.h" @@ -62,6 +64,20 @@ void XtcReaderChapterSelectionActivity::loop() { return; } + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && freeink::ui::listSelectIndex(selectorIndex, downId, totalItems)) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < totalItems) { + selectorIndex = tappedId; + const auto& chapters = xtc->getChapters(); + setResult(PageResult{chapters[selectorIndex].startPage}); + finish(); + return; + } + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { const auto& chapters = xtc->getChapters(); if (!chapters.empty() && selectorIndex >= 0 && selectorIndex < static_cast(chapters.size())) { @@ -133,7 +149,9 @@ void XtcReaderChapterSelectionActivity::render(RenderLock&&) { for (int i = pageStartIndex; i < static_cast(chapters.size()) && i < pageStartIndex + pageItems; i++) { const auto& chapter = chapters[i]; const char* title = chapter.name.empty() ? tr(STR_UNNAMED) : chapter.name.c_str(); - renderer.drawText(UI_10_FONT_ID, contentX + 20, 60 + contentY + (i % pageItems) * 30, title, i != selectorIndex); + const int itemY = 60 + contentY + (i % pageItems) * 30; + renderer.drawText(UI_10_FONT_ID, contentX + 20, itemY, title, i != selectorIndex); + TouchRegistry::getInstance().add(Rect{contentX, itemY - 2, contentWidth, 30}, i, TouchRegistry::Item); } // Skip button hints in landscape CW mode (they overlap content) diff --git a/src/activities/settings/ClearCacheActivity.cpp b/src/activities/settings/ClearCacheActivity.cpp index b0dcbfbb..efa63c3a 100644 --- a/src/activities/settings/ClearCacheActivity.cpp +++ b/src/activities/settings/ClearCacheActivity.cpp @@ -122,6 +122,27 @@ void ClearCacheActivity::clearCache() { void ClearCacheActivity::loop() { if (state == WARNING) { + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY)) { + const int actionTop = renderer.getScreenHeight() - UITheme::getInstance().getMetrics().buttonHintsHeight - 12; + if (tapY >= actionTop) { + if (tapX < renderer.getScreenWidth() / 2) { + LOG_DBG("CLEAR_CACHE", "User cancelled via touch"); + goBack(); + } else { + LOG_DBG("CLEAR_CACHE", "User confirmed via touch, starting cache clear"); + { + RenderLock lock(*this); + state = CLEARING; + } + requestUpdateAndWait(); + clearCache(); + } + return; + } + } + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { LOG_DBG("CLEAR_CACHE", "User confirmed, starting cache clear"); { @@ -141,7 +162,9 @@ void ClearCacheActivity::loop() { } if (state == SUCCESS || state == FAILED) { - if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + int tapX = 0; + int tapY = 0; + if (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasScreenTapped(tapX, tapY)) { goBack(); } return; diff --git a/src/activities/settings/ClockSyncActivity.cpp b/src/activities/settings/ClockSyncActivity.cpp index 407af69a..1f75192f 100644 --- a/src/activities/settings/ClockSyncActivity.cpp +++ b/src/activities/settings/ClockSyncActivity.cpp @@ -62,6 +62,13 @@ void ClockSyncActivity::loop() { if (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { finish(); + return; + } + + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY)) { + finish(); } } diff --git a/src/activities/settings/FontDownloadActivity.cpp b/src/activities/settings/FontDownloadActivity.cpp index cc381b4b..2bf24314 100644 --- a/src/activities/settings/FontDownloadActivity.cpp +++ b/src/activities/settings/FontDownloadActivity.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -420,6 +421,38 @@ bool FontDownloadActivity::isSelectedFamilyDeletable() const { return family.installed && !family.hasUpdate; } +void FontDownloadActivity::activateSelectedItem() { + if (families_.empty()) return; + + if (isDownloadAllRow(selectedIndex_)) { + currentFileIndex_ = 0; + currentFileTotal_ = 0; + for (const auto& f : families_) { + if (!f.installed) currentFileTotal_ += f.files.size(); + } + + downloadAll(); + } else if (isUpdateAllRow(selectedIndex_)) { + currentFileIndex_ = 0; + currentFileTotal_ = 0; + for (const auto& f : families_) { + if (f.hasUpdate) currentFileTotal_ += f.files.size(); + } + updateAll(); + } else { + auto& family = families_[familyIndexFromList(selectedIndex_)]; + if (!family.installed || family.hasUpdate) { + currentFileIndex_ = 0; + currentFileTotal_ = family.files.size(); + downloadFamily(family); + } else { + promptDeleteSelectedFamily(); + return; + } + } + requestUpdateAndWait(); +} + // --- Input handling --- void FontDownloadActivity::loop() { @@ -437,6 +470,18 @@ void FontDownloadActivity::loop() { return; } + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && freeink::ui::listSelectIndex(selectedIndex_, downId, listSize)) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < listSize) { + selectedIndex_ = tappedId; + activateSelectedItem(); + return; + } + buttonNavigator_.onNextRelease([this, listSize] { selectedIndex_ = ButtonNavigator::nextIndex(selectedIndex_, listSize); requestUpdate(); @@ -458,36 +503,8 @@ void FontDownloadActivity::loop() { }); if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { - if (!families_.empty()) { - if (isDownloadAllRow(selectedIndex_)) { - currentFileIndex_ = 0; - currentFileTotal_ = 0; - for (const auto& f : families_) { - if (!f.installed) currentFileTotal_ += f.files.size(); - } - - downloadAll(); - } else if (isUpdateAllRow(selectedIndex_)) { - currentFileIndex_ = 0; - currentFileTotal_ = 0; - for (const auto& f : families_) { - if (f.hasUpdate) currentFileTotal_ += f.files.size(); - } - updateAll(); - } else { - auto& family = families_[familyIndexFromList(selectedIndex_)]; - if (!family.installed || family.hasUpdate) { - currentFileIndex_ = 0; - currentFileTotal_ = family.files.size(); - downloadFamily(family); - } else { - promptDeleteSelectedFamily(); - return; - } - } - requestUpdateAndWait(); - return; - } + activateSelectedItem(); + return; } } else if (state_ == COMPLETE) { if (mappedInput.wasPressed(MappedInputManager::Button::Back) || diff --git a/src/activities/settings/FontDownloadActivity.h b/src/activities/settings/FontDownloadActivity.h index 7686b24b..38a45e13 100644 --- a/src/activities/settings/FontDownloadActivity.h +++ b/src/activities/settings/FontDownloadActivity.h @@ -99,6 +99,7 @@ class FontDownloadActivity : public Activity { bool isDownloadAllRow(int index) const; bool isUpdateAllRow(int index) const; bool isSelectedFamilyDeletable() const; + void activateSelectedItem(); void promptDeleteSelectedFamily(); void onDeleteConfirmationResult(const ActivityResult& result); int familyIndexFromList(int listIndex) const { return listIndex - specialRowCount(); } diff --git a/src/activities/settings/KOReaderAuthActivity.cpp b/src/activities/settings/KOReaderAuthActivity.cpp index 72921c16..e70dc7d9 100644 --- a/src/activities/settings/KOReaderAuthActivity.cpp +++ b/src/activities/settings/KOReaderAuthActivity.cpp @@ -101,8 +101,10 @@ void KOReaderAuthActivity::render(RenderLock&&) { void KOReaderAuthActivity::loop() { if (state == SUCCESS || state == FAILED) { + int tapX = 0; + int tapY = 0; if (mappedInput.wasPressed(MappedInputManager::Button::Back) || - mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + mappedInput.wasPressed(MappedInputManager::Button::Confirm) || mappedInput.wasScreenTapped(tapX, tapY)) { finish(); } } diff --git a/src/activities/settings/KOReaderSettingsActivity.cpp b/src/activities/settings/KOReaderSettingsActivity.cpp index 60edddcc..9aab7d08 100644 --- a/src/activities/settings/KOReaderSettingsActivity.cpp +++ b/src/activities/settings/KOReaderSettingsActivity.cpp @@ -11,6 +11,7 @@ #include "activities/util/KeyboardEntryActivity.h" #include "components/UITheme.h" #include "fontIds.h" +#include namespace { constexpr int MENU_ITEMS = 5; @@ -33,6 +34,19 @@ void KOReaderSettingsActivity::loop() { return; } + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && freeink::ui::listSelectIndex(selectedIndex, downId, MENU_ITEMS)) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < MENU_ITEMS) { + selectedIndex = tappedId; + handleSelection(); + requestUpdate(); + return; + } + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { handleSelection(); return; diff --git a/src/activities/settings/OpdsSettingsActivity.cpp b/src/activities/settings/OpdsSettingsActivity.cpp index ec07c1f9..a3a7f914 100644 --- a/src/activities/settings/OpdsSettingsActivity.cpp +++ b/src/activities/settings/OpdsSettingsActivity.cpp @@ -11,6 +11,7 @@ #include "activities/util/KeyboardEntryActivity.h" #include "components/UITheme.h" #include "fontIds.h" +#include namespace { // Editable fields: Name, URL, Username, Password. @@ -53,12 +54,26 @@ void OpdsSettingsActivity::loop() { return; } + const int menuItems = getMenuItemCount(); + + int downId = -1; + if (mappedInput.wasItemTouchedDown(downId) && freeink::ui::listSelectIndex(selectedIndex, downId, menuItems)) { + requestUpdate(); + } + + int tappedId = -1; + if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < menuItems) { + selectedIndex = tappedId; + handleSelection(); + requestUpdate(); + return; + } + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { handleSelection(); return; } - const int menuItems = getMenuItemCount(); buttonNavigator.onNext([this, menuItems] { selectedIndex = (selectedIndex + 1) % menuItems; requestUpdate(); diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index 905e1933..3f19dc88 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -145,7 +145,21 @@ void OtaUpdateActivity::render(RenderLock&&) { void OtaUpdateActivity::loop() { if (state == WAITING_CONFIRMATION) { - if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + int tapX = 0; + int tapY = 0; + bool touchUpdate = false; + if (mappedInput.wasScreenTapped(tapX, tapY)) { + const int actionTop = renderer.getScreenHeight() - UITheme::getInstance().getMetrics().buttonHintsHeight - 12; + if (tapY >= actionTop) { + if (tapX < renderer.getScreenWidth() / 2) { + finish(); + return; + } + touchUpdate = true; + } + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm) || touchUpdate) { LOG_DBG("OTA", "New update available, starting download..."); { RenderLock lock(*this); @@ -192,14 +206,18 @@ void OtaUpdateActivity::loop() { } if (state == FAILED) { - if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + int tapX = 0; + int tapY = 0; + if (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasScreenTapped(tapX, tapY)) { finish(); } return; } if (state == NO_UPDATE) { - if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + int tapX = 0; + int tapY = 0; + if (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasScreenTapped(tapX, tapY)) { finish(); } return; diff --git a/src/activities/settings/SdFirmwareUpdateActivity.cpp b/src/activities/settings/SdFirmwareUpdateActivity.cpp index b0ca1f63..f7dc54c7 100644 --- a/src/activities/settings/SdFirmwareUpdateActivity.cpp +++ b/src/activities/settings/SdFirmwareUpdateActivity.cpp @@ -186,8 +186,10 @@ void SdFirmwareUpdateActivity::performUpdate() { void SdFirmwareUpdateActivity::loop() { if (state == State::FAILED) { + int tapX = 0; + int tapY = 0; if (mappedInput.wasPressed(MappedInputManager::Button::Back) || - mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + mappedInput.wasPressed(MappedInputManager::Button::Confirm) || mappedInput.wasScreenTapped(tapX, tapY)) { if (recoveryMode) { // Go back to picker so user can try a different .bin state = State::PICKING; diff --git a/src/activities/util/BmpViewerActivity.cpp b/src/activities/util/BmpViewerActivity.cpp index def41015..f36d3425 100644 --- a/src/activities/util/BmpViewerActivity.cpp +++ b/src/activities/util/BmpViewerActivity.cpp @@ -185,6 +185,36 @@ void BmpViewerActivity::loop() { return; } + const auto swipe = mappedInput.wasSwipe(); + if (swipe == MappedInputManager::SwipeDir::Right) { + if (siblingImages.size() > 1 && currentImageIndex > 0) { + currentImageIndex--; + std::string dirPath = FsHelpers::extractFolderPath(filePath); + if (dirPath.back() != '/') dirPath += "/"; + filePath = dirPath + siblingImages[currentImageIndex]; + onEnter(); + } + return; + } + if (swipe == MappedInputManager::SwipeDir::Left) { + if (siblingImages.size() > 1 && currentImageIndex != -1 && + currentImageIndex < static_cast(siblingImages.size()) - 1) { + currentImageIndex++; + std::string dirPath = FsHelpers::extractFolderPath(filePath); + if (dirPath.back() != '/') dirPath += "/"; + filePath = dirPath + siblingImages[currentImageIndex]; + onEnter(); + } + return; + } + + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY)) { + doSetSleepCover(); + return; + } + if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { doSetSleepCover(); return; @@ -214,4 +244,4 @@ void BmpViewerActivity::loop() { } return; } -} \ No newline at end of file +} diff --git a/src/activities/util/IntervalSelectionActivity.cpp b/src/activities/util/IntervalSelectionActivity.cpp index 8323b7cb..ea4c3f5b 100644 --- a/src/activities/util/IntervalSelectionActivity.cpp +++ b/src/activities/util/IntervalSelectionActivity.cpp @@ -25,6 +25,20 @@ void IntervalSelectionActivity::adjustValue(const int delta) { requestUpdate(); } +bool IntervalSelectionActivity::setValueFromTouch(const int x, const int y) { + const int screenWidth = renderer.getScreenWidth(); + const int barWidth = std::min(360, std::max(0, screenWidth - 40)); + constexpr int barHeight = 16; + const int barX = std::max(0, (screenWidth - barWidth) / 2); + const int barY = 140; + if (y < barY - 28 || y > barY + barHeight + 36 || x < barX - 20 || x > barX + barWidth + 20) return false; + + const int clampedX = std::max(barX, std::min(x, barX + barWidth)); + const int range = std::max(1, maxValue - minValue); + value = clampedValue(minValue + (clampedX - barX) * range / std::max(1, barWidth)); + return true; +} + void IntervalSelectionActivity::loop() { if (ignoreConfirmRelease) { if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) { @@ -50,6 +64,31 @@ void IntervalSelectionActivity::loop() { return; } + switch (mappedInput.wasSwipe()) { + case MappedInputManager::SwipeDir::Left: + adjustValue(-smallStep); + return; + case MappedInputManager::SwipeDir::Right: + adjustValue(smallStep); + return; + case MappedInputManager::SwipeDir::Up: + adjustValue(largeStep); + return; + case MappedInputManager::SwipeDir::Down: + adjustValue(-largeStep); + return; + case MappedInputManager::SwipeDir::None: + break; + } + + int tapX = 0; + int tapY = 0; + if (mappedInput.wasScreenTapped(tapX, tapY) && setValueFromTouch(tapX, tapY)) { + setResult(IntervalResult{static_cast(value)}); + finish(); + return; + } + buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Left}, [this] { adjustValue(-smallStep); }); buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Right}, [this] { adjustValue(smallStep); }); buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Up}, [this] { adjustValue(largeStep); }); diff --git a/src/activities/util/IntervalSelectionActivity.h b/src/activities/util/IntervalSelectionActivity.h index d20fc0c4..ee0fc108 100644 --- a/src/activities/util/IntervalSelectionActivity.h +++ b/src/activities/util/IntervalSelectionActivity.h @@ -49,4 +49,5 @@ class IntervalSelectionActivity final : public Activity { void adjustValue(int delta); int clampedValue(int candidate) const; + bool setValueFromTouch(int x, int y); };