pager: move Home-jump to Alt+Backspace, works everywhere including mid-edit

Alt+Shift previously did double duty: toggle Caps Lock while editing a
field, or jump Home otherwise. Splitting these apart -- Alt+Backspace is
now the dedicated Home-jump gesture, and unlike Alt+Shift it isn't
context-dependent: it works whether or not a field is being edited, so
it's reachable from anywhere without first backing out of what you're
typing. Alt+Shift outside a field is now a no-op instead.

PagerKeyboard.cpp suppresses a Backspace press entirely while Alt is
held (no '\b' ring-push, no hold-to-back/hold-to-unlock tracking) and
reports it as a one-shot chord instead, mirroring the existing Alt+Shift
chord plumbing. Also closes the accent/@-mention popups first, since
those live on lv_layer_top() outside the tab content the Home jump
switches away from.

Signed-off-by: Tesso M Costa <tesso.martins@gmail.com>
This commit is contained in:
Tesso M Costa
2026-07-14 12:15:29 -06:00
parent 5c97d02fbe
commit e5cd766f73
4 changed files with 86 additions and 28 deletions
+7 -3
View File
@@ -65,9 +65,12 @@ Hold **Fn (Alt)** for numbers/symbols instead:
Real momentary Shift, just like a normal keyboard.
- **Hold Fn (Alt), then press Shift**: while you're editing a text field,
toggles **Caps Lock** on/off — stays uppercase until you repeat the chord.
Anywhere else (not editing a field), the same chord instead jumps straight
to the **Home** screen.
Anywhere else (not editing a field), the chord does nothing.
- Shift alone, tapped with nothing else, does nothing (as expected).
- **Hold Fn (Alt), then press Backspace**: jumps straight to the **Home**
screen — works everywhere, including while you're actively editing a field
(unlike Alt+Shift above, this one isn't context-dependent). Doesn't delete
a character or trigger the plain-Backspace gestures below.
### Special keys
@@ -157,7 +160,8 @@ dimmable brightness curve.
| Fn tap alone | Next field |
| Hold Shift + letter | Momentary uppercase |
| Fn + Shift (editing a field) | Toggle Caps Lock |
| Fn + Shift (not editing a field) | Jump to Home |
| Fn + Shift (not editing a field) | Nothing |
| Fn + Backspace (anywhere) | Jump to Home |
| Fn + Space | Enter accent picker |
| @ + letters (composer) | Auto-focuses the mention list — no Fn+Space needed |
| Enter | Select / send / message action menu |
+35 -10
View File
@@ -41,15 +41,21 @@ static constexpr char s_symbolMap[KB_ROWS][KB_COLS] = {
// Shift-key semantics) — held Alt THEN a Shift press instead chords into
// Alt+Shift, reported via s_alt_shift_chord_pending. What that chord DOES is
// a UI-level decision (UITask.cpp): Caps Lock toggle while editing a text
// field, or jump Home otherwise — this driver has no idea which field (if
// any) is focused, so it only reports the chord, it doesn't act on it (see
// pagerKeyboardConsumeAltShiftChord()/pagerKeyboardToggleCaps()). Note: Alt
// (row2,col0) and Shift (row2,col8) share row2, and row0/row1 col8 are
// 'o'/'l' — holding Alt+Shift+O or Alt+Shift+L all three at once will
// phantom-ghost a 'q'/'a' at the row2/col0 intersection (classic
// diode-less-matrix 3-key rectangle, no software fix possible); harmless in
// practice since the intended gesture is hold-Alt-tap-Shift-release-both,
// not holding all three simultaneously.
// field, no-op otherwise — this driver has no idea which field (if any) is
// focused, so it only reports the chord, it doesn't act on it (see
// pagerKeyboardConsumeAltShiftChord()/pagerKeyboardToggleCaps()). Held Alt
// THEN a Backspace press similarly chords into Alt+Backspace
// (s_alt_backspace_chord_pending) — unlike Alt+Shift this one has a single,
// context-independent effect (jump Home, everywhere, including mid-edit),
// so it's just as driver-agnostic to report but never conditional at the UI
// layer. Note: Alt (row2,col0) and Shift (row2,col8) share row2, and
// row0/row1 col8 are 'o'/'l' — holding Alt+Shift+O or Alt+Shift+L all three
// at once will phantom-ghost a 'q'/'a' at the row2/col0 intersection
// (classic diode-less-matrix 3-key rectangle, no software fix possible);
// harmless in practice since the intended gesture is
// hold-Alt-tap-Shift-release-both, not holding all three simultaneously.
// Backspace (row2,col9) sits one column over from Shift, so Alt+Backspace
// doesn't share this exact ghosting risk with any base-layer letter.
static constexpr uint8_t kAltPos = 2 * KB_COLS + 0; // row2,col0 ('\0' in both layers)
static constexpr uint8_t kShiftPos = 2 * KB_COLS + 8; // row2,col8 ('\0' in both layers)
static constexpr uint8_t kBackspacePos = 2 * KB_COLS + 9; // row2,col9 ('\0' in both layers)
@@ -63,6 +69,7 @@ static bool s_alt_tap_pending = false; // Alt pressed+released with nothing els
static bool s_caps = false;
static bool s_shift_held = false; // momentary Shift, mirrors s_backspace_held/s_space_held
static bool s_alt_shift_chord_pending = false; // one-shot, see pagerKeyboardConsumeAltShiftChord()
static bool s_alt_backspace_chord_pending = false; // one-shot, see pagerKeyboardConsumeAltBackspaceChord()
static bool s_backspace_held = false;
static bool s_space_held = false;
@@ -128,7 +135,19 @@ void pagerKeyboardPoll() {
}
continue;
}
if (code == kBackspacePos) { s_backspace_held = pressed; if (pressed) ringPush('\b'); continue; }
if (code == kBackspacePos) {
if (pressed) {
// Alt+Backspace is a distinct chord (jump Home, everywhere -- see
// pagerKeyboardConsumeAltBackspaceChord()), not a delete: suppress
// both the '\b' ring-push AND s_backspace_held, so the plain-
// Backspace hold gestures (back / unlock) never also see this press.
if (s_alt) s_alt_backspace_chord_pending = true;
else { s_backspace_held = true; ringPush('\b'); }
} else {
s_backspace_held = false;
}
continue;
}
if (code == kSpacePos) { s_space_held = pressed; if (pressed) ringPush(' '); continue; }
if (!pressed) continue; // base/symbol keys only emit on press
@@ -182,4 +201,10 @@ bool pagerKeyboardConsumeAltShiftChord() {
void pagerKeyboardToggleCaps() { s_caps = !s_caps; }
bool pagerKeyboardConsumeAltBackspaceChord() {
if (!s_alt_backspace_chord_pending) return false;
s_alt_backspace_chord_pending = false;
return true;
}
#endif
+14 -4
View File
@@ -56,10 +56,12 @@ void pagerKeyboardMarkAltUsed();
* symbol-layer or Alt+turn hold. Consumes the pending flag on read. */
bool pagerKeyboardConsumeAltTap();
/** True while Backspace is physically held (raw state, mirrors
* pagerKeyboardAltHeld()). A press still immediately ring-pushes '\b' as
* before; this is for callers that want to detect a long hold separately
* (e.g. UITask's press-and-hold "back" gesture). */
/** True while Backspace is physically held WITHOUT Alt (raw state, mirrors
* pagerKeyboardAltHeld()). A plain press still immediately ring-pushes '\b'
* as before; this is for callers that want to detect a long hold separately
* (e.g. UITask's press-and-hold "back" gesture). Alt+Backspace is a
* different gesture entirely (see pagerKeyboardConsumeAltBackspaceChord())
* and never sets this or ring-pushes '\b'. */
bool pagerKeyboardBackspaceHeld();
/** True while Space is physically held (raw state, mirrors
@@ -80,4 +82,12 @@ bool pagerKeyboardConsumeAltShiftChord();
* above only applying while a text field is actually being edited. */
void pagerKeyboardToggleCaps();
/** One-shot: true exactly once after Alt(Fn)+Backspace is chorded (Backspace
* pressed while Alt is held) — jumps Home, everywhere (editing a field or
* not), unlike the Alt+Shift chord above. Suppresses the normal Backspace
* press entirely: no '\b' ring-push, and pagerKeyboardBackspaceHeld() never
* reports held for this press, so it can't also fire the plain-Backspace
* hold-to-back/hold-to-unlock gestures. Consumes the pending flag on read. */
bool pagerKeyboardConsumeAltBackspaceChord();
#endif
+30 -11
View File
@@ -31177,18 +31177,35 @@ static void updatePagerAltTapNext() {
}
// Alt(Fn)+Shift chord (PagerKeyboard.cpp only reports it, since the driver has
// no UI visibility): while actually editing a text field, toggle Caps Lock
// (the field is where "Caps Lock" means anything); everywhere else — no field
// focused, or a field merely bound but nav focus has moved off it (same "ta"
// derivation handleHwKey() uses) — jump straight Home instead, since Caps
// Lock silently flipping with no field to see it in was reported as
// surprising/purposeless outside of typing.
// no UI visibility): toggles Caps Lock while actually editing a text field
// (the field is where "Caps Lock" means anything) and is a deliberate no-op
// everywhere else (Home-jump duty moved to Alt+Backspace below, so there's no
// longer a reason to make this chord do anything outside a field — Caps Lock
// silently flipping with no field to see it in was reported as
// surprising/purposeless before this split).
static void updatePagerAltShiftChord() {
if (!pagerKeyboardConsumeAltShiftChord()) return;
lv_obj_t* ta_focused = lv_keyboard_get_textarea(g_lv.keyboard);
lv_obj_t* ta = (ta_focused && s_nav_group && lv_group_get_focused(s_nav_group) == ta_focused) ? ta_focused : nullptr;
if (ta) pagerKeyboardToggleCaps();
else navGoToMainTab(HOME_TAB_INDEX);
if (!ta) return;
pagerKeyboardToggleCaps();
if (g_lv.task) g_lv.task->noteUserInput();
}
// Alt(Fn)+Backspace chord: jump straight Home, unconditionally -- unlike
// Alt+Shift above, this one is NOT context-dependent (works whether or not a
// field is being edited, per explicit request). Fires instead of a normal
// Backspace press: PagerKeyboard.cpp suppresses the '\b' ring-push and the
// hold-to-back/hold-to-unlock tracking entirely for an Alt-held Backspace
// press, so there's no double-action to guard against here.
static void updatePagerAltBackspaceChord() {
if (!pagerKeyboardConsumeAltBackspaceChord()) return;
// The accent/@-mention pickers live on lv_layer_top(), outside the tab
// content navGoToMainTab() switches away from -- close them explicitly
// first so a stray overlay doesn't keep floating over the Home screen.
accentBoxHide();
mentionBoxHide();
navGoToMainTab(HOME_TAB_INDEX);
if (g_lv.task) g_lv.task->noteUserInput();
}
@@ -42834,11 +42851,12 @@ void UITask::loop() {
if (pagerKeyboardReadKey() <= 0) break;
any = true;
}
// Discard any Alt tap / Alt+Shift chord picked up while idle-dimmed -- it
// must not fire updatePagerAltTapNext()'s NEXT / updatePagerAltShiftChord()'s
// toggle-or-Home the instant the screen wakes.
// Discard any Alt tap / Alt+Shift / Alt+Backspace chord picked up while
// idle-dimmed -- none of them may fire (NEXT / Caps toggle / jump Home)
// the instant the screen wakes.
pagerKeyboardConsumeAltTap();
pagerKeyboardConsumeAltShiftChord();
pagerKeyboardConsumeAltBackspaceChord();
// Hard-locked: an ordinary keypress must NOT wake/unlock -- only holding
// Backspace does (updatePagerBackspaceUnlockHold, already polled above).
if (any && !g_lv.task->isManualLock()) g_lv.task->wakeScreen();
@@ -42850,6 +42868,7 @@ void UITask::loop() {
}
updatePagerAltTapNext();
updatePagerAltShiftChord();
updatePagerAltBackspaceChord();
updatePagerBackspaceHold(now);
updatePagerSpaceHold(now);
}