v2.2: Custom Unicode font for accented characters (DE/TR/FR/ES/IT), 4-category menu reorganization, black/green UI theme, header layout improvements

This commit is contained in:
Eiswolf-BG
2026-08-05 01:07:08 +02:00
parent 7fedf860e5
commit 2b07fd036f
20 changed files with 1115 additions and 478 deletions

View File

@@ -14,11 +14,12 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) {
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); uint16_t accent = danger ? TFT_RED : TFT_GREEN;
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(accent, TFT_BLACK);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -59,14 +60,14 @@ namespace {
auto redraw = [&]() { auto redraw = [&]() {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::AIRLINE_ADD_TITLE)); tft.println(I18n::t(StringId::AIRLINE_ADD_TITLE));
tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_NAVY); tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_BLACK);
tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_DARKGREY); tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 30, TFT_GREEN);
tft.setTextSize(2); tft.setTextSize(2);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(14, 47); tft.setCursor(14, 47);
tft.print(buf); tft.print(buf);
tft.setTextSize(1); tft.setTextSize(1);
@@ -76,7 +77,7 @@ namespace {
for (uint8_t i = 0; i < 7; i++) drawButton(tft, row3Rects[i], String(ROW3[i])); for (uint8_t i = 0; i < 7; i++) drawButton(tft, row3Rects[i], String(ROW3[i]));
drawButton(tft, backspaceBtn, "<-"); drawButton(tft, backspaceBtn, "<-");
drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), TFT_MAROON); drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), true);
drawButton(tft, confirmBtn, I18n::t(StringId::ADD)); drawButton(tft, confirmBtn, I18n::t(StringId::ADD));
}; };
@@ -126,10 +127,10 @@ void run(TFT_eSPI& tft) {
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 8); tft.setCursor(10, 8);
tft.println(I18n::t(StringId::AIRLINE_FILTER_TITLE)); tft.println(I18n::t(StringId::AIRLINE_FILTER_TITLE));
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 26); tft.setCursor(10, 26);
tft.println(I18n::t(StringId::AIRLINE_FILTER_DESC1)); tft.println(I18n::t(StringId::AIRLINE_FILTER_DESC1));
tft.setCursor(10, 38); tft.setCursor(10, 38);
@@ -147,13 +148,13 @@ void run(TFT_eSPI& tft) {
rowRects[i] = rowRect; rowRects[i] = rowRect;
removeRects[i] = removeRect; removeRects[i] = removeRect;
tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_NAVY); tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK);
tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString(AirlineFilter::icaoAt(i), rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.drawString(AirlineFilter::icaoAt(i), rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
drawButton(tft, removeRect, "X", TFT_MAROON); drawButton(tft, removeRect, "X", true);
y += ROW_H + ROW_GAP; y += ROW_H + ROW_GAP;
} }

View File

