Add Tab and Cover touch kinds to TouchRegistry
Extends the Kind enum with two new touch interaction types: Tab (2) and Cover (3), enabling support for additional UI element touch handling.
This commit is contained in:
@@ -82,6 +82,22 @@ bool MappedInputManager::wasItemTapped(int& id) const {
|
||||
return TouchRegistry::getInstance().hitTest(lx, ly, TouchRegistry::Item, id);
|
||||
}
|
||||
|
||||
bool MappedInputManager::wasTabTapped(int& id) const {
|
||||
float nx = 0.0f, ny = 0.0f;
|
||||
if (!gpio.wasTouchTap(nx, ny)) return false;
|
||||
int lx = 0, ly = 0;
|
||||
renderer.tapToLogical(nx, ny, lx, ly);
|
||||
return TouchRegistry::getInstance().hitTest(lx, ly, TouchRegistry::Tab, id);
|
||||
}
|
||||
|
||||
bool MappedInputManager::wasCoverTapped(int& id) const {
|
||||
float nx = 0.0f, ny = 0.0f;
|
||||
if (!gpio.wasTouchTap(nx, ny)) return false;
|
||||
int lx = 0, ly = 0;
|
||||
renderer.tapToLogical(nx, ny, lx, ly);
|
||||
return TouchRegistry::getInstance().hitTest(lx, ly, TouchRegistry::Cover, id);
|
||||
}
|
||||
|
||||
bool MappedInputManager::wasPressed(const Button button) const {
|
||||
// A top-left tap fires on the release frame; expose it on Back's press edge too
|
||||
// so menus that act on wasPressed(Back) also respond. Deliberately NOT folded
|
||||
|
||||
@@ -31,6 +31,11 @@ class MappedInputManager {
|
||||
// element's id. Activities treat the id as "select + activate". False on
|
||||
// non-touch devices or when the tap missed every target.
|
||||
bool wasItemTapped(int& id) const;
|
||||
// Like wasItemTapped, but for tab-bar tabs (id = tab index) and cover/card
|
||||
// targets (id = item index). Distinct kinds so screens with both a list and a
|
||||
// tab bar / cover (Home, Settings) don't confuse them.
|
||||
bool wasTabTapped(int& id) const;
|
||||
bool wasCoverTapped(int& id) const;
|
||||
bool wasAnyPressed() const;
|
||||
bool wasAnyReleased() const;
|
||||
unsigned long getHeldTime() const;
|
||||
|
||||
@@ -189,7 +189,14 @@ void HomeActivity::loop() {
|
||||
selectorIndex = static_cast<int>(recentBooks.size()) + tappedId;
|
||||
}
|
||||
|
||||
if (tapped || mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
// A tap on the continue-reading cover selects that book (home selector index).
|
||||
int coverId = -1;
|
||||
const bool coverTapped = mappedInput.wasCoverTapped(coverId);
|
||||
if (coverTapped && coverId >= 0 && coverId < static_cast<int>(recentBooks.size())) {
|
||||
selectorIndex = coverId;
|
||||
}
|
||||
|
||||
if (tapped || coverTapped || mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (selectorIndex < recentBooks.size()) {
|
||||
onSelectBook(recentBooks[selectorIndex].path);
|
||||
} else {
|
||||
|
||||
@@ -126,6 +126,15 @@ void SettingsActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// A tap on a category tab switches to it (keeps focus on the tab row); the
|
||||
// hasChangedCategory block below swaps in that category's settings list.
|
||||
int tabId = -1;
|
||||
if (mappedInput.wasTabTapped(tabId) && tabId >= 0 && tabId < categoryCount) {
|
||||
selectedCategoryIndex = tabId;
|
||||
selectedSettingIndex = 0;
|
||||
hasChangedCategory = true;
|
||||
}
|
||||
|
||||
// Handle actions with early return
|
||||
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
|
||||
if (selectedSettingIndex == 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "components/TouchRegistry.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
@@ -336,6 +337,18 @@ void KeyboardEntryActivity::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// A tap selects the key and presses it in one gesture. Encoded id = row*100+col
|
||||
// (bottom function row = getContentRowCount()). Skipped in cursor mode, where a
|
||||
// tap on a key would be ambiguous with cursor editing.
|
||||
int tappedKey = -1;
|
||||
if (!cursorMode && mappedInput.wasItemTapped(tappedKey)) {
|
||||
selectedRow = tappedKey / 100;
|
||||
selectedCol = tappedKey % 100;
|
||||
if (handleKeyPress()) {
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
if (confirmHeld && !confirmLongHandled && !cursorMode) {
|
||||
if (handleKeyPress()) {
|
||||
@@ -646,6 +659,8 @@ void KeyboardEntryActivity::render(RenderLock&&) {
|
||||
if (snippetIdx < URL_SNIPPET_COUNT) {
|
||||
GUI.drawKeyboardKey(renderer, Rect{keyX, rowY, keyWidth, keyHeight}, urlSnippets[snippetIdx],
|
||||
activeKeySelected, nullptr);
|
||||
TouchRegistry::getInstance().add(Rect{keyX, rowY, keyWidth, keyHeight}, row * 100 + col,
|
||||
TouchRegistry::Item);
|
||||
}
|
||||
} else {
|
||||
const KeyDef& key = layout[row][col];
|
||||
@@ -663,6 +678,7 @@ void KeyboardEntryActivity::render(RenderLock&&) {
|
||||
const bool showSecondary = !symMode && row == 0 && secondaryChar != '\0';
|
||||
GUI.drawKeyboardKey(renderer, Rect{keyX, rowY, keyWidth, keyHeight}, primaryBuf, activeKeySelected,
|
||||
showSecondary ? secondaryBuf : nullptr);
|
||||
TouchRegistry::getInstance().add(Rect{keyX, rowY, keyWidth, keyHeight}, row * 100 + col, TouchRegistry::Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,6 +707,8 @@ void KeyboardEntryActivity::render(RenderLock&&) {
|
||||
const bool activeKeySelected = isSelected && !cursorMode;
|
||||
GUI.drawKeyboardKey(renderer, Rect{keyX, bottomRowY, bottomKeyWidth, bottomKeyHeight}, bottomKeys[i].label,
|
||||
activeKeySelected, nullptr, bottomKeys[i].themeType);
|
||||
TouchRegistry::getInstance().add(Rect{keyX, bottomRowY, bottomKeyWidth, bottomKeyHeight}, contentRows * 100 + i,
|
||||
TouchRegistry::Item);
|
||||
}
|
||||
|
||||
if (cursorMode) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// so add()/hitTest() are a single branch on the C3.
|
||||
class TouchRegistry {
|
||||
public:
|
||||
enum Kind : uint8_t { Item = 0, Back = 1 };
|
||||
enum Kind : uint8_t { Item = 0, Back = 1, Tab = 2, Cover = 3 };
|
||||
|
||||
static TouchRegistry& getInstance();
|
||||
|
||||
@@ -36,7 +36,7 @@ class TouchRegistry {
|
||||
bool hitTest(int x, int y, Kind kind, int& outId) const;
|
||||
|
||||
private:
|
||||
static constexpr size_t CAPACITY = 48; // worst case excl. keyboard grid (Phase 2)
|
||||
static constexpr size_t CAPACITY = 64; // worst case is the keyboard grid (~45 keys)
|
||||
|
||||
struct Target {
|
||||
Rect rect;
|
||||
|
||||
@@ -419,10 +419,16 @@ void BaseTheme::drawTabBar(const GfxRenderer& renderer, const Rect rect, const s
|
||||
|
||||
int currentX = rect.x + BaseMetrics::values.contentSidePadding;
|
||||
|
||||
int tabIndex = 0;
|
||||
for (const auto& tab : tabs) {
|
||||
const int textWidth =
|
||||
renderer.getTextWidth(UI_12_FONT_ID, tab.label, tab.selected ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR);
|
||||
|
||||
// Tap target spans the label plus the inter-tab gap, full bar height.
|
||||
TouchRegistry::getInstance().add(
|
||||
Rect{currentX - 3, rect.y, textWidth + BaseMetrics::values.tabSpacing, rect.height}, tabIndex++,
|
||||
TouchRegistry::Tab);
|
||||
|
||||
// Draw underline for selected tab
|
||||
if (tab.selected) {
|
||||
if (selected) {
|
||||
@@ -495,6 +501,11 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
||||
const int bookY = rect.y;
|
||||
const int bookHeight = baseHeight;
|
||||
|
||||
// Tapping the continue-reading cover opens recentBooks[0] (home selector 0).
|
||||
if (hasContinueReading) {
|
||||
TouchRegistry::getInstance().add(Rect{bookX, bookY, bookWidth, bookHeight}, 0, TouchRegistry::Cover);
|
||||
}
|
||||
|
||||
// Bookmark dimensions (used in multiple places)
|
||||
const int bookmarkWidth = bookWidth / 8;
|
||||
const int bookmarkHeight = bookHeight / 5;
|
||||
|
||||
@@ -183,9 +183,14 @@ void LyraTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const std::ve
|
||||
renderer.fillRectDither(rect.x, rect.y, rect.width, rect.height, Color::LightGray);
|
||||
}
|
||||
|
||||
int tabIndex = 0;
|
||||
for (const auto& tab : tabs) {
|
||||
const int textWidth = renderer.getTextWidth(UI_10_FONT_ID, tab.label, EpdFontFamily::REGULAR);
|
||||
|
||||
TouchRegistry::getInstance().add(
|
||||
Rect{currentX, rect.y, textWidth + 2 * hPaddingInSelection + LyraMetrics::values.tabSpacing, rect.height},
|
||||
tabIndex++, TouchRegistry::Tab);
|
||||
|
||||
if (tab.selected) {
|
||||
if (selected) {
|
||||
renderer.fillRoundedRect(currentX, rect.y + 1, textWidth + 2 * hPaddingInSelection, rect.height - 4,
|
||||
@@ -419,6 +424,14 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
||||
coverWidth = LyraMetrics::values.homeCoverHeight * 0.6;
|
||||
}
|
||||
|
||||
// Tapping the continue-reading cover opens recentBooks[0] (home selector 0).
|
||||
if (hasContinueReading) {
|
||||
TouchRegistry::getInstance().add(
|
||||
Rect{LyraMetrics::values.contentSidePadding, tileY, coverWidth + 2 * hPaddingInSelection,
|
||||
LyraMetrics::values.homeCoverHeight + 2 * hPaddingInSelection},
|
||||
0, TouchRegistry::Cover);
|
||||
}
|
||||
|
||||
// Draw book card regardless, fill with message based on `hasContinueReading`
|
||||
// Draw cover image as background if available (inside the box)
|
||||
// Only load from SD on first render, then use stored buffer
|
||||
|
||||
@@ -99,6 +99,9 @@ void RoundedRaffTheme::drawTabBar(const GfxRenderer& renderer, Rect rect, const
|
||||
const int tabWidth = slotWidth - 8;
|
||||
const auto& tab = tabs[i];
|
||||
|
||||
TouchRegistry::getInstance().add(Rect{slotX, rect.y, slotWidth, rect.height}, static_cast<int>(i),
|
||||
TouchRegistry::Tab);
|
||||
|
||||
if (tab.selected) {
|
||||
renderer.fillRoundedRect(tabX, tabY, tabWidth, tabHeight, 18, selected ? Color::Black : Color::DarkGray);
|
||||
}
|
||||
@@ -126,6 +129,13 @@ void RoundedRaffTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con
|
||||
const int imgY = tileY + (tileHeight - RoundedRaffMetrics::values.homeCoverHeight) / 2;
|
||||
const int tileX = RoundedRaffMetrics::values.contentSidePadding;
|
||||
|
||||
// Tapping the continue-reading cover opens recentBooks[0] (home selector 0).
|
||||
if (hasContinueReading) {
|
||||
TouchRegistry::getInstance().add(
|
||||
Rect{tileX + (tileWidth - coverWidth) / 2, imgY, coverWidth, RoundedRaffMetrics::values.homeCoverHeight}, 0,
|
||||
TouchRegistry::Cover);
|
||||
}
|
||||
|
||||
// Draw book card regardless, fill with message based on `hasContinueReading`
|
||||
// Draw cover image as background if available (inside the box)
|
||||
// Only load from SD on first render, then use stored buffer
|
||||
|
||||
Reference in New Issue
Block a user