Clarify touch gesture and input API comments

Simplify and clarify documentation for touch gesture detection (back gesture, item taps, long-press) and board detection flags. No functional changes, just improved comment readability.
This commit is contained in:
Justin Mitchell
2026-06-15 19:42:09 -04:00
parent 9f3dddabe6
commit cef0d267b7
13 changed files with 40 additions and 83 deletions
+3 -5
View File
@@ -59,11 +59,9 @@ class HalGPIO {
inline bool deviceIsX3() const { return _deviceType == DeviceType::X3; }
inline bool deviceIsX4() const { return _deviceType == DeviceType::X4; }
// True on the Xteink X3/X4 transflective C3 boards. Distinct from
// deviceIsX3/X4 (which only tell the two C3 variants apart and both stay
// "X4" on non-C3 boards): this keys off BoardConfig::ACTIVE.board, so it is
// the reliable "is this an Xteink device" check used to gate features that
// are Xteink-only (sunlight fading fix) or non-Xteink-only (touch controls).
// True on the Xteink X3/X4 boards. Unlike deviceIsX3/X4 (which both stay "X4"
// on non-C3 boards), this keys off BoardConfig::ACTIVE.board, so it reliably
// gates Xteink-only features (sunlight fix) and non-Xteink-only ones (touch).
bool isXteinkDevice() const;
// Start button GPIO and setup SPI for screen and SD card
+3 -6
View File
@@ -57,22 +57,19 @@ bool MappedInputManager::mapButton(const Button button, bool (HalGPIO::*fn)(uint
return false;
}
// Top-left corner of the panel (panel-native, normalized). Generous so it's easy
// to hit; v1 is not yet orientation-mapped (see wasBackGesture NOTE in header).
// Top-left corner fallback, as a fraction of the logical screen. Generous to hit.
static constexpr float BACK_GESTURE_FRAC_X = 0.22f;
static constexpr float BACK_GESTURE_FRAC_Y = 0.12f;
bool MappedInputManager::wasBackGesture() const {
float nx = 0.0f, ny = 0.0f;
if (!gpio.wasTouchTap(nx, ny)) return false;
// A tap on the theme's header back area (orientation-mapped) acts as Back.
int lx = 0, ly = 0;
renderer.tapToLogical(nx, ny, lx, ly);
// A tap on the theme's header Back target acts as Back.
int id = 0;
if (TouchRegistry::getInstance().hitTest(lx, ly, TouchRegistry::Back, id)) return true;
// Fallback corner gesture: top-left of the LOGICAL screen (orientation-mapped), so
// it lands on the visual top-left in every orientation and works on screens with no
// header/Back target (e.g. the reader).
// Else the top-left corner, for screens with no Back target (e.g. the reader).
return lx <= renderer.getScreenWidth() * BACK_GESTURE_FRAC_X &&
ly <= renderer.getScreenHeight() * BACK_GESTURE_FRAC_Y;
}
+10 -18
View File
@@ -21,28 +21,20 @@ class MappedInputManager {
bool wasPressed(Button button) const;
bool wasReleased(Button button) const;
bool isPressed(Button button) const;
// Reusable touch "back" gesture: a tap released in the top-left corner, OR a tap
// on the header back area registered by the theme. Folded into Back's press/
// release edges, so every screen gets it with no per-activity code. False on
// non-touch devices.
// Touch "back" gesture: a tap on the theme's header Back target, or in the
// top-left corner. Folded into Back's edges, so every screen gets it for free.
bool wasBackGesture() const;
// One-shot: if a tap this frame hit a registered interactive element (theme
// draw methods register them via TouchRegistry), returns true and writes the
// element's id. Activities treat the id as "select + activate". False on
// non-touch devices or when the tap missed every target.
// True (and writes the id) if a tap this frame hit a TouchRegistry item.
// Activities treat the id as "select + activate". False on non-touch devices.
bool wasItemTapped(int& id) const;
// Press-edge analogue of wasItemTapped: fires on touch-DOWN over an item, so the
// activity can move its selection to that item (showing the selected state) before
// release. Release still activates via wasItemTapped. Mirrors button nav (move
// selection, then confirm).
// Press-edge of wasItemTapped: fires on touch-DOWN over an item so the activity
// can show it selected before release. Mirrors button nav (move, then confirm).
bool wasItemTouchedDown(int& id) const;
// Long-press variant of wasItemTapped: true on release of a touch over an item
// held past the long-press threshold (a subset of wasItemTapped's releases, so
// check this first). Lets a screen distinguish tap vs press-and-hold on touch.
// Subset of wasItemTapped's releases held past the long-press threshold (check
// this first). Distinguishes tap vs press-and-hold.
bool wasItemLongPressed(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.
// wasItemTapped for tab-bar tabs (id = tab index) and cover/card targets
// (id = item index). Distinct kinds so a screen with both doesn't confuse them.
bool wasTabTapped(int& id) const;
bool wasCoverTapped(int& id) const;
bool wasAnyPressed() const;
+2 -6
View File
@@ -253,12 +253,8 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
SettingInfo::Toggle(StrId::STR_CLOCK_SYNCED, &CrossPointSettings::clockHasBeenSynced, "clockHasBeenSynced",
StrId::STR_CUSTOMISE_STATUS_BAR),
};
// The sunlight fading fix targets the Xteink X3/X4 transflective panel, so
// it only appears on those devices. Every other (touch) device instead gets
// the touch reader controls toggle (tap page back/forward + press-and-hold,
// mirroring the physical buttons). Keyed off the board identity
// (gpio.isXteinkDevice()), not gpio.hasTouch(), so the reader runtime gate
// and this visibility gate share one source of truth.
// Sunlight fading fix is Xteink-only (transflective panel); other devices get
// the touch reader controls toggle instead. Same gate as the reader runtime.
if (!gpio.isXteinkDevice()) {
v.erase(std::remove_if(v.begin(), v.end(),
[](const SettingInfo& s) { return s.nameId == StrId::STR_SUNLIGHT_FADING_FIX; }),
+3 -6
View File
@@ -179,12 +179,9 @@ void HomeActivity::loop() {
requestUpdate();
});
// A tap on a menu button selects + activates it. The button menu registers
// menu-local ids (it is drawn with selectorIndex offset by recentBooks.size()),
// so map the tapped id back into the global selector space. (The recent-book
// cover is a separate, single-item draw path — tappable in a later phase.)
// Touch-down moves the selector to the pressed menu button (shows selected state),
// like Up/Down; release opens it below.
// Tap a menu button to select + activate it. The button menu registers
// menu-local ids (drawn with selectorIndex offset by recentBooks.size()), so map
// back into the global selector space. Touch-down shows it selected; release opens.
int downId = -1;
if (mappedInput.wasItemTouchedDown(downId)) {
selectorIndex = static_cast<int>(recentBooks.size()) + downId;
+2 -6
View File
@@ -256,12 +256,8 @@ void EpubReaderActivity::loop() {
requestUpdate();
}
// Touch reader controls mirror the side page buttons (left third = back, right
// third = forward, press-and-hold = long-press behavior) and the Confirm
// button (center press-and-hold = open menu). The top-left Back corner is
// consumed by wasReleased(Back) earlier, so it never reaches here. No-op on
// Xteink / when the setting is off. wasTouchTap is idempotent within a frame,
// so reading it here and again below is safe.
// Touch page nav (idempotent within a frame, so read here and again below). The
// top-left Back corner is consumed by wasReleased(Back) above, never reaching here.
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
// Enter reader menu activity (Confirm release, or a center touch-and-hold).
+4 -8
View File
@@ -65,14 +65,10 @@ inline PageTurnResult detectPageTurn(const MappedInputManager& input) {
return {prev, next, tiltPrev || tiltNext};
}
// Touch reader controls: a tap on the left third of the (oriented) screen turns
// back a page, the right third turns forward, mirroring the side page buttons.
// The center third opens the reader menu on press-and-hold (see center/heldMs;
// the menu/Back gesture is handled by each reader). heldMs carries the contact
// duration so callers can apply the same long-press behavior (chapter skip /
// orientation change) as the buttons. Gated off the Xteink devices (no touch)
// and behind the touchReaderControls setting; returns all-false otherwise, so
// non-touch readers pay a single branch.
// Touch reader controls: left third = page back, right third = forward, center =
// open menu on press-and-hold (see isTouchMenuGesture). heldMs is the contact
// duration, for the same long-press behavior as the buttons (chapter skip).
// All-false on Xteink (no touch) or when the setting is off.
struct TouchPageTurn {
bool prev;
bool next;
+1 -2
View File
@@ -72,8 +72,7 @@ void TxtReaderActivity::loop() {
return;
}
// Touch reader controls mirror the side page buttons (left third = back,
// right third = forward). No-op on Xteink / when the setting is off.
// Touch page nav (left third = back, right third = forward).
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
+1 -4
View File
@@ -53,10 +53,7 @@ void XtcReaderActivity::onExit() {
}
void XtcReaderActivity::loop() {
// Touch reader controls mirror the side page buttons (left third = back, right
// third = forward, press-and-hold = chapter skip) and the Confirm button
// (center press-and-hold = chapter selection). No-op on Xteink / when the
// setting is off.
// Touch page nav; center hold opens chapter selection (the Confirm analogue).
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
// Enter chapter selection activity (Confirm release, or a center touch-and-hold).
+3 -5
View File
@@ -115,11 +115,9 @@ void SettingsActivity::onExit() {
void SettingsActivity::loop() {
bool hasChangedCategory = false;
// A tap on a settings row selects + activates it in one gesture. The list is drawn
// with selectedIndex = selectedSettingIndex - 1 (row 0 is the category tab), so map
// the tapped 0-based row back by +1. (Category tab bar is tappable in a later phase.)
// Touch-down moves the selection to the pressed row (shows selected state); release
// toggles/activates it below. (Row 0 is the tab bar, so settings list id 0 -> index 1.)
// Tap a settings row to select + activate it. Row 0 is the tab bar, so the list
// is drawn at selectedSettingIndex - 1; map the tapped row back by +1. Touch-down
// shows it selected; release toggles/activates below.
int downId = -1;
if (mappedInput.wasItemTouchedDown(downId) && downId >= 0 && downId < settingsCount) {
selectedSettingIndex = downId + 1;
@@ -337,11 +337,9 @@ void KeyboardEntryActivity::loop() {
}
}
// A tap selects the key and presses it. Encoded id = row*100+col (bottom function
// row = getContentRowCount()). A touch-and-hold inserts the alternate character
// (numbers/symbols on a letter), mirroring the button long-press — wasItemLongPressed
// is checked first since it's a subset of wasItemTapped's releases. Skipped in
// cursor mode, where a tap on a key would be ambiguous with cursor editing.
// Tap a key to press it (encoded id = row*100+col). Touch-and-hold inserts the
// alternate char, like the button long-press (check wasItemLongPressed first, it's
// a subset of these releases). Skipped in cursor mode (ambiguous with editing).
int tappedKey = -1;
if (!cursorMode && mappedInput.wasItemTapped(tappedKey)) {
selectedRow = tappedKey / 100;
+4 -9
View File
@@ -8,15 +8,10 @@
#include "components/themes/BaseTheme.h" // Rect
// Frame-scoped registry of tappable UI elements. Theme draw methods record each
// interactive element's LOGICAL rect + id during render() (on the render task);
// the input layer hit-tests a tap against it during the next loop() (main task).
//
// Lock-free single-writer (render) / single-reader (loop) via double buffering:
// render writes the back buffer and publish() atomically flips it to live, so the
// reader never observes a half-built frame. No per-frame heap allocation.
//
// Runtime-gated: disabled on boards without touch (setEnabled(gpio.hasTouch())),
// so add()/hitTest() are a single branch on the C3.
// element's logical rect + id during render() (render task); the input layer
// hit-tests a tap in the next loop() (main task). Lock-free via double buffering:
// render writes the back buffer, publish() flips it live. No per-frame heap.
// Runtime-gated off on boards without touch (setEnabled).
class TouchRegistry {
public:
enum Kind : uint8_t { Item = 0, Back = 1, Tab = 2, Cover = 3 };
+1 -3
View File
@@ -355,9 +355,7 @@ void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount,
}
void BaseTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* title, const char* subtitle) const {
// Left strip of the header is a tap-to-go-back zone (orientation-mapped; the
// title is centered so this area is normally empty). Mirrors the legacy corner
// gesture but as a real, theme-positioned target.
// Left strip of the header is a tap-to-go-back zone (title is centered, so it's empty).
TouchRegistry::getInstance().add(Rect{rect.x, rect.y, 64, rect.height + 8}, -1, TouchRegistry::Back);
// Hide last battery draw