@@ -64,7 +64,7 @@ void run(TFT_eSPI& tft) {
for (uint8_t i = 0; i < 4; i++) { for (uint8_t i = 0; i < 4; i++) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setTextSize(1); tft.setTextSize(1);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.drawString(I18n::t(StringId::CALIB_TITLE), W / 2, H / 2 - 12); tft.drawString(I18n::t(StringId::CALIB_TITLE), W / 2, H / 2 - 12);
@@ -88,7 +88,7 @@ void run(TFT_eSPI& tft) {
TouchInput::saveCalibration(); TouchInput::saveCalibration();
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::CALIB_SAVED)); tft.println(I18n::t(StringId::CALIB_SAVED));
delay(800); delay(800);

View File

@@ -1,12 +1,6 @@
#pragma once #pragma once
#include <Arduino.h> #include <Arduino.h>
// Zentrales Uebersetzungssystem: jeder feste UI-Text der App hat eine
// StringId. Die eigentlichen Uebersetzungen liegen in je einer Datei pro
// Sprache (i18n_en.h, i18n_de.h, i18n_fr.h, i18n_tr.h, i18n_es.h, i18n_it.h) -
// jede Datei enthaelt ein Array in GENAU derselben Reihenfolge wie dieses
// Enum. Neue Menuepunkte/Texte: hier eine neue StringId VOR COUNT einfuegen,
// dann in ALLEN 6 Sprachdateien an derselben Position ergaenzen.
enum class StringId : uint8_t { enum class StringId : uint8_t {
OK = 0, OK = 0,
CANCEL, CANCEL,
@@ -139,17 +133,20 @@ enum class StringId : uint8_t {
LANGUAGE_TITLE, LANGUAGE_TITLE,
COUNT // IMMER als letztes Element - markiert die Gesamtanzahl MENU_CATEGORY_REGION,
MENU_CATEGORY_WIFI,
MENU_CATEGORY_SYSTEM,
MENU_CATEGORY_FLIGHT,
WIFI_NETWORKS_TITLE,
WIFI_ADD_NETWORK,
WIFI_EMPTY_SLOT,
COUNT
}; };
namespace I18n { namespace I18n {
constexpr uint8_t LANG_COUNT = 6; constexpr uint8_t LANG_COUNT = 6;
// Liefert den uebersetzten Text fuer 'id' in der aktuell in den
// Einstellungen gewaehlten Sprache (SettingsStore::language()).
const char* t(StringId id); const char* t(StringId id);
// Eigennamen der Sprachen (fuer das Sprachauswahl-Menue), IMMER im
// jeweiligen Idiom selbst geschrieben (z.B. "Deutsch", nicht "German").
const char* languageName(uint8_t index); const char* languageName(uint8_t index);
} }

View File

@@ -4,10 +4,10 @@
static const char* const I18N_DE[] = { static const char* const I18N_DE[] = {
"OK", "OK",
"Abbrechen", "Abbrechen",
"Zurueck", "Zurück",
"< Zurueck", "< Zurück",
"Laedt...", "Lädt...",
"Hinzufuegen", "Hinzufügen",
"AN", "AN",
"AUS", "AUS",
@@ -20,13 +20,13 @@ static const char* const I18N_DE[] = {
"Ermittle Standort...", "Ermittle Standort...",
"Bereit!", "Bereit!",
"Fuer diese App wird", "Für diese App wird",
"eine SD-Karte", "eine SD-Karte",
"benoetigt", "benötigt",
"SD-Karte einlegen und Geraet neu starten", "SD-Karte einlegen und Gerät neu starten",
"Touch-Kalibrierung", "Touch-Kalibrierung",
"Bitte den Kreis beruehren:", "Bitte den Kreis berühren:",
"Oben links", "Oben links",
"Oben rechts", "Oben rechts",
"Unten rechts", "Unten rechts",
@@ -35,14 +35,14 @@ static const char* const I18N_DE[] = {
"Suche WLAN...", "Suche WLAN...",
"Keine Netzwerke gefunden", "Keine Netzwerke gefunden",
"WLAN waehlen:", "WLAN wählen:",
"Verbunden!", "Verbunden!",
"Verbindung fehlgeschlagen.", "Verbindung fehlgeschlagen.",
"Zurueck zur Netzwerkliste...", "Zurück zur Netzwerkliste...",
"Passwort:", "Passwort:",
"Leerzeichen", "Leerzeichen",
"Verbinden", "Verbinden",
"Zurueck zur Liste", "Zurück zur Liste",
"Bereits 3 Netzwerke gespeichert.", "Bereits 3 Netzwerke gespeichert.",
"Erst eins entfernen.", "Erst eins entfernen.",
"Verbinde...", "Verbinde...",
@@ -60,7 +60,7 @@ static const char* const I18N_DE[] = {
"Logbuch-Dateien", "Logbuch-Dateien",
"Weitere Einstellungen >", "Weitere Einstellungen >",
"Notfall-Alarm: ", "Notfall-Alarm: ",
"Naeherungs-LED: ", "Näherungs-LED: ",
"LED-Herzschlag: ", "LED-Herzschlag: ",
"Flugbuch: ", "Flugbuch: ",
"Bodenfahrzeuge ausblenden: ", "Bodenfahrzeuge ausblenden: ",
@@ -69,7 +69,7 @@ static const char* const I18N_DE[] = {
"Einstellungen wiederherstellen", "Einstellungen wiederherstellen",
"Backup gespeichert.", "Backup gespeichert.",
"Backup fehlgeschlagen.", "Backup fehlgeschlagen.",
"Wiederhergestellt. Geraet neu starten!", "Wiederhergestellt. Gerät neu starten!",
"Wiederherstellung fehlgeschlagen.", "Wiederherstellung fehlgeschlagen.",
"Sprache", "Sprache",
"Einheiten", "Einheiten",
@@ -77,46 +77,46 @@ static const char* const I18N_DE[] = {
"Ausgeblendete Airlines", "Ausgeblendete Airlines",
"Flugzeuge dieser Airlines werden", "Flugzeuge dieser Airlines werden",
"im Radar ausgeblendet.", "im Radar ausgeblendet.",
"Airline hinzufuegen", "Airline hinzufügen",
"Airline hinzufuegen (ICAO-Code)", "Airline hinzufügen (ICAO-Code)",
"Standort-Presets", "Standort-Presets",
"Automatisch (GPS/IP)", "Automatisch (GPS/IP)",
"Preset", "Preset",
"(leer)", "(leer)",
"Preset hinzufuegen", "Preset hinzufügen",
"Breitengrad (z.B. 48.6833)", "Breitengrad (z.B. 48.6833)",
"Laengengrad (z.B. 9.8333)", "Längengrad (z.B. 9.8333)",
"Statistik", "Statistik",
"Heute geloggte Flugzeuge", "Heute geloggte Flugzeuge",
"Insgesamt geloggte Flugzeuge", "Insgesamt geloggte Flugzeuge",
"Tage mit Sichtungen", "Tage mit Sichtungen",
"Durchschnitt pro Tag", "Durchschnitt pro Tag",
"Flugbuch zuruecksetzen", "Flugbuch zurücksetzen",
"Nochmal tippen zum Bestaetigen!", "Nochmal tippen zum Bestätigen!",
"Flugbuch-Daten zurueckgesetzt.", "Flugbuch-Daten zurückgesetzt.",
"Laufzeit: ", "Laufzeit: ",
"Logbuch-Dateien", "Logbuch-Dateien",
"Noch keine Flugbuch-Eintraege.", "Noch keine Flugbuch-Einträge.",
"Zeige die letzten ", "Zeige die letzten ",
" von ", " von ",
" Tagen", " Tagen",
" Flugzeuge", " Flugzeuge",
"Fuer Details antippen", "Für Details antippen",
"Modell: ", "Modell: ",
"Typ: ", "Typ: ",
"laedt...", "lädt...",
"unbekannt", "unbekannt",
"Hoehe: ", "Höhe: ",
"Geschw.: ", "Geschw.: ",
"Distanz: ", "Distanz: ",
"Kurs: ", "Kurs: ",
"Sitzplaetze (geschaetzt): ", "Sitzplätze (geschätzt): ",
"Sitzplaetze: unbekannt", "Sitzplätze: unbekannt",
"Woanders antippen zum Schliessen", "Woanders antippen zum Schliessen",
"Steigen: ", "Steigen: ",
"Sinken: ", "Sinken: ",
@@ -132,6 +132,15 @@ static const char* const I18N_DE[] = {
"Imperial (ft, kt, nm)", "Imperial (ft, kt, nm)",
"Sprache", "Sprache",
"Land/Region",
"WLAN/Netzwerk",
"System",
"Flugoptionen",
"WLAN-Netzwerke",
"Netzwerk hinzufügen",
"(leer)",
}; };
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

@@ -132,6 +132,15 @@ static const char* const I18N_EN[] = {
"Imperial (ft, kt, nm)", "Imperial (ft, kt, nm)",
"Language", "Language",
"Region & Language",
"WiFi / Network",
"System",
"Flight Options",
"WiFi networks",
"Add network",
"(empty)",
}; };
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

@@ -4,10 +4,10 @@
static const char* const I18N_ES[] = { static const char* const I18N_ES[] = {
"OK", "OK",
"Cancelar", "Cancelar",
"Atras", "Atrás",
"< Atras", "< Atrás",
"Cargando...", "Cargando...",
"Anadir", "Añadir",
"ON", "ON",
"OFF", "OFF",
@@ -16,30 +16,30 @@ static const char* const I18N_ES[] = {
"Tarjeta SD: OK", "Tarjeta SD: OK",
"Conectando WiFi...", "Conectando WiFi...",
"WiFi OK: ", "WiFi OK: ",
"WiFi FALLO", "WiFi FALLÓ",
"Obteniendo ubicacion...", "Obteniendo ubicación...",
"Listo!", "¡Listo!",
"Esta app necesita", "Esta app necesita",
"una tarjeta SD", "una tarjeta SD",
"requerida", "requerida",
"Inserte una tarjeta y reinicie el dispositivo", "Inserte una tarjeta y reinicie el dispositivo",
"Calibracion tactil", "Calibración táctil",
"Toque el circulo:", "Toque el círculo:",
"Arriba izquierda", "Arriba izquierda",
"Arriba derecha", "Arriba derecha",
"Abajo derecha", "Abajo derecha",
"Abajo izquierda", "Abajo izquierda",
"Calibracion guardada!", "¡Calibración guardada!",
"Buscando WiFi...", "Buscando WiFi...",
"No se encontraron redes", "No se encontraron redes",
"Elegir WiFi:", "Elegir WiFi:",
"Conectado!", "¡Conectado!",
"Conexion fallida.", "Conexión fallida.",
"Volviendo a la lista...", "Volviendo a la lista...",
"Contrasena:", "Contraseña:",
"Espacio", "Espacio",
"Conectar", "Conectar",
"Volver a la lista", "Volver a la lista",
@@ -50,59 +50,59 @@ static const char* const I18N_ES[] = {
"Ajustes", "Ajustes",
"Ajustes (2/2)", "Ajustes (2/2)",
"Calibrar tactil", "Calibrar táctil",
"Pantalla: invertida (toque)", "Pantalla: invertida (toque)",
"Pantalla: normal (toque)", "Pantalla: normal (toque)",
"Gestionar redes WiFi", "Gestionar redes WiFi",
"Ubicaciones guardadas", "Ubicaciones guardadas",
"Apagado de pantalla: ", "Apagado de pantalla: ",
"Estadisticas", "Estadísticas",
"Archivos del registro", "Archivos del registro",
"Mas ajustes >", "Más ajustes >",
"Alerta de emergencia: ", "Alerta de emergencia: ",
"LED de proximidad: ", "LED de proximidad: ",
"Latido LED: ", "Latido LED: ",
"Registro de vuelos: ", "Registro de vuelos: ",
"Ocultar vehiculos terrestres: ", "Ocultar vehículos terrestres: ",
"Filtro de aerolineas", "Filtro de aerolíneas",
"Copia de seguridad", "Copia de seguridad",
"Restaurar ajustes", "Restaurar ajustes",
"Copia guardada.", "Copia guardada.",
"Fallo la copia de seguridad.", "Falló la copia de seguridad.",
"Restaurado. Reinicie el dispositivo!", "¡Restaurado. Reinicie el dispositivo!",
"Fallo la restauracion.", "Falló la restauración.",
"Idioma", "Idioma",
"Unidades", "Unidades",
"Aerolineas ocultas", "Aerolíneas ocultas",
"Los aviones de estas aerolineas", "Los aviones de estas aerolíneas",
"se ocultan del radar.", "se ocultan del radar.",
"Anadir aerolinea", "Añadir aerolínea",
"Anadir aerolinea (codigo OACI)", "Añadir aerolínea (código OACI)",
"Ubicaciones guardadas", "Ubicaciones guardadas",
"Auto (GPS/IP)", "Auto (GPS/IP)",
"Ubicacion", "Ubicación",
"(vacio)", "(vacío)",
"Anadir ubicacion", "Añadir ubicación",
"Latitud (ej. 48.6833)", "Latitud (ej. 48.6833)",
"Longitud (ej. 9.8333)", "Longitud (ej. 9.8333)",
"Estadisticas", "Estadísticas",
"Aviones registrados hoy", "Aviones registrados hoy",
"Aviones registrados en total", "Aviones registrados en total",
"Dias con avistamientos", "Días con avistamientos",
"Promedio por dia", "Promedio por día",
"Reiniciar registro", "Reiniciar registro",
"Toque de nuevo para confirmar!", "¡Toque de nuevo para confirmar!",
"Registro reiniciado.", "Registro reiniciado.",
"Tiempo activo: ", "Tiempo activo: ",
"Archivos del registro", "Archivos del registro",
"Aun no hay registros.", "Aún no hay registros.",
"Mostrando los ultimos ", "Mostrando los últimos ",
" de ", " de ",
" dias", " días",
" aviones", " aviones",
"Toque para ver detalles", "Toque para ver detalles",
@@ -124,14 +124,23 @@ static const char* const I18N_ES[] = {
"Squawk: ", "Squawk: ",
"Guardado: ", "Guardado: ",
"Fallo la captura de pantalla", "Falló la captura de pantalla",
"Unidades", "Unidades",
"Auto (segun ubicacion)", "Auto (según ubicación)",
"Metrico (km, m, km/h)", "Métrico (km, m, km/h)",
"Imperial (ft, kt, nm)", "Imperial (ft, kt, nm)",
"Idioma", "Idioma",
"Región e idioma",
"WiFi / Red",
"Sistema",
"Opciones de vuelo",
"Redes WiFi",
"Añadir red",
"(vacío)",
}; };
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

@@ -16,71 +16,71 @@ static const char* const I18N_FR[] = {
"Carte SD: OK", "Carte SD: OK",
"Connexion WiFi...", "Connexion WiFi...",
"WiFi OK: ", "WiFi OK: ",
"ECHEC WiFi", "ÉCHEC WiFi",
"Localisation...", "Localisation...",
"Pret !", "Prêt !",
"Pour cette appli une", "Pour cette appli une",
"carte SD est", "carte SD est",
"requise", "requise",
"Inserez une carte et redemarrez l'appareil", "Insérez une carte et redémarrez l'appareil",
"Etalonnage tactile", "Étalonnage tactile",
"Touchez le cercle :", "Touchez le cercle :",
"Haut gauche", "Haut gauche",
"Haut droite", "Haut droite",
"Bas droite", "Bas droite",
"Bas gauche", "Bas gauche",
"Etalonnage enregistre !", "Étalonnage enregistré !",
"Recherche WiFi...", "Recherche WiFi...",
"Aucun reseau trouve", "Aucun réseau trouvé",
"Choisir WiFi :", "Choisir WiFi :",
"Connecte !", "Connecté !",
"Echec de connexion.", "Échec de connexion.",
"Retour a la liste...", "Retour à la liste...",
"Mot de passe :", "Mot de passe :",
"Espace", "Espace",
"Connecter", "Connecter",
"Retour a la liste", "Retour à la liste",
"Deja 3 reseaux enregistres.", "Déjà 3 réseaux enregistrés.",
"Supprimez-en un d'abord.", "Supprimez-en un d'abord.",
"Connexion...", "Connexion...",
"WiFi : ", "WiFi : ",
"Reglages", "Réglages",
"Reglages (2/2)", "Réglages (2/2)",
"Etalonner le tactile", "Étalonner le tactile",
"Ecran : inverse (touchez)", "Écran : inversé (touchez)",
"Ecran : normal (touchez)", "Écran : normal (touchez)",
"Gerer les reseaux WiFi", "Gérer les réseaux WiFi",
"Positions enregistrees", "Positions enregistrées",
"Extinction ecran : ", "Extinction écran : ",
"Statistiques", "Statistiques",
"Fichiers du journal", "Fichiers du journal",
"Plus de reglages >", "Plus de réglages >",
"Alerte urgence : ", "Alerte urgence : ",
"LED de proximite : ", "LED de proximité : ",
"Battement LED : ", "Battement LED : ",
"Journal de vol : ", "Journal de vol : ",
"Masquer vehicules au sol : ", "Masquer véhicules au sol : ",
"Filtre compagnies", "Filtre compagnies",
"Sauvegarder reglages", "Sauvegarder réglages",
"Restaurer reglages", "Restaurer réglages",
"Sauvegarde enregistree.", "Sauvegarde enregistrée.",
"Echec de la sauvegarde.", "Échec de la sauvegarde.",
"Restaure. Redemarrez l'appareil !", "Restauré. Redémarrez l'appareil !",
"Echec de la restauration.", "Échec de la restauration.",
"Langue", "Langue",
"Unites", "Unités",
"Compagnies masquees", "Compagnies masquées",
"Les avions de ces compagnies sont", "Les avions de ces compagnies sont",
"masques du radar.", "masqués du radar.",
"Ajouter une compagnie", "Ajouter une compagnie",
"Ajouter (code OACI)", "Ajouter (code OACI)",
"Positions enregistrees", "Positions enregistrées",
"Auto (GPS/IP)", "Auto (GPS/IP)",
"Position", "Position",
"(vide)", "(vide)",
@@ -89,25 +89,25 @@ static const char* const I18N_FR[] = {
"Longitude (ex. 9.8333)", "Longitude (ex. 9.8333)",
"Statistiques", "Statistiques",
"Avions enregistres aujourd'hui", "Avions enregistrés aujourd'hui",
"Avions enregistres au total", "Avions enregistrés au total",
"Jours avec observations", "Jours avec observations",
"Moyenne par jour", "Moyenne par jour",
"Reinitialiser le journal", "Réinitialiser le journal",
"Retouchez pour confirmer !", "Retouchez pour confirmer !",
"Journal reinitialise.", "Journal réinitialisé.",
"Duree de fonctionnement : ", "Durée de fonctionnement : ",
"Fichiers du journal", "Fichiers du journal",
"Aucune entree pour le moment.", "Aucune entrée pour le moment.",
"Affichage des ", "Affichage des ",
" derniers sur ", " derniers sur ",
" jours", " jours",
" avions", " avions",
"Touchez pour les details", "Touchez pour les détails",
"Modele : ", "Modèle : ",
"Type : ", "Type : ",
"chargement...", "chargement...",
"inconnu", "inconnu",
@@ -115,23 +115,32 @@ static const char* const I18N_FR[] = {
"Vitesse : ", "Vitesse : ",
"Dist. : ", "Dist. : ",
"Cap : ", "Cap : ",
"Sieges (estime) : ", "Sièges (estimé) : ",
"Sieges : inconnu", "Sièges : inconnu",
"Touchez ailleurs pour fermer", "Touchez ailleurs pour fermer",
"Montee : ", "Montée : ",
"Descente : ", "Descente : ",
"Vol en palier", "Vol en palier",
"Squawk : ", "Squawk : ",
"Enregistre : ", "Enregistré : ",
"Echec de la capture d'ecran", "Échec de la capture d'écran",
"Unites", "Unités",
"Auto (selon la position)", "Auto (selon la position)",
"Metrique (km, m, km/h)", "Métrique (km, m, km/h)",
"Imperial (ft, kt, nm)", "Impérial (ft, kt, nm)",
"Langue", "Langue",
"Région et langue",
"WiFi / Réseau",
"Système",
"Options de vol",
"Réseaux WiFi",
"Ajouter un réseau",
"(vide)",
}; };
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

@@ -43,7 +43,7 @@ static const char* const I18N_IT[] = {
"Spazio", "Spazio",
"Connetti", "Connetti",
"Torna alla lista", "Torna alla lista",
"Gia 3 reti salvate.", "Già 3 reti salvate.",
"Rimuovine una prima.", "Rimuovine una prima.",
"Connessione...", "Connessione...",
"WiFi: ", "WiFi: ",
@@ -60,7 +60,7 @@ static const char* const I18N_IT[] = {
"File del registro", "File del registro",
"Altre impostazioni >", "Altre impostazioni >",
"Allarme emergenza: ", "Allarme emergenza: ",
"LED di prossimita: ", "LED di prossimità: ",
"Battito LED: ", "Battito LED: ",
"Diario di volo: ", "Diario di volo: ",
"Nascondi veicoli a terra: ", "Nascondi veicoli a terra: ",
@@ -72,7 +72,7 @@ static const char* const I18N_IT[] = {
"Ripristinato. Riavviare il dispositivo!", "Ripristinato. Riavviare il dispositivo!",
"Ripristino fallito.", "Ripristino fallito.",
"Lingua", "Lingua",
"Unita", "Unità",
"Compagnie nascoste", "Compagnie nascoste",
"Gli aerei di queste compagnie", "Gli aerei di queste compagnie",
@@ -96,7 +96,7 @@ static const char* const I18N_IT[] = {
"Reimposta registro", "Reimposta registro",
"Tocca di nuovo per confermare!", "Tocca di nuovo per confermare!",
"Registro reimpostato.", "Registro reimpostato.",
"Tempo di attivita: ", "Tempo di attività: ",
"File del registro", "File del registro",
"Ancora nessun dato.", "Ancora nessun dato.",
@@ -126,12 +126,21 @@ static const char* const I18N_IT[] = {
"Salvato: ", "Salvato: ",
"Screenshot fallito", "Screenshot fallito",
"Unita", "Unità",
"Auto (in base alla posizione)", "Auto (in base alla posizione)",
"Metrico (km, m, km/h)", "Metrico (km, m, km/h)",
"Imperiale (ft, kt, nm)", "Imperiale (ft, kt, nm)",
"Lingua", "Lingua",
"Regione e lingua",
"WiFi / Rete",
"Sistema",
"Opzioni di volo",
"Reti WiFi",
"Aggiungi rete",
"(vuoto)",
}; };
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

@@ -3,135 +3,144 @@
static const char* const I18N_TR[] = { static const char* const I18N_TR[] = {
"Tamam", "Tamam",
"Iptal", "İptal",
"Geri", "Geri",
"< Geri", "< Geri",
"Yukleniyor...", "Yükleniyor...",
"Ekle", "Ekle",
"ACIK", "AÇIK",
"KAPALI", "KAPALI",
"Asla", "Asla",
"SD Kart: OK", "SD Kart: OK",
"WiFi'ye baglaniyor...", "WiFi'ye bağlanıyor...",
"WiFi OK: ", "WiFi OK: ",
"WiFi BASARISIZ", "WiFi BAŞARISIZ",
"Konum aliniyor...", "Konum alınıyor...",
"Hazir!", "Hazır!",
"Bu uygulama icin bir", "Bu uygulama için bir",
"SD kart", "SD kart",
"gereklidir", "gereklidir",
"Bir kart takin ve cihazi yeniden baslatin", "Bir kart takın ve cihazı yeniden başlatın",
"Dokunmatik Kalibrasyon", "Dokunmatik Kalibrasyon",
"Lutfen daireye dokunun:", "Lütfen daireye dokunun:",
"Sol ust", "Sol üst",
"Sag ust", "Sağ üst",
"Sag alt", "Sağ alt",
"Sol alt", "Sol alt",
"Kalibrasyon kaydedildi!", "Kalibrasyon kaydedildi!",
"WiFi taraniyor...", "WiFi taranıyor...",
"Ag bulunamadi", "Ağ bulunamadı",
"WiFi secin:", "WiFi seçin:",
"Baglandi!", "Bağlandı!",
"Baglanti basarisiz.", "Bağlantı başarısız.",
"Ag listesine donuluyor...", "Ağ listesine dönülüyor...",
"Sifre:", "Şifre:",
"Bosluk", "Boşluk",
"Baglan", "Bağlan",
"Listeye don", "Listeye dön",
"Zaten 3 ag kayitli.", "Zaten 3 ağ kayıtlı.",
"Once birini kaldirin.", "Önce birini kaldırın.",
"Baglaniyor...", "Bağlanıyor...",
"WiFi: ", "WiFi: ",
"Ayarlar", "Ayarlar",
"Ayarlar (2/2)", "Ayarlar (2/2)",
"Dokunmatigi kalibre et", "Dokunmatiği kalibre et",
"Ekran: ters (dokun)", "Ekran: ters (dokun)",
"Ekran: normal (dokun)", "Ekran: normal (dokun)",
"WiFi aglarini yonet", "WiFi ağlarını yönet",
"Konum on ayarlari", "Konum ön ayarları",
"Ekran zaman asimi: ", "Ekran zaman aşımı: ",
"Istatistikler", "İstatistikler",
"Kayit defteri dosyalari", "Kayıt defteri dosyaları",
"Diger ayarlar >", "Diğer ayarlar >",
"Acil durum alarmi: ", "Acil durum alarmı: ",
"Yakinlik LED'i: ", "Yakınlık LED'i: ",
"LED kalp atisi: ", "LED kalp atışı: ",
"Ucus kayit defteri: ", "Uçuş kayıt defteri: ",
"Kara araclarini gizle: ", "Kara araçlarını gizle: ",
"Havayolu filtresi", "Havayolu filtresi",
"Ayarlari yedekle", "Ayarları yedekle",
"Ayarlari geri yukle", "Ayarları geri yükle",
"Yedekleme kaydedildi.", "Yedekleme kaydedildi.",
"Yedekleme basarisiz.", "Yedekleme başarısız.",
"Geri yuklendi. Cihazi yeniden baslatin!", "Geri yüklendi. Cihazı yeniden başlatın!",
"Geri yukleme basarisiz.", "Geri yükleme başarısız.",
"Dil", "Dil",
"Birimler", "Birimler",
"Gizli havayollari", "Gizli havayolları",
"Bu havayollarina ait ucaklar", "Bu havayollarına ait uçaklar",
"radarda gizlenir.", "radarda gizlenir.",
"Havayolu ekle", "Havayolu ekle",
"Havayolu ekle (ICAO kodu)", "Havayolu ekle (ICAO kodu)",
"Konum on ayarlari", "Konum ön ayarları",
"Otomatik (GPS/IP)", "Otomatik (GPS/IP)",
"On ayar", "Ön ayar",
"(bos)", "(boş)",
"On ayar ekle", "Ön ayar ekle",
"Enlem (orn. 48.6833)", "Enlem (örn. 48.6833)",
"Boylam (orn. 9.8333)", "Boylam (örn. 9.8333)",
"Istatistikler", "İstatistikler",
"Bugun kaydedilen ucaklar", "Bugün kaydedilen uçaklar",
"Toplam kaydedilen ucaklar", "Toplam kaydedilen uçaklar",
"Gozlem yapilan gunler", "Gözlem yapılan günler",
"Gunluk ortalama", "Günlük ortalama",
"Kayit defterini sifirla", "Kayıt defterini sıfırla",
"Onaylamak icin tekrar dokunun!", "Onaylamak için tekrar dokunun!",
"Kayit defteri sifirlandi.", "Kayıt defteri sıfırlandı.",
"Calisma suresi: ", "Çalışma süresi: ",
"Kayit defteri dosyalari", "Kayıt defteri dosyaları",
"Henuz kayit yok.", "Henüz kayıt yok.",
"Son ", "Son ",
" / ", " / ",
" gun gosteriliyor", " gün gösteriliyor",
" ucak", " uçak",
"Detaylar icin dokunun", "Detaylar için dokunun",
"Model: ", "Model: ",
"Tip: ", "Tip: ",
"yukleniyor...", "yükleniyor...",
"bilinmiyor", "bilinmiyor",
"Yuk.: ", "Yük.: ",
"Hiz: ", "Hız: ",
"Mesafe: ", "Mesafe: ",
"Yon: ", "Yön: ",
"Koltuk (tahmini): ", "Koltuk (tahmini): ",
"Koltuk: bilinmiyor", "Koltuk: bilinmiyor",
"Kapatmak icin baska yere dokunun", "Kapatmak için başka yere dokunun",
"Tirmanis: ", "Tırmanış: ",
"Alcalma: ", "Alçalma: ",
"Duz ucus", "Düz uçuş",
"Squawk: ", "Squawk: ",
"Kaydedildi: ", "Kaydedildi: ",
"Ekran goruntusu basarisiz", "Ekran görüntüsü başarısız",
"Birimler", "Birimler",
"Otomatik (konuma gore)", "Otomatik (konuma göre)",
"Metrik (km, m, km/sa)", "Metrik (km, m, km/sa)",
"Imperyal (ft, kt, nm)", "İmperyal (ft, kt, nm)",
"Dil", "Dil",
"Bölge ve Dil",
"WiFi / Ağ",
"Sistem",
"Uçuş Seçenekleri",
"WiFi ağları",
"Ağ ekle",
"(boş)",
}; };
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

@@ -14,11 +14,13 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool active = false) {
uint16_t bg = active ? TFT_GREEN : TFT_BLACK;
uint16_t fg = active ? TFT_BLACK : TFT_GREEN;
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -37,7 +39,7 @@ void run(TFT_eSPI& tft) {
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 6); tft.setCursor(10, 6);
tft.println(I18n::t(StringId::LANGUAGE_TITLE)); tft.println(I18n::t(StringId::LANGUAGE_TITLE));
@@ -46,8 +48,7 @@ void run(TFT_eSPI& tft) {
for (uint8_t i = 0; i < I18n::LANG_COUNT; i++) { for (uint8_t i = 0; i < I18n::LANG_COUNT; i++) {
langRects[i] = rowRect(i); langRects[i] = rowRect(i);
drawButton(tft, langRects[i], I18n::languageName(i), drawButton(tft, langRects[i], I18n::languageName(i), i == current);
(i == current) ? TFT_DARKGREEN : TFT_NAVY);
} }
Rect backBtn = rowRect(I18n::LANG_COUNT); Rect backBtn = rowRect(I18n::LANG_COUNT);

View File

@@ -14,11 +14,15 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label,
bool active = false, bool danger = false) {
uint16_t accent = danger ? TFT_RED : TFT_GREEN;
uint16_t bg = active ? accent : TFT_BLACK;
uint16_t fg = active ? TFT_BLACK : accent;
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -43,10 +47,6 @@ namespace {
KEY_W, KEY_H}; KEY_W, KEY_H};
} }
// WICHTIG: Backspace muss VOR den Cancel/OK-Buttons enden (mit
// Abstand), sonst ueberlappen sich die Tap-Flaechen - Backspace wird
// in der Pruef-Reihenfolge zuerst getestet, wuerde also faelschlich
// jeden Tap auf "Cancel" abfangen (Bug: Cancel-Button reagierte nicht).
Rect backspaceBtn = {(int16_t)GRID_LEFT, (int16_t)(GRID_TOP + 4 * (KEY_H + KEY_GAP)), Rect backspaceBtn = {(int16_t)GRID_LEFT, (int16_t)(GRID_TOP + 4 * (KEY_H + KEY_GAP)),
(int16_t)(3 * KEY_W + 2 * KEY_GAP), 30}; (int16_t)(3 * KEY_W + 2 * KEY_GAP), 30};
Rect cancelBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), 110, 40}; Rect cancelBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), 110, 40};
@@ -57,21 +57,21 @@ namespace {
auto redraw = [&]() { auto redraw = [&]() {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(title); tft.println(title);
tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_NAVY); tft.fillRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_BLACK);
tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_DARKGREY); tft.drawRect(8, 40, Config::SCREEN_WIDTH - 16, 34, TFT_GREEN);
tft.setTextSize(2); tft.setTextSize(2);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(14, 49); tft.setCursor(14, 49);
tft.print(buf); tft.print(buf);
tft.setTextSize(1); tft.setTextSize(1);
for (uint8_t i = 0; i < 12; i++) drawButton(tft, keyRects[i], keys[i]); for (uint8_t i = 0; i < 12; i++) drawButton(tft, keyRects[i], keys[i]);
drawButton(tft, backspaceBtn, "<- Backspace"); drawButton(tft, backspaceBtn, "<- Backspace");
drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), TFT_MAROON); drawButton(tft, cancelBtn, I18n::t(StringId::CANCEL), false, true);
drawButton(tft, confirmBtn, I18n::t(StringId::OK)); drawButton(tft, confirmBtn, I18n::t(StringId::OK));
}; };
@@ -133,7 +133,7 @@ void run(TFT_eSPI& tft) {
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 8); tft.setCursor(10, 8);
tft.println(I18n::t(StringId::LOCATION_TITLE)); tft.println(I18n::t(StringId::LOCATION_TITLE));
@@ -143,8 +143,7 @@ void run(TFT_eSPI& tft) {
Rect autoRect = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), ROW_H}; Rect autoRect = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), ROW_H};
String autoLabel = I18n::t(StringId::LOCATION_AUTO); String autoLabel = I18n::t(StringId::LOCATION_AUTO);
drawButton(tft, autoRect, active < 0 ? ("> " + autoLabel) : autoLabel, drawButton(tft, autoRect, active < 0 ? ("> " + autoLabel) : autoLabel, active < 0);
active < 0 ? TFT_DARKGREEN : TFT_NAVY);
y += ROW_H + ROW_GAP; y += ROW_H + ROW_GAP;
Rect rowRects[LocationPresets::MAX_PRESETS]; Rect rowRects[LocationPresets::MAX_PRESETS];
@@ -163,13 +162,13 @@ void run(TFT_eSPI& tft) {
char coords[24]; char coords[24];
snprintf(coords, sizeof(coords), "%d: %.3f, %.3f", i + 1, lat, lon); snprintf(coords, sizeof(coords), "%d: %.3f, %.3f", i + 1, lat, lon);
String label = (active == (int8_t)i ? "> " : "") + presetWord + " " + coords; String label = (active == (int8_t)i ? "> " : "") + presetWord + " " + coords;
drawButton(tft, rowRect, label, (active == (int8_t)i) ? TFT_DARKGREEN : TFT_NAVY); drawButton(tft, rowRect, label, active == (int8_t)i);
drawButton(tft, removeRect, "X", TFT_MAROON); drawButton(tft, removeRect, "X", false, true);
} else { } else {
tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK); tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK);
tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
String label = presetWord + " " + String(i + 1) + " " + I18n::t(StringId::LOCATION_PRESET_EMPTY); String label = presetWord + " " + String(i + 1) + " " + I18n::t(StringId::LOCATION_PRESET_EMPTY);
tft.drawString(label, rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.drawString(label, rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);

View File

@@ -15,10 +15,10 @@ namespace {
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label) {
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_NAVY); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -29,10 +29,10 @@ namespace {
void run(TFT_eSPI& tft) { void run(TFT_eSPI& tft) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 8); tft.setCursor(10, 8);
tft.println(I18n::t(StringId::LOGFILES_TITLE)); tft.println(I18n::t(StringId::LOGFILES_TITLE));
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, 30); tft.setCursor(10, 30);
tft.print(I18n::t(StringId::LOADING)); tft.print(I18n::t(StringId::LOADING));
@@ -42,12 +42,12 @@ void run(TFT_eSPI& tft) {
Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40}; Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40};
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 8); tft.setCursor(10, 8);
tft.println(I18n::t(StringId::LOGFILES_TITLE)); tft.println(I18n::t(StringId::LOGFILES_TITLE));
if (count == 0) { if (count == 0) {
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, 40); tft.setCursor(10, 40);
tft.println(I18n::t(StringId::LOGFILES_EMPTY)); tft.println(I18n::t(StringId::LOGFILES_EMPTY));
} else { } else {
@@ -55,7 +55,7 @@ void run(TFT_eSPI& tft) {
int16_t y = 30; int16_t y = 30;
if (count > VISIBLE_ROWS) { if (count > VISIBLE_ROWS) {
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, y); tft.setCursor(10, y);
tft.print(String(I18n::t(StringId::LOGFILES_SHOWING_PREFIX)) + VISIBLE_ROWS + tft.print(String(I18n::t(StringId::LOGFILES_SHOWING_PREFIX)) + VISIBLE_ROWS +
I18n::t(StringId::LOGFILES_OF) + count + I18n::t(StringId::LOGFILES_DAYS_SUFFIX)); I18n::t(StringId::LOGFILES_OF) + count + I18n::t(StringId::LOGFILES_DAYS_SUFFIX));
@@ -66,7 +66,7 @@ void run(TFT_eSPI& tft) {
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, y); tft.setCursor(10, y);
tft.print(days[i].date); tft.print(days[i].date);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(110, y); tft.setCursor(110, y);
tft.print(String(days[i].count) + I18n::t(StringId::LOGFILES_AIRCRAFT_SUFFIX)); tft.print(String(days[i].count) + I18n::t(StringId::LOGFILES_AIRCRAFT_SUFFIX));
y += 20; y += 20;

View File

@@ -29,6 +29,7 @@
#include "screenshot.h" #include "screenshot.h"
#include "i18n.h" #include "i18n.h"
#include "first_run_language_screen.h" #include "first_run_language_screen.h"
#include "ui_font.h"
TFT_eSPI tft = TFT_eSPI(); TFT_eSPI tft = TFT_eSPI();
@@ -59,35 +60,70 @@ struct Rect {
} }
}; };
Rect menuBtn = {Config::SCREEN_WIDTH - 60, 3, 54, 22}; Rect menuBtn = {Config::SCREEN_WIDTH - 90, 3, 54, 22};
Rect camBtn = {(int16_t)(menuBtn.x - 46), 3, 42, 22}; Rect camBtn = {(int16_t)(menuBtn.x - 46), 3, 42, 22};
void drawMenuButton() { void drawMenuButton() {
tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_NAVY); tft.fillRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_BLACK);
tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_DARKGREY); tft.drawRoundRect(menuBtn.x, menuBtn.y, menuBtn.w, menuBtn.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Menu", menuBtn.x + menuBtn.w / 2, menuBtn.y + menuBtn.h / 2); tft.drawString("Menu", menuBtn.x + menuBtn.w / 2, menuBtn.y + menuBtn.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
void drawCamButton() { void drawCamButton() {
tft.fillRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_NAVY); tft.fillRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_BLACK);
tft.drawRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_DARKGREY); tft.drawRoundRect(camBtn.x, camBtn.y, camBtn.w, camBtn.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.drawString("Cam", camBtn.x + camBtn.w / 2, camBtn.y + camBtn.h / 2); tft.drawString("Cam", camBtn.x + camBtn.w / 2, camBtn.y + camBtn.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
void drawWifiIcon(int16_t rightX, int16_t rowTop, int16_t rowH, int8_t rssi) {
uint8_t level;
if (rssi >= -55) level = 4;
else if (rssi >= -65) level = 3;
else if (rssi >= -75) level = 2;
else if (rssi >= -85) level = 1;
else level = 0;
constexpr uint8_t BAR_W = 3;
constexpr uint8_t BAR_GAP = 2;
constexpr uint8_t BAR_COUNT = 4;
int16_t baseline = rowTop + rowH - 1;
int16_t x = rightX - BAR_COUNT * (BAR_W + BAR_GAP);
for (uint8_t i = 0; i < BAR_COUNT; i++) {
int16_t barH = 3 + i * 2;
uint16_t color = (i < level) ? TFT_GREEN : TFT_DARKGREY;
tft.fillRect(x, baseline - barH, BAR_W, barH, color);
x += BAR_W + BAR_GAP;
}
}
constexpr int16_t WIFI_ICON_W = 22;
constexpr int16_t WIFI_ICON_H = 14;
void updateWifiIcon() {
int16_t iconX = Config::SCREEN_WIDTH - WIFI_ICON_W - 2;
int16_t iconY = 2;
tft.fillRect(iconX, iconY, WIFI_ICON_W, WIFI_ICON_H, TFT_BLACK);
if (WiFi.status() == WL_CONNECTED) {
drawWifiIcon(Config::SCREEN_WIDTH - 4, iconY, WIFI_ICON_H, WiFi.RSSI());
}
}
void drawHeader() { void drawHeader() {
tft.fillRect(0, 0, Config::SCREEN_WIDTH, CONTENT_TOP, TFT_BLACK); tft.fillRect(0, 0, Config::SCREEN_WIDTH, CONTENT_TOP, TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(1); tft.setTextSize(1);
tft.setCursor(6, 10); tft.setCursor(6, 10);
tft.println("Eiswolfs Flightradar"); tft.println("Eiswolfs FR");
drawCamButton(); drawCamButton();
drawMenuButton(); drawMenuButton();
updateWifiIcon();
} }
void updateStatusLine() { void updateStatusLine() {
@@ -106,13 +142,7 @@ void updateStatusLine() {
tft.print(timeBuf); tft.print(timeBuf);
} }
if (WiFi.status() == WL_CONNECTED) { updateWifiIcon();
char rssiBuf[14];
snprintf(rssiBuf, sizeof(rssiBuf), "WiFi %ddBm", WiFi.RSSI());
tft.setTextDatum(TR_DATUM);
tft.drawString(rssiBuf, Config::SCREEN_WIDTH - 4, HEADER_TITLE_H + 2);
tft.setTextDatum(TL_DATUM);
}
} }
void takeScreenshotWithFeedback() { void takeScreenshotWithFeedback() {
@@ -183,6 +213,7 @@ void setup() {
tft.init(); tft.init();
tft.setRotation(0); tft.setRotation(0);
tft.setFreeFont(&UiFont11pt);
tft.invertDisplay(true); tft.invertDisplay(true);
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
@@ -200,10 +231,6 @@ void setup() {
} }
SdStorage::seedDefaultDataFiles(); SdStorage::seedDefaultDataFiles();
// "Allererster Start" erkennen: config.txt existiert noch nicht (wird
// erst beim ersten SettingsStore::save()-Aufruf angelegt). Muss VOR
// SettingsStore::load() geprueft werden, da load() die Datei nicht
// erzeugt, nur liest.
bool isFirstRun = !SD.exists(Config::SD_SETTINGS_FILE); bool isFirstRun = !SD.exists(Config::SD_SETTINGS_FILE);
SettingsStore::load(); SettingsStore::load();
@@ -239,9 +266,6 @@ void setup() {
} }
} }
// --- Sprachauswahl: NUR beim allerersten Start, direkt nach Touch-
// Kalibrierung und WLAN-Ersteinrichtung, bevor irgendwelche weiteren
// Texte in (falscher) Standardsprache gezeigt werden. ---
if (isFirstRun) { if (isFirstRun) {
FirstRunLanguageScreen::run(tft); FirstRunLanguageScreen::run(tft);
SplashScreen::begin(tft); SplashScreen::begin(tft);

View File

@@ -23,20 +23,33 @@ namespace {
} }
}; };
constexpr int16_t ROW_H = 26; constexpr int16_t ROW_H = 24;
constexpr int16_t ROW_GAP = 3; constexpr int16_t ROW_GAP = 2;
constexpr int16_t ROW_START_Y = 20; constexpr int16_t ROW_START_Y = 18;
Rect rowRect(uint8_t index) { Rect rowRect(uint8_t index) {
return {10, (int16_t)(ROW_START_Y + index * (ROW_H + ROW_GAP)), return {10, (int16_t)(ROW_START_Y + index * (ROW_H + ROW_GAP)),
(int16_t)(Config::SCREEN_WIDTH - 20), ROW_H}; (int16_t)(Config::SCREEN_WIDTH - 20), ROW_H};
} }
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { constexpr int16_t CAT_ROW_H = 50;
constexpr int16_t CAT_ROW_GAP = 10;
constexpr int16_t CAT_START_Y = 30;
Rect catRowRect(uint8_t index) {
return {10, (int16_t)(CAT_START_Y + index * (CAT_ROW_H + CAT_ROW_GAP)),
(int16_t)(Config::SCREEN_WIDTH - 20), CAT_ROW_H};
}
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label,
bool active = false, bool danger = false) {
uint16_t accent = danger ? TFT_RED : TFT_GREEN;
uint16_t bg = active ? accent : TFT_BLACK;
uint16_t fg = active ? TFT_BLACK : accent;
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -57,28 +70,113 @@ namespace {
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
delay(1200); delay(1200);
} }
enum class Page { Main, Region, Wifi, System, Flight };
} }
void run(TFT_eSPI& tft) { void run(TFT_eSPI& tft) {
uint8_t page = 0; Page page = Page::Main;
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(10, 2);
tft.println(page == 0 ? I18n::t(StringId::MENU_SETTINGS) : I18n::t(StringId::MENU_SETTINGS_PAGE2));
if (page == 0) { if (page == Page::Main) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 6);
tft.println(I18n::t(StringId::MENU_SETTINGS));
Rect regionBtn = catRowRect(0);
Rect wifiBtn = catRowRect(1);
Rect systemBtn = catRowRect(2);
Rect flightBtn = catRowRect(3);
Rect backBtn = catRowRect(4);
drawButton(tft, regionBtn, I18n::t(StringId::MENU_CATEGORY_REGION));
drawButton(tft, wifiBtn, I18n::t(StringId::MENU_CATEGORY_WIFI));
drawButton(tft, systemBtn, I18n::t(StringId::MENU_CATEGORY_SYSTEM));
drawButton(tft, flightBtn, I18n::t(StringId::MENU_CATEGORY_FLIGHT));
drawButton(tft, backBtn, I18n::t(StringId::BACK));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (regionBtn.contains(tap.x, tap.y)) {
page = Page::Region;
} else if (wifiBtn.contains(tap.x, tap.y)) {
page = Page::Wifi;
} else if (systemBtn.contains(tap.x, tap.y)) {
page = Page::System;
} else if (flightBtn.contains(tap.x, tap.y)) {
page = Page::Flight;
} else if (backBtn.contains(tap.x, tap.y)) {
done = true;
}
} else if (page == Page::Region) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 2);
tft.println(I18n::t(StringId::MENU_CATEGORY_REGION));
Rect languageBtn = rowRect(0);
Rect unitsBtn = rowRect(1);
Rect backBtn = rowRect(2);
drawButton(tft, languageBtn, String(I18n::t(StringId::MENU_LANGUAGE)) + ": " + I18n::languageName(SettingsStore::language()));
drawButton(tft, unitsBtn, I18n::t(StringId::MENU_UNITS));
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (languageBtn.contains(tap.x, tap.y)) {
LanguageScreen::run(tft);
} else if (unitsBtn.contains(tap.x, tap.y)) {
UnitsScreen::run(tft);
} else if (backBtn.contains(tap.x, tap.y)) {
page = Page::Main;
}
} else if (page == Page::Wifi) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 2);
tft.println(I18n::t(StringId::MENU_CATEGORY_WIFI));
Rect wifiManageBtn = rowRect(0);
Rect backBtn = rowRect(1);
drawButton(tft, wifiManageBtn, I18n::t(StringId::MENU_MANAGE_WIFI));
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (wifiManageBtn.contains(tap.x, tap.y)) {
WifiManageScreen::run(tft);
} else if (backBtn.contains(tap.x, tap.y)) {
page = Page::Main;
}
} else if (page == Page::System) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 2);
tft.println(I18n::t(StringId::MENU_CATEGORY_SYSTEM));
Rect calibBtn = rowRect(0); Rect calibBtn = rowRect(0);
Rect invertBtn = rowRect(1); Rect invertBtn = rowRect(1);
Rect wifiBtn = rowRect(2); Rect timeoutBtn = rowRect(2);
Rect locationBtn = rowRect(3); Rect backupBtn = rowRect(3);
Rect timeoutBtn = rowRect(4); Rect restoreBtn = rowRect(4);
Rect statsBtn = rowRect(5); Rect backBtn = rowRect(5);
Rect logFilesBtn = rowRect(6);
Rect moreBtn = rowRect(7);
Rect backBtn = rowRect(8);
drawButton(tft, calibBtn, I18n::t(StringId::MENU_CALIBRATE)); drawButton(tft, calibBtn, I18n::t(StringId::MENU_CALIBRATE));
@@ -87,13 +185,10 @@ void run(TFT_eSPI& tft) {
: I18n::t(StringId::MENU_DISPLAY_NORMAL); : I18n::t(StringId::MENU_DISPLAY_NORMAL);
drawButton(tft, invertBtn, invertLabel); drawButton(tft, invertBtn, invertLabel);
drawButton(tft, wifiBtn, I18n::t(StringId::MENU_MANAGE_WIFI));
drawButton(tft, locationBtn, I18n::t(StringId::MENU_LOCATION_PRESETS));
drawButton(tft, timeoutBtn, screenTimeoutLabel(SettingsStore::screenTimeoutMinutes())); drawButton(tft, timeoutBtn, screenTimeoutLabel(SettingsStore::screenTimeoutMinutes()));
drawButton(tft, statsBtn, I18n::t(StringId::MENU_STATISTICS)); drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP));
drawButton(tft, logFilesBtn, I18n::t(StringId::MENU_LOGBOOK_FILES)); drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE));
drawButton(tft, moreBtn, I18n::t(StringId::MENU_MORE_SETTINGS)); drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
drawButton(tft, backBtn, I18n::t(StringId::BACK));
TouchInput::Point tap; TouchInput::Point tap;
while (true) { while (true) {
@@ -107,89 +202,10 @@ void run(TFT_eSPI& tft) {
bool newState = !SettingsStore::displayInverted(); bool newState = !SettingsStore::displayInverted();
SettingsStore::setDisplayInverted(newState); SettingsStore::setDisplayInverted(newState);
tft.invertDisplay(newState); tft.invertDisplay(newState);
} else if (wifiBtn.contains(tap.x, tap.y)) {
WifiManageScreen::run(tft);
} else if (locationBtn.contains(tap.x, tap.y)) {
LocationPresetsScreen::run(tft);
} else if (timeoutBtn.contains(tap.x, tap.y)) { } else if (timeoutBtn.contains(tap.x, tap.y)) {
uint8_t current = SettingsStore::screenTimeoutMinutes(); uint8_t current = SettingsStore::screenTimeoutMinutes();
uint8_t next = (current >= 10) ? 0 : (current + 1); uint8_t next = (current >= 10) ? 0 : (current + 1);
SettingsStore::setScreenTimeoutMinutes(next); SettingsStore::setScreenTimeoutMinutes(next);
} else if (statsBtn.contains(tap.x, tap.y)) {
StatsScreen::run(tft);
} else if (logFilesBtn.contains(tap.x, tap.y)) {
LogbookFilesScreen::run(tft);
} else if (moreBtn.contains(tap.x, tap.y)) {
page = 1;
} else if (backBtn.contains(tap.x, tap.y)) {
done = true;
}
} else if (page == 1) {
Rect emergencyBtn = rowRect(0);
Rect proximityBtn = rowRect(1);
Rect heartbeatBtn = rowRect(2);
Rect logbookBtn = rowRect(3);
Rect groundBtn = rowRect(4);
Rect airlineBtn = rowRect(5);
Rect moreBtn = rowRect(6);
Rect backBtn = rowRect(7);
drawButton(tft, emergencyBtn, I18n::t(StringId::MENU_EMERGENCY_ALERT) + onOff(SettingsStore::emergencyAlertEnabled()));
drawButton(tft, proximityBtn, I18n::t(StringId::MENU_PROXIMITY_LED) + onOff(SettingsStore::proximityAlertEnabled()));
drawButton(tft, heartbeatBtn, I18n::t(StringId::MENU_LED_HEARTBEAT) + onOff(SettingsStore::ledHeartbeatEnabled()));
drawButton(tft, logbookBtn, I18n::t(StringId::MENU_FLIGHT_LOGBOOK) + onOff(SettingsStore::flightLogbookEnabled()));
drawButton(tft, groundBtn, I18n::t(StringId::MENU_HIDE_GROUND) + onOff(SettingsStore::hideGroundVehicles()));
drawButton(tft, airlineBtn, I18n::t(StringId::MENU_AIRLINE_FILTER));
drawButton(tft, moreBtn, I18n::t(StringId::MENU_MORE_SETTINGS));
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (emergencyBtn.contains(tap.x, tap.y)) {
SettingsStore::setEmergencyAlertEnabled(!SettingsStore::emergencyAlertEnabled());
} else if (proximityBtn.contains(tap.x, tap.y)) {
SettingsStore::setProximityAlertEnabled(!SettingsStore::proximityAlertEnabled());
} else if (heartbeatBtn.contains(tap.x, tap.y)) {
SettingsStore::setLedHeartbeatEnabled(!SettingsStore::ledHeartbeatEnabled());
} else if (logbookBtn.contains(tap.x, tap.y)) {
SettingsStore::setFlightLogbookEnabled(!SettingsStore::flightLogbookEnabled());
} else if (groundBtn.contains(tap.x, tap.y)) {
SettingsStore::setHideGroundVehicles(!SettingsStore::hideGroundVehicles());
} else if (airlineBtn.contains(tap.x, tap.y)) {
AirlineFilterScreen::run(tft);
} else if (moreBtn.contains(tap.x, tap.y)) {
page = 2;
} else if (backBtn.contains(tap.x, tap.y)) {
page = 0;
}
} else {
Rect languageBtn = rowRect(0);
Rect unitsBtn = rowRect(1);
Rect backupBtn = rowRect(2);
Rect restoreBtn = rowRect(3);
Rect backBtn = rowRect(4);
drawButton(tft, languageBtn, String(I18n::t(StringId::MENU_LANGUAGE)) + ": " + I18n::languageName(SettingsStore::language()));
drawButton(tft, unitsBtn, I18n::t(StringId::MENU_UNITS));
drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP));
drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE),
SettingsBackup::hasBackup() ? TFT_NAVY : TFT_DARKGREY);
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (languageBtn.contains(tap.x, tap.y)) {
LanguageScreen::run(tft);
} else if (unitsBtn.contains(tap.x, tap.y)) {
UnitsScreen::run(tft);
} else if (backupBtn.contains(tap.x, tap.y)) { } else if (backupBtn.contains(tap.x, tap.y)) {
bool ok = SettingsBackup::backup(); bool ok = SettingsBackup::backup();
showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED), showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED),
@@ -201,7 +217,62 @@ void run(TFT_eSPI& tft) {
ok ? TFT_GREEN : TFT_RED); ok ? TFT_GREEN : TFT_RED);
} }
} else if (backBtn.contains(tap.x, tap.y)) { } else if (backBtn.contains(tap.x, tap.y)) {
page = 1; page = Page::Main;
}
} else { // Page::Flight
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 2);
tft.println(I18n::t(StringId::MENU_CATEGORY_FLIGHT));
Rect statsBtn = rowRect(0);
Rect logFilesBtn = rowRect(1);
Rect logbookBtn = rowRect(2);
Rect heartbeatBtn = rowRect(3);
Rect proximityBtn = rowRect(4);
Rect emergencyBtn = rowRect(5);
Rect locationBtn = rowRect(6);
Rect airlineBtn = rowRect(7);
Rect groundBtn = rowRect(8);
Rect backBtn = rowRect(9);
drawButton(tft, statsBtn, I18n::t(StringId::MENU_STATISTICS));
drawButton(tft, logFilesBtn, I18n::t(StringId::MENU_LOGBOOK_FILES));
drawButton(tft, logbookBtn, I18n::t(StringId::MENU_FLIGHT_LOGBOOK) + onOff(SettingsStore::flightLogbookEnabled()));
drawButton(tft, heartbeatBtn, I18n::t(StringId::MENU_LED_HEARTBEAT) + onOff(SettingsStore::ledHeartbeatEnabled()));
drawButton(tft, proximityBtn, I18n::t(StringId::MENU_PROXIMITY_LED) + onOff(SettingsStore::proximityAlertEnabled()));
drawButton(tft, emergencyBtn, I18n::t(StringId::MENU_EMERGENCY_ALERT) + onOff(SettingsStore::emergencyAlertEnabled()));
drawButton(tft, locationBtn, I18n::t(StringId::MENU_LOCATION_PRESETS));
drawButton(tft, airlineBtn, I18n::t(StringId::MENU_AIRLINE_FILTER));
drawButton(tft, groundBtn, I18n::t(StringId::MENU_HIDE_GROUND) + onOff(SettingsStore::hideGroundVehicles()));
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
delay(20);
}
if (statsBtn.contains(tap.x, tap.y)) {
StatsScreen::run(tft);
} else if (logFilesBtn.contains(tap.x, tap.y)) {
LogbookFilesScreen::run(tft);
} else if (logbookBtn.contains(tap.x, tap.y)) {
SettingsStore::setFlightLogbookEnabled(!SettingsStore::flightLogbookEnabled());
} else if (heartbeatBtn.contains(tap.x, tap.y)) {
SettingsStore::setLedHeartbeatEnabled(!SettingsStore::ledHeartbeatEnabled());
} else if (proximityBtn.contains(tap.x, tap.y)) {
SettingsStore::setProximityAlertEnabled(!SettingsStore::proximityAlertEnabled());
} else if (emergencyBtn.contains(tap.x, tap.y)) {
SettingsStore::setEmergencyAlertEnabled(!SettingsStore::emergencyAlertEnabled());
} else if (locationBtn.contains(tap.x, tap.y)) {
LocationPresetsScreen::run(tft);
} else if (airlineBtn.contains(tap.x, tap.y)) {
AirlineFilterScreen::run(tft);
} else if (groundBtn.contains(tap.x, tap.y)) {
SettingsStore::setHideGroundVehicles(!SettingsStore::hideGroundVehicles());
} else if (backBtn.contains(tap.x, tap.y)) {
page = Page::Main;
} }
} }
} }

