Add example-name hint to the preset-name keyboard

The preset-name keypad (used both when adding a preset via manual
coordinates and after an address search) only showed "Name
(optional):" with no indication of what kind of name was expected.
Adds a localized hint line with example names (e.g. "Home, Work,
Holiday home") under the title in both runPresetNameKeypad()
(location_presets_screen.cpp) and runNameKeypad()
(address_search_screen.cpp), measuring the header height dynamically
via getCursorY() the same way runAddressKeyboard() already does, so
the input field and keyboard shift down to make room regardless of
translation length.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Eiswolf-BG
2026-08-11 01:18:14 +02:00
parent 795c008af6
commit a757d693e7
9 changed files with 58 additions and 11 deletions

View File

@@ -296,7 +296,19 @@ namespace {
constexpr int16_t KEY_H = 30; constexpr int16_t KEY_H = 30;
constexpr int16_t KEY_GAP = 3; constexpr int16_t KEY_GAP = 3;
constexpr int16_t ROW0_Y = 78; constexpr int16_t FIELD_H = 34;
// Kopfbereich (Titel + Namens-Hinweis) einmal zeichnen, um seine
// tatsaechliche Hoehe per getCursorY() zu messen - selbes Muster wie
// in runAddressKeyboard() oben.
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14);
tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT));
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.println(I18n::t(StringId::LOCATION_NAME_HINT));
int16_t fieldY = (int16_t)(tft.getCursorY() + 4);
int16_t ROW0_Y = (int16_t)(fieldY + FIELD_H + 8);
auto layoutRow = [&](const char* row, int16_t y, Rect* outRects, uint8_t n) { auto layoutRow = [&](const char* row, int16_t y, Rect* outRects, uint8_t n) {
int16_t usableW = Config::SCREEN_WIDTH - 8; int16_t usableW = Config::SCREEN_WIDTH - 8;
@@ -326,12 +338,14 @@ namespace {
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14); tft.setCursor(10, 14);
tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT)); tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT));
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.println(I18n::t(StringId::LOCATION_NAME_HINT));
tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_BLACK); tft.fillRect(8, fieldY, Config::SCREEN_WIDTH - 16, FIELD_H, TFT_BLACK);
tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_GREEN); tft.drawRect(8, fieldY, Config::SCREEN_WIDTH - 16, FIELD_H, TFT_GREEN);
tft.setTextSize(2); tft.setTextSize(2);
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(14, 66); tft.setCursor(14, (int16_t)(fieldY + 26));
tft.print(buf); tft.print(buf);
tft.setTextSize(1); tft.setTextSize(1);

View File

@@ -268,6 +268,13 @@ enum class StringId : uint8_t {
FIRST_RUN_COMPLETE_BODY1, FIRST_RUN_COMPLETE_BODY1,
FIRST_RUN_COMPLETE_BODY2, FIRST_RUN_COMPLETE_BODY2,
// Hinweis mit Beispielnamen unter dem Titel der Namens-Tastatur
// (location_presets_screen.cpp::runPresetNameKeypad UND
// address_search_screen.cpp::runNameKeypad, beide spiegeln sich) -
// ohne jeden Hinweis wussten Einsteiger nicht, was fuer ein Name hier
// gemeint ist (Preset-Name wie "Zuhause", nicht z.B. ein Ort/Adresse).
LOCATION_NAME_HINT,
COUNT COUNT
}; };

View File

