From 45e46cd3abab5ed05aa95fb5cb8326d6ade08cd9 Mon Sep 17 00:00:00 2001 From: Kaj Schittecat Date: Wed, 24 Jun 2026 12:27:36 +0200 Subject: [PATCH] touch: EU keyboard layouts (FR/NL/DE/ES/IT) + auto-select by UI language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds French (AZERTY), Dutch, German (QWERTZ), Spanish and Italian layouts — both the on-screen keyboards and the T-Deck physical-keyboard maps (KeyboardLayoutId FR..IT). Picking a UI language now auto-enables and selects the matching keyboard layout, so choosing French gives AZERTY without a separate trip to keyboard settings. Accented letters still come from the long-press accent popup; no new fonts required. Extended the settings layout-enable list to match the larger enum (was a latent out-of-bounds once the layout count grew). Ported from PR #29 by @samuelcoustet — the layout data applied cleanly; the UITask wiring was re-applied by hand against current main (the PR was cut from beta_12 and no longer merges). Co-Authored-By: samuelcoustet <257299705+samuelcoustet@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui-touch/KeyboardLayouts.cpp | 251 +++++++++++++++++++++++++++++++ src/ui-touch/KeyboardLayouts.h | 5 + src/ui-touch/UITask.cpp | 44 +++++- 3 files changed, 297 insertions(+), 3 deletions(-) diff --git a/src/ui-touch/KeyboardLayouts.cpp b/src/ui-touch/KeyboardLayouts.cpp index 8cd1b5e..a036c8c 100644 --- a/src/ui-touch/KeyboardLayouts.cpp +++ b/src/ui-touch/KeyboardLayouts.cpp @@ -221,6 +221,155 @@ static const lv_btnmatrix_ctrl_t kb_en_upper_ctrl[] = { LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 }; +/* ---------- French (AZERTY) on-screen keyboard ---------- */ +/* Keeps the proven LVGL control rows/punctuation, but swaps the alpha rows to + * a familiar French AZERTY order. Accents still come from the existing popup. */ +static const char* kb_fr_lower[] = { + "1#", "a", "z", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", + "ABC", "q", "s", "d", "f", "g", "h", "j", "k", "l", "m", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "w", "x", "c", "v", "b", "n", ".", ",", ":", "", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_fr_lower_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_HIDDEN, + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +static const char* kb_fr_upper[] = { + "1#", "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", + "abc", "Q", "S", "D", "F", "G", "H", "J", "K", "L", "M", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "W", "X", "C", "V", "B", "N", ".", ",", ":", "", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_fr_upper_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_HIDDEN, + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +/* Dutch largely stays QWERTY, but a dedicated IJ key makes the most common + * digraph directly reachable without forcing users through the accent popup. */ +static const char* kb_nl_lower[] = { + "1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", + "ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", "ij", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_nl_lower_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(2), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +static const char* kb_nl_upper[] = { + "1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", + "abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", "IJ", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_nl_upper_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(2), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +/* German QWERTZ keeps the proven LVGL control rows and swaps the Y/Z alpha + * positions. Umlauts / eszett still come from the accent popup. */ +static const char* kb_de_lower[] = { + "1#", "q", "w", "e", "r", "t", "z", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", + "ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "y", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_de_lower_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +static const char* kb_de_upper[] = { + "1#", "Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", + "abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "Y", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_de_upper_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +/* Spanish keeps QWERTY but promotes n-tilde directly onto the alpha deck. */ +static const char* kb_es_lower[] = { + "1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", + "ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", "ñ", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_es_lower_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +static const char* kb_es_upper[] = { + "1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", + "abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Ñ", LV_SYMBOL_NEW_LINE, "\n", + "_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_es_upper_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +/* Italian gets the common accented vowels directly on the base layer. */ +static const char* kb_it_lower[] = { + "1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", + "ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", "à", LV_SYMBOL_NEW_LINE, "\n", + "è", "ì", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_it_lower_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + +static const char* kb_it_upper[] = { + "1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", + "abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", "À", LV_SYMBOL_NEW_LINE, "\n", + "È", "Ì", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n", + LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" +}; + +static const lv_btnmatrix_ctrl_t kb_it_upper_ctrl[] = { + LV_KEYBOARD_CTRL_BTN_FLAGS | 5, EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), EN_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7, + LV_KEYBOARD_CTRL_BTN_FLAGS | 6, EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), EN_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7, + EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | EN_KB_BTN(1), + LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 +}; + struct OsKeyboardLayout { KeyboardLayoutId id; const char* name; @@ -252,6 +401,21 @@ static const OsKeyboardLayout k_os_layouts[] = { { KeyboardLayoutId::AR, "AR", kb_ar_lower, kb_ar_lower_ctrl, kb_ar_lower, kb_ar_lower_ctrl }, // unicameral: same map for both + { KeyboardLayoutId::FR, "FR", + kb_fr_lower, kb_fr_lower_ctrl, + kb_fr_upper, kb_fr_upper_ctrl }, + { KeyboardLayoutId::NL, "NL", + kb_nl_lower, kb_nl_lower_ctrl, + kb_nl_upper, kb_nl_upper_ctrl }, + { KeyboardLayoutId::DE, "DE", + kb_de_lower, kb_de_lower_ctrl, + kb_de_upper, kb_de_upper_ctrl }, + { KeyboardLayoutId::ES, "ES", + kb_es_lower, kb_es_lower_ctrl, + kb_es_upper, kb_es_upper_ctrl }, + { KeyboardLayoutId::IT, "IT", + kb_it_lower, kb_it_lower_ctrl, + kb_it_upper, kb_it_upper_ctrl }, }; /* ================================================================ @@ -271,6 +435,11 @@ static const HwKeyboardLayout k_hw_layouts[] = { { KeyboardLayoutId::SR, "SR" }, { KeyboardLayoutId::EL, "EL" }, { KeyboardLayoutId::AR, "AR" }, + { KeyboardLayoutId::FR, "FR" }, + { KeyboardLayoutId::NL, "NL" }, + { KeyboardLayoutId::DE, "DE" }, + { KeyboardLayoutId::ES, "ES" }, + { KeyboardLayoutId::IT, "IT" }, }; /* Bulgarian phonetic mapping for T-Deck. @@ -424,6 +593,83 @@ static const char* hw_ar_digits[10] = { nullptr, "ج", "د", "ذ", "ز", "ط", "ظ", "و", nullptr, nullptr }; +/* French AZERTY mapping for the T-Deck's US-QWERTY physical keyboard. + * We only remap the alpha keys so punctuation/digits remain predictable on the + * compact hardware; accented letters are still available through accent popups. */ +static const char* hw_fr_lower[26] = { + "q", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "a", "r", "s", "t", + "u", "v", "z", "x", "y", "w" +}; + +static const char* hw_fr_upper[26] = { + "Q", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "A", "R", "S", "T", + "U", "V", "Z", "X", "Y", "W" +}; + +static const char* hw_fr_digits[10] = { nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr }; +static const char* hw_fr_digits_shift[10] = { nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr }; + +/* German QWERTZ on the T-Deck's US-QWERTY hardware: swap Y/Z and keep the rest + * predictable. Umlauts / eszett stay available through accent popups. */ +static const char* hw_de_lower[26] = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "z", "y" +}; +static const char* hw_de_upper[26] = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Z", "Y" +}; +static const char* hw_de_digits[10] = { nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr }; +static const char* hw_de_digits_shift[10] = { nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr }; + +/* Dutch keeps the standard alpha matrix but exposes the common IJ digraph on a + * digit slot so compact hardware still gets a language-specific shortcut. */ +static const char* hw_nl_lower[26] = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z" +}; +static const char* hw_nl_upper[26] = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z" +}; +static const char* hw_nl_digits[10] = { nullptr, "ij", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +static const char* hw_nl_digits_shift[10] = { nullptr, "IJ", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + +/* Spanish stays close to US QWERTY; use the number row to expose n-tilde on + * compact hardware where the semicolon key is absent. */ +static const char* hw_es_lower[26] = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z" +}; +static const char* hw_es_upper[26] = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z" +}; +static const char* hw_es_digits[10] = { nullptr, "ñ", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +static const char* hw_es_digits_shift[10] = { nullptr, "Ñ", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + +/* Italian uses the number row to surface the common accented vowels directly. */ +static const char* hw_it_lower[26] = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", "z" +}; +static const char* hw_it_upper[26] = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z" +}; +static const char* hw_it_digits[10] = { nullptr, "à", "è", "ì", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +static const char* hw_it_digits_shift[10] = { nullptr, "À", "È", "Ì", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + /* ================================================================ * Runtime state * ================================================================ */ @@ -499,6 +745,11 @@ static const HwPhoneticMap k_hw_maps[KEYBOARD_LAYOUT_COUNT] = { /* SR */ { hw_sr_lower, hw_sr_upper, hw_sr_digits, hw_sr_digits_shift }, /* EL */ { hw_el_lower, hw_el_upper, hw_el_digits, hw_el_digits_shift }, /* AR */ { hw_ar_lower, hw_ar_lower, hw_ar_digits, hw_ar_digits }, // unicameral + /* FR */ { hw_fr_lower, hw_fr_upper, hw_fr_digits, hw_fr_digits_shift }, + /* NL */ { hw_nl_lower, hw_nl_upper, hw_nl_digits, hw_nl_digits_shift }, + /* DE */ { hw_de_lower, hw_de_upper, hw_de_digits, hw_de_digits_shift }, + /* ES */ { hw_es_lower, hw_es_upper, hw_es_digits, hw_es_digits_shift }, + /* IT */ { hw_it_lower, hw_it_upper, hw_it_digits, hw_it_digits_shift }, }; const char* keyboardLayoutMapHwKey(KeyboardLayoutId id, int key, bool shifted) { diff --git a/src/ui-touch/KeyboardLayouts.h b/src/ui-touch/KeyboardLayouts.h index e3e2442..38c9b5f 100644 --- a/src/ui-touch/KeyboardLayouts.h +++ b/src/ui-touch/KeyboardLayouts.h @@ -19,6 +19,11 @@ enum class KeyboardLayoutId : uint8_t { SR = 4, // Serbian (phonetic) — best-effort, validate with a native speaker EL = 5, // Greek (ΕΛΟΤ-style phonetic) AR = 6, // Arabic (RTL, Arabic-101 positions) — EXPERIMENTAL, needs on-device validation + FR = 7, // French (AZERTY) + NL = 8, // Dutch (QWERTY + accent popups) + DE = 9, // German (QWERTZ) + ES = 10, // Spanish (QWERTY + accent popups) + IT = 11, // Italian (QWERTY + accent popups) Count }; diff --git a/src/ui-touch/UITask.cpp b/src/ui-touch/UITask.cpp index 60f118a..132c0e3 100644 --- a/src/ui-touch/UITask.cpp +++ b/src/ui-touch/UITask.cpp @@ -3174,7 +3174,7 @@ static void kbSetRotateArrowsOpa(lv_opa_t opa) { // a little key showing the active layout's 2-letter code; tapping it cycles // English -> each enabled secondary layout -> back, like the T-Deck's SPACE. static const char* kbLayoutCode(KeyboardLayoutId id) { - static const char* k_codes[] = { "EN", "BG", "RU", "UK", "SR", "EL", "AR" }; + static const char* k_codes[] = { "EN", "BG", "RU", "UK", "SR", "EL", "AR", "FR", "NL", "DE", "ES", "IT" }; const uint8_t i = static_cast(id); return (i < (sizeof k_codes / sizeof k_codes[0])) ? k_codes[i] : "EN"; } @@ -3191,6 +3191,41 @@ static void kbLangCycleCb(lv_event_t* e) { if (g_lv.task) g_lv.task->showAlert(keyboardLayoutName(next), 800); } +// Picking a UI language auto-enables + selects the matching keyboard layout (so choosing French +// gives AZERTY without a separate trip to keyboard settings). EU layouts via #29 (samuelcoustet). +static KeyboardLayoutId kbDefaultLayoutForUiLang(uint8_t lang) { + switch (lang) { + case LANG_NL: return KeyboardLayoutId::NL; + case LANG_DE: return KeyboardLayoutId::DE; + case LANG_ES: return KeyboardLayoutId::ES; + case LANG_IT: return KeyboardLayoutId::IT; + case LANG_BG: return KeyboardLayoutId::BG; + case LANG_RU: return KeyboardLayoutId::RU; + case LANG_UK: return KeyboardLayoutId::UK; + case LANG_SR: return KeyboardLayoutId::SR; + case LANG_EL: return KeyboardLayoutId::EL; + case LANG_FR: return KeyboardLayoutId::FR; + default: return KeyboardLayoutId::EN; + } +} +static void kbApplyUiLangDefault(uint8_t lang) { + KeyboardLayoutId target = kbDefaultLayoutForUiLang(lang); + uint32_t mask = keyboardLayoutsGetEnabledMask(); + const uint8_t target_id = static_cast(target); + if (target != KeyboardLayoutId::EN && !(mask & (1u << target_id))) { + mask |= (1u << target_id); + keyboardLayoutsSetEnabledMask(mask); +#if defined(ESP32) + touchPrefsSetEnabledLayouts(static_cast(mask)); +#endif + } + if (g_lv.keyboard) keyboardLayoutsApply(g_lv.keyboard, target); +#if defined(ESP32) + touchPrefsSetKeyboardLayout(target_id); +#endif + kbLangBtnRefresh(); +} + static void kbShowRotateArrows(bool show) { if (s_kb_rot_left_btn) show ? lv_obj_clear_flag(s_kb_rot_left_btn, LV_OBJ_FLAG_HIDDEN) : lv_obj_add_flag (s_kb_rot_left_btn, LV_OBJ_FLAG_HIDDEN); @@ -7660,9 +7695,11 @@ static void buildDeviceSettings(int sec) { uint16_t en_mask = 0; #endif /* One row per non-English layout; index id-1 into the display names. - Keep in KeyboardLayoutId order (BG=1 .. AR=6). */ + Keep in KeyboardLayoutId order (BG=1 .. IT=11). MUST have one entry per + non-EN layout or the loop below reads past the end. */ static const char* k_kb_disp[] = { - "Bulgarian", "Russian", "Ukrainian", "Serbian", "Greek", "Arabic (experimental)" + "Bulgarian", "Russian", "Ukrainian", "Serbian", "Greek", "Arabic (experimental)", + "French", "Dutch", "German", "Spanish", "Italian" }; for (int id = 1; id < KEYBOARD_LAYOUT_COUNT; ++id) { int h = settingsRowLabel(body, y, 4, k_kb_disp[id - 1], COLOR_TEXT, &g_font_12, 56); @@ -19665,6 +19702,7 @@ static void langChosenCb(lv_event_t* e) { touchPrefsSetUiLang(lang); #endif i18nSetLang(lang); + kbApplyUiLangDefault(lang); // auto-enable + select the matching keyboard layout for this UI language if (g_lv.task) g_lv.task->rebootDevice(); }