View File

@@ -30,7 +30,7 @@ namespace {
int16_t infoTop; int16_t infoTop;
}; };
constexpr int16_t INFO_BAR_H = 50; constexpr int16_t INFO_BAR_H = 64;
Layout computeLayout(int16_t top) { Layout computeLayout(int16_t top) {
Layout L; Layout L;
@@ -45,7 +45,7 @@ namespace {
return L; return L;
} }
constexpr int16_t DETAIL_PANEL_H = 190; constexpr int16_t DETAIL_PANEL_H = 264;
struct HitPoint { struct HitPoint {
int16_t x, y; int16_t x, y;
@@ -177,10 +177,10 @@ namespace {
} }
void drawButton(TFT_eSPI& gfx, const Rect& r, const String& label) { void drawButton(TFT_eSPI& gfx, const Rect& r, const String& label) {
gfx.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_NAVY); gfx.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
gfx.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); gfx.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN);
gfx.setTextDatum(MC_DATUM); gfx.setTextDatum(MC_DATUM);
gfx.setTextColor(TFT_WHITE, TFT_NAVY); gfx.setTextColor(TFT_GREEN, TFT_BLACK);
gfx.drawString(label, r.x + r.w / 2, r.y + r.h / 2); gfx.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
gfx.setTextDatum(TL_DATUM); gfx.setTextDatum(TL_DATUM);
} }
@@ -210,7 +210,7 @@ namespace {
gfx.setTextColor(TFT_WHITE, TFT_BLACK); gfx.setTextColor(TFT_WHITE, TFT_BLACK);
for (uint8_t i = 0; i < 3; i++) { for (uint8_t i = 0; i < 3; i++) {
int16_t x0 = i * segW + 6; int16_t x0 = i * segW + 6;
gfx.fillCircle(x0, y + 4, 3, items[i].color); gfx.fillCircle(x0, y - 5, 3, items[i].color);
gfx.setCursor(x0 + 7, y); gfx.setCursor(x0 + 7, y);
gfx.print(items[i].label); gfx.print(items[i].label);
} }
@@ -279,8 +279,8 @@ namespace {
void updateLine(TFT_eSPI& gfx, int16_t y, int16_t h, int16_t maxWidth, void updateLine(TFT_eSPI& gfx, int16_t y, int16_t h, int16_t maxWidth,
uint16_t fg, String& cached, const String& newText, bool forceFull) { uint16_t fg, String& cached, const String& newText, bool forceFull) {
if (!forceFull && cached == newText) return; if (!forceFull && cached == newText) return;
gfx.fillRect(0, y - 2, Config::SCREEN_WIDTH, h, TFT_NAVY); gfx.fillRect(0, y - 14, Config::SCREEN_WIDTH, h, TFT_BLACK);
gfx.setTextColor(fg, TFT_NAVY); gfx.setTextColor(fg, TFT_BLACK);
printLineTruncated(gfx, 8, y, maxWidth, newText); printLineTruncated(gfx, 8, y, maxWidth, newText);
cached = newText; cached = newText;
} }
@@ -291,54 +291,55 @@ namespace {
int16_t panelTop = Config::SCREEN_HEIGHT - DETAIL_PANEL_H; int16_t panelTop = Config::SCREEN_HEIGHT - DETAIL_PANEL_H;
constexpr int16_t textMaxWidth = Config::SCREEN_WIDTH - 16; constexpr int16_t textMaxWidth = Config::SCREEN_WIDTH - 16;
constexpr int16_t LINE_H = 22;
bool forceFull = !lastPanel.valid || strcmp(lastPanel.hex, a.hex) != 0; bool forceFull = !lastPanel.valid || strcmp(lastPanel.hex, a.hex) != 0;
if (forceFull) { if (forceFull) {
gfx.fillRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_NAVY); gfx.fillRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_BLACK);
gfx.drawRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_DARKGREY); gfx.drawRect(0, panelTop, Config::SCREEN_WIDTH, DETAIL_PANEL_H, TFT_GREEN);
lastPanel = PanelState{}; lastPanel = PanelState{};
strncpy(lastPanel.hex, a.hex, sizeof(lastPanel.hex) - 1); strncpy(lastPanel.hex, a.hex, sizeof(lastPanel.hex) - 1);
} }
int16_t y = panelTop + 6; int16_t y = panelTop + 26;
{ {
String txt = a.callsign[0] ? a.callsign : a.hex; String txt = a.callsign[0] ? a.callsign : a.hex;
if (forceFull || lastPanel.callsignText != txt) { if (forceFull || lastPanel.callsignText != txt) {
gfx.fillRect(0, y - 2, Config::SCREEN_WIDTH, 20, TFT_NAVY); gfx.fillRect(0, y - 22, Config::SCREEN_WIDTH, 28, TFT_BLACK);
gfx.setTextColor(TFT_WHITE, TFT_NAVY); gfx.setTextColor(TFT_GREEN, TFT_BLACK);
gfx.setTextSize(2); gfx.setTextSize(2);
printLineTruncated(gfx, 8, y, textMaxWidth, txt); printLineTruncated(gfx, 8, y, textMaxWidth, txt);
gfx.setTextSize(1); gfx.setTextSize(1);
lastPanel.callsignText = txt; lastPanel.callsignText = txt;
} }
} }
y += 22; y += 30;
updateLine(gfx, y, 15, textMaxWidth, TFT_GREEN, lastPanel.airlineText, updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.airlineText,
String(a.airlineName), forceFull); String(a.airlineName), forceFull);
y += 15; y += LINE_H;
String modelLine = details.loading String modelLine = details.loading
? String(I18n::t(StringId::DETAIL_MODEL)) + I18n::t(StringId::DETAIL_LOADING_DOTS) ? String(I18n::t(StringId::DETAIL_MODEL)) + I18n::t(StringId::DETAIL_LOADING_DOTS)
: String(I18n::t(StringId::DETAIL_MODEL)) + (details.model[0] ? details.model : I18n::t(StringId::DETAIL_UNKNOWN)); : String(I18n::t(StringId::DETAIL_MODEL)) + (details.model[0] ? details.model : I18n::t(StringId::DETAIL_UNKNOWN));
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.modelText, modelLine, forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.modelText, modelLine, forceFull);
y += 15; y += LINE_H;
String typeLine = String(I18n::t(StringId::DETAIL_TYPE)) + (a.typeCode[0] ? a.typeCode : I18n::t(StringId::DETAIL_UNKNOWN)); String typeLine = String(I18n::t(StringId::DETAIL_TYPE)) + (a.typeCode[0] ? a.typeCode : I18n::t(StringId::DETAIL_UNKNOWN));
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.typeText, typeLine, forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.typeText, typeLine, forceFull);
y += 15; y += LINE_H;
char buf[48]; char buf[48];
snprintf(buf, sizeof(buf), "%s%.0fm / %.0fft", I18n::t(StringId::DETAIL_ALT), snprintf(buf, sizeof(buf), "%s%.0fm / %.0fft", I18n::t(StringId::DETAIL_ALT),
Units::feetToMeters((float)a.altBaroFt), (float)a.altBaroFt); Units::feetToMeters((float)a.altBaroFt), (float)a.altBaroFt);
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.altText, String(buf), forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.altText, String(buf), forceFull);
y += 15; y += LINE_H;
snprintf(buf, sizeof(buf), "%s%.0fkm/h / %.0fkt", I18n::t(StringId::DETAIL_SPEED), snprintf(buf, sizeof(buf), "%s%.0fkm/h / %.0fkt", I18n::t(StringId::DETAIL_SPEED),
Units::ktToKmh(a.groundSpeedKt), a.groundSpeedKt); Units::ktToKmh(a.groundSpeedKt), a.groundSpeedKt);
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.speedText, String(buf), forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.speedText, String(buf), forceFull);
y += 15; y += LINE_H;
String climbLine; String climbLine;
if (a.vertRateFtMin > 100) { if (a.vertRateFtMin > 100) {
@@ -350,32 +351,66 @@ namespace {
} else { } else {
climbLine = I18n::t(StringId::DETAIL_LEVEL); climbLine = I18n::t(StringId::DETAIL_LEVEL);
} }
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.climbText, climbLine, forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.climbText, climbLine, forceFull);
y += 15; y += LINE_H;
snprintf(buf, sizeof(buf), "%s%.0fkm / %.0fnm %s%.0f", I18n::t(StringId::DETAIL_DIST), snprintf(buf, sizeof(buf), "%s%.0fkm / %.0fnm %s%.0f", I18n::t(StringId::DETAIL_DIST),
a.distanceKm, Units::kmToNm(a.distanceKm), I18n::t(StringId::DETAIL_HDG), a.headingDeg); a.distanceKm, Units::kmToNm(a.distanceKm), I18n::t(StringId::DETAIL_HDG), a.headingDeg);
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.distHeadingText, String(buf), forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.distHeadingText, String(buf), forceFull);
y += 15; y += LINE_H;
String squawkLine = String(I18n::t(StringId::DETAIL_SQUAWK)) + (a.squawk[0] ? a.squawk : I18n::t(StringId::DETAIL_UNKNOWN)); String squawkLine = String(I18n::t(StringId::DETAIL_SQUAWK)) + (a.squawk[0] ? a.squawk : I18n::t(StringId::DETAIL_UNKNOWN));
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.squawkText, squawkLine, forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.squawkText, squawkLine, forceFull);
y += 15; y += LINE_H;
String seatsLine = a.estSeats > 0 String seatsLine = a.estSeats > 0
? String(I18n::t(StringId::DETAIL_SEATS_EST)) + a.estSeats ? String(I18n::t(StringId::DETAIL_SEATS_EST)) + a.estSeats
: String(I18n::t(StringId::DETAIL_SEATS_UNKNOWN)); : String(I18n::t(StringId::DETAIL_SEATS_UNKNOWN));
updateLine(gfx, y, 15, textMaxWidth, TFT_WHITE, lastPanel.seatsText, seatsLine, forceFull); updateLine(gfx, y, LINE_H, textMaxWidth, TFT_GREEN, lastPanel.seatsText, seatsLine, forceFull);
y += 18; y += 22;
if (forceFull) { if (forceFull) {
gfx.setTextColor(TFT_DARKGREY, TFT_NAVY); gfx.setTextColor(TFT_DARKGREEN, TFT_BLACK);
gfx.setCursor(8, y); gfx.setCursor(8, y);
gfx.print(I18n::t(StringId::DETAIL_TAP_CLOSE)); gfx.print(I18n::t(StringId::DETAIL_TAP_CLOSE));
} }
lastPanel.valid = true; lastPanel.valid = true;
} }
constexpr uint8_t STAR_COUNT = 28;
struct Star {
int16_t x, y;
uint8_t phase;
uint8_t speed;
};
Star stars[STAR_COUNT];
bool starsInitialized = false;
void initStarsIfNeeded() {
if (starsInitialized) return;
int16_t panelTop = Config::SCREEN_HEIGHT - DETAIL_PANEL_H;
randomSeed((uint32_t)esp_random());
for (uint8_t i = 0; i < STAR_COUNT; i++) {
stars[i].x = 6 + random(0, Config::SCREEN_WIDTH - 12);
stars[i].y = panelTop + 10 + random(0, DETAIL_PANEL_H - 20);
stars[i].phase = (uint8_t)random(0, 256);
stars[i].speed = 1 + random(0, 3);
}
starsInitialized = true;
}
void updateDetailPanelStars(TFT_eSPI& gfx) {
initStarsIfNeeded();
for (uint8_t i = 0; i < STAR_COUNT; i++) {
stars[i].phase += stars[i].speed;
uint8_t bright = (stars[i].phase < 128)
? (uint8_t)(stars[i].phase * 2)
: (uint8_t)((255 - stars[i].phase) * 2);
uint16_t color = gfx.color565(0, bright, 0);
gfx.drawPixel(stars[i].x, stars[i].y, color);
}
}
} }
void render(TFT_eSPI& tft, int16_t top) { void render(TFT_eSPI& tft, int16_t top) {
@@ -470,12 +505,6 @@ void render(TFT_eSPI& tft, int16_t top) {
tft.drawCircle(pt.x, pt.y, 12, TFT_RED); tft.drawCircle(pt.x, pt.y, 12, TFT_RED);
} }
// Rufzeichen als kleines farbiges Label ueber dem Punkt
// Transparent zeichnen (KEIN schwarzer Hintergrund-Kasten) - sonst
// "beisst" der Label-Hintergrund ein Stueck der Ring-/Fadenkreuz-
// Linie weg, wenn sich beides ueberschneidet. Da das Label bei jedem
// Sweep-Tick am selben Fleck neu gezeichnet wird, sah das wie
// Dauerflackern genau an den Beschriftungen aus.
tft.setTextColor(color); tft.setTextColor(color);
tft.setTextDatum(BC_DATUM); tft.setTextDatum(BC_DATUM);
const char* label = a.callsign[0] ? a.callsign : a.hex; const char* label = a.callsign[0] ? a.callsign : a.hex;
@@ -506,21 +535,26 @@ void render(TFT_eSPI& tft, int16_t top) {
int16_t infoTop = L.infoTop; int16_t infoTop = L.infoTop;
tft.drawFastHLine(0, infoTop, Config::SCREEN_WIDTH, TFT_DARKGREY); tft.drawFastHLine(0, infoTop, Config::SCREEN_WIDTH, TFT_DARKGREY);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(8, infoTop + 6); tft.setCursor(8, infoTop + 20);
tft.print(I18n::t(StringId::RADAR_TAP_FOR_DETAILS)); tft.print(I18n::t(StringId::RADAR_TAP_FOR_DETAILS));
char rangeLabel[8]; char rangeLabel[8];
snprintf(rangeLabel, sizeof(rangeLabel), "%.0fkm", rangeKm); snprintf(rangeLabel, sizeof(rangeLabel), "%.0fkm", rangeKm);
drawButton(tft, L.rangeBtn, rangeLabel); drawButton(tft, L.rangeBtn, rangeLabel);
drawLegend(tft, infoTop + 30); drawLegend(tft, infoTop + 44);
} }
tft.endWrite(); tft.endWrite();
} }
void tick(TFT_eSPI& tft, int16_t top, uint32_t deltaMs) { void tick(TFT_eSPI& tft, int16_t top, uint32_t deltaMs) {
if (selectedHex[0]) return; if (selectedHex[0]) {
tft.startWrite();
updateDetailPanelStars(tft);
tft.endWrite();
return;
}
Layout L = computeLayout(top); Layout L = computeLayout(top);
float rangeKm = Config::RANGE_STEPS_KM[SettingsStore::rangeIndex()]; float rangeKm = Config::RANGE_STEPS_KM[SettingsStore::rangeIndex()];

View File

@@ -14,17 +14,18 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) {
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); uint16_t accent = danger ? TFT_RED : TFT_GREEN;
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(accent, TFT_BLACK);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
void drawStatRow(TFT_eSPI& tft, int16_t y, const String& label, const String& value) { void drawStatRow(TFT_eSPI& tft, int16_t y, const String& label, const String& value) {
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, y); tft.setCursor(10, y);
tft.print(label); tft.print(label);
tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
@@ -39,10 +40,10 @@ namespace {
void run(TFT_eSPI& tft) { void run(TFT_eSPI& tft) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::STATS_TITLE)); tft.println(I18n::t(StringId::STATS_TITLE));
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, 44); tft.setCursor(10, 44);
tft.print(I18n::t(StringId::LOADING)); tft.print(I18n::t(StringId::LOADING));
@@ -60,7 +61,7 @@ void run(TFT_eSPI& tft) {
auto redraw = [&]() { auto redraw = [&]() {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::STATS_TITLE)); tft.println(I18n::t(StringId::STATS_TITLE));
@@ -84,12 +85,12 @@ void run(TFT_eSPI& tft) {
uint32_t upM = (upSec % 3600) / 60; uint32_t upM = (upSec % 3600) / 60;
char upBuf[8]; char upBuf[8];
snprintf(upBuf, sizeof(upBuf), "%luh %lum", (unsigned long)upH, (unsigned long)upM); snprintf(upBuf, sizeof(upBuf), "%luh %lum", (unsigned long)upH, (unsigned long)upM);
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.setCursor(10, 210); tft.setCursor(10, 210);
tft.print(String(I18n::t(StringId::STATS_UPTIME_PREFIX)) + upBuf); tft.print(String(I18n::t(StringId::STATS_UPTIME_PREFIX)) + upBuf);
if (confirmPending) { if (confirmPending) {
drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_CONFIRM), TFT_MAROON); drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_CONFIRM), true);
} else { } else {
drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_BTN)); drawButton(tft, resetBtn, I18n::t(StringId::STATS_RESET_BTN));
} }