@@ -227,6 +227,8 @@ static const char* const I18N_DE[] = {
"Einrichtung abgeschlossen", "Einrichtung abgeschlossen",
"Der Einrichtungsprozess ist jetzt abgeschlossen. Alle Daten wurden auf der SD-Karte im Ordner ", "Der Einrichtungsprozess ist jetzt abgeschlossen. Alle Daten wurden auf der SD-Karte im Ordner ",
" abgespeichert. Ab dem nächsten Start verbindet sich das WLAN automatisch.", " abgespeichert. Ab dem nächsten Start verbindet sich das WLAN automatisch.",
"z.B. Zuhause, Arbeit, Ferienhaus",
}; };
static_assert(sizeof(I18N_DE) / sizeof(I18N_DE[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_DE) / sizeof(I18N_DE[0]) == (size_t)StringId::COUNT,

View File

@@ -228,6 +228,8 @@ static const char* const I18N_EN[] = {
"Setup Complete", "Setup Complete",
"The setup process is now complete. All data has been saved to the SD card in the folder ", "The setup process is now complete. All data has been saved to the SD card in the folder ",
". From the next boot, WiFi will connect automatically.", ". From the next boot, WiFi will connect automatically.",
"e.g. Home, Work, Holiday home",
}; };
static_assert(sizeof(I18N_EN) / sizeof(I18N_EN[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_EN) / sizeof(I18N_EN[0]) == (size_t)StringId::COUNT,

View File

@@ -227,6 +227,8 @@ static const char* const I18N_ES[] = {
"Configuración completada", "Configuración completada",
"El proceso de configuración ya ha finalizado. Todos los datos se han guardado en la tarjeta SD, en la carpeta ", "El proceso de configuración ya ha finalizado. Todos los datos se han guardado en la tarjeta SD, en la carpeta ",
". A partir del próximo inicio, el WiFi se conectará automáticamente.", ". A partir del próximo inicio, el WiFi se conectará automáticamente.",
"ej. Casa, Trabajo, Casa de vacaciones",
}; };
static_assert(sizeof(I18N_ES) / sizeof(I18N_ES[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_ES) / sizeof(I18N_ES[0]) == (size_t)StringId::COUNT,

View File

@@ -227,6 +227,8 @@ static const char* const I18N_FR[] = {
"Configuration terminée", "Configuration terminée",
"Le processus de configuration est maintenant terminé. Toutes les données ont été enregistrées sur la carte SD dans le dossier ", "Le processus de configuration est maintenant terminé. Toutes les données ont été enregistrées sur la carte SD dans le dossier ",
". À partir du prochain démarrage, le WiFi se connectera automatiquement.", ". À partir du prochain démarrage, le WiFi se connectera automatiquement.",
"ex. Maison, Travail, Maison de vacances",
}; };
static_assert(sizeof(I18N_FR) / sizeof(I18N_FR[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_FR) / sizeof(I18N_FR[0]) == (size_t)StringId::COUNT,

View File

@@ -227,6 +227,8 @@ static const char* const I18N_IT[] = {
"Configurazione completata", "Configurazione completata",
"Il processo di configurazione è ora completato. Tutti i dati sono stati salvati sulla scheda SD nella cartella ", "Il processo di configurazione è ora completato. Tutti i dati sono stati salvati sulla scheda SD nella cartella ",
". Dal prossimo avvio, il WiFi si connetterà automaticamente.", ". Dal prossimo avvio, il WiFi si connetterà automaticamente.",
"es. Casa, Lavoro, Casa vacanze",
}; };
static_assert(sizeof(I18N_IT) / sizeof(I18N_IT[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_IT) / sizeof(I18N_IT[0]) == (size_t)StringId::COUNT,

View File

@@ -227,6 +227,8 @@ static const char* const I18N_TR[] = {
"Kurulum Tamamlandı", "Kurulum Tamamlandı",
"Kurulum işlemi artık tamamlandı. Tüm veriler SD karttaki şu klasöre kaydedildi: ", "Kurulum işlemi artık tamamlandı. Tüm veriler SD karttaki şu klasöre kaydedildi: ",
". Bir sonraki başlatmadan itibaren WiFi otomatik olarak bağlanacak.", ". Bir sonraki başlatmadan itibaren WiFi otomatik olarak bağlanacak.",
"örn. Ev, İş, Tatil evi",
}; };
static_assert(sizeof(I18N_TR) / sizeof(I18N_TR[0]) == (size_t)StringId::COUNT, static_assert(sizeof(I18N_TR) / sizeof(I18N_TR[0]) == (size_t)StringId::COUNT,

View File

@@ -132,7 +132,19 @@ namespace {
constexpr int16_t KEY_H = 30; constexpr int16_t KEY_H = 30;
constexpr int16_t KEY_GAP = 3; constexpr int16_t KEY_GAP = 3;
constexpr int16_t ROW0_Y = 78; constexpr int16_t FIELD_H = 34;
// Kopfbereich (Titel + Namens-Hinweis) einmal zeichnen, um seine
// tatsaechliche Hoehe per getCursorY() zu messen - selbes Muster wie
// in address_search_screen.cpp::runAddressKeyboard().
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14);
tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT));
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.println(I18n::t(StringId::LOCATION_NAME_HINT));
int16_t fieldY = (int16_t)(tft.getCursorY() + 4);
int16_t ROW0_Y = (int16_t)(fieldY + FIELD_H + 8);
auto layoutRow = [&](const char* row, int16_t y, Rect* outRects, uint8_t n) { auto layoutRow = [&](const char* row, int16_t y, Rect* outRects, uint8_t n) {
int16_t usableW = Config::SCREEN_WIDTH - 8; int16_t usableW = Config::SCREEN_WIDTH - 8;
@@ -146,9 +158,9 @@ namespace {
Rect digitRects[10], row1Rects[10], row2Rects[9], row3Rects[7]; Rect digitRects[10], row1Rects[10], row2Rects[9], row3Rects[7];
layoutRow(DIGITS, ROW0_Y, digitRects, 10); layoutRow(DIGITS, ROW0_Y, digitRects, 10);
layoutRow(ROW1, ROW0_Y + (KEY_H + KEY_GAP), row1Rects, 10); layoutRow(ROW1, (int16_t)(ROW0_Y + (KEY_H + KEY_GAP)), row1Rects, 10);
layoutRow(ROW2, ROW0_Y + 2 * (KEY_H + KEY_GAP), row2Rects, 9); layoutRow(ROW2, (int16_t)(ROW0_Y + 2 * (KEY_H + KEY_GAP)), row2Rects, 9);
layoutRow(ROW3, ROW0_Y + 3 * (KEY_H + KEY_GAP), row3Rects, 7); layoutRow(ROW3, (int16_t)(ROW0_Y + 3 * (KEY_H + KEY_GAP)), row3Rects, 7);
Rect spaceBtn = {4, (int16_t)(ROW0_Y + 4 * (KEY_H + KEY_GAP)), 150, KEY_H}; Rect spaceBtn = {4, (int16_t)(ROW0_Y + 4 * (KEY_H + KEY_GAP)), 150, KEY_H};
Rect backspaceBtn = {158, (int16_t)(ROW0_Y + 4 * (KEY_H + KEY_GAP)), (int16_t)(Config::SCREEN_WIDTH - 8 - 154), KEY_H}; Rect backspaceBtn = {158, (int16_t)(ROW0_Y + 4 * (KEY_H + KEY_GAP)), (int16_t)(Config::SCREEN_WIDTH - 8 - 154), KEY_H};
@@ -163,12 +175,14 @@ namespace {
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14); tft.setCursor(10, 14);
tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT)); tft.println(I18n::t(StringId::LOCATION_NAME_PROMPT));
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.println(I18n::t(StringId::LOCATION_NAME_HINT));
tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_BLACK); tft.fillRect(8, fieldY, Config::SCREEN_WIDTH - 16, FIELD_H, TFT_BLACK);
tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_GREEN); tft.drawRect(8, fieldY, Config::SCREEN_WIDTH - 16, FIELD_H, TFT_GREEN);
tft.setTextSize(2); tft.setTextSize(2);
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(14, 66); tft.setCursor(14, (int16_t)(fieldY + 26));
tft.print(buf); tft.print(buf);
tft.setTextSize(1); tft.setTextSize(1);