diff --git a/src/airline_filter_screen.cpp b/src/airline_filter_screen.cpp index a0bbd82..e231455 100644 --- a/src/airline_filter_screen.cpp +++ b/src/airline_filter_screen.cpp @@ -1,6 +1,7 @@ #include "airline_filter_screen.h" #include "airline_filter.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -25,6 +26,7 @@ namespace { } String runLetterKeypad(TFT_eSPI& tft) { + MenuStars::reset(); constexpr const char* ROW1 = "QWERTYUIOP"; constexpr const char* ROW2 = "ASDFGHJKL"; constexpr const char* ROW3 = "ZXCVBNM"; @@ -85,7 +87,7 @@ namespace { while (!done) { TouchInput::Point tap; - if (!TouchInput::wasTapped(tap)) { delay(20); continue; } + if (!TouchInput::wasTapped(tap)) { MenuStars::update(tft); delay(20); continue; } bool handled = false; for (uint8_t i = 0; i < 10 && !handled; i++) { @@ -125,6 +127,7 @@ void run(TFT_eSPI& tft) { constexpr int16_t REMOVE_BTN_W = 60; bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -172,6 +175,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/calibration_screen.cpp b/src/calibration_screen.cpp index 6ae0c4e..50f08b6 100644 --- a/src/calibration_screen.cpp +++ b/src/calibration_screen.cpp @@ -1,5 +1,6 @@ #include "calibration_screen.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -21,10 +22,11 @@ namespace { tft.fillCircle(x, y, 3, TFT_WHITE); } - RawAvg waitForTap() { + RawAvg waitForTap(TFT_eSPI& tft) { RawAvg avg; while (!TouchInput::rawPoint().touched) { + MenuStars::update(tft); delay(10); } @@ -73,7 +75,8 @@ void run(TFT_eSPI& tft) { tft.setTextDatum(TL_DATUM); drawTarget(tft, targets[i].x, targets[i].y); - samples[i] = waitForTap(); + MenuStars::reset(); + samples[i] = waitForTap(tft); } int16_t xmin = (samples[0].avgX() + samples[3].avgX()) / 2; diff --git a/src/first_run_language_screen.cpp b/src/first_run_language_screen.cpp index daad2a3..4cb5b46 100644 --- a/src/first_run_language_screen.cpp +++ b/src/first_run_language_screen.cpp @@ -1,6 +1,7 @@ #include "first_run_language_screen.h" #include "settings_store.h" #include "touch_input.h" +#include "menu_stars.h" #include "i18n.h" #include "config.h" @@ -45,9 +46,10 @@ void run(TFT_eSPI& tft) { drawButton(tft, langRects[i], I18n::languageName(i)); } + MenuStars::reset(); while (true) { TouchInput::Point tap; - if (!TouchInput::wasTapped(tap)) { delay(20); continue; } + if (!TouchInput::wasTapped(tap)) { MenuStars::update(tft); delay(20); continue; } for (uint8_t i = 0; i < I18n::LANG_COUNT; i++) { if (langRects[i].contains(tap.x, tap.y)) { diff --git a/src/language_screen.cpp b/src/language_screen.cpp index c16501c..98dede7 100644 --- a/src/language_screen.cpp +++ b/src/language_screen.cpp @@ -1,6 +1,7 @@ #include "language_screen.h" #include "settings_store.h" #include "touch_input.h" +#include "menu_stars.h" #include "i18n.h" #include "config.h" @@ -37,6 +38,7 @@ namespace { void run(TFT_eSPI& tft) { bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -57,6 +59,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/location_presets_screen.cpp b/src/location_presets_screen.cpp index 5f2bfbb..36e4ade 100644 --- a/src/location_presets_screen.cpp +++ b/src/location_presets_screen.cpp @@ -1,6 +1,7 @@ #include "location_presets_screen.h" #include "location_presets.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -28,6 +29,7 @@ namespace { } String runNumericKeypad(TFT_eSPI& tft, const String& title) { + MenuStars::reset(); char buf[16] = {0}; uint8_t len = 0; @@ -79,7 +81,7 @@ namespace { while (!done) { TouchInput::Point tap; - if (!TouchInput::wasTapped(tap)) { delay(20); continue; } + if (!TouchInput::wasTapped(tap)) { MenuStars::update(tft); delay(20); continue; } bool handled = false; for (uint8_t i = 0; i < 12 && !handled; i++) { @@ -131,6 +133,7 @@ void run(TFT_eSPI& tft) { constexpr int16_t REMOVE_BTN_W = 60; bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -188,6 +191,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/logbook_files_screen.cpp b/src/logbook_files_screen.cpp index 8a06686..635f0d8 100644 --- a/src/logbook_files_screen.cpp +++ b/src/logbook_files_screen.cpp @@ -1,6 +1,7 @@ #include "logbook_files_screen.h" #include "flight_logbook.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -74,6 +75,7 @@ void run(TFT_eSPI& tft) { } drawButton(tft, backBtn, I18n::t(StringId::BACK)); + MenuStars::reset(); bool done = false; while (!done) { @@ -81,6 +83,7 @@ void run(TFT_eSPI& tft) { if (TouchInput::wasTapped(tap) && backBtn.contains(tap.x, tap.y)) { done = true; } + MenuStars::update(tft); delay(20); } } diff --git a/src/main.cpp b/src/main.cpp index 52ae2be..aa84302 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ #include "screenshot.h" #include "i18n.h" #include "first_run_language_screen.h" +#include "menu_stars.h" #include "ui_font.h" TFT_eSPI tft = TFT_eSPI(); @@ -256,6 +257,7 @@ void setup() { uint32_t waitStart = millis(); while (WifiMgr::getState() == WifiMgr::State::Connecting && millis() - waitStart < 16000) { WifiMgr::update(); + MenuStars::update(tft); delay(100); } @@ -280,6 +282,7 @@ void setup() { while (LocationManager::currentSource() == LocationManager::Source::None && millis() - locStart < 8000) { LocationManager::requestIpLookupIfNeeded(); + MenuStars::update(tft); delay(200); } SplashScreen::setStatusLine(tft, 2, I18n::t(StringId::SPLASH_READY)); @@ -294,7 +297,7 @@ void setup() { NetTask::begin(); - SplashScreen::waitRemaining(); + SplashScreen::waitRemaining(tft); drawHeader(); updateStatusLine(); diff --git a/src/menu_screen.cpp b/src/menu_screen.cpp index 8204860..1fbe498 100644 --- a/src/menu_screen.cpp +++ b/src/menu_screen.cpp @@ -10,6 +10,7 @@ #include "units_screen.h" #include "settings_backup.h" #include "settings_store.h" +#include "menu_stars.h" #include "i18n.h" #include "config.h" @@ -77,6 +78,7 @@ namespace { void run(TFT_eSPI& tft) { Page page = Page::Main; bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); @@ -101,6 +103,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } @@ -132,6 +135,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } @@ -170,6 +174,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } @@ -227,6 +232,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/menu_stars.cpp b/src/menu_stars.cpp new file mode 100644 index 0000000..cc290ee --- /dev/null +++ b/src/menu_stars.cpp @@ -0,0 +1,52 @@ +#include "menu_stars.h" +#include "config.h" + +namespace MenuStars { + +namespace { + constexpr uint8_t STAR_COUNT = 34; + struct Star { + int16_t x, y; + uint8_t phase; + uint8_t speed; + }; + Star stars[STAR_COUNT]; + bool initialized = false; + + uint32_t lastUpdateMs = 0; + constexpr uint32_t UPDATE_INTERVAL_MS = 60; + + void initStars() { + randomSeed((uint32_t)esp_random()); + for (uint8_t i = 0; i < STAR_COUNT; i++) { + stars[i].x = (int16_t)random(4, Config::SCREEN_WIDTH - 4); + stars[i].y = (int16_t)random(4, Config::SCREEN_HEIGHT - 4); + stars[i].phase = (uint8_t)random(0, 256); + stars[i].speed = (uint8_t)(1 + random(0, 3)); + } + initialized = true; + } +} + +void reset() { + initialized = false; +} + +void update(TFT_eSPI& tft) { + if (!initialized) initStars(); + + uint32_t now = millis(); + if (now - lastUpdateMs < UPDATE_INTERVAL_MS) return; + lastUpdateMs = now; + + 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 = tft.color565(0, bright, 0); + tft.drawPixel(stars[i].x, stars[i].y, color); + } +} + +} \ No newline at end of file diff --git a/src/menu_stars.h b/src/menu_stars.h new file mode 100644 index 0000000..e61a833 --- /dev/null +++ b/src/menu_stars.h @@ -0,0 +1,17 @@ +#pragma once +#include + +// Gemeinsames Sterne-Twinkle-Modul fuer ALLE Menue-/Untermenue-Bildschirme +// (schwarzer Hintergrund) sowie den Splash-Screen - dieselbe Optik wie im +// Flugzeug-Detail-Panel des Radars. +namespace MenuStars { + // Verteilt die Sterne neu ueber den ganzen Bildschirm - einmal beim + // Betreten eines neuen Screens aufrufen (NICHT bei jedem Redraw + // innerhalb desselben Screens, sonst "springt" die Animation staendig). + void reset(); + + // In der Warte-/Idle-Schleife eines Screens aufrufen (bei jedem + // Schleifendurchlauf ist ok - die Funktion drosselt sich intern selbst + // auf ca. alle 60ms, um das Display nicht unnoetig oft anzusprechen). + void update(TFT_eSPI& tft); +} \ No newline at end of file diff --git a/src/splash_screen.cpp b/src/splash_screen.cpp index 1b7fa66..583661f 100644 --- a/src/splash_screen.cpp +++ b/src/splash_screen.cpp @@ -1,4 +1,5 @@ #include "splash_screen.h" +#include "menu_stars.h" namespace SplashScreen { @@ -10,7 +11,6 @@ namespace { constexpr int16_t STATUS_START_Y = 260; constexpr uint8_t MAX_STATUS_LINES = 3; - // Dezente konzentrische Ringe + Fadenkreuz als "Zielfernrohr"-Hintergrund void drawRadarReticle(TFT_eSPI& tft, int16_t cx, int16_t cy) { uint16_t dim = 0x0320; tft.drawCircle(cx, cy, 80, dim); @@ -20,14 +20,12 @@ namespace { tft.drawFastVLine(cx, cy - 80, 160, dim); } - // Echte kleine Jet-Silhouette für den Hintergrund void drawMiniJet(TFT_eSPI& tft, int16_t x, int16_t y, uint16_t color) { tft.drawLine(x, y - 10, x, y + 12, color); tft.drawLine(x - 12, y + 2, x + 12, y + 2, color); tft.drawLine(x - 5, y + 10, x + 5, y + 10, color); } - // Echte kleine Helikopter-Silhouette für den Hintergrund void drawMiniHeli(TFT_eSPI& tft, int16_t x, int16_t y, uint16_t color) { tft.drawLine(x - 8, y - 6, x + 8, y - 6, color); tft.drawLine(x, y - 6, x, y + 4, color); @@ -35,41 +33,33 @@ namespace { tft.drawLine(x - 4, y + 7, x + 4, y + 7, color); } - // Haupt-Flugzeug in der Mitte (Unverändert) void drawAirplane(TFT_eSPI& tft, int16_t cx) { uint16_t color = TFT_GREEN; - // Rumpf (nur Umriss) tft.drawTriangle(cx + 15, 142, cx - 15, 200, cx - 10, 202, color); - // Deltafluegel (nur Umriss) tft.drawTriangle(cx + 5, 164, cx - 47, 175, cx - 3, 179, color); tft.drawTriangle(cx + 5, 164, cx + 30, 211, cx - 3, 179, color); - // Leitwerk (nur Umriss) tft.drawTriangle(cx - 11, 198, cx - 23, 201, cx - 6, 210, color); } } void begin(TFT_eSPI& tft) { startMs = millis(); + MenuStars::reset(); int16_t cx = tft.width() / 2; tft.fillScreen(TFT_BLACK); drawRadarReticle(tft, cx, 174); - // Hintergrund-Flugzeuge & Helikopter dezent im Hintergrund - gleicher - // gedaempfter Gruenton wie die Radar-Ringe (0x0320), NICHT das volle - // TFT_GREEN (das war vorher gleich hell wie das zentrale Flugzeug und - // wirkte dadurch nicht wie ein dezenter Hintergrund). uint16_t dimGreen = 0x0320; drawMiniJet(tft, cx - 45, 135, dimGreen); drawMiniJet(tft, cx + 50, 160, dimGreen); drawMiniHeli(tft, cx - 35, 215, dimGreen); drawMiniHeli(tft, cx + 45, 115, dimGreen); - // Zentrales Flugzeug (unverändert im Vordergrund) drawAirplane(tft, cx); tft.setTextDatum(MC_DATUM); @@ -97,10 +87,12 @@ void setStatusLine(TFT_eSPI& tft, uint8_t slot, const String& text, uint16_t col tft.setTextDatum(TL_DATUM); } -void waitRemaining() { +void waitRemaining(TFT_eSPI& tft) { uint32_t elapsed = millis() - startMs; - if (elapsed < MIN_DISPLAY_MS) { - delay(MIN_DISPLAY_MS - elapsed); + while (elapsed < MIN_DISPLAY_MS) { + MenuStars::update(tft); + delay(20); + elapsed = millis() - startMs; } } diff --git a/src/splash_screen.h b/src/splash_screen.h index c575269..c882d82 100644 --- a/src/splash_screen.h +++ b/src/splash_screen.h @@ -4,6 +4,11 @@ namespace SplashScreen { void begin(TFT_eSPI& tft); + void setStatusLine(TFT_eSPI& tft, uint8_t slot, const String& text, uint16_t color = TFT_WHITE); - void waitRemaining(); -} + + // Blockiert, bis seit begin() mindestens MIN_DISPLAY_MS vergangen sind. + // Direkt vor dem Verlassen des Splash-Screens aufrufen. Zeichnet waehrend + // des Wartens dieselbe Sterne-Animation wie die Menues. + void waitRemaining(TFT_eSPI& tft); +} \ No newline at end of file diff --git a/src/stats_screen.cpp b/src/stats_screen.cpp index cba6052..d3cf4fa 100644 --- a/src/stats_screen.cpp +++ b/src/stats_screen.cpp @@ -1,6 +1,7 @@ #include "stats_screen.h" #include "flight_logbook.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -24,12 +25,6 @@ namespace { tft.setTextDatum(TL_DATUM); } - // Label (gedaempftes Gruen) direkt ueber dem Wert (helles Gruen) - BEIDE - // in derselben Schriftgroesse (Size 1). Bewusst KEINE gemischten - // Groessen mehr in einer Zeile: das Zusammenspiel aus Grundlinien- - // Verankerung + Size-2-Skalierung unseres Fonts fuehrte trotz - // rechnerisch korrektem Abstand zu sichtbaren Ueberschneidungen. Mit - // einheitlicher Groesse ist der noetige Abstand simpel und zuverlaessig. void drawStatRow(TFT_eSPI& tft, int16_t labelY, const String& label, const String& value) { tft.setTextColor(TFT_DARKGREEN, TFT_BLACK); tft.setCursor(10, labelY); @@ -108,6 +103,7 @@ void run(TFT_eSPI& tft) { }; redraw(); + MenuStars::reset(); bool done = false; while (!done) { @@ -136,6 +132,7 @@ void run(TFT_eSPI& tft) { done = true; } } + MenuStars::update(tft); delay(20); } } diff --git a/src/units_screen.cpp b/src/units_screen.cpp index d7cda71..1356814 100644 --- a/src/units_screen.cpp +++ b/src/units_screen.cpp @@ -1,6 +1,7 @@ #include "units_screen.h" #include "settings_store.h" #include "touch_input.h" +#include "menu_stars.h" #include "i18n.h" #include "config.h" @@ -40,6 +41,7 @@ void run(TFT_eSPI& tft) { StringId labels[MODE_COUNT] = {StringId::UNITS_AUTO, StringId::UNITS_METRIC, StringId::UNITS_IMPERIAL}; bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -60,6 +62,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/wifi_manage_screen.cpp b/src/wifi_manage_screen.cpp index 9d46d79..f2c02c5 100644 --- a/src/wifi_manage_screen.cpp +++ b/src/wifi_manage_screen.cpp @@ -2,6 +2,7 @@ #include "wifi_manager.h" #include "wifi_setup_screen.h" #include "touch_input.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -32,6 +33,7 @@ void run(TFT_eSPI& tft) { constexpr int16_t REMOVE_BTN_W = 60; bool done = false; + MenuStars::reset(); while (!done) { tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_GREEN, TFT_BLACK); @@ -77,6 +79,7 @@ void run(TFT_eSPI& tft) { TouchInput::Point tap; while (true) { if (TouchInput::wasTapped(tap)) break; + MenuStars::update(tft); delay(20); } diff --git a/src/wifi_setup_screen.cpp b/src/wifi_setup_screen.cpp index 3d547d6..cc422d2 100644 --- a/src/wifi_setup_screen.cpp +++ b/src/wifi_setup_screen.cpp @@ -1,6 +1,7 @@ #include "wifi_setup_screen.h" #include "touch_input.h" #include "wifi_manager.h" +#include "menu_stars.h" #include "config.h" #include "i18n.h" @@ -187,6 +188,7 @@ namespace { } bool run(TFT_eSPI& tft) { + MenuStars::reset(); stage = Stage::Scanning; skipped = false; connectSucceeded = false; @@ -332,6 +334,7 @@ bool run(TFT_eSPI& tft) { } if (!needsRedraw) { + MenuStars::update(tft); delay(20); continue; }