461
src/ui_font.h Normal file
View File

@@ -0,0 +1,461 @@
#pragma once
// Automatisch generierter Font (DejaVu Sans Mono, 11px) fuer TFT_eSPI GFXFF.
// Deckt Basis-ASCII + Umlaute/Akzente fuer DE/TR/FR/ES/IT ab (U+0020-U+015F).
// NICHT von Hand bearbeiten - siehe Kommentar in i18n.h fuer den Generator-Hinweis.
#include <pgmspace.h>
const uint8_t UiFont11ptBitmaps[] PROGMEM = {
0x00, 0xFD, 0xB6, 0x80, 0x14, 0x49, 0xF9, 0x42, 0x9F, 0x92, 0x28, 0x23, 0xE9, 0x47, 0x14, 0xBE,
0x21, 0x00, 0xE2, 0x8E, 0x46, 0x23, 0x71, 0x47, 0x71, 0x04, 0x18, 0xB6, 0x58, 0x9F, 0xE0, 0x6A,
0xAA, 0x90, 0xA5, 0x55, 0xA0, 0x25, 0x5C, 0xEA, 0x90, 0x21, 0x3E, 0x42, 0x00, 0x58, 0xE0, 0xC0,
0x08, 0x84, 0x42, 0x11, 0x08, 0x80, 0x7B, 0x38, 0x65, 0x86, 0x1C, 0xDE, 0xE1, 0x08, 0x42, 0x10,
0x9F, 0x7A, 0x10, 0x43, 0x18, 0xC4, 0x3F, 0x7A, 0x10, 0x5E, 0x0C, 0x18, 0x5E, 0x18, 0x62, 0x92,
0xCB, 0xF0, 0x82, 0xFA, 0x08, 0x3E, 0x0C, 0x10, 0x7E, 0x3D, 0x08, 0x2E, 0xC6, 0x18, 0x5E, 0xFC,
0x20, 0x84, 0x10, 0x82, 0x10, 0x7A, 0x18, 0x5E, 0x86, 0x18, 0x5E, 0x7A, 0x18, 0x61, 0x7C, 0x10,
0xBC, 0xCC, 0x50, 0x58, 0x04, 0xEC, 0x1C, 0x0C, 0xFC, 0x0F, 0xC0, 0x81, 0xC0, 0xCE, 0xC0, 0xE1,
0x36, 0x44, 0x04, 0x39, 0x38, 0x67, 0xA6, 0x99, 0xF0, 0x40, 0xE0, 0x30, 0xC3, 0x12, 0x49, 0xE8,
0x61, 0xFA, 0x18, 0x7E, 0x86, 0x18, 0x7E, 0x39, 0x18, 0x20, 0x82, 0x04, 0x4E, 0xF2, 0x28, 0x61,
0x86, 0x18, 0xBC, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x3F, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x20, 0x39,
0x18, 0x20, 0x8E, 0x14, 0x4E, 0x86, 0x18, 0x7F, 0x86, 0x18, 0x61, 0xF9, 0x08, 0x42, 0x10, 0x9F,
0x38, 0x42, 0x10, 0x86, 0x2E, 0x8A, 0x4A, 0x30, 0xA2, 0x48, 0xA1, 0x82, 0x08, 0x20, 0x82, 0x08,
0x3F, 0x87, 0x3C, 0xED, 0xB6, 0x18, 0x61, 0x87, 0x1A, 0x69, 0x96, 0x58, 0xE1, 0x7B, 0x38, 0x61,
0x86, 0x1C, 0xDE, 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x7B, 0x38, 0x61, 0x86, 0x1C, 0xDE, 0x0C,
0xF9, 0x0A, 0x14, 0x2F, 0x91, 0x21, 0x41, 0x7A, 0x18, 0x3C, 0x0C, 0x18, 0x5E, 0xFE, 0x20, 0x40,
0x81, 0x02, 0x04, 0x08, 0x86, 0x18, 0x61, 0x86, 0x18, 0x5E, 0x86, 0x14, 0x92, 0x48, 0xC3, 0x0C,
0x83, 0x26, 0x4D, 0x56, 0xCD, 0x91, 0x22, 0x85, 0x24, 0x8C, 0x31, 0x24, 0xA1, 0xC6, 0x88, 0xA1,
0xC1, 0x02, 0x04, 0x08, 0xFC, 0x20, 0x84, 0x21, 0x84, 0x3F, 0xEA, 0xAA, 0xB0, 0x82, 0x10, 0x42,
0x10, 0x42, 0x08, 0xD5, 0x55, 0x70, 0x31, 0x28, 0x40, 0xFE, 0x44, 0xF0, 0x5F, 0x18, 0xBC, 0x84,
0x21, 0xE8, 0xC6, 0x31, 0xF0, 0x7E, 0x21, 0x0C, 0x3C, 0x08, 0x42, 0xF8, 0xC6, 0x31, 0x78, 0x74,
0x7F, 0x08, 0x3C, 0x19, 0x09, 0xF2, 0x10, 0x84, 0x20, 0x7C, 0x63, 0x18, 0xBC, 0x2E, 0x84, 0x21,
0x6C, 0xC6, 0x31, 0x88, 0x20, 0x01, 0xC2, 0x10, 0x84, 0xF8, 0x20, 0x72, 0x49, 0x27, 0x00, 0x84,
0x21, 0x2A, 0x62, 0x92, 0x88, 0xE1, 0x08, 0x42, 0x10, 0x84, 0x18, 0xFD, 0x6B, 0x5A, 0xD4, 0xB6,
0x63, 0x18, 0xC4, 0x74, 0x63, 0x18, 0xB8, 0xF4, 0x63, 0x18, 0xFA, 0x10, 0x7C, 0x63, 0x18, 0xBC,
0x21, 0xF9, 0x88, 0x88, 0x7C, 0x38, 0x30, 0xF8, 0x21, 0x3E, 0x42, 0x10, 0x87, 0x8C, 0x63, 0x18,
0xBC, 0x8C, 0x54, 0xA5, 0x10, 0x83, 0x05, 0x52, 0xA2, 0x85, 0x00, 0xDA, 0x88, 0x45, 0x6C, 0x8C,
0x94, 0xA6, 0x11, 0x18, 0xF8, 0x8C, 0xC4, 0x7C, 0x39, 0x08, 0x4C, 0x10, 0x84, 0x21, 0xC0, 0xFF,
0xE0, 0xE1, 0x08, 0x41, 0x90, 0x84, 0x27, 0x00, 0xE0, 0x70, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0xF0, 0xFE, 0x18, 0x61, 0x86, 0x18, 0x61,
0x87, 0xF0, 0x00, 0xBF, 0x10, 0x9F, 0xA9, 0x4B, 0x4F, 0x10, 0x80, 0x3A, 0x10, 0x8F, 0x21, 0x1F,
0x85, 0xE4, 0x92, 0x7A, 0x10, 0x82, 0x88, 0xA3, 0x61, 0x0F, 0x84, 0x08, 0xF7, 0x80, 0x7C, 0x19,
0x68, 0xB4, 0x41, 0xF0, 0xA0, 0x38, 0xBA, 0x8D, 0x19, 0xE8, 0x8E, 0x00, 0xFF, 0x9F, 0x0F, 0x25,
0x29, 0x12, 0x24, 0xFC, 0x10, 0x40, 0xE0, 0x38, 0xFA, 0xFD, 0x5A, 0x68, 0x8E, 0x00, 0xE0, 0xF7,
0x80, 0x21, 0x3E, 0x42, 0x03, 0xE0, 0xE5, 0x70, 0xE8, 0xF0, 0x50, 0x8A, 0x28, 0xA2, 0x8B, 0xF8,
0x20, 0x7F, 0xDF, 0x7D, 0x74, 0x51, 0x45, 0x14, 0xC0, 0x70, 0xC9, 0x70, 0x69, 0x96, 0x0F, 0x91,
0x22, 0x52, 0x90, 0x60, 0x82, 0x1C, 0x1F, 0x80, 0x8E, 0x3C, 0x20, 0x60, 0x82, 0x1C, 0x1F, 0x81,
0xC1, 0x08, 0x70, 0x70, 0x81, 0x18, 0x1F, 0x80, 0x8E, 0x3C, 0x20, 0x20, 0x22, 0x6C, 0x87, 0x40,
0x80, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x08, 0x40, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18,
0x40, 0x31, 0x20, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x69, 0x60, 0x0C, 0x30, 0xC4, 0x92,
0x7A, 0x18, 0x40, 0x48, 0x03, 0x0C, 0x31, 0x24, 0x9E, 0x86, 0x10, 0x30, 0xC2, 0x08, 0x30, 0xC4,
0x9E, 0x4A, 0x10, 0x3C, 0xC5, 0x17, 0x51, 0xC9, 0x27, 0x39, 0x18, 0x20, 0x82, 0x04, 0x4E, 0x20,
0x80, 0x40, 0x80, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xE0,
0x82, 0x0F, 0xC0, 0x21, 0x40, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x50, 0x0F, 0xE0, 0x83,
0xF8, 0x20, 0x83, 0xF0, 0x41, 0x01, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x08, 0x81, 0xF2, 0x10, 0x84,
0x21, 0x3E, 0x22, 0x81, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x50, 0x3E, 0x42, 0x10, 0x84, 0x27, 0xC0,
0x78, 0x89, 0x0F, 0x14, 0x28, 0x51, 0x3C, 0x69, 0x60, 0x21, 0xC6, 0x9A, 0x65, 0x96, 0x38, 0x40,
0x40, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0x87, 0x37, 0x80, 0x08, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0x87,
0x37, 0x80, 0x31, 0x20, 0x1E, 0xCE, 0x18, 0x61, 0x87, 0x37, 0x80, 0x69, 0x60, 0x1E, 0xCE, 0x18,
0x61, 0x87, 0x37, 0x80, 0x48, 0x07, 0xB3, 0x86, 0x18, 0x61, 0xCD, 0xE0, 0x8A, 0x88, 0xA8, 0x80,
0x3E, 0xCD, 0x1A, 0x55, 0x2C, 0x59, 0xDE, 0x40, 0x80, 0x21, 0x86, 0x18, 0x61, 0x86, 0x17, 0x80,
0x08, 0x40, 0x21, 0x86, 0x18, 0x61, 0x86, 0x17, 0x80, 0x31, 0x20, 0x21, 0x86, 0x18, 0x61, 0x86,
0x17, 0x80, 0x48, 0x08, 0x61, 0x86, 0x18, 0x61, 0x85, 0xE0, 0x08, 0x20, 0x06, 0x34, 0x45, 0x0E,
0x08, 0x10, 0x20, 0x40, 0x83, 0xE8, 0x61, 0x87, 0xE8, 0x20, 0x64, 0xA5, 0x4A, 0x4A, 0x31, 0xB8,
0x20, 0x81, 0xE0, 0xBE, 0x31, 0x78, 0x11, 0x01, 0xE0, 0xBE, 0x31, 0x78, 0x22, 0x81, 0xE0, 0xBE,
0x31, 0x78, 0x6A, 0xC1, 0xE0, 0xBE, 0x31, 0x78, 0x50, 0x3C, 0x17, 0xC6, 0x2F, 0x72, 0x9C, 0x0F,
0x05, 0xF1, 0x8B, 0xC0, 0xD9, 0x7F, 0x4A, 0x6C, 0x7E, 0x21, 0x0C, 0x3C, 0x84, 0x20, 0x80, 0xE8,
0xFE, 0x10, 0x78, 0x18, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x22, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x50,
0x1D, 0x1F, 0xC2, 0x0F, 0x20, 0x81, 0xC2, 0x10, 0x84, 0xF8, 0x11, 0x01, 0xC2, 0x10, 0x84, 0xF8,
0x22, 0x81, 0xC2, 0x10, 0x84, 0xF8, 0x50, 0x38, 0x42, 0x10, 0x9F, 0x43, 0x84, 0xE8, 0xC6, 0x31,
0x70, 0x6A, 0xC1, 0x6C, 0xC6, 0x31, 0x88, 0x20, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x11, 0x00, 0xE8,
0xC6, 0x31, 0x70, 0x22, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0xED, 0xC0, 0xE8, 0xC6, 0x31, 0x70, 0x50,
0x1D, 0x18, 0xC6, 0x2E, 0x20, 0x3E, 0x02, 0x00, 0x3D, 0x15, 0xD9, 0x65, 0xE0, 0x20, 0x81, 0x18,
0xC6, 0x31, 0x78, 0x11, 0x01, 0x18, 0xC6, 0x31, 0x78, 0x22, 0x81, 0x18, 0xC6, 0x31, 0x78, 0x50,
0x23, 0x18, 0xC6, 0x2F, 0x11, 0x01, 0x19, 0x29, 0x4C, 0x22, 0x30, 0x84, 0x21, 0xE8, 0xC6, 0x31,
0xF4, 0x20, 0x50, 0x23, 0x25, 0x29, 0x84, 0x46, 0x00, 0x78, 0x03, 0x0C, 0x31, 0x24, 0x9E, 0x86,
0x10, 0x70, 0x3C, 0x17, 0xC6, 0x2F, 0x48, 0xC0, 0x0C, 0x30, 0xC4, 0x92, 0x7A, 0x18, 0x40, 0x53,
0x81, 0xE0, 0xBE, 0x31, 0x78, 0x30, 0xC3, 0x12, 0x49, 0xE8, 0x61, 0x08, 0x20, 0xF0, 0x5F, 0x18,
0xBC, 0x43, 0x08, 0x20, 0x0E, 0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x08, 0x80, 0xFC, 0x42, 0x18,
0x78, 0x30, 0xC0, 0x0E, 0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x32, 0x80, 0xFC, 0x42, 0x18, 0x78,
0x20, 0x03, 0x91, 0x82, 0x08, 0x20, 0x44, 0xE0, 0x20, 0x1F, 0x88, 0x43, 0x0F, 0x50, 0xC0, 0x0E,
0x46, 0x08, 0x20, 0x81, 0x13, 0x80, 0x51, 0x80, 0xFC, 0x42, 0x18, 0x78, 0x61, 0x80, 0x3C, 0x8A,
0x18, 0x61, 0x86, 0x2F, 0x00, 0x0A, 0x14, 0x23, 0xC8, 0x91, 0x22, 0x44, 0x78, 0x78, 0x89, 0x0F,
0x14, 0x28, 0x51, 0x3C, 0x08, 0xF0, 0x9E, 0x8A, 0x28, 0xA2, 0x78, 0x70, 0x0F, 0xE0, 0x83, 0xF8,
0x20, 0x83, 0xF0, 0x70, 0x1D, 0x1F, 0xC2, 0x0F, 0x50, 0xC0, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F,
0xC0, 0x51, 0x80, 0xE8, 0xFE, 0x10, 0x78, 0x20, 0x0F, 0xE0, 0x83, 0xF8, 0x20, 0x83, 0xF0, 0x20,
0x1D, 0x1F, 0xC2, 0x0F, 0xFE, 0x08, 0x3F, 0x82, 0x08, 0x3F, 0x10, 0x60, 0x74, 0x7F, 0x08, 0x3C,
0x86, 0x50, 0x80, 0x3F, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, 0x51, 0x00, 0xE8, 0xFE, 0x10, 0x78,
0x21, 0x40, 0x0E, 0x46, 0x08, 0x23, 0x85, 0x13, 0x80, 0x22, 0x80, 0xF8, 0xC6, 0x31, 0x78, 0x5C,
0x50, 0xC0, 0x0E, 0x46, 0x08, 0x23, 0x85, 0x13, 0x80, 0x53, 0x80, 0xF8, 0xC6, 0x31, 0x78, 0x5C,
0x20, 0x03, 0x91, 0x82, 0x08, 0xE1, 0x44, 0xE0, 0x20, 0x1F, 0x18, 0xC6, 0x2F, 0x0B, 0x80, 0x39,
0x18, 0x20, 0x8E, 0x14, 0x4E, 0x00, 0x82, 0x00, 0x21, 0x00, 0xF8, 0xC6, 0x31, 0x78, 0x5C, 0x31,
0x20, 0x21, 0x86, 0x1F, 0xE1, 0x86, 0x18, 0x40, 0x22, 0x81, 0x08, 0x42, 0xD9, 0x8C, 0x63, 0x10,
0x42, 0xFF, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x43, 0xC4, 0x1E, 0x45, 0x14, 0x51, 0x44, 0xED,
0xC1, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x6A, 0xC1, 0xC2, 0x10, 0x84, 0xF8, 0x70, 0x3E, 0x42, 0x10,
0x84, 0x27, 0xC0, 0x70, 0x38, 0x42, 0x10, 0x9F, 0x53, 0x81, 0xF2, 0x10, 0x84, 0x21, 0x3E, 0x53,
0x81, 0xC2, 0x10, 0x84, 0xF8, 0xF9, 0x08, 0x42, 0x10, 0x9F, 0x43, 0x00, 0x20, 0x01, 0xC2, 0x10,
0x84, 0xF9, 0x08, 0x20, 0x3E, 0x42, 0x10, 0x84, 0x27, 0xC0, 0xE1, 0x08, 0x42, 0x7C, 0xE6, 0x85,
0x0A, 0x14, 0x28, 0x50, 0xF7, 0x24, 0x00, 0x1F, 0x24, 0x92, 0x49, 0xFC, 0x11, 0xC0, 0x22, 0x80,
0x70, 0x84, 0x21, 0x0C, 0x5C, 0x25, 0x0E, 0x22, 0x22, 0x22, 0xC0, 0x8A, 0x4A, 0x30, 0xA2, 0x48,
0xA1, 0x00, 0x82, 0x00, 0x84, 0x21, 0x2A, 0x62, 0x92, 0x88, 0x0C, 0x40, 0x95, 0x31, 0x49, 0x44,
0x41, 0x00, 0x20, 0x82, 0x08, 0x20, 0x82, 0x0F, 0xC0, 0x08, 0x41, 0xC2, 0x10, 0x84, 0x21, 0x08,
0x30, 0x82, 0x08, 0x20, 0x82, 0x08, 0x3F, 0x00, 0x82, 0x00, 0xE1, 0x08, 0x42, 0x10, 0x84, 0x18,
0x0C, 0x40, 0x92, 0x48, 0x20, 0x82, 0x08, 0x3F, 0xE4, 0x92, 0x08, 0x20, 0x82, 0x08, 0x18, 0x82,
0x08, 0xA2, 0x82, 0x08, 0x3F, 0xE0, 0x40, 0x81, 0x12, 0x24, 0x08, 0x10, 0x18, 0x40, 0x81, 0x63,
0x0C, 0x08, 0x10, 0x3F, 0xE1, 0x0A, 0x66, 0x50, 0x84, 0x18, 0x08, 0x40, 0x21, 0xC6, 0x9A, 0x65,
0x96, 0x38, 0x40, 0x18, 0x81, 0x6C, 0xC6, 0x31, 0x88, 0x87, 0x1A, 0x69, 0x96, 0x58, 0xE1, 0x00,
0x84, 0x00, 0xB6, 0x63, 0x18, 0xC4, 0x04, 0x40, 0x50, 0x80, 0x21, 0xC6, 0x9A, 0x65, 0x96, 0x38,
0x40, 0x51, 0x01, 0x6C, 0xC6, 0x31, 0x88, 0x41, 0x02, 0x01, 0xE2, 0x64, 0x48, 0x91, 0x22, 0xBB,
0x18, 0x61, 0x86, 0x18, 0x61, 0x04, 0x60, 0xB6, 0x63, 0x18, 0xC4, 0x27, 0x78, 0x07, 0xB3, 0x86,
0x18, 0x61, 0xCD, 0xE0, 0x70, 0x1D, 0x18, 0xC6, 0x2E, 0x48, 0xC0, 0x1E, 0xCE, 0x18, 0x61, 0x87,
0x37, 0x80, 0x53, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x50, 0x07, 0xB3, 0x86, 0x18, 0x61, 0xCD, 0xE0,
0x2A, 0x80, 0xE8, 0xC6, 0x31, 0x70, 0x7E, 0x49, 0x27, 0x92, 0x49, 0x1F, 0xDD, 0x6F, 0x4A, 0x6C,
0x10, 0x20, 0x07, 0xC8, 0x50, 0xA1, 0x7C, 0x89, 0x0A, 0x08, 0x18, 0x81, 0xE9, 0x42, 0x10, 0x80,
0xF9, 0x0A, 0x14, 0x2F, 0x91, 0x21, 0x41, 0x00, 0x40, 0x80, 0x7A, 0x50, 0x84, 0x20, 0x08, 0xC0,
0x60, 0xC0, 0x07, 0xC8, 0x50, 0xA1, 0x7C, 0x89, 0x0A, 0x08, 0xA6, 0x0F, 0x98, 0x88, 0x80, 0x08,
0x40, 0x1E, 0x86, 0x0F, 0x03, 0x06, 0x17, 0x80, 0x18, 0x80, 0xF8, 0x70, 0x61, 0xF0, 0x21, 0x40,
0x1E, 0x86, 0x0F, 0x03, 0x06, 0x17, 0x80, 0x22, 0x80, 0xF8, 0x70, 0x61, 0xF0, 0x7A, 0x18, 0x3C,
0x0C, 0x18, 0x5E, 0x21, 0x80, 0x7C, 0x38, 0x30, 0xF8, 0x8C,
};
const GFXglyph UiFont11ptGlyphs[] PROGMEM = {
{ 0, 1, 1, 7, 0, -1 }, // 0x0020
{ 1, 1, 8, 7, 3, -8 }, // 0x0021
{ 2, 3, 3, 7, 2, -8 }, // 0x0022
{ 4, 7, 8, 7, 0, -8 }, // 0x0023
{ 11, 5, 10, 7, 1, -8 }, // 0x0024
{ 18, 6, 8, 7, 0, -8 }, // 0x0025
{ 24, 6, 8, 7, 1, -8 }, // 0x0026
{ 30, 1, 3, 7, 3, -8 }, // 0x0027
{ 31, 2, 10, 7, 2, -9 }, // 0x0028
{ 34, 2, 10, 7, 2, -9 }, // 0x0029
{ 37, 5, 6, 7, 1, -8 }, // 0x002A
{ 41, 5, 5, 7, 1, -6 }, // 0x002B
{ 45, 2, 3, 7, 2, -2 }, // 0x002C
{ 46, 3, 1, 7, 2, -4 }, // 0x002D
{ 47, 1, 2, 7, 3, -2 }, // 0x002E
{ 48, 5, 9, 7, 1, -8 }, // 0x002F
{ 54, 6, 8, 7, 1, -8 }, // 0x0030
{ 60, 5, 8, 7, 1, -8 }, // 0x0031
{ 65, 6, 8, 7, 1, -8 }, // 0x0032
{ 71, 6, 8, 7, 1, -8 }, // 0x0033
{ 77, 6, 8, 7, 1, -8 }, // 0x0034
{ 83, 6, 8, 7, 1, -8 }, // 0x0035
{ 89, 6, 8, 7, 1, -8 }, // 0x0036
{ 95, 6, 8, 7, 1, -8 }, // 0x0037
{ 101, 6, 8, 7, 1, -8 }, // 0x0038
{ 107, 6, 8, 7, 1, -8 }, // 0x0039
{ 113, 1, 6, 7, 3, -6 }, // 0x003A
{ 114, 2, 7, 7, 2, -6 }, // 0x003B
{ 116, 6, 5, 7, 1, -6 }, // 0x003C
{ 120, 6, 3, 7, 0, -5 }, // 0x003D
{ 123, 6, 5, 7, 1, -6 }, // 0x003E
{ 127, 4, 8, 7, 2, -8 }, // 0x003F
{ 131, 6, 10, 7, 1, -8 }, // 0x0040
{ 139, 6, 8, 7, 1, -8 }, // 0x0041
{ 145, 6, 8, 7, 1, -8 }, // 0x0042
{ 151, 6, 8, 7, 1, -8 }, // 0x0043
{ 157, 6, 8, 7, 1, -8 }, // 0x0044
{ 163, 6, 8, 7, 1, -8 }, // 0x0045
{ 169, 6, 8, 7, 1, -8 }, // 0x0046
{ 175, 6, 8, 7, 1, -8 }, // 0x0047
{ 181, 6, 8, 7, 1, -8 }, // 0x0048
{ 187, 5, 8, 7, 1, -8 }, // 0x0049
{ 192, 5, 8, 7, 1, -8 }, // 0x004A
{ 197, 6, 8, 7, 1, -8 }, // 0x004B
{ 203, 6, 8, 7, 1, -8 }, // 0x004C
{ 209, 6, 8, 7, 1, -8 }, // 0x004D
{ 215, 6, 8, 7, 1, -8 }, // 0x004E
{ 221, 6, 8, 7, 1, -8 }, // 0x004F
{ 227, 6, 8, 7, 1, -8 }, // 0x0050
{ 233, 6, 9, 7, 1, -8 }, // 0x0051
{ 240, 7, 8, 7, 1, -8 }, // 0x0052
{ 247, 6, 8, 7, 1, -8 }, // 0x0053
{ 253, 7, 8, 7, 0, -8 }, // 0x0054
{ 260, 6, 8, 7, 1, -8 }, // 0x0055
{ 266, 6, 8, 7, 1, -8 }, // 0x0056
{ 272, 7, 8, 7, 0, -8 }, // 0x0057
{ 279, 6, 8, 7, 1, -8 }, // 0x0058
{ 285, 7, 8, 7, 0, -8 }, // 0x0059
{ 292, 6, 8, 7, 1, -8 }, // 0x005A
{ 298, 2, 10, 7, 2, -9 }, // 0x005B
{ 301, 5, 9, 7, 1, -8 }, // 0x005C
{ 307, 2, 10, 7, 2, -9 }, // 0x005D
{ 310, 6, 3, 7, 0, -8 }, // 0x005E
{ 313, 7, 1, 7, 0, 2 }, // 0x005F
{ 314, 3, 2, 7, 2, -9 }, // 0x0060
{ 315, 5, 6, 7, 1, -6 }, // 0x0061
{ 319, 5, 9, 7, 1, -9 }, // 0x0062
{ 325, 5, 6, 7, 1, -6 }, // 0x0063
{ 329, 5, 9, 7, 1, -9 }, // 0x0064
{ 335, 5, 6, 7, 1, -6 }, // 0x0065
{ 339, 5, 9, 7, 1, -9 }, // 0x0066
{ 345, 5, 8, 7, 1, -6 }, // 0x0067
{ 350, 5, 9, 7, 1, -9 }, // 0x0068
{ 356, 5, 9, 7, 1, -9 }, // 0x0069
{ 362, 3, 11, 7, 1, -9 }, // 0x006A
{ 367, 5, 9, 7, 1, -9 }, // 0x006B
{ 373, 5, 9, 7, 0, -9 }, // 0x006C
{ 379, 5, 6, 7, 1, -6 }, // 0x006D
{ 383, 5, 6, 7, 1, -6 }, // 0x006E
{ 387, 5, 6, 7, 1, -6 }, // 0x006F
{ 391, 5, 8, 7, 1, -6 }, // 0x0070
{ 396, 5, 8, 7, 1, -6 }, // 0x0071
{ 401, 4, 6, 7, 2, -6 }, // 0x0072
{ 404, 5, 6, 7, 1, -6 }, // 0x0073
{ 408, 5, 8, 7, 0, -8 }, // 0x0074
{ 413, 5, 6, 7, 1, -6 }, // 0x0075
{ 417, 5, 6, 7, 1, -6 }, // 0x0076
{ 421, 7, 6, 7, 0, -6 }, // 0x0077
{ 427, 5, 6, 7, 1, -6 }, // 0x0078
{ 431, 5, 8, 7, 1, -6 }, // 0x0079
{ 436, 5, 6, 7, 1, -6 }, // 0x007A
{ 440, 5, 10, 7, 1, -9 }, // 0x007B
{ 447, 1, 11, 7, 3, -9 }, // 0x007C
{ 449, 5, 10, 7, 1, -9 }, // 0x007D
{ 456, 6, 2, 7, 1, -4 }, // 0x007E
{ 458, 6, 10, 7, 1, -8 }, // 0x007F
{ 466, 6, 10, 7, 1, -8 }, // 0x0080
{ 474, 6, 10, 7, 1, -8 }, // 0x0081
{ 482, 6, 10, 7, 1, -8 }, // 0x0082
{ 490, 6, 10, 7, 1, -8 }, // 0x0083
{ 498, 6, 10, 7, 1, -8 }, // 0x0084
{ 506, 6, 10, 7, 1, -8 }, // 0x0085
{ 514, 6, 10, 7, 1, -8 }, // 0x0086
{ 522, 6, 10, 7, 1, -8 }, // 0x0087
{ 530, 6, 10, 7, 1, -8 }, // 0x0088
{ 538, 6, 10, 7, 1, -8 }, // 0x0089
{ 546, 6, 10, 7, 1, -8 }, // 0x008A
{ 554, 6, 10, 7, 1, -8 }, // 0x008B
{ 562, 6, 10, 7, 1, -8 }, // 0x008C
{ 570, 6, 10, 7, 1, -8 }, // 0x008D
{ 578, 6, 10, 7, 1, -8 }, // 0x008E
{ 586, 6, 10, 7, 1, -8 }, // 0x008F
{ 594, 6, 10, 7, 1, -8 }, // 0x0090
{ 602, 6, 10, 7, 1, -8 }, // 0x0091
{ 610, 6, 10, 7, 1, -8 }, // 0x0092
{ 618, 6, 10, 7, 1, -8 }, // 0x0093
{ 626, 6, 10, 7, 1, -8 }, // 0x0094
{ 634, 6, 10, 7, 1, -8 }, // 0x0095
{ 642, 6, 10, 7, 1, -8 }, // 0x0096
{ 650, 6, 10, 7, 1, -8 }, // 0x0097
{ 658, 6, 10, 7, 1, -8 }, // 0x0098
{ 666, 6, 10, 7, 1, -8 }, // 0x0099
{ 674, 6, 10, 7, 1, -8 }, // 0x009A
{ 682, 6, 10, 7, 1, -8 }, // 0x009B
{ 690, 6, 10, 7, 1, -8 }, // 0x009C
{ 698, 6, 10, 7, 1, -8 }, // 0x009D
{ 706, 6, 10, 7, 1, -8 }, // 0x009E
{ 714, 6, 10, 7, 1, -8 }, // 0x009F
{ 722, 1, 1, 7, 0, -1 }, // 0x00A0
{ 723, 1, 8, 7, 3, -6 }, // 0x00A1
{ 724, 5, 10, 7, 1, -8 }, // 0x00A2
{ 731, 5, 8, 7, 1, -8 }, // 0x00A3
{ 736, 6, 6, 7, 1, -6 }, // 0x00A4
{ 741, 7, 8, 7, 0, -8 }, // 0x00A5
{ 748, 1, 9, 7, 3, -8 }, // 0x00A6
{ 750, 5, 9, 7, 1, -8 }, // 0x00A7
{ 756, 3, 1, 7, 2, -8 }, // 0x00A8
{ 757, 7, 7, 7, 0, -7 }, // 0x00A9
{ 764, 4, 6, 7, 1, -8 }, // 0x00AA
{ 767, 6, 5, 7, 1, -6 }, // 0x00AB
{ 771, 6, 3, 7, 1, -5 }, // 0x00AC
{ 774, 3, 1, 7, 2, -4 }, // 0x00AD
{ 775, 7, 7, 7, 0, -7 }, // 0x00AE
{ 782, 3, 1, 7, 2, -8 }, // 0x00AF
{ 783, 3, 3, 7, 2, -8 }, // 0x00B0
{ 785, 5, 7, 7, 1, -7 }, // 0x00B1
{ 790, 3, 4, 7, 2, -8 }, // 0x00B2
{ 792, 3, 4, 7, 2, -8 }, // 0x00B3
{ 794, 3, 2, 7, 3, -9 }, // 0x00B4
{ 795, 6, 8, 7, 1, -6 }, // 0x00B5
{ 801, 6, 9, 7, 1, -8 }, // 0x00B6
{ 808, 1, 2, 7, 3, -5 }, // 0x00B7
{ 809, 2, 2, 7, 2, 0 }, // 0x00B8
{ 810, 3, 4, 7, 2, -8 }, // 0x00B9
{ 812, 4, 6, 7, 1, -8 }, // 0x00BA
{ 815, 6, 5, 7, 1, -6 }, // 0x00BB
{ 819, 6, 10, 7, 0, -9 }, // 0x00BC
{ 827, 6, 10, 7, 0, -9 }, // 0x00BD
{ 835, 6, 10, 7, 0, -9 }, // 0x00BE
{ 843, 4, 8, 7, 1, -6 }, // 0x00BF
{ 847, 6, 11, 7, 1, -11 }, // 0x00C0
{ 856, 6, 11, 7, 1, -11 }, // 0x00C1
{ 865, 6, 11, 7, 1, -11 }, // 0x00C2
{ 874, 6, 11, 7, 1, -11 }, // 0x00C3
{ 883, 6, 10, 7, 1, -10 }, // 0x00C4
{ 891, 6, 10, 7, 1, -10 }, // 0x00C5
{ 899, 6, 8, 7, 0, -8 }, // 0x00C6
{ 905, 6, 10, 7, 1, -8 }, // 0x00C7
{ 913, 6, 11, 7, 1, -11 }, // 0x00C8
{ 922, 6, 11, 7, 1, -11 }, // 0x00C9
{ 931, 6, 11, 7, 1, -11 }, // 0x00CA
{ 940, 6, 10, 7, 1, -10 }, // 0x00CB
{ 948, 5, 11, 7, 1, -11 }, // 0x00CC
{ 955, 5, 11, 7, 1, -11 }, // 0x00CD
{ 962, 5, 11, 7, 1, -11 }, // 0x00CE
{ 969, 5, 10, 7, 1, -10 }, // 0x00CF
{ 976, 7, 8, 7, 0, -8 }, // 0x00D0
{ 983, 6, 11, 7, 1, -11 }, // 0x00D1
{ 992, 6, 11, 7, 1, -11 }, // 0x00D2
{ 1001, 6, 11, 7, 1, -11 }, // 0x00D3
{ 1010, 6, 11, 7, 1, -11 }, // 0x00D4
{ 1019, 6, 11, 7, 1, -11 }, // 0x00D5
{ 1028, 6, 10, 7, 1, -10 }, // 0x00D6
{ 1036, 5, 5, 7, 1, -6 }, // 0x00D7
{ 1040, 7, 8, 7, 0, -8 }, // 0x00D8
{ 1047, 6, 11, 7, 1, -11 }, // 0x00D9
{ 1056, 6, 11, 7, 1, -11 }, // 0x00DA
{ 1065, 6, 11, 7, 1, -11 }, // 0x00DB
{ 1074, 6, 10, 7, 1, -10 }, // 0x00DC
{ 1082, 7, 11, 7, 0, -11 }, // 0x00DD
{ 1092, 6, 8, 7, 1, -8 }, // 0x00DE
{ 1098, 5, 9, 7, 1, -9 }, // 0x00DF
{ 1104, 5, 9, 7, 1, -9 }, // 0x00E0
{ 1110, 5, 9, 7, 1, -9 }, // 0x00E1
{ 1116, 5, 9, 7, 1, -9 }, // 0x00E2
{ 1122, 5, 9, 7, 1, -9 }, // 0x00E3
{ 1128, 5, 8, 7, 1, -8 }, // 0x00E4
{ 1133, 5, 10, 7, 1, -10 }, // 0x00E5
{ 1140, 5, 6, 7, 1, -6 }, // 0x00E6
{ 1144, 5, 8, 7, 1, -6 }, // 0x00E7
{ 1149, 5, 9, 7, 1, -9 }, // 0x00E8
{ 1155, 5, 9, 7, 1, -9 }, // 0x00E9
{ 1161, 5, 9, 7, 1, -9 }, // 0x00EA
{ 1167, 5, 8, 7, 1, -8 }, // 0x00EB
{ 1172, 5, 9, 7, 1, -9 }, // 0x00EC
{ 1178, 5, 9, 7, 1, -9 }, // 0x00ED
{ 1184, 5, 9, 7, 1, -9 }, // 0x00EE
{ 1190, 5, 8, 7, 1, -8 }, // 0x00EF
{ 1195, 5, 9, 7, 1, -9 }, // 0x00F0
{ 1201, 5, 9, 7, 1, -9 }, // 0x00F1
{ 1207, 5, 9, 7, 1, -9 }, // 0x00F2
{ 1213, 5, 9, 7, 1, -9 }, // 0x00F3
{ 1219, 5, 9, 7, 1, -9 }, // 0x00F4
{ 1225, 5, 9, 7, 1, -9 }, // 0x00F5
{ 1231, 5, 8, 7, 1, -8 }, // 0x00F6
{ 1236, 5, 5, 7, 1, -6 }, // 0x00F7
{ 1240, 6, 6, 7, 0, -6 }, // 0x00F8
{ 1245, 5, 9, 7, 1, -9 }, // 0x00F9
{ 1251, 5, 9, 7, 1, -9 }, // 0x00FA
{ 1257, 5, 9, 7, 1, -9 }, // 0x00FB
{ 1263, 5, 8, 7, 1, -8 }, // 0x00FC
{ 1268, 5, 11, 7, 1, -9 }, // 0x00FD
{ 1275, 5, 11, 7, 1, -9 }, // 0x00FE
{ 1282, 5, 10, 7, 1, -8 }, // 0x00FF
{ 1289, 6, 10, 7, 1, -10 }, // 0x0100
{ 1297, 5, 8, 7, 1, -8 }, // 0x0101
{ 1302, 6, 11, 7, 1, -11 }, // 0x0102
{ 1311, 5, 9, 7, 1, -9 }, // 0x0103
{ 1317, 6, 10, 7, 1, -8 }, // 0x0104
{ 1325, 5, 8, 7, 1, -6 }, // 0x0105
{ 1330, 6, 11, 7, 1, -11 }, // 0x0106
{ 1339, 5, 9, 7, 1, -9 }, // 0x0107
{ 1345, 6, 11, 7, 1, -11 }, // 0x0108
{ 1354, 5, 9, 7, 1, -9 }, // 0x0109
{ 1360, 6, 10, 7, 1, -10 }, // 0x010A
{ 1368, 5, 8, 7, 1, -8 }, // 0x010B
{ 1373, 6, 11, 7, 1, -11 }, // 0x010C
{ 1382, 5, 9, 7, 1, -9 }, // 0x010D
{ 1388, 6, 11, 7, 1, -11 }, // 0x010E
{ 1397, 7, 9, 7, 1, -9 }, // 0x010F
{ 1405, 7, 8, 7, 0, -8 }, // 0x0110
{ 1412, 6, 9, 7, 1, -9 }, // 0x0111
{ 1419, 6, 10, 7, 1, -10 }, // 0x0112
{ 1427, 5, 8, 7, 1, -8 }, // 0x0113
{ 1432, 6, 11, 7, 1, -11 }, // 0x0114
{ 1441, 5, 9, 7, 1, -9 }, // 0x0115
{ 1447, 6, 10, 7, 1, -10 }, // 0x0116
{ 1455, 5, 8, 7, 1, -8 }, // 0x0117
{ 1460, 6, 10, 7, 1, -8 }, // 0x0118
{ 1468, 5, 8, 7, 1, -6 }, // 0x0119
{ 1473, 6, 11, 7, 1, -11 }, // 0x011A
{ 1482, 5, 9, 7, 1, -9 }, // 0x011B
{ 1488, 6, 11, 7, 1, -11 }, // 0x011C
{ 1497, 5, 11, 7, 1, -9 }, // 0x011D
{ 1504, 6, 11, 7, 1, -11 }, // 0x011E
{ 1513, 5, 11, 7, 1, -9 }, // 0x011F
{ 1520, 6, 10, 7, 1, -10 }, // 0x0120
{ 1528, 5, 10, 7, 1, -8 }, // 0x0121
{ 1535, 6, 11, 7, 1, -8 }, // 0x0122
{ 1544, 5, 11, 7, 1, -9 }, // 0x0123
{ 1551, 6, 11, 7, 1, -11 }, // 0x0124
{ 1560, 5, 12, 7, 1, -12 }, // 0x0125
{ 1568, 8, 8, 7, 0, -8 }, // 0x0126
{ 1576, 6, 9, 7, 0, -9 }, // 0x0127
{ 1583, 5, 11, 7, 1, -11 }, // 0x0128
{ 1590, 5, 9, 7, 1, -9 }, // 0x0129
{ 1596, 5, 10, 7, 1, -10 }, // 0x012A
{ 1603, 5, 8, 7, 1, -8 }, // 0x012B
{ 1608, 5, 11, 7, 1, -11 }, // 0x012C
{ 1615, 5, 9, 7, 1, -9 }, // 0x012D
{ 1621, 5, 10, 7, 1, -8 }, // 0x012E
{ 1628, 5, 11, 7, 1, -9 }, // 0x012F
{ 1635, 5, 10, 7, 1, -10 }, // 0x0130
{ 1642, 5, 6, 7, 1, -6 }, // 0x0131
{ 1646, 7, 8, 7, 0, -8 }, // 0x0132
{ 1653, 6, 11, 7, 0, -9 }, // 0x0133
{ 1662, 5, 11, 7, 1, -11 }, // 0x0134
{ 1669, 4, 11, 7, 1, -9 }, // 0x0135
{ 1675, 6, 11, 7, 1, -8 }, // 0x0136
{ 1684, 5, 12, 7, 1, -9 }, // 0x0137
{ 1692, 5, 6, 7, 1, -6 }, // 0x0138
{ 1696, 6, 11, 7, 1, -11 }, // 0x0139
{ 1705, 5, 12, 7, 0, -12 }, // 0x013A
{ 1713, 6, 11, 7, 1, -8 }, // 0x013B
{ 1722, 5, 12, 7, 0, -9 }, // 0x013C
{ 1730, 6, 8, 7, 1, -8 }, // 0x013D
{ 1736, 6, 9, 7, 0, -9 }, // 0x013E
{ 1743, 6, 8, 7, 1, -8 }, // 0x013F
{ 1749, 7, 9, 7, 0, -9 }, // 0x0140
{ 1757, 7, 8, 7, 0, -8 }, // 0x0141
{ 1764, 5, 9, 7, 0, -9 }, // 0x0142
{ 1770, 6, 11, 7, 1, -11 }, // 0x0143
{ 1779, 5, 9, 7, 1, -9 }, // 0x0144
{ 1785, 6, 11, 7, 1, -8 }, // 0x0145
{ 1794, 5, 9, 7, 1, -6 }, // 0x0146
{ 1800, 6, 11, 7, 1, -11 }, // 0x0147
{ 1809, 5, 9, 7, 1, -9 }, // 0x0148
{ 1815, 7, 9, 7, 0, -9 }, // 0x0149
{ 1823, 6, 10, 7, 1, -8 }, // 0x014A
{ 1831, 5, 8, 7, 1, -6 }, // 0x014B
{ 1836, 6, 10, 7, 1, -10 }, // 0x014C
{ 1844, 5, 8, 7, 1, -8 }, // 0x014D
{ 1849, 6, 11, 7, 1, -11 }, // 0x014E
{ 1858, 5, 9, 7, 1, -9 }, // 0x014F
{ 1864, 6, 10, 7, 1, -10 }, // 0x0150
{ 1872, 5, 9, 7, 1, -9 }, // 0x0151
{ 1878, 6, 8, 7, 1, -8 }, // 0x0152
{ 1884, 5, 6, 7, 1, -6 }, // 0x0153
{ 1888, 7, 11, 7, 1, -11 }, // 0x0154
{ 1898, 5, 9, 7, 2, -9 }, // 0x0155
{ 1904, 7, 11, 7, 1, -8 }, // 0x0156
{ 1914, 5, 9, 7, 1, -6 }, // 0x0157
{ 1920, 7, 11, 7, 1, -11 }, // 0x0158
{ 1930, 4, 9, 7, 2, -9 }, // 0x0159
{ 1935, 6, 11, 7, 1, -11 }, // 0x015A
{ 1944, 5, 9, 7, 1, -9 }, // 0x015B
{ 1950, 6, 11, 7, 1, -11 }, // 0x015C
{ 1959, 5, 9, 7, 1, -9 }, // 0x015D
{ 1965, 6, 10, 7, 1, -8 }, // 0x015E
{ 1973, 5, 8, 7, 1, -6 }, // 0x015F
};
const GFXfont UiFont11pt PROGMEM = {
(uint8_t *)UiFont11ptBitmaps,
(GFXglyph *)UiFont11ptGlyphs,
0x0020, 0x015F, 17
};

