Commit Graph

20 Commits

Author SHA1 Message Date
Jakub
dfb993de53 feat(clock,keyboard): alarm repeat + non-Latin keyboard alphabets
Alarm repeat (Clock Tools):
- NodePrefs::alarm_repeat_mask (weekday bitmask, struct tm::tm_wday
  convention) — new Repeat row cycles Off/Daily/Weekdays/Weekends.
  computeAlarmNextFire() scans the next 7 days for a matching weekday when
  set; evaluateAlarm() only clears alarm_on (one-shot) when the mask is
  empty, otherwise re-arms. mask==0 is byte-for-byte the original one-shot
  behaviour, so existing users see no change.

On-screen keyboard alphabets (Settings > Keyboard > Alphabet):
- KeyboardWidget reworked from single-byte ASCII cells to UTF-8 codepoints
  (insertion, backspace and T9 in-place cycling all codepoint-aware now, so a
  multi-byte character is never split) — see kbApplyCapsUtf8/kbUtf8Len/
  kbUtf8CharAt/kbUtf8LastCharBytes.
- Cyrillic, Greek and an Extended Latin set (Polish/Czech/Slovak/German/
  French/Spanish/Nordic diacritics) join the keyboard's existing #@/abc page
  cycle (Latin -> alt alphabet -> Symbols -> Latin) — no new key needed.
  NodePrefs::keyboard_alt_alphabet picks which one, if any, is active.
- Lemon font is forced on transiently inside KeyboardWidget::render() (saved
  and restored every call) whenever the alt-alphabet page is showing or
  already-typed text has non-ASCII bytes, so composing is visible even if
  Font is set to Default.
- Each script's caps-shift rule is distinct and documented in
  kbApplyCapsUtf8: flat -0x20 for ASCII/Cyrillic/Greek (with the ё/ς
  exceptions), flat -0x20 for Latin-1 (à-þ), and an adjacent-pair -1 for
  Latin Extended-A (verified against every character actually used, not a
  blanket rule for that whole Unicode block).

NodePrefs schema: two tail-appended fields this session (alarm_repeat_mask,
keyboard_alt_alphabet), SCHEMA_SENTINEL 0xC0DE001B -> 0xC0DE001D,
sizeof(NodePrefs) 2496 -> 2504 (verified via a standalone host compile).

Docs: Clock Tools' Repeat row, Settings > Keyboard > Alphabet, and the
Rooms keyboard note (no longer ASCII-only once an alphabet is enabled).

Not build-verified — no PlatformIO toolchain available this session. Caps
mappings cross-checked against Python's Unicode case tables; UTF-8 literal
bytes verified at the byte level.
2026-07-10 16:01:42 +02:00
Jakub
21a34451d9 refactor(ui): dedupe caps-shift and favourites "+" tile
Two duplications surfaced by a framework-consistency pass:

- KeyboardWidget applied the a-z shift-uppercase at five draw/commit sites
  with an inline `if (caps && ch >= 'a' && ch <= 'z')`. Fold them into one
  `kbApplyCaps(ch, caps)` helper.
- The favourites grid drew the empty "+" tile from two branches (the gone-slot
  prune and the always-empty slot). Route both through a single `has_contact`
  flag so the "+" and the trailing selection-colour reset each live at one site.

No behaviour change. Builds green: WioTrackerL1_companion_solo_dual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:18:36 +02:00
Jakub
866e44c4e2 feat(keyboard): show digit on each T9 key; complete v1.22 docs
T9 keys now read like a phone keypad — each cell is labelled <digit><group>
(e.g. 2abc), with the digit matching what the multi-tap cycle lands on after
the letters. No separator space: the widest group (".,!?'-") plus the digit
already fills a narrow 128px OLED cell at 3 columns.

Also fills in the v1.22 release notes (T9 keyboard, trail auto-save, Clock
Tools in Tools, Map/Shutdown reorderable, battery-read fix, the UI-audit fix
batch, discover-dedup, favourites self-heal, preset-name placeholder, OLED
frame-skip, e-ink full-refresh) and syncs the solo-feature docs: new Keyboard
settings section, Trail Auto-save row, and Clock Tools reachable from Tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 20:08:29 +02:00
Jakub
3419e590fe fix(keyboard): rename QWERTY label to ABC (layout is alphabetical, not QWERTY)
The on-screen keyboard grid has been a-b-c...z alphabetical order since
it was first introduced, never an actual QWERTY row layout. The
"QWERTY" name only appeared with the T9 option (needed something to
contrast it against) and was inaccurate from the start.
2026-07-02 18:33:40 +02:00
marcin
3363867d98 Added T9 on-screen keyboard as an option 2026-07-02 12:21:52 +02:00
Jakub
92643d1721 fix(ui): scale trail-map markers for e-ink, add wrap-around nav
Trail map dots/diamonds/cross/start markers and the north indicator were
drawn as raw unscaled pixels, so they shrank to near-invisible specks on
landscape e-ink's larger font scale; they now route through the mini-icon
framework (icons.h) like the rest of the UI, and the grid intersection dots
scale with it too. The keyboard's preview/grid separator had the same bug
(hardcoded 1px) and now uses display.sepH().

