Add touch reader controls for page navigation
Implements tap zones on touch devices for page back/forward navigation and press-and-hold actions, mirroring physical button behavior. Adds isXteinkDevice() helper to distinguish Xteink X3/X4 boards from touch devices. The sunlight fading fix setting is now exclusive to Xteink devices, while touch devices get the new touch reader controls toggle instead.
This commit is contained in:
@@ -230,6 +230,9 @@ class CrossPointSettings {
|
||||
uint8_t uiTheme = LYRA;
|
||||
// Sunlight fading compensation
|
||||
uint8_t fadingFix = 0;
|
||||
// Touch reader controls: tap zones for page back/forward + press-and-hold,
|
||||
// mirroring the physical buttons. Touch devices only (hidden elsewhere).
|
||||
uint8_t touchReaderControls = 1;
|
||||
// Power button return from footnotes (1 = enabled, 0 = disabled)
|
||||
uint8_t pwrBtnFootnoteBack = 1;
|
||||
// Use book's embedded CSS styles for EPUB rendering (1 = enabled, 0 = disabled)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <HalClock.h>
|
||||
#include <HalGPIO.h>
|
||||
#include <HalTiltSensor.h>
|
||||
#include <I18n.h>
|
||||
#include <SdCardFontRegistry.h>
|
||||
@@ -252,6 +253,26 @@ 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.
|
||||
if (!gpio.isXteinkDevice()) {
|
||||
v.erase(std::remove_if(v.begin(), v.end(),
|
||||
[](const SettingInfo& s) { return s.nameId == StrId::STR_SUNLIGHT_FADING_FIX; }),
|
||||
v.end());
|
||||
for (auto it = v.begin(); it != v.end(); ++it) {
|
||||
if (it->nameId == StrId::STR_SIDE_BTN_LAYOUT) {
|
||||
v.insert(it, SettingInfo::Toggle(StrId::STR_TOUCH_READER_CONTROLS,
|
||||
&CrossPointSettings::touchReaderControls, "touchReaderControls",
|
||||
StrId::STR_CAT_CONTROLS));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only show tilt page turn setting when the QMI8658 IMU is present (X3)
|
||||
if (halTiltSensor.isAvailable()) {
|
||||
// Insert after the short power button setting (end of Controls section)
|
||||
|
||||
@@ -338,7 +338,15 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
// Touch reader controls mirror the side page buttons (left third = back,
|
||||
// right third = forward, press-and-hold = long-press behavior). 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.
|
||||
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
|
||||
|
||||
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
prevTriggered = prevTriggered || touch.prev;
|
||||
nextTriggered = nextTriggered || touch.next;
|
||||
if (!prevTriggered && !nextTriggered) {
|
||||
return;
|
||||
}
|
||||
@@ -356,7 +364,8 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool longPress = !fromTilt && mappedInput.getHeldTime() > ReaderUtils::SKIP_HOLD_MS;
|
||||
const unsigned long heldMs = (touch.prev || touch.next) ? touch.heldMs : mappedInput.getHeldTime();
|
||||
const bool longPress = !fromTilt && heldMs > ReaderUtils::SKIP_HOLD_MS;
|
||||
|
||||
// Don't skip chapter after screenshot
|
||||
if (gpio.wasReleased(HalGPIO::BTN_POWER) && gpio.wasReleased(HalGPIO::BTN_DOWN)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <CrossPointSettings.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalGPIO.h>
|
||||
#include <HalTiltSensor.h>
|
||||
#include <Logging.h>
|
||||
|
||||
@@ -61,6 +62,40 @@ 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.
|
||||
// heldMs carries the contact duration so callers can apply the same long-press
|
||||
// behavior (chapter skip / orientation change) as the buttons. The center column
|
||||
// is left for the menu/Back gesture handled by each reader. Gated off the Xteink
|
||||
// devices (no touch) and behind the touchReaderControls setting; returns all-false
|
||||
// otherwise, so non-touch readers pay a single branch.
|
||||
struct TouchPageTurn {
|
||||
bool prev;
|
||||
bool next;
|
||||
unsigned long heldMs;
|
||||
};
|
||||
|
||||
inline TouchPageTurn detectTouchPageTurn(GfxRenderer& renderer) {
|
||||
TouchPageTurn result{false, false, 0};
|
||||
if (gpio.isXteinkDevice() || !SETTINGS.touchReaderControls) {
|
||||
return result;
|
||||
}
|
||||
float nx = 0.0f, ny = 0.0f;
|
||||
if (!gpio.wasTouchTap(nx, ny)) {
|
||||
return result;
|
||||
}
|
||||
int lx = 0, ly = 0;
|
||||
renderer.tapToLogical(nx, ny, lx, ly);
|
||||
const int third = renderer.getScreenWidth() / 3;
|
||||
if (lx < third) {
|
||||
result.prev = true;
|
||||
} else if (lx >= 2 * third) {
|
||||
result.next = true;
|
||||
}
|
||||
result.heldMs = gpio.lastTouchHeldMs();
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void displayWithRefreshCycle(const GfxRenderer& renderer, int& pagesUntilFullRefresh) {
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
|
||||
@@ -72,7 +72,13 @@ void TxtReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
// Touch reader controls mirror the side page buttons (left third = back,
|
||||
// right third = forward). No-op on Xteink / when the setting is off.
|
||||
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
|
||||
|
||||
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
prevTriggered = prevTriggered || touch.prev;
|
||||
nextTriggered = nextTriggered || touch.next;
|
||||
if (!prevTriggered && !nextTriggered) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,14 @@ void XtcReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
// Touch reader controls mirror the side page buttons (left third = back,
|
||||
// right third = forward, press-and-hold = chapter skip). No-op on Xteink /
|
||||
// when the setting is off.
|
||||
const auto touch = ReaderUtils::detectTouchPageTurn(renderer);
|
||||
|
||||
auto [prevTriggered, nextTriggered, fromTilt] = ReaderUtils::detectPageTurn(mappedInput);
|
||||
prevTriggered = prevTriggered || touch.prev;
|
||||
nextTriggered = nextTriggered || touch.next;
|
||||
if (!prevTriggered && !nextTriggered) {
|
||||
return;
|
||||
}
|
||||
@@ -95,8 +102,9 @@ void XtcReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool skipPages = !fromTilt && SETTINGS.longPressButtonBehavior == SETTINGS.CHAPTER_SKIP &&
|
||||
mappedInput.getHeldTime() > ReaderUtils::SKIP_HOLD_MS;
|
||||
const unsigned long heldMs = (touch.prev || touch.next) ? touch.heldMs : mappedInput.getHeldTime();
|
||||
const bool skipPages =
|
||||
!fromTilt && SETTINGS.longPressButtonBehavior == SETTINGS.CHAPTER_SKIP && heldMs > ReaderUtils::SKIP_HOLD_MS;
|
||||
const int skipAmount = skipPages ? 10 : 1;
|
||||
|
||||
if (prevTriggered) {
|
||||
|
||||
Reference in New Issue
Block a user