View File

@@ -14,11 +14,13 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool active = false) {
uint16_t bg = active ? TFT_GREEN : TFT_BLACK;
uint16_t fg = active ? TFT_BLACK : TFT_GREEN;
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -40,7 +42,7 @@ void run(TFT_eSPI& tft) {
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::UNITS_TITLE)); tft.println(I18n::t(StringId::UNITS_TITLE));
@@ -49,8 +51,7 @@ void run(TFT_eSPI& tft) {
for (uint8_t i = 0; i < MODE_COUNT; i++) { for (uint8_t i = 0; i < MODE_COUNT; i++) {
modeRects[i] = rowRect(i); modeRects[i] = rowRect(i);
drawButton(tft, modeRects[i], I18n::t(labels[i]), drawButton(tft, modeRects[i], I18n::t(labels[i]), i == current);
(i == current) ? TFT_DARKGREEN : TFT_NAVY);
} }
Rect backBtn = rowRect(MODE_COUNT); Rect backBtn = rowRect(MODE_COUNT);

View File

@@ -3,6 +3,7 @@
#include "wifi_setup_screen.h" #include "wifi_setup_screen.h"
#include "touch_input.h" #include "touch_input.h"
#include "config.h" #include "config.h"
#include "i18n.h"
namespace WifiManageScreen { namespace WifiManageScreen {
@@ -14,11 +15,12 @@ namespace {
} }
}; };
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, uint16_t bg = TFT_NAVY) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool danger = false) {
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); uint16_t accent = danger ? TFT_RED : TFT_GREEN;
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, TFT_BLACK);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(accent, TFT_BLACK);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -32,9 +34,9 @@ void run(TFT_eSPI& tft) {
bool done = false; bool done = false;
while (!done) { while (!done) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println("WiFi networks"); tft.println(I18n::t(StringId::WIFI_NETWORKS_TITLE));
uint8_t count = WifiMgr::networkCount(); uint8_t count = WifiMgr::networkCount();
int16_t y = 40; int16_t y = 40;
@@ -50,13 +52,13 @@ void run(TFT_eSPI& tft) {
if (i < count) { if (i < count) {
drawButton(tft, rowRect, WifiMgr::networkSsid(i)); drawButton(tft, rowRect, WifiMgr::networkSsid(i));
drawButton(tft, removeRect, "X", TFT_MAROON); drawButton(tft, removeRect, "X", true);
} else { } else {
tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK); tft.fillRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_BLACK);
tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREY); tft.drawRoundRect(rowRect.x, rowRect.y, rowRect.w, rowRect.h, 4, TFT_DARKGREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_DARKGREY, TFT_BLACK); tft.setTextColor(TFT_DARKGREEN, TFT_BLACK);
tft.drawString("(empty)", rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2); tft.drawString(I18n::t(StringId::WIFI_EMPTY_SLOT), rowRect.x + rowRect.w / 2, rowRect.y + rowRect.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
y += ROW_H + ROW_GAP; y += ROW_H + ROW_GAP;
@@ -65,12 +67,12 @@ void run(TFT_eSPI& tft) {
Rect addBtn = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), 40}; Rect addBtn = {10, y, (int16_t)(Config::SCREEN_WIDTH - 20), 40};
bool canAdd = count < Config::MAX_WIFI_NETWORKS; bool canAdd = count < Config::MAX_WIFI_NETWORKS;
if (canAdd) { if (canAdd) {
drawButton(tft, addBtn, "Add network"); drawButton(tft, addBtn, I18n::t(StringId::WIFI_ADD_NETWORK));
} }
y += 48; y += 48;
Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40}; Rect backBtn = {10, (int16_t)(Config::SCREEN_HEIGHT - 50), (int16_t)(Config::SCREEN_WIDTH - 20), 40};
drawButton(tft, backBtn, "Back"); drawButton(tft, backBtn, I18n::t(StringId::BACK));
TouchInput::Point tap; TouchInput::Point tap;
while (true) { while (true) {

View File

@@ -78,10 +78,6 @@ namespace {
char passwordBuf[64] = {0}; char passwordBuf[64] = {0};
uint8_t passwordLen = 0; uint8_t passwordLen = 0;
// Shift-Verhalten wie am Handy: einmal antippen = NUR der naechste
// Buchstabe wird gross geschrieben, danach automatisch wieder klein.
// Zweimal schnell hintereinander antippen (Doppeltipp) = Feststelltaste
// (bleibt an, bis erneut einmal angetippt wird).
enum class ShiftState { Off, OneShot, Locked }; enum class ShiftState { Off, OneShot, Locked };
ShiftState shiftState = ShiftState::Off; ShiftState shiftState = ShiftState::Off;
uint32_t lastShiftTapMs = 0; uint32_t lastShiftTapMs = 0;
@@ -95,11 +91,12 @@ namespace {
Rect cancelBtn = {Config::SCREEN_WIDTH - 34, 4, 30, 24}; Rect cancelBtn = {Config::SCREEN_WIDTH - 34, 4, 30, 24};
void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool highlighted = false) { void drawButton(TFT_eSPI& tft, const Rect& r, const String& label, bool highlighted = false) {
uint16_t bg = highlighted ? TFT_DARKGREEN : TFT_NAVY; uint16_t bg = highlighted ? TFT_GREEN : TFT_BLACK;
uint16_t fg = highlighted ? TFT_BLACK : TFT_GREEN;
tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg); tft.fillRoundRect(r.x, r.y, r.w, r.h, 4, bg);
tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_DARKGREY); tft.drawRoundRect(r.x, r.y, r.w, r.h, 4, TFT_GREEN);
tft.setTextDatum(MC_DATUM); tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_WHITE, bg); tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2); tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM); tft.setTextDatum(TL_DATUM);
} }
@@ -134,9 +131,6 @@ namespace {
bool upper = (shiftState != ShiftState::Off); bool upper = (shiftState != ShiftState::Off);
passwordBuf[passwordLen++] = upper ? (char)toupper(k.ch) : k.ch; passwordBuf[passwordLen++] = upper ? (char)toupper(k.ch) : k.ch;
passwordBuf[passwordLen] = 0; passwordBuf[passwordLen] = 0;
// Nach genau einem Buchstaben automatisch wieder auf
// klein zurueckfallen - AUSSER die Feststelltaste
// (Doppeltipp) ist aktiv, die bleibt an.
if (shiftState == ShiftState::OneShot) shiftState = ShiftState::Off; if (shiftState == ShiftState::OneShot) shiftState = ShiftState::Off;
} }
break; break;
@@ -145,13 +139,10 @@ namespace {
bool isDoubleTap = (nowMs - lastShiftTapMs) <= SHIFT_DOUBLE_TAP_MS; bool isDoubleTap = (nowMs - lastShiftTapMs) <= SHIFT_DOUBLE_TAP_MS;
lastShiftTapMs = nowMs; lastShiftTapMs = nowMs;
if (isDoubleTap) { if (isDoubleTap) {
// Doppeltipp: Feststelltaste an-/ausschalten.
shiftState = (shiftState == ShiftState::Locked) ? ShiftState::Off : ShiftState::Locked; shiftState = (shiftState == ShiftState::Locked) ? ShiftState::Off : ShiftState::Locked;
} else if (shiftState == ShiftState::Off) { } else if (shiftState == ShiftState::Off) {
// Einfacher Tipp: nur der naechste Buchstabe wird gross.
shiftState = ShiftState::OneShot; shiftState = ShiftState::OneShot;
} else { } else {
// Einfacher Tipp waehrend OneShot/Locked schaltet ab.
shiftState = ShiftState::Off; shiftState = ShiftState::Off;
} }
break; break;
@@ -208,7 +199,7 @@ bool run(TFT_eSPI& tft) {
WifiMgr::beginScan(); WifiMgr::beginScan();
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setTextSize(1); tft.setTextSize(1);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.println(I18n::t(StringId::WIFI_SCANNING)); tft.println(I18n::t(StringId::WIFI_SCANNING));
@@ -231,7 +222,7 @@ bool run(TFT_eSPI& tft) {
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println(ssidCount == 0 ? I18n::t(StringId::WIFI_NO_NETWORKS) : I18n::t(StringId::WIFI_SELECT)); tft.println(ssidCount == 0 ? I18n::t(StringId::WIFI_NO_NETWORKS) : I18n::t(StringId::WIFI_SELECT));
drawCancelButton(tft); drawCancelButton(tft);
} }
@@ -244,7 +235,7 @@ bool run(TFT_eSPI& tft) {
stage = Stage::Done; stage = Stage::Done;
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println(I18n::t(StringId::WIFI_CONNECTED_BANG)); tft.println(I18n::t(StringId::WIFI_CONNECTED_BANG));
delay(900); delay(900);
return true; return true;
@@ -254,7 +245,7 @@ bool run(TFT_eSPI& tft) {
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.setTextColor(TFT_RED, TFT_BLACK); tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println(I18n::t(StringId::WIFI_CONNECTION_FAILED)); tft.println(I18n::t(StringId::WIFI_CONNECTION_FAILED));
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 30); tft.setCursor(10, 30);
tft.println(I18n::t(StringId::WIFI_BACK_TO_LIST_MSG)); tft.println(I18n::t(StringId::WIFI_BACK_TO_LIST_MSG));
delay(1400); delay(1400);
@@ -319,7 +310,7 @@ bool run(TFT_eSPI& tft) {
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.setTextColor(TFT_RED, TFT_BLACK); tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println(I18n::t(StringId::WIFI_ALREADY_3)); tft.println(I18n::t(StringId::WIFI_ALREADY_3));
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 30); tft.setCursor(10, 30);
tft.println(I18n::t(StringId::WIFI_REMOVE_ONE_FIRST)); tft.println(I18n::t(StringId::WIFI_REMOVE_ONE_FIRST));
delay(1600); delay(1600);
@@ -330,7 +321,7 @@ bool run(TFT_eSPI& tft) {
stage = Stage::Connecting; stage = Stage::Connecting;
tft.fillScreen(TFT_BLACK); tft.fillScreen(TFT_BLACK);
tft.setCursor(10, 10); tft.setCursor(10, 10);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println(I18n::t(StringId::WIFI_CONNECTING)); tft.println(I18n::t(StringId::WIFI_CONNECTING));
} else if (backBtn.contains(tap.x, tap.y)) { } else if (backBtn.contains(tap.x, tap.y)) {
stage = Stage::PickSsid; stage = Stage::PickSsid;
@@ -363,16 +354,16 @@ bool run(TFT_eSPI& tft) {
drawCancelButton(tft); drawCancelButton(tft);
} else if (stage == Stage::EnterPassword) { } else if (stage == Stage::EnterPassword) {
tft.fillRect(0, 0, Config::SCREEN_WIDTH, KB_TOP - 4, TFT_BLACK); tft.fillRect(0, 0, Config::SCREEN_WIDTH, KB_TOP - 4, TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 6); tft.setCursor(10, 6);
tft.printf("%s%s", I18n::t(StringId::WIFI_LABEL_PREFIX), ssidList[selectedIndex].c_str()); tft.printf("%s%s", I18n::t(StringId::WIFI_LABEL_PREFIX), ssidList[selectedIndex].c_str());
tft.setCursor(10, 22); tft.setCursor(10, 22);
tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println(I18n::t(StringId::WIFI_PASSWORD_LABEL)); tft.println(I18n::t(StringId::WIFI_PASSWORD_LABEL));
tft.fillRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_NAVY); tft.fillRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_BLACK);
tft.drawRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_DARKGREY); tft.drawRect(8, 38, Config::SCREEN_WIDTH - 16, 22, TFT_GREEN);
tft.setCursor(12, 44); tft.setCursor(12, 44);
tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.print(passwordBuf); tft.print(passwordBuf);
int16_t rowY0 = KB_TOP; int16_t rowY0 = KB_TOP;