From 39ea684932f0dc062dc92a6a5db97ecb93c17f90 Mon Sep 17 00:00:00 2001 From: spohtl Date: Sat, 28 Dec 2024 14:20:45 -0500 Subject: [PATCH] 1st pass for defering settings reboot --- generated/ui_320x240/screens.c | 46 ++++++++++ generated/ui_320x240/screens.h | 151 +++++++++++++++++---------------- include/TFTView_320x240.h | 5 ++ include/ViewController.h | 2 + locale/lv_i18n.c | 36 ++++++++ source/TFTView_320x240.cpp | 94 ++++++++++++++++---- source/ViewController.cpp | 14 +++ 7 files changed, 259 insertions(+), 89 deletions(-) diff --git a/generated/ui_320x240/screens.c b/generated/ui_320x240/screens.c index 3fb56bc..e0e86fc 100644 --- a/generated/ui_320x240/screens.c +++ b/generated/ui_320x240/screens.c @@ -1255,6 +1255,39 @@ void create_screen_main_screen() { lv_obj_set_style_pad_column(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); { lv_obj_t *parent_obj = obj; + { + // BasicSettingsApplyButton + lv_obj_t *obj = lv_btn_create(parent_obj); + objects.basic_settings_apply_button = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(95), 40); + add_style_settings_button_style(obj); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4db270), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_text_color(obj, lv_color_hex(0xffb24d8f), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4a5e4a), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_align(obj, LV_ALIGN_TOP_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_shadow_width(obj, 0, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xfffafaf4), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff294337), LV_PART_MAIN | LV_STATE_PRESSED); + lv_obj_set_style_text_color(obj, lv_color_hex(0xff101010), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_margin_left(obj, 40, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_set_style_margin_right(obj, 40, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_state(obj, LV_STATE_DISABLED); + { + lv_obj_t *parent_obj = obj; + { + // BasicSettingsApplyLabel + lv_obj_t *obj = lv_label_create(parent_obj); + objects.basic_settings_apply_label = obj; + lv_obj_set_pos(obj, 0, 0); + lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT); + lv_label_set_long_mode(obj, LV_LABEL_LONG_DOT); + lv_label_set_text(obj, _("Apply & Reboot")); + lv_obj_set_style_bg_color(obj, lv_color_hex(0xff000000), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_set_style_align(obj, LV_ALIGN_CENTER, LV_PART_MAIN | LV_STATE_DEFAULT); + } + } + } { // BasicSettingsUserButton lv_obj_t *obj = lv_btn_create(parent_obj); @@ -1267,6 +1300,7 @@ void create_screen_main_screen() { lv_obj_set_style_bg_color(obj, lv_color_hex(0xff4db270), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_text_color(obj, lv_color_hex(0xff015114), LV_PART_MAIN | LV_STATE_PRESSED); lv_obj_set_style_text_color(obj, lv_color_hex(0xff808080), LV_PART_MAIN | LV_STATE_DISABLED); + lv_obj_center(obj); { lv_obj_t *parent_obj = obj; { @@ -2062,6 +2096,18 @@ void create_screen_main_screen() { lv_label_set_text(obj, _("Settings & Tools")); lv_obj_set_style_align(obj, LV_ALIGN_LEFT_MID, LV_PART_MAIN | LV_STATE_DEFAULT); } + { + // TopBasicSettingsUnsavedLabel + lv_obj_t *obj = lv_label_create(parent_obj); + objects.top_basic_settings_unsaved_label = obj; + lv_obj_set_pos(obj, 25, 0); + lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_style_text_color(obj, lv_color_hex(0xffde3232), LV_PART_MAIN | LV_STATE_DEFAULT); + lv_label_set_text(obj, _("Unsaved changes ...")); + lv_obj_set_style_align(obj, LV_ALIGN_LEFT_MID, LV_PART_MAIN | LV_STATE_DEFAULT); + lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); + } { // TopBasicSettingsImage lv_obj_t *obj = lv_img_create(parent_obj); diff --git a/generated/ui_320x240/screens.h b/generated/ui_320x240/screens.h index 271358d..c6ff7b4 100644 --- a/generated/ui_320x240/screens.h +++ b/generated/ui_320x240/screens.h @@ -1,70 +1,70 @@ -#ifndef EEZ_LVGL_UI_SCREENS_H -#define EEZ_LVGL_UI_SCREENS_H - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -// advanced settings -extern lv_obj_t * ui_AdvancedSettingsPanel; -extern lv_obj_t * ui_SettingsTabView; -extern lv_obj_t * ui_TabPageGeneral; -extern lv_obj_t * ui_GeneralLanguageButton; -extern lv_obj_t * ui_LanguageLabel; -extern lv_obj_t * ui_GeneralTimezoneButton; -extern lv_obj_t * ui_TimezoneLabel; -extern lv_obj_t * ui_GeneralScreenButton; -extern lv_obj_t * ui_ScreenLabel; -extern lv_obj_t * ui_GeneralMapsButton; -extern lv_obj_t * ui_MapsLabel; -extern lv_obj_t * ui_GeneralAudioButton; -extern lv_obj_t * ui_AudioLabel1; -extern lv_obj_t * ui_TabPageRadio; -extern lv_obj_t * ui_RadioBluetoothButton; -extern lv_obj_t * ui_BluetoothLabel; -extern lv_obj_t * ui_RadioDeviceButton; -extern lv_obj_t * ui_DeviceLabel; -extern lv_obj_t * ui_RadioDisplayButton; -extern lv_obj_t * ui_DisplayLabel; -extern lv_obj_t * ui_RadioLoRaButton; -extern lv_obj_t * ui_LoRaLabel; -extern lv_obj_t * ui_RadioNetworkButton; -extern lv_obj_t * ui_NetworkLabel; -extern lv_obj_t * ui_RadioPositionButton; -extern lv_obj_t * ui_PositionLabel; -extern lv_obj_t * ui_RadioPowerButton; -extern lv_obj_t * ui_PowerLabel; -extern lv_obj_t * ui_TabPageModules; -extern lv_obj_t * ui_ModuleCannedMsgButton; -extern lv_obj_t * ui_CannedMsgLabel; -extern lv_obj_t * ui_ModuleSaFButton; -extern lv_obj_t * ui_StoreAndForwardLabel; -extern lv_obj_t * ui_ModuleTelemetryButton; -extern lv_obj_t * ui_TelemetryLabel; -extern lv_obj_t * ui_ModuleMQTTButton; -extern lv_obj_t * ui_MQTTLabel; -extern lv_obj_t * ui_ModuleRangeTestButton; -extern lv_obj_t * ui_RangeTestLabel; -extern lv_obj_t * ui_ModuleAudioButton; -extern lv_obj_t * ui_AudioLabel; -extern lv_obj_t * ui_ModuleSerialButton; -extern lv_obj_t * ui_SerialLabel; -extern lv_obj_t * ui_ModuleExtNotificationButton; -extern lv_obj_t * ui_ExtNotificationLabel; -extern lv_obj_t * ui_ModuleNeighborInfoButton; -extern lv_obj_t * ui_NeighborInfoLabel; -extern lv_obj_t * ui_ModuleAmbientLightingButton; -extern lv_obj_t * ui_AmbientLightingLabel; -extern lv_obj_t * ui_ModuleDetectionSensorButton; -extern lv_obj_t * ui_DetectionSensorLabel; -extern lv_obj_t * ui_ModuleRemoteHardwareButton; -extern lv_obj_t * ui_RemoteHardwareLabel; - -void create_tabview_settings(void); - +#ifndef EEZ_LVGL_UI_SCREENS_H +#define EEZ_LVGL_UI_SCREENS_H + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// advanced settings +extern lv_obj_t * ui_AdvancedSettingsPanel; +extern lv_obj_t * ui_SettingsTabView; +extern lv_obj_t * ui_TabPageGeneral; +extern lv_obj_t * ui_GeneralLanguageButton; +extern lv_obj_t * ui_LanguageLabel; +extern lv_obj_t * ui_GeneralTimezoneButton; +extern lv_obj_t * ui_TimezoneLabel; +extern lv_obj_t * ui_GeneralScreenButton; +extern lv_obj_t * ui_ScreenLabel; +extern lv_obj_t * ui_GeneralMapsButton; +extern lv_obj_t * ui_MapsLabel; +extern lv_obj_t * ui_GeneralAudioButton; +extern lv_obj_t * ui_AudioLabel1; +extern lv_obj_t * ui_TabPageRadio; +extern lv_obj_t * ui_RadioBluetoothButton; +extern lv_obj_t * ui_BluetoothLabel; +extern lv_obj_t * ui_RadioDeviceButton; +extern lv_obj_t * ui_DeviceLabel; +extern lv_obj_t * ui_RadioDisplayButton; +extern lv_obj_t * ui_DisplayLabel; +extern lv_obj_t * ui_RadioLoRaButton; +extern lv_obj_t * ui_LoRaLabel; +extern lv_obj_t * ui_RadioNetworkButton; +extern lv_obj_t * ui_NetworkLabel; +extern lv_obj_t * ui_RadioPositionButton; +extern lv_obj_t * ui_PositionLabel; +extern lv_obj_t * ui_RadioPowerButton; +extern lv_obj_t * ui_PowerLabel; +extern lv_obj_t * ui_TabPageModules; +extern lv_obj_t * ui_ModuleCannedMsgButton; +extern lv_obj_t * ui_CannedMsgLabel; +extern lv_obj_t * ui_ModuleSaFButton; +extern lv_obj_t * ui_StoreAndForwardLabel; +extern lv_obj_t * ui_ModuleTelemetryButton; +extern lv_obj_t * ui_TelemetryLabel; +extern lv_obj_t * ui_ModuleMQTTButton; +extern lv_obj_t * ui_MQTTLabel; +extern lv_obj_t * ui_ModuleRangeTestButton; +extern lv_obj_t * ui_RangeTestLabel; +extern lv_obj_t * ui_ModuleAudioButton; +extern lv_obj_t * ui_AudioLabel; +extern lv_obj_t * ui_ModuleSerialButton; +extern lv_obj_t * ui_SerialLabel; +extern lv_obj_t * ui_ModuleExtNotificationButton; +extern lv_obj_t * ui_ExtNotificationLabel; +extern lv_obj_t * ui_ModuleNeighborInfoButton; +extern lv_obj_t * ui_NeighborInfoLabel; +extern lv_obj_t * ui_ModuleAmbientLightingButton; +extern lv_obj_t * ui_AmbientLightingLabel; +extern lv_obj_t * ui_ModuleDetectionSensorButton; +extern lv_obj_t * ui_DetectionSensorLabel; +extern lv_obj_t * ui_ModuleRemoteHardwareButton; +extern lv_obj_t * ui_RemoteHardwareLabel; + +void create_tabview_settings(void); + typedef struct _objects_t { lv_obj_t *boot_screen; lv_obj_t *main_screen; @@ -489,6 +489,9 @@ typedef struct _objects_t { lv_obj_t *blank_screen_button; lv_obj_t *screen_lock_button_matrix; lv_obj_t *lock_screen_digits_label; + lv_obj_t *basic_settings_apply_button; + lv_obj_t *basic_settings_apply_label; + lv_obj_t *top_basic_settings_unsaved_label; } objects_t; extern objects_t objects; @@ -518,13 +521,13 @@ void tick_screen_calibration_screen(); void create_user_widget_ok_cancel_widget(lv_obj_t *parent_obj, int startWidgetIndex); void tick_user_widget_ok_cancel_widget(int startWidgetIndex); - + void create_screens(); void tick_screen(int screen_index); - - -#ifdef __cplusplus -} -#endif - + + +#ifdef __cplusplus +} +#endif + #endif /*EEZ_LVGL_UI_SCREENS_H*/ \ No newline at end of file diff --git a/include/TFTView_320x240.h b/include/TFTView_320x240.h index 2668b6e..17429af 100644 --- a/include/TFTView_320x240.h +++ b/include/TFTView_320x240.h @@ -74,6 +74,7 @@ class TFTView_320x240 : public MeshtasticView void notifyResync(bool show) override; void notifyReboot(bool show) override; void notifyShutdown(void) override; + void notifyConfigReboot(bool show); void blankScreen(bool enable) override; void screenSaving(bool enabled) override; bool isScreenLocked(void) override; @@ -191,6 +192,8 @@ class TFTView_320x240 : public MeshtasticView void ui_set_active(lv_obj_t *b, lv_obj_t *p, lv_obj_t *tp); void showKeyboard(lv_obj_t *textArea); lv_obj_t *showQrCode(lv_obj_t *parent, const char *data); + void configTransactionOpen(); + void checkForConfigTransactionClose(); void enablePanel(lv_obj_t *panel); void disablePanel(lv_obj_t *panel); @@ -256,6 +259,7 @@ class TFTView_320x240 : public MeshtasticView static void ui_event_message_ready(lv_event_t *e); + static void ui_event_apply_button(lv_event_t *e); static void ui_event_user_button(lv_event_t *e); static void ui_event_role_button(lv_event_t *e); static void ui_event_region_button(lv_event_t *e); @@ -350,6 +354,7 @@ class TFTView_320x240 : public MeshtasticView std::array ch_label; // indexable label list for settings meshtastic_Channel *channel_scratch; // temporary scratch copy of channel db lv_obj_t *qr; // qr code + bool configRebootRequired; // set when AdminModule will reboot if settings are applied // extended default device profile struct with additional required data struct meshtastic_DeviceProfile_ext : meshtastic_DeviceProfile { diff --git a/include/ViewController.h b/include/ViewController.h index 33ae335..2a57221 100644 --- a/include/ViewController.h +++ b/include/ViewController.h @@ -43,6 +43,8 @@ class ViewController virtual bool sendConfig(meshtastic_Config_BluetoothConfig &&bluetooth, uint32_t nodeId = 0); virtual bool sendConfig(meshtastic_Config_SecurityConfig &&security, uint32_t nodeId = 0); virtual bool sendConfig(meshtastic_Channel &channel, uint32_t nodeId = 0); + virtual bool openConfigTransaction(uint32_t nodeId = 0); + virtual bool closeConfigTransaction(uint32_t nodeId = 0); // module config virtual bool sendConfig(meshtastic_ModuleConfig_MQTTConfig &&mqtt, uint32_t nodeId = 0); diff --git a/locale/lv_i18n.c b/locale/lv_i18n.c index 85bddea..0a09a0c 100644 --- a/locale/lv_i18n.c +++ b/locale/lv_i18n.c @@ -158,6 +158,7 @@ static lv_i18n_phrase_t de_singulars[] = { {"Resynch ...", "Synchronisierung ..."}, {"Rebooting ...", "Neustart ..."}, {"Shutting down ...", "Herunterfahren ..."}, + {"Config changed!\nRebooting ...", "Konfiguration geändert!\nNeustart ..."}, {"silent", "stumm"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX ausgeschaltet!"}, @@ -173,6 +174,8 @@ static lv_i18n_phrase_t de_singulars[] = { {"!Enter Filter ...", "!Filter Name..."}, {"Enter Filter ...", "Filter Name..."}, {"region unset", "keine Region"}, + {"Unsaved changes ...", "Nicht gespeichert ..."}, + {"Apply & Reboot", "Anwenden & neu starten"}, {NULL, NULL} // End mark }; @@ -310,6 +313,7 @@ static lv_i18n_phrase_t es_singulars[] = { {"Resynch ...", "Resincronizando ..."}, {"Rebooting ...", "Reiniciando ..."}, {"Shutting down ...", "Apagando ..."}, + {"Config changed!\nRebooting ...", "¡Config. cambiada!\nReiniciando ..."}, {"silent", "silenciado"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX apagado!"}, @@ -326,6 +330,8 @@ static lv_i18n_phrase_t es_singulars[] = { {"!Enter Filter ...", "!Introduce filtro..."}, {"Enter Filter ...", "Introduce filtro ..."}, {"region unset", "región no configurada"}, + {"Unsaved changes ...", "Cambios no guardados ..."}, + {"Apply & Reboot", "Aplicar & reiniciar"}, {NULL, NULL} // End mark }; @@ -452,6 +458,9 @@ static lv_i18n_phrase_t fi_singulars[] = { {"Resynch ...", "Synkronoi uudelleen ..."}, {"Rebooting ...", "Käynnistetään uudelleen ..."}, {"Shutting down ...", "Sammutetetaan ..."}, + {"Config changed!\nRebooting ...", "Konfiguraatio muuttunut!\nKäynnistetään uudelleen ..."}, + {"Unsaved changes ...", "Ei tallennettu ..."}, + {"Apply & Reboot", "Käyttää & käynnistä uudelleen"}, {NULL, NULL} // End mark }; @@ -583,6 +592,7 @@ static lv_i18n_phrase_t fr_singulars[] = { {"Resynch ...", "Resynch ..."}, {"Rebooting ...", "Redémarrage ..."}, {"Shutting down ...", "Arrêt..."}, + {"Config changed!\nRebooting ...", "Configuration modifiée!\nRedémarrage ..."}, {"silent", "silencieux"}, {"WiFi: ", "WiFi: "}, {"WiFi: %s", "WiFi: %s"}, @@ -601,6 +611,8 @@ static lv_i18n_phrase_t fr_singulars[] = { {"Enter Filter ...", "Filtrer ..."}, {"region unset", "Région non configurée"}, {"no signal", "aucun signal"}, + {"Unsaved changes ...", "Modif. non enregistrées ..."}, + {"Apply & Reboot", "Appliquer et redémarrer"}, {NULL, NULL} // End mark }; @@ -743,6 +755,7 @@ static lv_i18n_phrase_t it_singulars[] = { {"Resynch ...", "Risinc ..."}, {"Rebooting ...", "Riavvio ..."}, {"Shutting down ...", "Spegnimento ..."}, + {"Config changed!\nRebooting ...", "Config. modificata!\nRiavvio ..."}, {"silent", "silenzioso"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Blocco: disattivato/disattivato"}, @@ -760,6 +773,8 @@ static lv_i18n_phrase_t it_singulars[] = { {"Banner & Sound", "Banner e Suono"}, {"Banner only", "Solo Banner"}, {"Sound only", "Solo Suono"}, + {"Unsaved changes ...", "Modifiche non salvate ..."}, + {"Apply & Reboot", "Applica & riavvia"}, {NULL, NULL} // End mark }; @@ -915,6 +930,7 @@ static lv_i18n_phrase_t nl_singulars[] = { {"Resynch ...", "Hersync ..."}, {"Rebooting ...", "Herstarten ..."}, {"Shutting down ...", "Uitschakelen ..."}, + {"Config changed!\nRebooting ...", "Config gewijzigd!\nHerstarten ..."}, {"silent", "stil"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Slot: uit/uit"}, @@ -929,6 +945,8 @@ static lv_i18n_phrase_t nl_singulars[] = { {"Banner & Sound", "Strook & Geluid"}, {"Banner only", "Alleen strook"}, {"Sound only", "Alleen geluid"}, + {"Unsaved changes ...", "Niet opgeslagen ..."}, + {"Apply & Reboot", "Toepassen & herstarten"}, {NULL, NULL} // End mark }; @@ -1064,6 +1082,7 @@ static lv_i18n_phrase_t no_singulars[] = { {"Resynch ...", "Synkroniserer ..."}, {"Rebooting ...", "Starter på nytt ..."}, {"Shutting down ...", "Skrur av ..."}, + {"Config changed!\nRebooting ...", "Konfig endret!\nStarter på nytt ..."}, {"silent", "stille"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Lås: av/av"}, @@ -1079,6 +1098,8 @@ static lv_i18n_phrase_t no_singulars[] = { {"Banner & Sound", "Banner & Lyd"}, {"Banner only", "Bare banner"}, {"Sound only", "Bare lyd"}, + {"Unsaved changes ...", "Ulagrede endringer ..."}, + {"Apply & Reboot", "Bruk og start på nytt"}, {NULL, NULL} // End mark }; @@ -1207,6 +1228,9 @@ static lv_i18n_phrase_t pl_singulars[] = { {"Secondary Channels", "Kanały dodatkowe"}, {"Rebooting ...", "Uruchamiam ponownie ..."}, {"Shutting down ...", "Wyłączam ..."}, + {"Config changed!\nRebooting ...", "Konfiguracja zmieniona!\nUruchamiam ponownie ..."}, + {"Unsaved changes ...", "Niezapisane zmiany ..."}, + {"Apply & Reboot", "Zastosuj & uruchom ponownie"}, {NULL, NULL} // End mark }; @@ -1357,6 +1381,9 @@ static lv_i18n_phrase_t pt_singulars[] = { {"Resynch ...", "Resincronizando..."}, {"Rebooting ...", "Reiniciando..."}, {"Shutting down ...", "Desligando..."}, + {"Config changed!\nRebooting ...", "Configuração alterada!\nReiniciando..."}, + {"Unsaved changes ...", "Alter. não guardadas ..."}, + {"Apply & Reboot", "Aplicar & reiniciar"}, {NULL, NULL} // End mark }; @@ -1510,6 +1537,7 @@ static lv_i18n_phrase_t ru_singulars[] = { {"Resynch ...", "Ресинхронизация ..."}, {"Rebooting ...", "Перезагрузка ..."}, {"Shutting down ...", "Выключение ..."}, + {"Config changed!\nRebooting ...", "Конфигурация изменена!\nПерезагрузка ..."}, {"silent", "тихий режим"}, {"WiFi: ", "WiFi: <не настроен>"}, {"Lock: off/off", "Блокировка: выкл/выкл"}, @@ -1526,6 +1554,8 @@ static lv_i18n_phrase_t ru_singulars[] = { {"Banner & Sound", "Баннер и звук"}, {"Banner only", "Только баннер"}, {"Sound only", "Только звук"}, + {"Unsaved changes ...", "Несохраненные изменения ..."}, + {"Apply & Reboot", "Применить & перезагрузить"}, {NULL, NULL} // End mark }; @@ -1666,6 +1696,7 @@ static lv_i18n_phrase_t se_singulars[] = { {"Resynch ...", "Synkroniserar om ..."}, {"Rebooting ...", "Startar om ..."}, {"Shutting down ...", "Stänger av ..."}, + {"Config changed!\nRebooting ...", "Konfiguration ändrad!\nStartar om ..."}, {"silent", "tyst"}, {"WiFi: ", "WiFi: "}, {"Lock: off/off", "Lås: av/av"}, @@ -1681,6 +1712,8 @@ static lv_i18n_phrase_t se_singulars[] = { {"Banner & Sound", "Banner & Ljud"}, {"Banner only", "Endast Banner"}, {"Sound only", "Endast ljud"}, + {"Unsaved changes ...", "Osparade ändringar ..."}, + {"Apply & Reboot", "Applicera & starta om"}, {NULL, NULL} // End mark }; @@ -1826,6 +1859,7 @@ static lv_i18n_phrase_t sr_singulars[] = { {"Resynch ...", "Sinhronizacija ..."}, {"Rebooting ...", "Resetovanje ..."}, {"Shutting down ...", "Gašenje ..."}, + {"Config changed!\nRebooting ...", "Konfiguracija promenjena!\nResetovanje ..."}, {"silent", "bez zvuka"}, {"WiFi: ", "WiFi: "}, {"LoRa TX off!", "LoRa TX ugašen!"}, @@ -1834,6 +1868,8 @@ static lv_i18n_phrase_t sr_singulars[] = { {"Banner & Sound", "Baner i zvuk"}, {"Banner only", "Samo baner"}, {"Sound only", "Samo zvuk"}, + {"Unsaved changes ...", "Nesačuvane promene ..."}, + {"Apply & Reboot", "Primeni & resetuj"}, {NULL, NULL} // End mark }; diff --git a/source/TFTView_320x240.cpp b/source/TFTView_320x240.cpp index 1ca5b2a..78bfd78 100644 --- a/source/TFTView_320x240.cpp +++ b/source/TFTView_320x240.cpp @@ -100,10 +100,25 @@ TFTView_320x240 *TFTView_320x240::instance(const DisplayDriverConfig &cfg) } TFTView_320x240::TFTView_320x240(const DisplayDriverConfig *cfg, DisplayDriver *driver) - : MeshtasticView(cfg, driver, new ViewController), screensInitialised(false), nodesFiltered(0), processingFilter(false), - packetLogEnabled(false), detectorRunning(false), packetCounter(0), actTime(0), uptime(0), lastHeard(0), hasPosition(false), - topNodeLL(nullptr), scans(0), selectedHops(0), chooseNodeSignalScanner(false), chooseNodeTraceRoute(false), qr(nullptr), - db{} + : MeshtasticView(cfg, driver, new ViewController) + , screensInitialised(false) + , nodesFiltered(0) + , processingFilter(false) + , packetLogEnabled(false) + , detectorRunning(false) + , packetCounter(0) + , actTime(0) + , uptime(0) + , lastHeard(0) + , hasPosition(false) + , topNodeLL(nullptr) + , scans(0) + , selectedHops(0) + , chooseNodeSignalScanner(false) + , chooseNodeTraceRoute(false) + , qr(nullptr) + , configRebootRequired(false) + , db{} { filter.active = false; highlight.active = false; @@ -321,6 +336,8 @@ void TFTView_320x240::init_screens(void) objects.home_wlan_button->user_data = (void *)0; objects.home_memory_button->user_data = (void *)0; + lv_obj_set_state(objects.basic_settings_apply_button, LV_STATE_DISABLED, true); + updateFreeMem(); screensInitialised = true; @@ -496,9 +513,11 @@ void TFTView_320x240::ui_events_init(void) auto ui_event_HomeButton = [](lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); TFTView_320x240 &view = *static_cast(e->user_data); view.ui_set_active(objects.home_button, objects.home_panel, objects.top_panel); } else if (event_code == LV_EVENT_LONG_PRESSED) { + THIS->checkForConfigTransactionClose(); // force re-sync with node THIS->controller->setConfigRequested(true); THIS->notifyResync(true); @@ -560,6 +579,7 @@ void TFTView_320x240::ui_events_init(void) // basic settings buttons lv_obj_add_event_cb(objects.basic_settings_user_button, ui_event_user_button, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(objects.basic_settings_apply_button, ui_event_apply_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_role_button, ui_event_role_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_region_button, ui_event_region_button, LV_EVENT_CLICKED, NULL); lv_obj_add_event_cb(objects.basic_settings_modem_preset_button, ui_event_preset_button, LV_EVENT_CLICKED, NULL); @@ -675,6 +695,7 @@ void TFTView_320x240::ui_event_NodesButton(lv_event_t *e) ignoreClicked = false; return; } + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.nodes_button, objects.nodes_panel, objects.top_nodes_panel); if (filterNeedsUpdate) { THIS->updateNodesFiltered(true); @@ -682,6 +703,7 @@ void TFTView_320x240::ui_event_NodesButton(lv_event_t *e) filterNeedsUpdate = false; } } else if (event_code == LV_EVENT_LONG_PRESSED) { + THIS->checkForConfigTransactionClose(); filterNeedsUpdate = true; ignoreClicked = true; THIS->ui_set_active(objects.nodes_button, objects.node_options_panel, objects.top_node_options_panel); @@ -744,10 +766,30 @@ void TFTView_320x240::ui_event_NodeButton(lv_event_t *e) } } +void TFTView_320x240::configTransactionOpen() +{ + if (!THIS->configRebootRequired) { + lv_obj_remove_state(objects.basic_settings_apply_button, LV_STATE_DISABLED); + lv_obj_add_flag(objects.top_basic_settings_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(objects.top_basic_settings_unsaved_label, LV_OBJ_FLAG_HIDDEN); + THIS->configRebootRequired = true; + THIS->controller->openConfigTransaction(); + } +} + +void TFTView_320x240::checkForConfigTransactionClose() +{ + if (THIS->configRebootRequired) { + THIS->notifyConfigReboot(true); + THIS->controller->closeConfigTransaction(); + } +} + void TFTView_320x240::ui_event_GroupsButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.groups_button, objects.groups_panel, objects.top_groups_panel); } } @@ -756,6 +798,7 @@ void TFTView_320x240::ui_event_ChannelButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); uint8_t ch = (uint8_t)(unsigned long)e->user_data; if (THIS->db.channel[ch].role != meshtastic_Channel_Role_DISABLED) THIS->showMessages(ch); @@ -768,6 +811,7 @@ void TFTView_320x240::ui_event_MessagesButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.messages_button, objects.chats_panel, objects.top_chats_panel); } } @@ -776,6 +820,7 @@ void TFTView_320x240::ui_event_MapButton(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED && THIS->activeSettings == eNone) { + THIS->checkForConfigTransactionClose(); THIS->ui_set_active(objects.map_button, objects.map_panel, objects.top_map_panel); } } @@ -1069,8 +1114,8 @@ void TFTView_320x240::ui_event_WLANButton(lv_event_t *e) meshtastic_Config_NetworkConfig &network = THIS->db.config.network; network.wifi_enabled = !network.wifi_enabled; Themes::recolorButton(objects.home_wlan_button, network.wifi_enabled); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_NetworkConfig{network}); - THIS->notifyReboot(true); } } } @@ -1233,6 +1278,14 @@ void TFTView_320x240::ui_event_message_ready(lv_event_t *e) // basic settings buttons +void TFTView_320x240::ui_event_apply_button(lv_event_t *e) +{ + lv_event_code_t event_code = lv_event_get_code(e); + if (event_code == LV_EVENT_CLICKED) { + THIS->checkForConfigTransactionClose(); + } +} + void TFTView_320x240::ui_event_user_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); @@ -1526,6 +1579,7 @@ void TFTView_320x240::ui_event_device_reboot_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED) { + THIS->controller->closeConfigTransaction(); THIS->controller->requestReboot(5, THIS->ownNode); lv_screen_load_anim(objects.blank_screen, LV_SCR_LOAD_ANIM_FADE_OUT, 4000, 1000, false); lv_obj_add_flag(objects.reboot_panel, LV_OBJ_FLAG_HIDDEN); @@ -1536,6 +1590,7 @@ void TFTView_320x240::ui_event_device_shutdown_button(lv_event_t *e) { lv_event_code_t event_code = lv_event_get_code(e); if (event_code == LV_EVENT_CLICKED) { + THIS->controller->closeConfigTransaction(); THIS->controller->requestShutdown(5, THIS->ownNode); lv_screen_load_anim(objects.blank_screen, LV_SCR_LOAD_ANIM_FADE_OUT, 4000, 1000, false); lv_obj_add_flag(objects.reboot_panel, LV_OBJ_FLAG_HIDDEN); @@ -2712,8 +2767,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) meshtastic_User user{}; // TODO: don't overwrite is_licensed strcpy(user.short_name, userShort); strcpy(user.long_name, userLong); + THIS->configTransactionOpen(); THIS->controller->sendConfig(user, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_username_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_user_button); @@ -2731,8 +2786,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) lv_label_set_text(objects.basic_settings_role_label, buf2); device.role = role; + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_DeviceConfig{device}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_device_role_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_role_button); @@ -2759,8 +2814,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) uint32_t defaultSlot = LoRaPresets::getDefaultSlot(region, THIS->db.config.lora.modem_preset); lora.region = region; lora.channel_num = (defaultSlot <= numChannels ? defaultSlot : 1); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_LoRaConfig{lora}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_region_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_region_button); @@ -2780,8 +2835,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) lora.use_preset = true; lora.modem_preset = preset; lora.channel_num = channelNum; + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_LoRaConfig{lora}, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_modem_preset_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_modem_preset_button); @@ -2814,8 +2869,8 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) if (strcmp(THIS->db.config.network.wifi_ssid, ssid) != 0 || strcmp(THIS->db.config.network.wifi_psk, psk) != 0) { strcpy(THIS->db.config.network.wifi_ssid, ssid); strcpy(THIS->db.config.network.wifi_psk, psk); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_Config_NetworkConfig{THIS->db.config.network}, THIS->ownNode); - THIS->notifyReboot(true); } // THIS->enablePanel(objects.home_panel); lv_obj_add_flag(objects.settings_wifi_panel, LV_OBJ_FLAG_HIDDEN); @@ -2827,9 +2882,10 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) meshtastic_Language lang = THIS->val2language(value); if (lang != THIS->db.uiConfig.language) { THIS->db.uiConfig.language = lang; + THIS->configTransactionOpen(); THIS->controller->storeUIConfig(THIS->db.uiConfig); + THIS->checkForConfigTransactionClose(); THIS->controller->requestReboot(3, THIS->ownNode); - THIS->notifyReboot(true); } lv_obj_add_flag(objects.settings_language_panel, LV_OBJ_FLAG_HIDDEN); @@ -2956,7 +3012,7 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) config.use_pwm = false; #endif } - THIS->notifyReboot(true); + THIS->configTransactionOpen(); THIS->controller->sendConfig(meshtastic_ModuleConfig_ExternalNotificationConfig{config}, THIS->ownNode); } else if (config.alert_message_buzzer && !alert_message) { silent = true; @@ -2975,7 +3031,7 @@ void TFTView_320x240::ui_event_ok(lv_event_t *e) } case eReset: { uint32_t option = lv_dropdown_get_selected(objects.settings_reset_dropdown); - THIS->notifyReboot(true); + THIS->controller->closeConfigTransaction(); THIS->controller->requestReset(option, THIS->ownNode); lv_obj_add_flag(objects.settings_reset_panel, LV_OBJ_FLAG_HIDDEN); lv_group_focus_obj(objects.basic_settings_reset_button); @@ -4411,6 +4467,11 @@ void TFTView_320x240::notifyShutdown(void) messageAlert(_("Shutting down ..."), true); } +void TFTView_320x240::notifyConfigReboot(bool show) +{ + messageAlert(_("Config changed!\nRebooting ..."), show); +} + void TFTView_320x240::blankScreen(bool enable) { ILOG_DEBUG("%s screen (%s)", enable ? "blank" : "unblank", screenLocked ? "locked" : "timeout"); @@ -5158,7 +5219,10 @@ void TFTView_320x240::enablePanel(lv_obj_t *panel) auto enableButtons = [](lv_obj_t *obj, void *) -> lv_obj_tree_walk_res_t { if (obj->class_p == &lv_button_class) { - lv_obj_clear_state(obj, LV_STATE_DISABLED); + // don't automatically enable the apply button if no settings were changed + if (obj != objects.basic_settings_apply_button || THIS->configRebootRequired) { + lv_obj_clear_state(obj, LV_STATE_DISABLED); + } } return LV_OBJ_TREE_WALK_NEXT; }; @@ -5207,7 +5271,7 @@ void TFTView_320x240::setGroupFocus(lv_obj_t *panel) } else if (panel == objects.settings_screen_lock_panel) { lv_group_focus_obj(objects.screen_lock_button_matrix); } else if (panel == objects.controller_panel) { - lv_group_focus_obj(objects.basic_settings_user_button); + lv_group_focus_obj(objects.basic_settings_apply_button); } else { for (int i = 0; i < lv_obj_get_child_count(panel); i++) { if (panel->spec_attr->children[i]->class_p == &lv_button_class) { diff --git a/source/ViewController.cpp b/source/ViewController.cpp index 7f267b1..2166bab 100644 --- a/source/ViewController.cpp +++ b/source/ViewController.cpp @@ -370,6 +370,20 @@ bool ViewController::sendConfig(meshtastic_ModuleConfig_PaxcounterConfig &&paxCo nodeId ? nodeId : myNodeNum); } +bool ViewController::openConfigTransaction(uint32_t nodeId) +{ + return sendAdminMessage( + meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_begin_edit_settings_tag}, + nodeId ? nodeId : myNodeNum); +} + +bool ViewController::closeConfigTransaction(uint32_t nodeId) +{ + return sendAdminMessage( + meshtastic_AdminMessage{.which_payload_variant = meshtastic_AdminMessage_commit_edit_settings_tag}, + nodeId ? nodeId : myNodeNum); +} + /** * @brief Generic admin message (takes lvalue) *