feat: Add touch coordinate mapping and RTOS task yielding (#2481)

Co-authored-by: Julia Nguyen <julia@uxj.io>
This commit is contained in:
Justin Mitchell
2026-07-20 16:31:07 -04:00
committed by GitHub
co-authored by Julia Nguyen
parent c9188a7347
commit f42fab1c66
123 changed files with 3564 additions and 1380 deletions
+3 -1
View File
@@ -20,7 +20,9 @@ void CrashActivity::onEnter() {
}
void CrashActivity::loop() {
if (mappedInput.isPressed(MappedInputManager::Button::Back)) {
int x = 0;
int y = 0;
if (mappedInput.wasPressed(MappedInputManager::Button::Back) || mappedInput.wasScreenTapped(x, y)) {
finish();
}
}
+35 -1
View File
@@ -205,8 +205,12 @@ void FileBrowserActivity::loop() {
const int pathReserved = renderer.getLineHeight(SMALL_FONT_ID) + UITheme::getInstance().getMetrics().verticalSpacing;
const int pageItems = UITheme::getNumberOfItemsPerPage(renderer, true, false, true, false, pathReserved);
const auto& metrics = UITheme::getInstance().getMetrics();
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const int contentHeight =
renderer.getScreenHeight() - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing - pathReserved;
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
auto activateSelected = [this] {
if (lockNextConfirmRelease) {
lockNextConfirmRelease = false;
return;
@@ -234,6 +238,11 @@ void FileBrowserActivity::loop() {
const std::string fullPath = cleanBasePath + entry;
auto handler = [this, fullPath](const ActivityResult& res) {
// The confirmation popup acts on button press; if that button is still
// held when we resume, swallow its release so it doesn't also act here
// (Back would go up a directory, Confirm would open the selection).
lockLongPressBack = mappedInput.isPressed(MappedInputManager::Button::Back);
lockNextConfirmRelease = mappedInput.isPressed(MappedInputManager::Button::Confirm);
if (!res.isCancelled) {
LOG_DBG("FileBrowser", "Attempting to delete: %s", fullPath.c_str());
if (removeDirFile(fullPath)) {
@@ -273,6 +282,19 @@ void FileBrowserActivity::loop() {
}
}
return;
};
int touchSel = static_cast<int>(selectorIndex);
const auto listTouch = handleListTouch(touchSel, static_cast<int>(files.size()), contentTop, contentHeight, false);
if (listTouch != ListTouchResult::None) {
selectorIndex = static_cast<size_t>(touchSel);
if (listTouch == ListTouchResult::Activated) activateSelected();
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
activateSelected();
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
@@ -303,6 +325,18 @@ void FileBrowserActivity::loop() {
}
int listSize = static_cast<int>(files.size());
const auto swipe = mappedInput.wasSwipe();
if (swipe == MappedInputManager::SwipeDir::Up) {
selectorIndex = ButtonNavigator::nextPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
requestUpdate();
return;
}
if (swipe == MappedInputManager::SwipeDir::Down) {
selectorIndex = ButtonNavigator::previousPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
requestUpdate();
return;
}
buttonNavigator.onNextRelease([this, listSize] {
selectorIndex = ButtonNavigator::nextIndex(static_cast<int>(selectorIndex), listSize);
requestUpdate();
+82 -24
View File
@@ -168,6 +168,34 @@ void HomeActivity::freeCoverBuffer() {
void HomeActivity::loop() {
const int menuCount = getMenuItemCount();
const auto& metrics = UITheme::getInstance().getMetrics();
auto activateSelection = [this] {
if (selectorIndex < recentBooks.size()) {
onSelectBook(recentBooks[selectorIndex].path);
return;
}
const int menuIndex = selectorIndex - static_cast<int>(recentBooks.size());
switch (indexToMenuItem(menuIndex, hasOpdsServers)) {
case HomeMenuItem::FILE_BROWSER:
onFileBrowserOpen();
break;
case HomeMenuItem::RECENTS:
onRecentsOpen();
break;
case HomeMenuItem::OPDS_BROWSER:
onOpdsBrowserOpen();
break;
case HomeMenuItem::FILE_TRANSFER:
onFileTransferOpen();
break;
case HomeMenuItem::SETTINGS_MENU:
onSettingsOpen();
break;
default:
break;
}
};
buttonNavigator.onNext([this, menuCount] {
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, menuCount);
@@ -179,6 +207,18 @@ void HomeActivity::loop() {
requestUpdate();
});
const auto swipe = mappedInput.wasSwipe();
if (swipe == MappedInputManager::SwipeDir::Up) {
selectorIndex = ButtonNavigator::nextIndex(selectorIndex, menuCount);
requestUpdate();
return;
}
if (swipe == MappedInputManager::SwipeDir::Down) {
selectorIndex = ButtonNavigator::previousIndex(selectorIndex, menuCount);
requestUpdate();
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) backPressSeen = true;
// Back is otherwise unused on the home menu: open the most recently read
@@ -190,31 +230,49 @@ void HomeActivity::loop() {
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (selectorIndex < recentBooks.size()) {
onSelectBook(recentBooks[selectorIndex].path);
} else {
const int menuIndex = selectorIndex - static_cast<int>(recentBooks.size());
switch (indexToMenuItem(menuIndex, hasOpdsServers)) {
case HomeMenuItem::FILE_BROWSER:
onFileBrowserOpen();
break;
case HomeMenuItem::RECENTS:
onRecentsOpen();
break;
case HomeMenuItem::OPDS_BROWSER:
onOpdsBrowserOpen();
break;
case HomeMenuItem::FILE_TRANSFER:
onFileTransferOpen();
break;
case HomeMenuItem::SETTINGS_MENU:
onSettingsOpen();
break;
default:
break;
}
int tx = 0;
int ty = 0;
if (!recentBooks.empty() && mappedInput.wasScreenTouchDown(tx, ty) && tx >= 0 && tx < renderer.getScreenWidth() &&
ty >= metrics.homeTopPadding && ty < metrics.homeTopPadding + metrics.homeCoverTileHeight) {
if (selectorIndex != 0) {
selectorIndex = 0;
requestUpdate();
}
return;
}
if (!recentBooks.empty() &&
mappedInput.wasTapInRect(0, metrics.homeTopPadding, renderer.getScreenWidth(), metrics.homeCoverTileHeight)) {
selectorIndex = 0;
activateSelection();
return;
}
const int menuTop = metrics.homeTopPadding + metrics.homeCoverTileHeight + metrics.homeMenuTopOffset;
const int renderedMenuSelection =
metrics.homeContinueReadingInMenu ? selectorIndex : selectorIndex - recentBooks.size();
const int renderedMenuCount =
menuCount - (metrics.homeContinueReadingInMenu ? 0 : static_cast<int>(recentBooks.size()));
int menuRow = -1;
const auto menuTouch = mappedInput.rowTouch(menuRow, menuTop, metrics.menuRowHeight + metrics.menuSpacing,
renderedMenuCount, 0, INT32_MAX, metrics.menuRowHeight);
if (menuTouch != MappedInputManager::RowTouch::None) {
const int touchedIndex =
metrics.homeContinueReadingInMenu ? menuRow : menuRow + static_cast<int>(recentBooks.size());
if (menuTouch == MappedInputManager::RowTouch::Down) {
if (selectorIndex != touchedIndex) {
selectorIndex = touchedIndex;
requestUpdate();
}
} else {
selectorIndex = touchedIndex;
activateSelection();
}
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
activateSelection();
}
}
+1
View File
@@ -80,4 +80,5 @@ class HomeActivity final : public Activity {
void onExit() override;
void loop() override;
void render(RenderLock&&) override;
bool isHomeActivity() const override { return true; }
};
@@ -43,6 +43,10 @@ void RecentBooksActivity::onExit() {
void RecentBooksActivity::loop() {
const int pageItems = UITheme::getInstance().getNumberOfItemsPerPage(renderer, true, false, true, true);
const auto& metrics = UITheme::getInstance().getMetrics();
const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const int contentHeight =
renderer.getScreenHeight() - contentTop - metrics.buttonHintsHeight - metrics.verticalSpacing;
// After a long-press has fired, swallow input until Confirm is physically released
// (so the release doesn't also open the book; re-arm only once the button is up).
@@ -71,11 +75,34 @@ void RecentBooksActivity::loop() {
}
}
int touchSel = static_cast<int>(selectorIndex);
const auto listTouch =
handleListTouch(touchSel, static_cast<int>(recentBooks.size()), contentTop, contentHeight, true);
if (listTouch != ListTouchResult::None) {
selectorIndex = static_cast<size_t>(touchSel);
if (listTouch == ListTouchResult::Activated) {
LOG_DBG("RBA", "Tapped recent book: %s", recentBooks[selectorIndex].path.c_str());
onSelectBook(recentBooks[selectorIndex].path);
}
return;
}
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
onGoHome();
}
int listSize = static_cast<int>(recentBooks.size());
const auto swipe = mappedInput.wasSwipe();
if (swipe == MappedInputManager::SwipeDir::Up) {
selectorIndex = ButtonNavigator::nextPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
requestUpdate();
return;
}
if (swipe == MappedInputManager::SwipeDir::Down) {
selectorIndex = ButtonNavigator::previousPageIndex(static_cast<int>(selectorIndex), listSize, pageItems);
requestUpdate();
return;
}
buttonNavigator.onNextRelease([this, listSize] {
selectorIndex = ButtonNavigator::nextIndex(static_cast<int>(selectorIndex), listSize);