Also makes UP/DOWN (and the keyboard's LEFT/RIGHT/UP/DOWN) wrap top<->bottom
across every list, menu and form selector, matching the behavior PopupMenu
already had.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 09:16:57 +02:00
MarekZegare4
8c0ccaed73 feat(ui): keyboard overhaul — shared instance, icon keys, symbols page
- Unify on the shared keyboard: WaypointsView drives UITask::keyboard()
  instead of its own instance; openKb() clears {loc}/{time} for literal
  fields (labels, coordinates). Reclaims the duplicate widget from the heap.
- Icon glyphs on the special row: caps ⇧, space ⎵ (two halves), delete ⌫,
  OK ✓ (reuses ICON_CHECK); {} and the page toggle stay text.
- Second character page (symbols), toggled by a "#@"/"abc" key. Tidy the
  letters page: drop the duplicate grid space, add the comma, group
  punctuation as . , ! ?
- Harden the preview buffers (KB_PREVIEW_CAP) against very wide displays.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 10:47:50 +02:00
Jakub
daaa5b1503 fix(messages): allow full-length text on the compose/send paths too
After widening the history buffers, the compose side was still the
bottleneck: the keyboard capped input at 139 bytes and the quick-message
expand buffers were 140. Raise KB_MAX_LEN to 160 (MeshCore's MAX_TEXT_LEN)
and size the expand/compose buffers to MSG_TEXT_BUF, so a message can be
typed, expanded, sent, stored and displayed at the full protocol length on
every path.

The custom-message editor now passes an explicit max bound to its 140-byte
store, so the wider keyboard buffer can't overflow it; the bot editor
already passed per-field maxima.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:15:30 +02:00
Jakub
d981f93e6e refactor(ui): drawSelectionRow helper for the 5-line invert/restore pattern
Eight screens repeated the same if/else block to invert the row
background for selected items. Add DisplayDriver::drawSelectionRow that
sets LIGHT, optionally fills the rect, and leaves the colour as DARK
when sel (so the caller's text renders inverted). 61 lines removed
across SettingsScreen, KeyboardWidget (cells + special row), ToolsScreen,
DashboardConfigScreen, BotScreen, RingtoneEditorScreen (note slots +
menu), NearbyScreen, QuickMsgScreen (4 list views).

Card-style rows (QuickMsg hist, NearbyScreen discover, RingtoneEditor
notes display) keep their original drawRect/partial-fill outline for
unselected — they don't match the simple-invert pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 20:33:02 +02:00
Jakub
5fde79fae7 fix(ui): translate keyboard preview buffer through UTF-8 handler
KeyboardWidget printed the text preview directly via display.print()
without calling translateUTF8ToBlocks first. When a reply prefix
contained a UTF-8 sender name (e.g. Cyrillic), raw multi-byte sequences
reached the display driver on non-Lemon renders.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:28:10 +02:00
Jakub
b279b95b2d refactor(ui): apply code review fixes — bugs, risk, optimisations, cleanup
Fixes 7 bugs (timing UB on unsigned long, abs() no-op, XBM CRC PROGMEM
scan, CayenneLPP heap thrash ×2, UTF-8 reply prefix, lock-seq reset on
wake), 3 risk items (notif melody stop guard, bot trigger pre-lowercase,
lock-seq cleared on display wake), plus optimisations (fmtMsgAge/
getTextWidth caching, RTTTL builder consolidated to NodePrefs, HP_*
constants and homePageLabel table moved to NodePrefs, DataStore flat
lambda schema, translateUTF8Static factored out) and minor cleanup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 20:03:45 +02:00
Jakub
9c671891d9 fix: e-ink portrait/landscape layout — separators, battery, keyboard, screens
- DisplayDriver: add sepH() (2px landscape, 1px OLED); fix transliterateCodepoint
  fallback '?' (was '\xDB' which broke UTF-8 word-wrap and rendered tiny block char)
- GxEPDDisplay: drawRect draws double border on landscape for visible 2px weight
- UITask: navigation dots scale with lh; content_y saves 16px by using dots_y+6;
  battery iconW=lh*2, fill margin bm scales with orientation; indicator spacing fixed;
  clock separator uses sepH(); alert popup tight and vertically centred
- KeyboardWidget: compact cell height (no stretch), multi-line preview, cursor always
  on last visible line
- BotScreen / NearbyScreen detail: use lineStep() as natural item height, shrink only
  when items don't fit (fixes portrait stretch)
- QuickMsgScreen: send button pinned to display bottom (height-lh-2), not floating
- SettingsScreen: AUTO_OFF guarded with #if AUTO_OFF_MILLIS>0; default sel=AUTO_LOCK
- All screens: separators updated to sepH()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 23:13:27 +02:00
Jakub
118f29b0a1 fix: landscape e-ink layout corrections — clock overlap, keyboard overflow, bar sizing, nearby detail
- GxEPDDisplay: fontAscender only applies to sz=1 with Lemon; sz=2 (NULL font)
  was incorrectly getting a 16px cursor offset, causing clock text to render
  16px lower than expected and overlap the date line
- KeyboardWidget: compute cell_h from available screen height instead of lh+1;
  tighten sep_y to lh so the grid fits within 122px even with Lemon font (lh=20)
- SettingsScreen renderBar: constrain box size to available width so the 5th
  buzzer-volume square doesn't overflow past the right edge with Lemon font
- NearbyScreen discover detail: manually truncate b64 public key by charWidth
  to guarantee one-line rendering; use dynamic step = (height-hdr)/5 so
  Status line doesn't fall off the bottom of the screen
- NearbyScreen contacts detail: merge dist+az into one line and use dynamic
  step to fit 5 lines within the display; removes the off-screen Seen: row
- AutoAdvertScreen: replace lineStep() gap before hints with 4px fixed gap
  so both hint lines fit within the display with large fonts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 19:11:25 +02:00
Jakub
1d8ca25469 fix: adaptive layout for e-ink landscape across all remaining screens
- GxEPDDisplay: sz2 uses 4× scale on landscape (clock 32px vs body 16px)
- UITask: clock date_y and splash date_y computed from lh2 after setTextSize(2)
- UITask: battery/mute/BT/advert indicators scale with lh; muted uses text 'M'
- UITask: hibernate hint split into two lines to prevent wrapping
- ToolsScreen: full refactor — headerH, listStart, lineStep, getCharWidth
- KeyboardWidget: all cell sizes computed from display metrics (cell_w, cell_h, spec_w)
- RingtoneEditorScreen: cell_w = charWidth×2+6 (was hardcoded 18)
- SettingsScreen: BRIGHTNESS hidden on e-ink; renderBar boxes scale with lh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 18:34:20 +02:00
Jakub
7b63af1068 refactor: use PopupMenu in keyboard and context menus, fix bugs
- KeyboardWidget: replace inline placeholder overlay with PopupMenu
- UITask QuickMsgScreen: replace inline CONTACT_PICK/CHANNEL_PICK context
  menus with PopupMenu; notif label stored in _ctx_notif_item at open time
- BotScreen: clear placeholder list for trigger field (expansion tokens
  would never match incoming literal text)
- MsgExpand: fix APPEND macro off-by-one (oi+_l < out_len-1 → < out_len)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 08:06:26 +02:00
Jakub
42b51935b1 feat: sensor placeholders in messages and keyboard picker
expandMsg now expands {temp}, {hum}, {pres}, {batt}, {alt}, {lux}, {dist},
{co2} from live sensor readings. The [{}] picker in the on-screen keyboard
shows only sensors currently detected by SensorManager and scrolls when
more than 3 items fit the overlay.

New SensorPlaceholders.h helper keeps sensor-to-keyboard wiring out of
UITask.cpp. MsgExpand.h handles all expansion logic centrally; MyMeshBot
and QuickMsgScreen pass &sensors and batt_volts to each call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 00:12:07 +02:00
Jakub
4063c2d296 style: shorten Del label to [Dl] to fit 25px special row cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
464f5cd7a2 fix: remove label prefix from keyboard preview for visual consistency
All three keyboard instances (Settings, QuickMsg, Bot) now show plain
text + cursor without any field-name prefix. Removes the label field
from KeyboardWidget entirely — the screen title already indicates context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
5fecafd4f9 fix: KeyboardWidget preview buffer size and KEY_CONTEXT_MENU handling
- preview[] increased from 32 to 40 bytes to safely fit max label (15 chars)
  + 20 chars text + cursor + null without snprintf truncation
- KEY_CONTEXT_MENU (long-press ENTER) now treated as CANCEL in keyboard
  input handler, restoring BotScreen behavior lost in the refactor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
07bd5f1167 refactor: extract on-screen keyboard into reusable KeyboardWidget module
All three keyboard implementations (SettingsScreen, QuickMsgScreen, BotScreen)
now share a single KeyboardWidget struct defined in KeyboardWidget.h, eliminating
~480 lines of duplicated render/input logic. Also fixes the SettingsScreen bug
where navigation keys were checked before the placeholder overlay, causing cursor
movement when the overlay was open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00