Add touch-down visual feedback to settings menus

Handle touch-down events separately from tap events in settings activities to provide immediate visual feedback when menu items are touched, before the tap is completed. This improves UI responsiveness by updating the selected index on touch-down.
This commit is contained in:
Justin Mitchell
2026-06-19 14:41:21 -04:00
parent 06ff5aa44a
commit 57c389c0b6
25 changed files with 351 additions and 52 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "freeink-sdk"]
path = freeink-sdk
url = https://github.com/Free-Ink/freeink-sdk.git
branch = main
branch = sticky
+1
View File
@@ -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
+10 -4
View File
@@ -1,5 +1,6 @@
#include "MappedInputManager.h"
#include <FreeInkUI.h>
#include <GfxRenderer.h>
#include <algorithm>
@@ -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;
}
+2
View File
@@ -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;
@@ -1,6 +1,7 @@
#include "OpdsBookBrowserActivity.h"
#include <GfxRenderer.h>
#include <FreeInkUI.h>
#include <I18n.h>
#include <Logging.h>
#include <OpdsStream.h>
@@ -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<int>(entries.size()), PAGE_ITEMS)) {
requestUpdate();
return;
}
int downId = -1;
if (mappedInput.wasItemTouchedDown(downId) &&
freeink::ui::listSelectIndex(selectorIndex, downId, static_cast<int>(entries.size()))) {
requestUpdate();
}
int tappedId = -1;
if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < static_cast<int>(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<size_t>(selectorIndex));
const int itemY = 60 + (i % PAGE_ITEMS) * 30;
renderer.drawText(UI_10_FONT_ID, 20, itemY, item.c_str(), i != static_cast<size_t>(selectorIndex));
TouchRegistry::getInstance().add(Rect{0, itemY - 2, pageWidth, 30}, static_cast<int>(i), TouchRegistry::Item);
}
}
renderer.displayBuffer();
}
void OpdsBookBrowserActivity::activateSelectedEntry() {
if (entries.empty() || selectorIndex < 0 || selectorIndex >= static_cast<int>(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;
@@ -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; }
};
+3 -1
View File
@@ -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();
}
}
@@ -1,11 +1,13 @@
#include "EpubReaderFootnotesActivity.h"
#include <FreeInkUI.h>
#include <GfxRenderer.h>
#include <I18n.h>
#include <algorithm>
#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<int>(footnotes.size()), visibleCount)) {
requestUpdate();
return;
}
int downId = -1;
if (mappedInput.wasItemTouchedDown(downId) &&
freeink::ui::listSelectIndex(selectedIndex, downId, static_cast<int>(footnotes.size()))) {
requestUpdate();
}
int tappedId = -1;
if (mappedInput.wasItemTapped(tappedId) && tappedId >= 0 && tappedId < static_cast<int>(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), "", "");
@@ -3,6 +3,8 @@
#include <GfxRenderer.h>
#include <I18n.h>
#include <algorithm>
#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); });
@@ -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);
};
@@ -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&&) {
@@ -1,11 +1,13 @@
#include "XtcReaderChapterSelectionActivity.h"
#include <FreeInkUI.h>
#include <GfxRenderer.h>
#include <I18n.h>
#include <algorithm>
#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<int>(chapters.size())) {
@@ -133,7 +149,9 @@ void XtcReaderChapterSelectionActivity::render(RenderLock&&) {
for (int i = pageStartIndex; i < static_cast<int>(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)
+24 -1
View File
@@ -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;
@@ -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();
}
}
@@ -2,6 +2,7 @@
#include <ArduinoJson.h>
#include <GfxRenderer.h>
#include <FreeInkUI.h>
#include <HalStorage.h>
#include <I18n.h>
#include <Logging.h>
@@ -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) ||
@@ -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(); }
@@ -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();
}
}
@@ -11,6 +11,7 @@
#include "activities/util/KeyboardEntryActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include <FreeInkUI.h>
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;
@@ -11,6 +11,7 @@
#include "activities/util/KeyboardEntryActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include <FreeInkUI.h>
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();
+21 -3
View File
@@ -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;
@@ -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;
+31 -1
View File
@@ -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<int>(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;
}
}
}
@@ -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<uint32_t>(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); });
@@ -49,4 +49,5 @@ class IntervalSelectionActivity final : public Activity {
void adjustValue(int delta);
int clampedValue(int candidate) const;
bool setValueFromTouch(int x, int y);
};