mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-10-15 03:59:55 +00:00
feat(pager): keyboard navigation and remappable tab keys, as on the T-Deck (#591)
The wheel is the Pager's only way to move the highlight, which is slow and imprecise. Bring the T-Deck's keyboard navigation to it: - With no text field focused, programmable letters move the highlight (default I up, K down, J left, L right) and scroll (U / O). Select and Back default to no letter, since Enter and Backspace-hold (the wheel's click / long-press) already do them. On the Map the letters move the highlight; W/A/X/D stay the map pan. - The tab keys are programmable like the T-Deck's, defaulting to the Pager's M/C/H/A/S, and the bar's labels show whatever is bound. - Settings > Keyboard gets the same "Keyboard navigation" switch (on by default here) and the same tap-a-row-then-press-a-key remap rows; the T-Deck's rows move into settingsNavKeyRows() so both boards share them. The Pager's switch only turns the letters on or off; it never empties the focus group, which the wheel always needs. - A remap refuses letters the Pager uses for fixed actions (Q/E slider nudge, W/A/X/D map pan; A stays allowed as a tab key). - In wheel edit mode on a slider, J/L preview like a turn and the select key closes the edit, matching the wheel. Settings: the Pager's kbd_nav and key bindings were never shown on that board, so they held the T-Deck defaults. Schema v67 (no new field) puts the Pager defaults in once on that board only. Two new strings, translated in every deploy/apps/lang file; i18n_builtin.h regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019M5VJ43X3nUULPqVcgrAH2
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8adea3558e
commit
6d655b8301
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Компактната навигаци
|
||||
Day Ден
|
||||
High contrast Висок контраст
|
||||
High contrast - restarting to apply it… Висок контраст - рестартиране за прилагане…
|
||||
Keyboard nav: on Клавиатурна навиг.: вкл.
|
||||
Landscape only Само в хоризонтална ориентация
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Буквите местят маркера, когато няма активно текстово поле. Enter избира; задръжте Backspace за назад.
|
||||
Night Нощ
|
||||
Theme save failed Неуспешно запазване на темата
|
||||
Ack Ack
|
||||
|
||||
@@ -37,7 +37,9 @@ Appearance Vzhled
|
||||
Condense Nav (restart to apply) Kompaktní navigace (pro použití restartujte)
|
||||
Condense Nav saved — restart to apply Kompaktní navigace uložena — pro použití restartujte
|
||||
Day Den
|
||||
Keyboard nav: on Navigace klávesnicí: zapnuta
|
||||
Landscape only Pouze na šířku
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Písmena posouvají zvýraznění, když není aktivní textové pole. Enter vybírá; podržením Backspace se vrátíte zpět.
|
||||
Night Noc
|
||||
Theme save failed Uložení motivu selhalo
|
||||
Ack Potvrzení
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Kompakte Navigation gespeichert — Neus
|
||||
Day Tag
|
||||
High contrast Hoher Kontrast
|
||||
High contrast - restarting to apply it… Hoher Kontrast - Neustart zum Anwenden…
|
||||
Keyboard nav: on Tastaturnav.: an
|
||||
Landscape only Nur im Querformat
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Buchstaben bewegen die Markierung, wenn kein Textfeld aktiv ist. Enter wählt aus; Rücktaste halten geht zurück.
|
||||
Night Nacht
|
||||
Theme save failed Design konnte nicht gespeichert werden
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Η συμπαγής πλοήγηση α
|
||||
Day Ημέρα
|
||||
High contrast Υψηλή αντίθεση
|
||||
High contrast - restarting to apply it… Υψηλή αντίθεση - επανεκκίνηση για εφαρμογή…
|
||||
Keyboard nav: on Πλοήγηση πληκτρ.: ανοιχτό
|
||||
Landscape only Μόνο σε οριζόντιο προσανατολισμό
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Τα γράμματα μετακινούν την επισήμανση όταν δεν είναι ενεργό πεδίο κειμένου. Το Enter επιλέγει· κρατήστε το Backspace για επιστροφή.
|
||||
Night Νύχτα
|
||||
Theme save failed Αποτυχία αποθήκευσης θέματος
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Navegación compacta guardada — reinic
|
||||
Day Día
|
||||
High contrast Alto contraste
|
||||
High contrast - restarting to apply it… Alto contraste - reiniciando para aplicarlo…
|
||||
Keyboard nav: on Nav. teclado: activ.
|
||||
Landscape only Solo en horizontal
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Las letras mueven el resaltado cuando no hay un campo de texto activo. Intro selecciona; mantén Retroceso para volver.
|
||||
Night Noche
|
||||
Theme save failed No se pudo guardar el tema
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Navigation compacte enregistrée — red
|
||||
Day Jour
|
||||
High contrast Contraste élevé
|
||||
High contrast - restarting to apply it… Contraste élevé - redémarrage pour l'appliquer…
|
||||
Keyboard nav: on Nav. clavier : activé
|
||||
Landscape only Paysage uniquement
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Les lettres déplacent la sélection quand aucun champ de texte n'est actif. Entrée sélectionne ; maintenez Retour arrière pour revenir.
|
||||
Night Nuit
|
||||
Theme save failed Échec de l’enregistrement du thème
|
||||
Ack Accusé
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Tömör navigáció mentve – indítsa
|
||||
Day Nappali
|
||||
High contrast Nagy kontraszt
|
||||
High contrast - restarting to apply it… Nagy kontraszt - újraindítás az alkalmazáshoz…
|
||||
Keyboard nav: on Billentyűzetnavigáció: bekapcsolva
|
||||
Landscape only Csak fekvő tájolásban
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. A betűk mozgatják a kijelölést, ha nincs aktív szövegmező. Az Enter kiválaszt; a Backspace nyomva tartása visszalép.
|
||||
Night Éjszakai
|
||||
Theme save failed Téma mentése sikertelen
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Navigazione compatta salvata — riavvia
|
||||
Day Giorno
|
||||
High contrast Contrasto elevato
|
||||
High contrast - restarting to apply it… Contrasto elevato - riavvio per applicarlo…
|
||||
Keyboard nav: on Nav. tastiera: on
|
||||
Landscape only Solo in orizzontale
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Le lettere spostano l'evidenziazione quando nessun campo di testo è attivo. Invio seleziona; tieni premuto Backspace per tornare indietro.
|
||||
Night Notte
|
||||
Theme save failed Salvataggio tema non riuscito
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Compacte navigatie opgeslagen — hersta
|
||||
Day Dag
|
||||
High contrast Hoog contrast
|
||||
High contrast - restarting to apply it… Hoog contrast - opnieuw opstarten om toe te passen…
|
||||
Keyboard nav: on Toetsenbordnav.: aan
|
||||
Landscape only Alleen liggend
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Letters verplaatsen de markering als er geen tekstveld actief is. Enter selecteert; houd Backspace ingedrukt om terug te gaan.
|
||||
Night Nacht
|
||||
Theme save failed Thema opslaan mislukt
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Navegação compacta salva — reinicie
|
||||
Day Dia
|
||||
High contrast Alto contraste
|
||||
High contrast - restarting to apply it… Alto contraste - reiniciando para aplicar…
|
||||
Keyboard nav: on Nav. teclado: ligado
|
||||
Landscape only Somente em paisagem
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. As letras movem o destaque quando nenhum campo de texto está ativo. Enter seleciona; segure Backspace para voltar.
|
||||
Night Noite
|
||||
Theme save failed Falha ao salvar o tema
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Navigare compactă salvată — reporne
|
||||
Day Zi
|
||||
High contrast Contrast ridicat
|
||||
High contrast - restarting to apply it… Contrast ridicat - repornire pentru aplicare…
|
||||
Keyboard nav: on Navigare tastatură: pornită
|
||||
Landscape only Doar în modul peisaj
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Literele mută selecția când niciun câmp de text nu este activ. Enter selectează; ține apăsat Backspace pentru înapoi.
|
||||
Night Noapte
|
||||
Theme save failed Salvarea temei a eșuat
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Компактная навигация
|
||||
Day День
|
||||
High contrast Высокая контрастность
|
||||
High contrast - restarting to apply it… Высокая контрастность - перезагрузка для применения…
|
||||
Keyboard nav: on Навигация клавиатурой: вкл.
|
||||
Landscape only Только в альбомной ориентации
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Буквы перемещают выделение, когда не активно текстовое поле. Enter выбирает; удерживайте Backspace, чтобы вернуться.
|
||||
Night Ночь
|
||||
Theme save failed Не удалось сохранить тему
|
||||
Ack Подтв.
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Компактна навигација
|
||||
Day Дан
|
||||
High contrast Висок контраст
|
||||
High contrast - restarting to apply it… Висок контраст - рестартовање ради примене…
|
||||
Keyboard nav: on Навиг. тастатуром: укљ.
|
||||
Landscape only Само у хоризонталном положају
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Слова померају истицање када ниједно текстуално поље није активно. Enter бира; држите Backspace за назад.
|
||||
Night Ноћ
|
||||
Theme save failed Чување теме није успело
|
||||
Ack Ack
|
||||
|
||||
@@ -39,7 +39,9 @@ Condense Nav saved — restart to apply Компактну навігацію з
|
||||
Day День
|
||||
High contrast Висока контрастність
|
||||
High contrast - restarting to apply it… Висока контрастність - перезавантаження для застосування…
|
||||
Keyboard nav: on Навігація клавіатурою: увімк.
|
||||
Landscape only Лише в альбомній орієнтації
|
||||
Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back. Літери переміщують виділення, коли не активне текстове поле. Enter вибирає; утримуйте Backspace, щоб повернутися.
|
||||
Night Ніч
|
||||
Theme save failed Не вдалося зберегти тему
|
||||
Ack Підтв.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace TouchPrefsSchema {
|
||||
|
||||
static constexpr uint16_t MAGIC = 0x5743; // 'WC' (WadaCfg)
|
||||
static constexpr uint8_t CURRENT_VERSION = 66; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Night/Day, with high-contrast variants, #296/#544; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced); v60: home_key_keeps_drawer (#491; default OFF); v61: ble_kbd_* (Bluetooth serves a keyboard instead of the phone app, its layout and the paired keyboard; default phone app); v62: ble_kbd_back (a second key that acts as Back; 0 = only Esc); v63: V4-R8 ui_scale reset before exposing font-only presets (#536); v64: report_ping + report_done_n (beta test reports: the opt-in install count and the tag this device already reported on; both default 0); v65: ThinkNode M9 ui_scale reset before exposing font-only presets, the same one-time correction v63 made for the V4-R8 (no new field); v66: condense_nav (landscape nav rail + icon-only Home launchers, #592; default OFF)
|
||||
static constexpr uint8_t CURRENT_VERSION = 67; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Night/Day, with high-contrast variants, #296/#544; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced); v60: home_key_keeps_drawer (#491; default OFF); v61: ble_kbd_* (Bluetooth serves a keyboard instead of the phone app, its layout and the paired keyboard; default phone app); v62: ble_kbd_back (a second key that acts as Back; 0 = only Esc); v63: V4-R8 ui_scale reset before exposing font-only presets (#536); v64: report_ping + report_done_n (beta test reports: the opt-in install count and the tag this device already reported on; both default 0); v65: ThinkNode M9 ui_scale reset before exposing font-only presets, the same one-time correction v63 made for the V4-R8 (no new field); v66: condense_nav (landscape nav rail + icon-only Home launchers, #592; default OFF); v67: T-Pager keyboard navigation (#591): kbd_nav ON and its own key bindings, a one-time reset on that board only (no new field)
|
||||
static constexpr uint8_t BROKEN_MID_INSERT_VERSION = 44;
|
||||
|
||||
// Persisted byte layout. New fields must be appended at the end: older blobs
|
||||
|
||||
@@ -66,6 +66,17 @@ static const uint8_t DEFAULT_UI_ROTATION = 0;
|
||||
using TouchCfg = TouchPrefsSchema::Config;
|
||||
|
||||
static TouchCfg s_cfg;
|
||||
#if defined(TLORA_PAGER)
|
||||
// The Pager's keyboard-navigation keys (#591): I up, K down, J left, L right, no
|
||||
// Select/Back letter (Enter and Backspace-hold do those), U/O scroll. Clear of the
|
||||
// Pager's fixed letters (Q/E slider nudge, W/A/X/D map pan) and of its tab keys.
|
||||
static void touchPrefsPagerNavKeyDefaults(TouchCfg& c) {
|
||||
const char* d = "ikjl";
|
||||
for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)(i < 4 ? d[i] : 0);
|
||||
c.nav_scroll_keys[0] = 'u';
|
||||
c.nav_scroll_keys[1] = 'o';
|
||||
}
|
||||
#endif
|
||||
static bool s_cfg_loaded = false;
|
||||
|
||||
// Legacy per-key names — only referenced by the one-time migration below.
|
||||
@@ -128,6 +139,8 @@ static void cfgSetDefaults(TouchCfg& c) {
|
||||
#endif
|
||||
#if defined(HAS_TANMATSU)
|
||||
c.kbd_nav = 1; // Tanmatsu: no touchscreen — keyboard nav is the only input, always on
|
||||
#elif defined(TLORA_PAGER)
|
||||
c.kbd_nav = 1; // Pager (#591): letter navigation on by default (I/K/J/L, U/O)
|
||||
#else
|
||||
c.kbd_nav = 0; // T-Deck / V4: keyboard navigation OFF by default (opt-in; persists once toggled on)
|
||||
#endif
|
||||
@@ -174,16 +187,24 @@ static void cfgSetDefaults(TouchCfg& c) {
|
||||
c.app_hide = (1u << 12); // APPHIDE_MQTT: the MQTT bridge starts hidden (experimental + privacy)
|
||||
memset(c.lang_file, 0, sizeof c.lang_file); // no file language: built-in ui_lang column
|
||||
c.sleep_idle = 0; // default: idle light-sleep OFF
|
||||
#if defined(TLORA_PAGER)
|
||||
{ const char* d = "mchas"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // Pager tab mnemonics M/C/H/A/S (#591)
|
||||
#else
|
||||
{ const char* d = "ertui"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // default tab hotkeys E/R/T/U/I
|
||||
#endif
|
||||
c.map_zoom_buttons = 0; // default: map zoom = slider
|
||||
#if defined(HAS_TANMATSU)
|
||||
{ const char* d = "wxads"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // Tanmatsu: W up/X down/A left/D right/S select; no Back letter (Esc/F-key), d[5]='\0'
|
||||
#elif defined(TLORA_PAGER)
|
||||
touchPrefsPagerNavKeyDefaults(c); // Pager (#591): I/K/J/L, no Select/Back letter, U/O scroll
|
||||
#else
|
||||
{ const char* d = "wzadsq"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // default W/Z/A/D/S/Q
|
||||
#endif
|
||||
c.home_is_drawer = 0; // default: Home = Commander screen
|
||||
#if defined(HAS_TANMATSU)
|
||||
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'v'; // Tanmatsu scroll-up F / scroll-down V
|
||||
#elif defined(TLORA_PAGER)
|
||||
// set with the direction keys above (touchPrefsPagerNavKeyDefaults)
|
||||
#else
|
||||
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'c'; // default scroll-up F / scroll-down C
|
||||
#endif
|
||||
@@ -270,6 +291,17 @@ static void cfgLoadOrMigrate() {
|
||||
// v64 new trailing fields. Forced off rather than inherited: a garbage 1
|
||||
// would start sending an install count nobody opted into.
|
||||
if (stored_version < 64) { s_cfg.report_ping = 0; s_cfg.report_done_n = 0; }
|
||||
#if defined(TLORA_PAGER)
|
||||
// v67 (#591): the Pager gains the T-Deck's keyboard navigation. Its kbd_nav and key
|
||||
// bindings were never shown on this board, so the stored values are just the T-Deck
|
||||
// defaults (W/Z/A/D..., E/R/T/U/I, nav off), which collide with the Pager's own
|
||||
// letters. Put this board's defaults in once; later choices persist. No new field.
|
||||
if (stored_version < 67) {
|
||||
s_cfg.kbd_nav = 1;
|
||||
{ const char* d = "mchas"; for (int i = 0; i < 5; i++) s_cfg.nav_keys[i] = (uint8_t)d[i]; }
|
||||
touchPrefsPagerNavKeyDefaults(s_cfg);
|
||||
}
|
||||
#endif
|
||||
if (stored_version < 66) s_cfg.condense_nav = 0; // v66 new trailing field: never stored before
|
||||
if (stored_version < 29 && s_cfg.ui_scale == 0) s_cfg.ui_scale = 1; // bump old 100% default -> Large (150%)
|
||||
if (stored_version < 30) s_cfg.boot_advert = 0; // #76 new trailing field: advert-on-boot off by default
|
||||
|
||||
+194
-68
@@ -2444,6 +2444,14 @@ static bool s_tb_nav = false; // no trackball — read by the
|
||||
static lv_indev_drv_t s_nav_keypad_drv;
|
||||
#endif
|
||||
|
||||
#if defined(TLORA_PAGER)
|
||||
// Keyboard navigation (#591): the T-Deck's programmable letter nav on the Pager's
|
||||
// keyboard. Unlike the T-Deck this never gates the focus group (the wheel always
|
||||
// drives it); it only decides whether the navigation letters move the highlight.
|
||||
// On by default; loaded from the pref at init, toggled in Settings > Keyboard.
|
||||
static bool s_kbd_nav = true;
|
||||
#endif
|
||||
|
||||
// Touchscreen-only boards whose only focus-group driver is a Bluetooth keyboard
|
||||
// (the Attaky's D-pad drives its own group): bleKbdUiTick() turns s_kbd_nav on
|
||||
// while a keyboard is connected. Off, the group stays empty and shows nothing.
|
||||
@@ -4531,10 +4539,20 @@ static void navHideFocus() {
|
||||
}
|
||||
|
||||
// ---- Keyboard-nav tab hotkeys (programmable; default E/R/T/U/I) ----
|
||||
static uint8_t s_nav_keys[5] = { 'e','r','t','u','i' }; // per main tab [chat,contacts,home,map,settings]; loaded from prefs at boot
|
||||
static uint8_t s_nav_keys[5] =
|
||||
#if defined(TLORA_PAGER)
|
||||
{ 'm','c','h','a','s' }; // Pager: its long-standing tab mnemonics, printed on the bar, now remappable (#591)
|
||||
#else
|
||||
{ 'e','r','t','u','i' }; // per main tab [chat,contacts,home,map,settings]; loaded from prefs at boot
|
||||
#endif
|
||||
static uint8_t s_dir_keys[8] =
|
||||
#if defined(HAS_TANMATSU)
|
||||
{ 'w','x','a','d','s', 0, 'f','v' }; // Tanmatsu control keys: up,down,left,right,select,(no back — Esc/F-key),scroll-up,scroll-down
|
||||
#elif defined(TLORA_PAGER)
|
||||
// Pager (#591): I/K/J/L move, U/O scroll. Select and Back stay on Enter and
|
||||
// Backspace-hold (or the wheel's click / long-press), so those slots start empty.
|
||||
// Clear of the Pager's fixed letters: M/C/H/A/S tabs, Q/E slider, W/A/X/D map pan.
|
||||
{ 'i','k','j','l', 0, 0, 'u','o' };
|
||||
#else
|
||||
{ 'w','z','a','d','s','q','f','c' }; // control keys: up,down,left,right,select,back,scroll-up,scroll-down; loaded from prefs at boot
|
||||
#endif
|
||||
@@ -5061,6 +5079,34 @@ static void navMenubarKeysSync() {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(TLORA_PAGER)
|
||||
// The Pager prints each tab's hotkey on the bar: the icon, then the letter beside it
|
||||
// (or under it on the Condense Nav rail). Built from s_nav_keys so a remap shows up.
|
||||
static void pagerTabLabel(int tab, char* out, size_t cap) {
|
||||
static const char* const kIcons[5] = { LV_SYMBOL_ENVELOPE, TOUCH_SYM_PERSON, LV_SYMBOL_HOME,
|
||||
LV_SYMBOL_GPS, LV_SYMBOL_SETTINGS };
|
||||
if (tab < 0 || tab >= 5) { if (cap) out[0] = '\0'; return; }
|
||||
const int lk = navKeyLower(s_nav_keys[tab]);
|
||||
const char up = (lk >= 'a' && lk <= 'z') ? (char)(lk - 'a' + 'A') : (char)lk;
|
||||
snprintf(out, cap, "%s%s%c", kIcons[tab], s_nav_condensed ? "\n" : " ", up);
|
||||
}
|
||||
static void pagerRefreshTabLabels() {
|
||||
if (!g_lv.tabview) return;
|
||||
char b[16];
|
||||
for (int t = 0; t < 5; t++) { pagerTabLabel(t, b, sizeof b); lv_tabview_rename_tab(g_lv.tabview, t, b); }
|
||||
}
|
||||
// Letters the Pager uses for fixed actions, so a remap cannot take them: Q/E nudge a
|
||||
// slider, and W/A/X/D pan the map. A stays allowed as a tab key (it is the Map key by
|
||||
// default, and on the Map a pan would only ever follow a jump to the tab you are on).
|
||||
static bool pagerKeyReserved(int lk, bool tab_key) {
|
||||
switch (lk) {
|
||||
case 'q': case 'e': case 'w': case 'x': case 'd': return true;
|
||||
case 'a': return !tab_key;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Apply a captured key to the tab being remapped (Settings → Keyboard).
|
||||
static void navKeyCaptureApply(int key) {
|
||||
const int t = s_navkey_capture; // 0-4 = tab hotkey, 5-12 = up/down/left/right/select/back/scroll-up/scroll-down
|
||||
@@ -5069,13 +5115,20 @@ static void navKeyCaptureApply(int key) {
|
||||
if (key == 0x1B || key == 0x08 || key == 0x7F) { if (g_lv.task) g_lv.task->showAlert(TR("Cancelled"), 700); return; }
|
||||
if (!((key>='a'&&key<='z') || (key>='A'&&key<='Z'))) { if (g_lv.task) g_lv.task->showAlert(TR("Letters only"), 900); return; }
|
||||
const int lk = navKeyLower(key);
|
||||
if (navKeyUsedBy(lk, t) >= 0) { if (g_lv.task) g_lv.task->showAlert(TR("Key already in use"), 1100); return; }
|
||||
if (navKeyUsedBy(lk, t) >= 0
|
||||
#if defined(TLORA_PAGER)
|
||||
|| pagerKeyReserved(lk, t < 5)
|
||||
#endif
|
||||
) { if (g_lv.task) g_lv.task->showAlert(TR("Key already in use"), 1100); return; }
|
||||
if (t < 5) {
|
||||
s_nav_keys[t] = (uint8_t)lk;
|
||||
#if defined(ESP32)
|
||||
touchPrefsSetNavKey(t, (uint8_t)lk);
|
||||
#endif
|
||||
navMenubarKeysSync(); // tab hotkey changed → refresh the menubar hint
|
||||
#if defined(TLORA_PAGER)
|
||||
pagerRefreshTabLabels(); // the Pager prints the letters in the tab labels themselves
|
||||
#endif
|
||||
} else {
|
||||
s_dir_keys[t - 5] = (uint8_t)lk;
|
||||
#if defined(ESP32)
|
||||
@@ -14392,6 +14445,18 @@ static void navMbarKeysToggleCb(lv_event_t* e) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TLORA_PAGER)
|
||||
// Keyboard navigation on the Pager (#591). Unlike the T-Deck's toggle this leaves the
|
||||
// focus group alone (the wheel always needs it); it only turns the letters on or off.
|
||||
static void pagerKbdNavToggleCb(lv_event_t* e) {
|
||||
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
|
||||
const bool on = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
|
||||
touchPrefsSetKbdNav(on);
|
||||
s_kbd_nav = on;
|
||||
if (g_lv.task) g_lv.task->showAlert(on ? TR("Keyboard nav: on") : TR("Keyboard nav: off"), 1100);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Hide the device/profile name in the status bar and park the clock on the left
|
||||
// where the name used to be. Applies immediately via updateGlobalStatusBar().
|
||||
static void hideNameToggleCb(lv_event_t* e) {
|
||||
@@ -15067,6 +15132,62 @@ static void themeContrastToggleCb(lv_event_t* e) {
|
||||
: (s_theme_day ? TOUCH_THEME_DAY : TOUCH_THEME_NIGHT));
|
||||
}
|
||||
|
||||
#if CAP_TRACKBALL || defined(TLORA_PAGER)
|
||||
// Settings > Keyboard: the programmable tab hotkeys and navigation keys, one row
|
||||
// each; tap (or click) a row, then press the new key. Shared by the T-Deck and the
|
||||
// Pager (#591) so both boards remap keys the same way. Returns the new y.
|
||||
static int settingsNavKeyRows(lv_obj_t* body, int y) {
|
||||
// Programmable tab hotkeys — tap a row, then press a key to reassign it.
|
||||
y += settingsRowLabel(body, y, 0, TR("Tab hotkeys \xe2\x80\x94 tap a row, then press a key"), COLOR_SUB, &g_font_12, 0) + 2;
|
||||
for (int t = 0; t < 5; t++) {
|
||||
lv_obj_t* row = lv_btn_create(body);
|
||||
lv_obj_set_size(row, lv_pct(100), SC(30));
|
||||
lv_obj_set_pos(row, 2, y);
|
||||
styleButton(row);
|
||||
lv_obj_add_event_cb(row, navKeyCaptureStartCb, LV_EVENT_CLICKED, (void*)(intptr_t)t);
|
||||
lv_obj_t* nm = lv_label_create(row);
|
||||
lv_label_set_text(nm, TR(kNavTabNames[t]));
|
||||
lv_obj_set_style_text_font(nm, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(nm, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
lv_obj_align(nm, LV_ALIGN_LEFT_MID, 8, 0);
|
||||
lv_obj_t* kv = lv_label_create(row);
|
||||
const int lk = navKeyLower(s_nav_keys[t]);
|
||||
char kb[2] = { (char)(lk >= 'a' && lk <= 'z' ? lk - 'a' + 'A' : (lk ? lk : '-')), 0 };
|
||||
lv_label_set_text(kv, kb);
|
||||
lv_obj_set_style_text_font(kv, &g_font_14, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(kv, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
|
||||
lv_obj_align(kv, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
s_navkey_row_val[t] = kv;
|
||||
y += SC(36);
|
||||
}
|
||||
// Programmable control keys (move/select/back + scroll up/down) — tap a row, press a key.
|
||||
y += settingsRowLabel(body, y, 0, TR("Navigation keys \xe2\x80\x94 tap a row, then press a key"), COLOR_SUB, &g_font_12, 0) + 2;
|
||||
for (int d = 0; d < 8; d++) {
|
||||
const int bi = d + 5; // binding index 5-12
|
||||
lv_obj_t* row = lv_btn_create(body);
|
||||
lv_obj_set_size(row, lv_pct(100), SC(30));
|
||||
lv_obj_set_pos(row, 2, y);
|
||||
styleButton(row);
|
||||
lv_obj_add_event_cb(row, navKeyCaptureStartCb, LV_EVENT_CLICKED, (void*)(intptr_t)bi);
|
||||
lv_obj_t* nm = lv_label_create(row);
|
||||
lv_label_set_text(nm, TR(kNavDirNames[d]));
|
||||
lv_obj_set_style_text_font(nm, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(nm, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
lv_obj_align(nm, LV_ALIGN_LEFT_MID, 8, 0);
|
||||
lv_obj_t* kv = lv_label_create(row);
|
||||
const int lk = navKeyLower(s_dir_keys[d]);
|
||||
char kb[2] = { (char)(lk >= 'a' && lk <= 'z' ? lk - 'a' + 'A' : (lk ? lk : '-')), 0 };
|
||||
lv_label_set_text(kv, kb);
|
||||
lv_obj_set_style_text_font(kv, &g_font_14, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(kv, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
|
||||
lv_obj_align(kv, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
s_navkey_row_val[bi] = kv;
|
||||
y += SC(36);
|
||||
}
|
||||
return y;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void buildDeviceSettings(int sec) {
|
||||
// One detail page per section: each block below is gated to its DSEC_* section
|
||||
// (skipped blocks don't advance y, so every page lays out from the top).
|
||||
@@ -16177,6 +16298,24 @@ static void buildDeviceSettings(int sec) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TLORA_PAGER)
|
||||
/* Keyboard navigation (#591), the T-Deck's feature on the Pager: with no text field
|
||||
focused, letters move the highlight (default I/K/J/L) and scroll (U/O), and the
|
||||
tab keys are the letters printed on the bar. On by default. Applied live. */
|
||||
{
|
||||
int h = settingsRowLabel(body, y, 4, TR("Keyboard navigation"), COLOR_TEXT, &g_font_12, 56);
|
||||
lv_obj_t* sw = lv_switch_create(body);
|
||||
lv_obj_align(sw, LV_ALIGN_TOP_RIGHT, 0, y);
|
||||
if (touchPrefsGetKbdNav()) lv_obj_add_state(sw, LV_STATE_CHECKED);
|
||||
lv_obj_add_event_cb(sw, pagerKbdNavToggleCb, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
y += LV_MAX(34, h + 10);
|
||||
y += settingsRowLabel(body, y, 0,
|
||||
TR("Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back."),
|
||||
COLOR_SUB, &g_font_12, 0) + 2;
|
||||
y = settingsNavKeyRows(body, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CAP_TRACKBALL
|
||||
#if defined(HAS_TDECK_KEYBOARD) // never the M9: nav is force-set on at boot there (the board's only input) and must not grow an off-switch
|
||||
/* Keyboard navigation: off (default) vs on. When no text field is focused, the
|
||||
@@ -16224,53 +16363,7 @@ static void buildDeviceSettings(int sec) {
|
||||
lv_obj_add_event_cb(sw2, navMbarKeysToggleCb, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||
y += LV_MAX(34, h2 + 10);
|
||||
}
|
||||
// Programmable tab hotkeys — tap a row, then press a key to reassign it.
|
||||
y += settingsRowLabel(body, y, 0, TR("Tab hotkeys \xe2\x80\x94 tap a row, then press a key"), COLOR_SUB, &g_font_12, 0) + 2;
|
||||
for (int t = 0; t < 5; t++) {
|
||||
lv_obj_t* row = lv_btn_create(body);
|
||||
lv_obj_set_size(row, lv_pct(100), SC(30));
|
||||
lv_obj_set_pos(row, 2, y);
|
||||
styleButton(row);
|
||||
lv_obj_add_event_cb(row, navKeyCaptureStartCb, LV_EVENT_CLICKED, (void*)(intptr_t)t);
|
||||
lv_obj_t* nm = lv_label_create(row);
|
||||
lv_label_set_text(nm, TR(kNavTabNames[t]));
|
||||
lv_obj_set_style_text_font(nm, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(nm, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
lv_obj_align(nm, LV_ALIGN_LEFT_MID, 8, 0);
|
||||
lv_obj_t* kv = lv_label_create(row);
|
||||
const int lk = navKeyLower(s_nav_keys[t]);
|
||||
char kb[2] = { (char)(lk >= 'a' && lk <= 'z' ? lk - 'a' + 'A' : lk), 0 };
|
||||
lv_label_set_text(kv, kb);
|
||||
lv_obj_set_style_text_font(kv, &g_font_14, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(kv, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
|
||||
lv_obj_align(kv, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
s_navkey_row_val[t] = kv;
|
||||
y += SC(36);
|
||||
}
|
||||
// Programmable control keys (move/select/back + scroll up/down) — tap a row, press a key.
|
||||
y += settingsRowLabel(body, y, 0, TR("Navigation keys \xe2\x80\x94 tap a row, then press a key"), COLOR_SUB, &g_font_12, 0) + 2;
|
||||
for (int d = 0; d < 8; d++) {
|
||||
const int bi = d + 5; // binding index 5-12
|
||||
lv_obj_t* row = lv_btn_create(body);
|
||||
lv_obj_set_size(row, lv_pct(100), SC(30));
|
||||
lv_obj_set_pos(row, 2, y);
|
||||
styleButton(row);
|
||||
lv_obj_add_event_cb(row, navKeyCaptureStartCb, LV_EVENT_CLICKED, (void*)(intptr_t)bi);
|
||||
lv_obj_t* nm = lv_label_create(row);
|
||||
lv_label_set_text(nm, TR(kNavDirNames[d]));
|
||||
lv_obj_set_style_text_font(nm, &g_font_12, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(nm, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
|
||||
lv_obj_align(nm, LV_ALIGN_LEFT_MID, 8, 0);
|
||||
lv_obj_t* kv = lv_label_create(row);
|
||||
const int lk = navKeyLower(s_dir_keys[d]);
|
||||
char kb[2] = { (char)(lk >= 'a' && lk <= 'z' ? lk - 'a' + 'A' : lk), 0 };
|
||||
lv_label_set_text(kv, kb);
|
||||
lv_obj_set_style_text_font(kv, &g_font_14, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(kv, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
|
||||
lv_obj_align(kv, LV_ALIGN_RIGHT_MID, -10, 0);
|
||||
s_navkey_row_val[bi] = kv;
|
||||
y += SC(36);
|
||||
}
|
||||
y = settingsNavKeyRows(body, y); // tab hotkeys + navigation keys (shared with the Pager)
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -43866,14 +43959,9 @@ static int tabForKey(int key) {
|
||||
// an open chat, settings detail, app page, or popup they remain inert so a
|
||||
// letter cannot unexpectedly abandon the inner screen.
|
||||
if (!navOnMainPage()) return -1;
|
||||
switch (key) {
|
||||
case 'm': case 'M': return CHAT_INBOX_TAB_INDEX;
|
||||
case 'c': case 'C': return CONTACTS_TAB_INDEX;
|
||||
case 'h': case 'H': return HOME_TAB_INDEX;
|
||||
case 'a': case 'A': return MAP_TAB_INDEX;
|
||||
case 's': case 'S': return SETTINGS_TAB_INDEX;
|
||||
default: return -1;
|
||||
}
|
||||
// Programmable like the T-Deck's tab hotkeys (#591), defaulting to M/C/H/A/S
|
||||
// (Settings > Keyboard). Always active, as the Pager's mnemonics always were.
|
||||
return navTabForHotkey(key);
|
||||
#else
|
||||
// Old fixed letter tab-jumps (h/m/c/l/s) removed — tab jumps are now the
|
||||
// programmable keyboard-nav hotkeys (navTabForHotkey, default E/R/T/U/I), active
|
||||
@@ -46148,7 +46236,7 @@ if (g_lv.task && g_lv.task->isManualLock()) {
|
||||
{ char _pb[28]; snprintf(_pb, sizeof _pb, "key 0x%02X '%c'", key & 0xFF,
|
||||
(key >= 32 && key < 127) ? (char)key : '.'); if (g_lv.task) g_lv.task->showAlert(_pb, 1400); }
|
||||
#endif
|
||||
#if CAP_TRACKBALL
|
||||
#if CAP_TRACKBALL || defined(TLORA_PAGER)
|
||||
// Remapping a tab hotkey (Settings → Keyboard): capture the next key press.
|
||||
if (s_navkey_capture >= 0) { navKeyCaptureApply(key); return; }
|
||||
#endif
|
||||
@@ -46298,6 +46386,40 @@ if (g_lv.task && g_lv.task->isManualLock()) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Keyboard navigation (#591): the T-Deck's programmable letters, defaulting here to
|
||||
// I/K/J/L to move the highlight and U/O to scroll (Settings > Keyboard). Only with
|
||||
// no text field focused (this is the !ta branch), so letters in a field still type.
|
||||
// On the Map the letters move the highlight too; W/A/X/D above stay the map pan.
|
||||
if (s_kbd_nav) {
|
||||
const int act = navDirForKey(key);
|
||||
if (act >= 0) {
|
||||
lv_obj_t* focused = s_nav_group ? lv_group_get_focused(s_nav_group) : nullptr;
|
||||
(void)focused;
|
||||
switch (act) {
|
||||
case 0: navMoveDir(NAV_UP); break;
|
||||
case 1: navMoveDir(NAV_DOWN); break;
|
||||
case 2: case 3:
|
||||
#if defined(HAS_PAGER_ENCODER)
|
||||
// Wheel edit mode on a slider: preview only, saved on the closing click.
|
||||
if (focused && focused == s_pager_slider_edit) { navSliderNudge(focused, act == 3 ? +1 : -1); break; }
|
||||
#endif
|
||||
navMoveDir(act == 3 ? NAV_RIGHT : NAV_LEFT);
|
||||
break;
|
||||
case 4: // select: the same as Enter / the wheel's click
|
||||
#if defined(HAS_PAGER_ENCODER)
|
||||
if (pagerSliderEditClick()) break;
|
||||
#endif
|
||||
if (!navEnterBubble()) navPushTap(LV_KEY_ENTER);
|
||||
break;
|
||||
case 5: pagerNavGoBack(); break; // back: the same as Backspace-hold / long-press
|
||||
case 6: navScrollFocused(true); break; // scroll up
|
||||
default: navScrollFocused(false); break; // scroll down
|
||||
}
|
||||
s_nav_show = true;
|
||||
if (g_lv.task) g_lv.task->noteUserInput();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if CAP_TRACKBALL || defined(HAS_TDECK_PRO) || defined(HAS_THINKNODE_M9)
|
||||
// A field is focused but we're in navigate mode: select/Enter starts editing it, so the
|
||||
@@ -55551,10 +55673,11 @@ static void buildUiTree() {
|
||||
// Condense Nav rail the letter sits under the icon instead of beside it).
|
||||
// Add order == index order: Chats(0), Contacts(1), Home(2, middle), Map(3), Settings(4).
|
||||
#if defined(TLORA_PAGER)
|
||||
const char* const pager_chats_tab_label =
|
||||
s_nav_condensed ? LV_SYMBOL_ENVELOPE "\nM" : LV_SYMBOL_ENVELOPE " M";
|
||||
const char* const pager_settings_tab_label =
|
||||
s_nav_condensed ? LV_SYMBOL_SETTINGS "\nS" : LV_SYMBOL_SETTINGS " S";
|
||||
// The letters are the (remappable, #591) tab hotkeys; see pagerTabLabel().
|
||||
char pager_tab_labels[5][16];
|
||||
for (int t = 0; t < 5; t++) pagerTabLabel(t, pager_tab_labels[t], sizeof pager_tab_labels[t]);
|
||||
const char* const pager_chats_tab_label = pager_tab_labels[CHAT_INBOX_TAB_INDEX];
|
||||
const char* const pager_settings_tab_label = pager_tab_labels[4];
|
||||
auto pagerTabIconBadgeX = [](int tab_index, const char* label, const char* icon) -> lv_coord_t {
|
||||
const lv_coord_t cell_w = lv_disp_get_hor_res(nullptr) / 5;
|
||||
const lv_coord_t label_w = lv_txt_get_width(label, strlen(label), &g_font_tab, 0, LV_TEXT_FLAG_NONE);
|
||||
@@ -55562,12 +55685,9 @@ static void buildUiTree() {
|
||||
return cell_w * tab_index + cell_w / 2 - (label_w - icon_w) / 2 - 8;
|
||||
};
|
||||
lv_obj_t* tab_chats = lv_tabview_add_tab(g_lv.tabview, pager_chats_tab_label);
|
||||
lv_obj_t* tab_contacts = lv_tabview_add_tab(g_lv.tabview,
|
||||
s_nav_condensed ? TOUCH_SYM_PERSON "\nC" : TOUCH_SYM_PERSON " C");
|
||||
lv_obj_t* tab_home = lv_tabview_add_tab(g_lv.tabview,
|
||||
s_nav_condensed ? LV_SYMBOL_HOME "\nH" : LV_SYMBOL_HOME " H");
|
||||
lv_obj_t* tab_map = lv_tabview_add_tab(g_lv.tabview,
|
||||
s_nav_condensed ? LV_SYMBOL_GPS "\nA" : LV_SYMBOL_GPS " A");
|
||||
lv_obj_t* tab_contacts = lv_tabview_add_tab(g_lv.tabview, pager_tab_labels[CONTACTS_TAB_INDEX]);
|
||||
lv_obj_t* tab_home = lv_tabview_add_tab(g_lv.tabview, pager_tab_labels[HOME_TAB_INDEX]);
|
||||
lv_obj_t* tab_map = lv_tabview_add_tab(g_lv.tabview, pager_tab_labels[MAP_TAB_INDEX]);
|
||||
#else
|
||||
lv_obj_t* tab_chats = lv_tabview_add_tab(g_lv.tabview, LV_SYMBOL_ENVELOPE);
|
||||
lv_obj_t* tab_contacts = lv_tabview_add_tab(g_lv.tabview, TOUCH_SYM_PERSON); // person icon (FA user)
|
||||
@@ -61975,6 +62095,12 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
#if defined(HAS_TANMATSU)
|
||||
bsp_input_get_queue(&s_nav_queue);
|
||||
#endif
|
||||
#if defined(TLORA_PAGER)
|
||||
// Keyboard navigation (#591): on/off plus the programmable tab and navigation keys.
|
||||
s_kbd_nav = touchPrefsGetKbdNav();
|
||||
for (int i = 0; i < 5; i++) { uint8_t k = touchPrefsGetNavKey(i); if (k) s_nav_keys[i] = k; }
|
||||
for (int i = 0; i < 8; i++) { uint8_t k = touchPrefsGetNavDirKey(i); if (k) s_dir_keys[i] = k; }
|
||||
#endif
|
||||
#else
|
||||
// Physical touchscreen indev — skipped in remote mode (the panel is a placeholder;
|
||||
// its 320x240 coords would map wrong onto the 480x800 web UI). The web pointer drives it.
|
||||
|
||||
@@ -422,6 +422,7 @@ static const I18nPair kBuiltin_hu[] = {
|
||||
{ "Keyboard light on", "Billentyűzetfény be" },
|
||||
{ "Keyboard nav: WASDZ on", "Billentyűzet navigáció: WASDZ bekapcsolva" },
|
||||
{ "Keyboard nav: off", "Billentyűzetnavigáció: kikapcsolva" },
|
||||
{ "Keyboard nav: on", "Billentyűzetnavigáció: bekapcsolva" },
|
||||
{ "Keyboard navigation", "Billentyűzetnavigáció" },
|
||||
{ "Keys", "Kulcsok" },
|
||||
{ "Known regions", "Ismert régiók" },
|
||||
@@ -439,6 +440,7 @@ static const I18nPair kBuiltin_hu[] = {
|
||||
{ "Leaving remote mode - rebooting...", "Kilépés a távoli módból - újraindítás..." },
|
||||
{ "Left", "Balra" },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Állítsuk be az eszközt.\n\nVálassz egy nevet, és válaszd ki a LoRa régiódat. Körülbelül egy percet vesz igénybe." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "A betűk mozgatják a kijelölést, ha nincs aktív szövegmező. Az Enter kiválaszt; a Backspace nyomva tartása visszalép." },
|
||||
{ "Letters only", "Csak betűk" },
|
||||
{ "Loading catalog…", "Katalógus betöltése…" },
|
||||
{ "Loading map tiles…\n\nKeep Wi-Fi connected.\nTiles appear as they arrive.", "Térképcsempék betöltése…\n\nWi-Fi kapcsolaton keresztül.\nA csempék érkezésükkor jelennek meg." },
|
||||
@@ -1434,6 +1436,7 @@ static const I18nPair kBuiltin_nl[] = {
|
||||
{ "Keyboard light on", "Toetsenbordverlichting aan" },
|
||||
{ "Keyboard nav: WASDZ on", "Toetsenbordnav.: WASDZ aan" },
|
||||
{ "Keyboard nav: off", "Toetsenbordnav.: uit" },
|
||||
{ "Keyboard nav: on", "Toetsenbordnav.: aan" },
|
||||
{ "Keyboard navigation", "Toetsenbordnavigatie" },
|
||||
{ "Keys", "Toetsen" },
|
||||
{ "LOS → %s", "Zichtlijn → %s" },
|
||||
@@ -1450,6 +1453,7 @@ static const I18nPair kBuiltin_nl[] = {
|
||||
{ "Leaving remote mode - rebooting...", "Externe modus verlaten - herstarten..." },
|
||||
{ "Left", "Links" },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Laten we je apparaat instellen.\n\nJe kiest een naam en je LoRa-regio. Duurt ongeveer een minuut." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Letters verplaatsen de markering als er geen tekstveld actief is. Enter selecteert; houd Backspace ingedrukt om terug te gaan." },
|
||||
{ "Letters only", "Alleen letters" },
|
||||
{ "LoRa antenna (P4)", "LoRa-antenne (P4)" },
|
||||
{ "Loading catalog…", "Catalogus laden…" },
|
||||
@@ -2369,6 +2373,7 @@ static const I18nPair kBuiltin_de[] = {
|
||||
{ "Keyboard light on", "Tastaturlicht ein" },
|
||||
{ "Keyboard nav: WASDZ on", "Tastaturnav.: WASDZ an" },
|
||||
{ "Keyboard nav: off", "Tastaturnav.: aus" },
|
||||
{ "Keyboard nav: on", "Tastaturnav.: an" },
|
||||
{ "Keyboard navigation", "Tastaturnavigation" },
|
||||
{ "Keys", "Tasten" },
|
||||
{ "LOS → %s", "Sichtlinie → %s" },
|
||||
@@ -2385,6 +2390,7 @@ static const I18nPair kBuiltin_de[] = {
|
||||
{ "Leaving remote mode - rebooting...", "Fernmodus wird beendet - Neustart..." },
|
||||
{ "Left", "Links" },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Richten wir dein Gerät ein.\n\nDu wählst einen Namen und deine LoRa-Region. Dauert etwa eine Minute." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Buchstaben bewegen die Markierung, wenn kein Textfeld aktiv ist. Enter wählt aus; Rücktaste halten geht zurück." },
|
||||
{ "Letters only", "Nur Buchstaben" },
|
||||
{ "LoRa antenna (P4)", "LoRa-Antenne (P4)" },
|
||||
{ "Loading catalog…", "Katalog wird geladen…" },
|
||||
@@ -3290,6 +3296,7 @@ static const I18nPair kBuiltin_fr[] = {
|
||||
{ "Keyboard light on", "Éclairage clavier activé" },
|
||||
{ "Keyboard nav: WASDZ on", "Nav. clavier : WASDZ activé" },
|
||||
{ "Keyboard nav: off", "Nav. clavier : désactivé" },
|
||||
{ "Keyboard nav: on", "Nav. clavier : activé" },
|
||||
{ "Keyboard navigation", "Navigation clavier" },
|
||||
{ "LOS → %s", "Vue directe → %s" },
|
||||
{ "Landscape (desktop)", "Paysage (bureau)" },
|
||||
@@ -3302,6 +3309,7 @@ static const I18nPair kBuiltin_fr[] = {
|
||||
{ "Leave empty if not required", "Laisser vide si non requis" },
|
||||
{ "Leaving remote mode - rebooting...", "Sortie du mode distant - redémarrage..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configurons votre appareil.\n\nVous choisirez un nom et votre région LoRa. Cela prend environ une minute." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Les lettres déplacent la sélection quand aucun champ de texte n'est actif. Entrée sélectionne ; maintenez Retour arrière pour revenir." },
|
||||
{ "Letters only", "Lettres uniquement" },
|
||||
{ "LoRa antenna (P4)", "Antenne LoRa (P4)" },
|
||||
{ "Loading catalog…", "Chargement du catalogue…" },
|
||||
@@ -4184,6 +4192,7 @@ static const I18nPair kBuiltin_es[] = {
|
||||
{ "Keyboard light on", "Luz del teclado encendida" },
|
||||
{ "Keyboard nav: WASDZ on", "Nav. teclado: WASDZ act." },
|
||||
{ "Keyboard nav: off", "Nav. teclado: desact." },
|
||||
{ "Keyboard nav: on", "Nav. teclado: activ." },
|
||||
{ "Keyboard navigation", "Navegación por teclado" },
|
||||
{ "LOS → %s", "Línea de vista → %s" },
|
||||
{ "Landscape (desktop)", "Horizontal (escritorio)" },
|
||||
@@ -4197,6 +4206,7 @@ static const I18nPair kBuiltin_es[] = {
|
||||
{ "Leave empty if not required", "Dejar vacío si no es necesario" },
|
||||
{ "Leaving remote mode - rebooting...", "Saliendo del modo remoto - reiniciando..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configuremos tu dispositivo.\n\nElegirás un nombre y tu región LoRa. Tarda alrededor de un minuto." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Las letras mueven el resaltado cuando no hay un campo de texto activo. Intro selecciona; mantén Retroceso para volver." },
|
||||
{ "Letters only", "Solo letras" },
|
||||
{ "LoRa antenna (P4)", "Antena LoRa (P4)" },
|
||||
{ "Loading catalog…", "Cargando catálogo…" },
|
||||
@@ -5084,6 +5094,7 @@ static const I18nPair kBuiltin_it[] = {
|
||||
{ "Keyboard light on", "Luce tastiera accesa" },
|
||||
{ "Keyboard nav: WASDZ on", "Nav. tastiera: WASDZ on" },
|
||||
{ "Keyboard nav: off", "Nav. tastiera: off" },
|
||||
{ "Keyboard nav: on", "Nav. tastiera: on" },
|
||||
{ "Keyboard navigation", "Navigazione da tastiera" },
|
||||
{ "LOS → %s", "Vista diretta → %s" },
|
||||
{ "Landscape (desktop)", "Orizzontale (desktop)" },
|
||||
@@ -5097,6 +5108,7 @@ static const I18nPair kBuiltin_it[] = {
|
||||
{ "Leave empty if not required", "Lascia vuoto se non richiesto" },
|
||||
{ "Leaving remote mode - rebooting...", "Uscita dalla modalità remota - riavvio..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Configuriamo il tuo dispositivo.\n\nSceglierai un nome e la tua regione LoRa. Richiede circa un minuto." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Le lettere spostano l'evidenziazione quando nessun campo di testo è attivo. Invio seleziona; tieni premuto Backspace per tornare indietro." },
|
||||
{ "Letters only", "Solo lettere" },
|
||||
{ "LoRa antenna (P4)", "Antenna LoRa (P4)" },
|
||||
{ "Loading catalog…", "Caricamento catalogo…" },
|
||||
@@ -5988,6 +6000,7 @@ static const I18nPair kBuiltin_ru[] = {
|
||||
{ "Keyboard light on", "Подсветка клавиатуры вкл." },
|
||||
{ "Keyboard nav: WASDZ on", "Навигация клавиатурой: WASDZ вкл." },
|
||||
{ "Keyboard nav: off", "Навигация клавиатурой: выкл." },
|
||||
{ "Keyboard nav: on", "Навигация клавиатурой: вкл." },
|
||||
{ "Keyboard navigation", "Навигация клавиатурой" },
|
||||
{ "LOS → %s", "Прямая видимость → %s" },
|
||||
{ "Landscape (desktop)", "Альбомная (десктоп)" },
|
||||
@@ -6001,6 +6014,7 @@ static const I18nPair kBuiltin_ru[] = {
|
||||
{ "Leave empty if not required", "Оставьте пустым, если не нужно" },
|
||||
{ "Leaving remote mode - rebooting...", "Выход из удалённого режима - перезагрузка..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Настроим ваше устройство.\n\nВы выберете имя и регион LoRa. Займёт около минуты." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Буквы перемещают выделение, когда не активно текстовое поле. Enter выбирает; удерживайте Backspace, чтобы вернуться." },
|
||||
{ "Letters only", "Только буквы" },
|
||||
{ "LoRa antenna (P4)", "Антенна LoRa (P4)" },
|
||||
{ "Loading catalog…", "Загрузка каталога…" },
|
||||
@@ -6899,6 +6913,7 @@ static const I18nPair kBuiltin_uk[] = {
|
||||
{ "Keyboard light on", "Підсвітка клавіатури увімк." },
|
||||
{ "Keyboard nav: WASDZ on", "Навігація клавіатурою: WASDZ увімк." },
|
||||
{ "Keyboard nav: off", "Навігація клавіатурою: вимк." },
|
||||
{ "Keyboard nav: on", "Навігація клавіатурою: увімк." },
|
||||
{ "Keyboard navigation", "Навігація клавіатурою" },
|
||||
{ "LOS → %s", "Пряма видимість → %s" },
|
||||
{ "Landscape (desktop)", "Альбомна (десктоп)" },
|
||||
@@ -6912,6 +6927,7 @@ static const I18nPair kBuiltin_uk[] = {
|
||||
{ "Leave empty if not required", "Залиште порожнім, якщо не потрібно" },
|
||||
{ "Leaving remote mode - rebooting...", "Вихід із віддаленого режиму - перезавантаження..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Налаштуймо ваш пристрій.\n\nВи виберете ім'я та регіон LoRa. Займе близько хвилини." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Літери переміщують виділення, коли не активне текстове поле. Enter вибирає; утримуйте Backspace, щоб повернутися." },
|
||||
{ "Letters only", "Лише літери" },
|
||||
{ "LoRa antenna (P4)", "Антена LoRa (P4)" },
|
||||
{ "Loading catalog…", "Завантаження каталогу…" },
|
||||
@@ -7808,6 +7824,7 @@ static const I18nPair kBuiltin_bg[] = {
|
||||
{ "Keyboard light on", "Подсветката на клавиатурата е вкл." },
|
||||
{ "Keyboard nav: WASDZ on", "Клавиатурна навиг.: WASDZ вкл." },
|
||||
{ "Keyboard nav: off", "Клавиатурна навиг.: изкл." },
|
||||
{ "Keyboard nav: on", "Клавиатурна навиг.: вкл." },
|
||||
{ "Keyboard navigation", "Навигация с клавиатура" },
|
||||
{ "LOS → %s", "Пряка видимост → %s" },
|
||||
{ "Landscape (desktop)", "Пейзаж (десктоп)" },
|
||||
@@ -7821,6 +7838,7 @@ static const I18nPair kBuiltin_bg[] = {
|
||||
{ "Leave empty if not required", "Оставете празно, ако не е нужно" },
|
||||
{ "Leaving remote mode - rebooting...", "Излизане от отдалечен режим - рестартиране..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Нека настроим устройството ви.\n\nЩе изберете име и вашия LoRa регион. Отнема около минута." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Буквите местят маркера, когато няма активно текстово поле. Enter избира; задръжте Backspace за назад." },
|
||||
{ "Letters only", "Само букви" },
|
||||
{ "LoRa antenna (P4)", "LoRa антена (P4)" },
|
||||
{ "Loading catalog…", "Зареждане на каталога…" },
|
||||
@@ -8717,6 +8735,7 @@ static const I18nPair kBuiltin_sr[] = {
|
||||
{ "Keyboard light on", "Осветљење тастатуре укљ." },
|
||||
{ "Keyboard nav: WASDZ on", "Навиг. тастатуром: WASDZ укљ." },
|
||||
{ "Keyboard nav: off", "Навиг. тастатуром: искљ." },
|
||||
{ "Keyboard nav: on", "Навиг. тастатуром: укљ." },
|
||||
{ "Keyboard navigation", "Навигација тастатуром" },
|
||||
{ "LOS → %s", "Оптичка видљивост → %s" },
|
||||
{ "Landscape (desktop)", "Положено (десктоп)" },
|
||||
@@ -8730,6 +8749,7 @@ static const I18nPair kBuiltin_sr[] = {
|
||||
{ "Leave empty if not required", "Оставите празно ако није потребно" },
|
||||
{ "Leaving remote mode - rebooting...", "Излазак из даљинског режима - рестартовање..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Хајде да подесимо ваш уређај.\n\nИзабраћете име и ваш LoRa регион. Траје око минут." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Слова померају истицање када ниједно текстуално поље није активно. Enter бира; држите Backspace за назад." },
|
||||
{ "Letters only", "Само слова" },
|
||||
{ "LoRa antenna (P4)", "LoRa антена (P4)" },
|
||||
{ "Loading catalog…", "Учитавање каталога…" },
|
||||
@@ -9624,6 +9644,7 @@ static const I18nPair kBuiltin_el[] = {
|
||||
{ "Keyboard light on", "Φωτισμός πληκτρολογίου ενεργός" },
|
||||
{ "Keyboard nav: WASDZ on", "Πλοήγηση πληκτρ.: WASDZ ενεργό" },
|
||||
{ "Keyboard nav: off", "Πλοήγηση πληκτρ.: κλειστό" },
|
||||
{ "Keyboard nav: on", "Πλοήγηση πληκτρ.: ανοιχτό" },
|
||||
{ "Keyboard navigation", "Πλοήγηση με πληκτρολόγιο" },
|
||||
{ "LOS → %s", "Οπτική επαφή → %s" },
|
||||
{ "Landscape (desktop)", "Οριζόντια (desktop)" },
|
||||
@@ -9637,6 +9658,7 @@ static const I18nPair kBuiltin_el[] = {
|
||||
{ "Leave empty if not required", "Αφήστε κενό αν δεν απαιτείται" },
|
||||
{ "Leaving remote mode - rebooting...", "Έξοδος από απομακρυσμένη λειτουργία - επανεκκίνηση..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Ας ρυθμίσουμε τη συσκευή σας.\n\nΘα επιλέξετε όνομα και την περιοχή LoRa. Διαρκεί περίπου ένα λεπτό." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Τα γράμματα μετακινούν την επισήμανση όταν δεν είναι ενεργό πεδίο κειμένου. Το Enter επιλέγει· κρατήστε το Backspace για επιστροφή." },
|
||||
{ "Letters only", "Μόνο γράμματα" },
|
||||
{ "LoRa antenna (P4)", "Κεραία LoRa (P4)" },
|
||||
{ "Loading catalog…", "Φόρτωση καταλόγου…" },
|
||||
@@ -10523,6 +10545,7 @@ static const I18nPair kBuiltin_pt_br[] = {
|
||||
{ "Keyboard light on", "Luz do teclado ligada" },
|
||||
{ "Keyboard nav: WASDZ on", "Nav. teclado: WASDZ ligado" },
|
||||
{ "Keyboard nav: off", "Nav. teclado: desligado" },
|
||||
{ "Keyboard nav: on", "Nav. teclado: ligado" },
|
||||
{ "Keyboard navigation", "Navegação por teclado" },
|
||||
{ "LOS → %s", "Linha de visada → %s" },
|
||||
{ "Landscape (desktop)", "Paisagem (desktop)" },
|
||||
@@ -10535,6 +10558,7 @@ static const I18nPair kBuiltin_pt_br[] = {
|
||||
{ "Leave empty if not required", "Deixe vazio se não for necessário" },
|
||||
{ "Leaving remote mode - rebooting...", "Saindo do modo remoto - reiniciando..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Vamos configurar seu dispositivo.\n\nVocê escolherá um nome e sua região LoRa. Leva cerca de um minuto." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "As letras movem o destaque quando nenhum campo de texto está ativo. Enter seleciona; segure Backspace para voltar." },
|
||||
{ "Letters only", "Somente letras" },
|
||||
{ "LoRa antenna (P4)", "Antena LoRa (P4)" },
|
||||
{ "Loading catalog…", "Carregando catálogo…" },
|
||||
@@ -11413,6 +11437,7 @@ static const I18nPair kBuiltin_ro[] = {
|
||||
{ "Keyboard light on", "Lumina tastaturii pornită" },
|
||||
{ "Keyboard nav: WASDZ on", "Navigare tastatură: WASDZ pornit" },
|
||||
{ "Keyboard nav: off", "Navigare tastatură: oprită" },
|
||||
{ "Keyboard nav: on", "Navigare tastatură: pornită" },
|
||||
{ "Keyboard navigation", "Navigare cu tastatura" },
|
||||
{ "LOS → %s", "Linie vizuală → %s" },
|
||||
{ "Landscape (desktop)", "Peisaj (desktop)" },
|
||||
@@ -11426,6 +11451,7 @@ static const I18nPair kBuiltin_ro[] = {
|
||||
{ "Leave empty if not required", "Lasă gol dacă nu e necesar" },
|
||||
{ "Leaving remote mode - rebooting...", "Se iese din modul la distanţă - repornire..." },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Să-ţi configurăm dispozitivul.\n\nÎţi alegi un nume şi regiunea LoRa. Durează cam un minut." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Literele mută selecția când niciun câmp de text nu este activ. Enter selectează; ține apăsat Backspace pentru înapoi." },
|
||||
{ "Letters only", "Doar litere" },
|
||||
{ "LoRa antenna (P4)", "Antenă LoRa (P4)" },
|
||||
{ "Loading catalog…", "Se încarcă catalogul…" },
|
||||
@@ -12427,6 +12453,7 @@ static const I18nPair kBuiltin_cs[] = {
|
||||
{ "Keyboard light on", "Světlo klávesnice zapnuto" },
|
||||
{ "Keyboard nav: WASDZ on", "Navigace klávesnicí: WASDZ zapnuta" },
|
||||
{ "Keyboard nav: off", "Navigace klávesnicí: vypnuta" },
|
||||
{ "Keyboard nav: on", "Navigace klávesnicí: zapnuta" },
|
||||
{ "Keyboard navigation", "Navigace klávesnicí" },
|
||||
{ "Keys", "Klávesy" },
|
||||
{ "Known regions", "Známé regiony" },
|
||||
@@ -12446,6 +12473,7 @@ static const I18nPair kBuiltin_cs[] = {
|
||||
{ "Leaving remote mode - restarting…", "Ukončování vzdáleného režimu - restartování…" },
|
||||
{ "Left", "Vlevo" },
|
||||
{ "Let's set up your device.\n\nYou'll pick a name and choose your LoRa region. Takes about a minute.", "Nastavme vaše zařízení.\n\nVyberete jméno a region LoRa. Zabere to asi minutu." },
|
||||
{ "Letters move the highlight when no text field is focused. Enter selects; hold Backspace to go back.", "Písmena posouvají zvýraznění, když není aktivní textové pole. Enter vybírá; podržením Backspace se vrátíte zpět." },
|
||||
{ "Letters only", "Pouze písmena" },
|
||||
{ "Link lost", "Spojení ztraceno" },
|
||||
{ "LoRa antenna (P4)", "LoRa anténa (P4)" },
|
||||
|
||||
Reference in New Issue
Block a user