fix(reader): correct slider side-button direction and legend on X3 (#2402) (#2428)

## Summary

* **What is the goal of this PR?** Fixes #2402. On the X3, the "Time to
Sleep"
picker's 5-minute side buttons were inverted (left increased, right
decreased)
  and the on-screen legend didn't match the physical buttons.
* **What changes are included?**
  * Flip the large-step (±5 min) direction on X3 so the left side button
decreases and the right increases, matching the layout. X4 is unchanged.
* Add a device-specific step-hint string
(`STR_SLEEP_TIMER_STEP_HINT_X3`)
selected via `gpio.deviceIsX3()`. X3 shows `+/-: 1 min Side buttons: 5
min`;
    X4 keeps the original `Left/Right: 1 min  Up/Down: 5 min`.
* Same fix applied to both slider pickers that read the raw up/down side
    buttons: the Time-to-Sleep picker and the "Go to %" picker
(`EpubReaderPercentSelectionActivity`), each with its own X3 hint string
    (`STR_SLEEP_TIMER_STEP_HINT_X3`, `STR_PERCENT_STEP_HINT_X3`).

## Additional Context

* **Root cause:** the X3's side buttons sit one on each edge of the
screen
(power on top), whereas the X4 has a vertical up/down rocker on the
right
edge. So `BTN_UP` is physically the *left* button on X3 but the *top*
button
  on X4. The picker mapped up→+5 / down→−5 unconditionally, which reads
naturally on the X4 rocker but inverted on the X3's left/right buttons.
The
  static legend ("Up/Down: 5 min") had the same X3-only mismatch.
* The codebase already detects the device at runtime and handles this
layout
difference elsewhere (e.g. `LyraTheme::drawSideButtonHints` draws "Up on
left,
Down on right" for X3), so this reuses the same `gpio.deviceIsX3()`
signal.
* Swept the codebase for other side-button sliders:
`ClockOffsetActivity` already
uses the `Next/Previous` (`ButtonNavigator`) abstraction, which resolves
to
left=decrement / right=increment on X3, so it needs no change. List/page
navigation (Wi-Fi, KOReader sync, BMP viewer) and the keyboard cursor
are also
unaffected. The two slider pickers above were the only ones using raw
up/down.
* **X4 is untouched** — same direction, same legend wording as before.
* **Tested on X3 hardware:** left side button now decreases, right
increases, and
  the legend matches. X4 not yet tested on device (no unit on hand); its
  behaviour and strings are unchanged from master.
* **Translations:** the new `STR_SLEEP_TIMER_STEP_HINT_X3` and
`STR_PERCENT_STEP_HINT_X3` were added to all 26 languages, but the
non-English
  ones are AI-generated and would benefit from a native-speaker pass —
particularly Hebrew (RTL ordering with a leading `+/-`), Kazakh, and
Vietnamese.

---

### AI Usage

Did you use AI tools to help write this code? _**YES**_

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom-Inge Larsen
2026-06-30 11:06:31 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent c9d27826f9
commit e4d3981469
30 changed files with 103 additions and 67 deletions
@@ -1,8 +1,11 @@
#include "EpubReaderPercentSelectionActivity.h"
#include <GfxRenderer.h>
#include <HalGPIO.h>
#include <I18n.h>
#include <cstdio>
#include "MappedInputManager.h"
#include "components/UITheme.h"
#include "fontIds.h"
@@ -51,8 +54,14 @@ void EpubReaderPercentSelectionActivity::loop() {
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Left}, [this] { adjustPercent(-kSmallStep); });
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Right}, [this] { adjustPercent(kSmallStep); });
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Up}, [this] { adjustPercent(kLargeStep); });
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Down}, [this] { adjustPercent(-kLargeStep); });
// On X3 the side buttons sit on the left/right edges of the screen rather than as a vertical up/down
// rocker (X4), so BTN_UP is physically the left button and BTN_DOWN the right one. Flip the large-step
// direction there so the left button decreases and the right button increases, matching the layout.
const int upDelta = gpio.deviceIsX3() ? -kLargeStep : kLargeStep;
const int downDelta = gpio.deviceIsX3() ? kLargeStep : -kLargeStep;
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Up}, [this, upDelta] { adjustPercent(upDelta); });
buttonNavigator.onPressAndContinuous({MappedInputManager::Button::Down},
[this, downDelta] { adjustPercent(downDelta); });
}
void EpubReaderPercentSelectionActivity::render(RenderLock&&) {
@@ -89,8 +98,13 @@ void EpubReaderPercentSelectionActivity::render(RenderLock&&) {
const int knobX = barX + 2 + fillWidth - 2;
renderer.fillRect(knobX, barY - 4, 4, barHeight + 8, true);
// Hint text for step sizes.
UITheme::drawCenteredText(renderer, screen, SMALL_FONT_ID, barY + 30, tr(STR_PERCENT_STEP_HINT), true);
// Two-line step hint built from separate label + value strings (front buttons = fine step, side
// buttons = coarse step), so the layout doesn't depend on a separator hidden in translated text.
char line[64];
snprintf(line, sizeof(line), "%s %d%%", I18N.get(StrId::STR_STEP_HINT_FRONT), kSmallStep);
UITheme::drawCenteredText(renderer, screen, SMALL_FONT_ID, barY + 30, line, true);
snprintf(line, sizeof(line), "%s %d%%", I18N.get(StrId::STR_STEP_HINT_SIDE), kLargeStep);
UITheme::drawCenteredText(renderer, screen, SMALL_FONT_ID, barY + 52, line, true);
// Button hints follow the current front button layout.
const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SELECT), "-", "+");