Files
flyradar/src/menu_screen.cpp

264 lines
11 KiB
C++

#include "menu_screen.h"
#include "touch_input.h"
#include "calibration_screen.h"
#include "wifi_manage_screen.h"
#include "stats_screen.h"
#include "logbook_files_screen.h"
#include "location_presets_screen.h"
#include "airline_filter_screen.h"
#include "language_screen.h"
#include "units_screen.h"
#include "settings_backup.h"
#include "settings_store.h"
#include "menu_stars.h"
#include "i18n.h"
#include "config.h"
namespace MenuScreen {
namespace {
struct Rect {
int16_t x, y, w, h;
bool contains(int16_t px, int16_t py) const {
return px >= x && px < x + w && py >= y && py < y + h;
}
};
constexpr int16_t ROW_H = 24;
constexpr int16_t ROW_GAP = 2;
constexpr int16_t ROW_START_Y = 18;
Rect rowRect(uint8_t index) {
return {10, (int16_t)(ROW_START_Y + index * (ROW_H + ROW_GAP)),
(int16_t)(Config::SCREEN_WIDTH - 20), ROW_H};
}
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.drawRoundRect(r.x, r.y, r.w, r.h, 4, accent);
tft.setTextDatum(MC_DATUM);
tft.setTextColor(fg, bg);
tft.drawString(label, r.x + r.w / 2, r.y + r.h / 2);
tft.setTextDatum(TL_DATUM);
}
String onOff(bool on) { return I18n::t(on ? StringId::ON : StringId::OFF); }
String screenTimeoutLabel(uint8_t minutes) {
String prefix = I18n::t(StringId::MENU_SCREEN_TIMEOUT_PREFIX);
if (minutes == 0) return prefix + I18n::t(StringId::NEVER);
return prefix + String(minutes) + " min";
}
void showBriefMessage(TFT_eSPI& tft, const String& msg, uint16_t color) {
tft.fillRect(0, Config::SCREEN_HEIGHT - 18, Config::SCREEN_WIDTH, 18, TFT_BLACK);
tft.setTextColor(color, TFT_BLACK);
tft.setTextDatum(MC_DATUM);
tft.drawString(msg, Config::SCREEN_WIDTH / 2, Config::SCREEN_HEIGHT - 9);
tft.setTextDatum(TL_DATUM);
delay(1200);
}
enum class Page { Main, Region, System, Flight };
}
void run(TFT_eSPI& tft) {
Page page = Page::Main;
bool done = false;
MenuStars::reset();
while (!done) {
tft.fillScreen(TFT_BLACK);
if (page == Page::Main) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14);
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;
MenuStars::update(tft);
delay(20);
}
if (regionBtn.contains(tap.x, tap.y)) {
page = Page::Region;
} else if (wifiBtn.contains(tap.x, tap.y)) {
WifiManageScreen::run(tft);
} 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, 14);
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;
MenuStars::update(tft);
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::System) {
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14);
tft.println(I18n::t(StringId::MENU_CATEGORY_SYSTEM));
Rect calibBtn = rowRect(0);
Rect invertBtn = rowRect(1);
Rect timeoutBtn = rowRect(2);
Rect backupBtn = rowRect(3);
Rect restoreBtn = rowRect(4);
Rect backBtn = rowRect(5);
drawButton(tft, calibBtn, I18n::t(StringId::MENU_CALIBRATE));
String invertLabel = SettingsStore::displayInverted()
? I18n::t(StringId::MENU_DISPLAY_INVERTED)
: I18n::t(StringId::MENU_DISPLAY_NORMAL);
drawButton(tft, invertBtn, invertLabel);
drawButton(tft, timeoutBtn, screenTimeoutLabel(SettingsStore::screenTimeoutMinutes()));
drawButton(tft, backupBtn, I18n::t(StringId::MENU_BACKUP));
drawButton(tft, restoreBtn, I18n::t(StringId::MENU_RESTORE));
drawButton(tft, backBtn, I18n::t(StringId::BACK_ARROW));
TouchInput::Point tap;
while (true) {
if (TouchInput::wasTapped(tap)) break;
MenuStars::update(tft);
delay(20);
}
if (calibBtn.contains(tap.x, tap.y)) {
CalibrationScreen::run(tft);
} else if (invertBtn.contains(tap.x, tap.y)) {
bool newState = !SettingsStore::displayInverted();
SettingsStore::setDisplayInverted(newState);
tft.invertDisplay(newState);
} else if (timeoutBtn.contains(tap.x, tap.y)) {
uint8_t current = SettingsStore::screenTimeoutMinutes();
uint8_t next = (current >= 10) ? 0 : (current + 1);
SettingsStore::setScreenTimeoutMinutes(next);
} else if (backupBtn.contains(tap.x, tap.y)) {
bool ok = SettingsBackup::backup();
showBriefMessage(tft, I18n::t(ok ? StringId::MENU_BACKUP_SAVED : StringId::MENU_BACKUP_FAILED),
ok ? TFT_GREEN : TFT_RED);
} else if (restoreBtn.contains(tap.x, tap.y)) {
if (SettingsBackup::hasBackup()) {
bool ok = SettingsBackup::restore();
showBriefMessage(tft, I18n::t(ok ? StringId::MENU_RESTORED : StringId::MENU_RESTORE_FAILED),
ok ? TFT_GREEN : TFT_RED);
}
} else if (backBtn.contains(tap.x, tap.y)) {
page = Page::Main;
}
} else { // Page::Flight
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.setCursor(10, 14);
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;
MenuStars::update(tft);
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;
}
}
}
}
}