From 9a44335601e7fcf15be45212751d9d95c14408b5 Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 29 Jan 2026 16:45:10 +1100 Subject: [PATCH] chore(color): implement publish/subscribe messaging to simplify code and further code consolidation (#7032) --- companion/.ci-trigger | 1 + radio/.gitignore | 1 - .../320x240/mask_ui_bg_tile_top_left.png | Bin 111 -> 0 bytes .../320x240/mask_ui_bg_tile_top_right.png | Bin 123 -> 0 bytes .../480x272/mask_ui_bg_tile_top_left.png | Bin 122 -> 0 bytes .../480x272/mask_ui_bg_tile_top_right.png | Bin 124 -> 0 bytes .../800x480/mask_ui_bg_tile_top_left.png | Bin 148 -> 0 bytes .../800x480/mask_ui_bg_tile_top_right.png | Bin 149 -> 0 bytes .../img-src/mask_ui_bg_tile_top_left.svg | 11 - .../img-src/mask_ui_bg_tile_top_right.svg | 11 - radio/src/edgetx.cpp | 38 +- radio/src/gui/colorlcd/CMakeLists.txt | 2 - radio/src/gui/colorlcd/LvglWrapper.cpp | 5 + radio/src/gui/colorlcd/bitmaps.cpp | 9 - radio/src/gui/colorlcd/bitmaps.h | 3 - .../gui/colorlcd/controls/color_editor.cpp | 61 +-- .../src/gui/colorlcd/controls/color_editor.h | 5 +- .../gui/colorlcd/controls/color_picker.cpp | 19 +- .../src/gui/colorlcd/controls/color_picker.h | 5 +- radio/src/gui/colorlcd/controls/curve.cpp | 35 +- radio/src/gui/colorlcd/controls/curve.h | 7 +- .../src/gui/colorlcd/controls/curve_param.cpp | 17 +- radio/src/gui/colorlcd/controls/curve_param.h | 7 +- .../gui/colorlcd/controls/gvar_numberedit.cpp | 9 - .../gui/colorlcd/controls/gvar_numberedit.h | 1 - .../colorlcd/controls/switch_warn_dialog.cpp | 33 +- .../colorlcd/controls/switch_warn_dialog.h | 7 +- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 4 +- radio/src/gui/colorlcd/layouts/layout6x1.cpp | 4 +- .../colorlcd/libui/bitmapbuffer_fileio.cpp | 2 + radio/src/gui/colorlcd/libui/button.cpp | 28 +- radio/src/gui/colorlcd/libui/button.h | 5 +- .../gui/colorlcd/libui/fullscreen_dialog.cpp | 98 +--- .../gui/colorlcd/libui/fullscreen_dialog.h | 31 +- radio/src/gui/colorlcd/libui/mainwindow.cpp | 56 +- radio/src/gui/colorlcd/libui/mainwindow.h | 4 +- radio/src/gui/colorlcd/libui/menu.cpp | 59 +-- radio/src/gui/colorlcd/libui/menu.h | 18 +- radio/src/gui/colorlcd/libui/menutoolbar.cpp | 22 +- radio/src/gui/colorlcd/libui/menutoolbar.h | 8 +- radio/src/gui/colorlcd/libui/messaging.cpp | 63 +++ radio/src/gui/colorlcd/libui/messaging.h | 55 ++ radio/src/gui/colorlcd/libui/page.cpp | 41 +- radio/src/gui/colorlcd/libui/page.h | 10 +- radio/src/gui/colorlcd/libui/popups.cpp | 31 +- radio/src/gui/colorlcd/libui/table.cpp | 49 -- radio/src/gui/colorlcd/libui/table.h | 2 - radio/src/gui/colorlcd/libui/view_text.cpp | 85 +-- radio/src/gui/colorlcd/libui/view_text.h | 7 +- radio/src/gui/colorlcd/libui/window.cpp | 47 +- radio/src/gui/colorlcd/libui/window.h | 49 +- radio/src/gui/colorlcd/lz4_fonts.h | 2 +- radio/src/gui/colorlcd/mainview/layout.cpp | 39 +- radio/src/gui/colorlcd/mainview/layout.h | 15 +- .../gui/colorlcd/mainview/screen_setup.cpp | 29 +- .../src/gui/colorlcd/mainview/screen_setup.h | 2 - radio/src/gui/colorlcd/mainview/topbar.cpp | 31 +- radio/src/gui/colorlcd/mainview/topbar.h | 17 +- .../mainview/view_logical_switches.cpp | 48 +- radio/src/gui/colorlcd/mainview/view_main.cpp | 32 +- radio/src/gui/colorlcd/mainview/view_main.h | 7 - .../mainview/view_main_decoration.cpp | 25 +- .../colorlcd/mainview/view_main_decoration.h | 10 +- radio/src/gui/colorlcd/mainview/widget.cpp | 11 +- radio/src/gui/colorlcd/mainview/widget.h | 1 - .../gui/colorlcd/mainview/widgets_container.h | 1 - .../gui/colorlcd/mainview/widgets_setup.cpp | 19 +- .../src/gui/colorlcd/mainview/widgets_setup.h | 14 +- radio/src/gui/colorlcd/model/curveedit.cpp | 93 ++-- radio/src/gui/colorlcd/model/curveedit.h | 9 +- .../gui/colorlcd/model/function_switches.cpp | 238 ++++----- .../gui/colorlcd/model/function_switches.h | 120 ++++- radio/src/gui/colorlcd/model/input_edit.cpp | 15 +- radio/src/gui/colorlcd/model/input_edit.h | 2 - radio/src/gui/colorlcd/model/model_curves.cpp | 20 +- radio/src/gui/colorlcd/model/model_curves.h | 2 +- .../src/gui/colorlcd/model/model_outputs.cpp | 9 +- radio/src/gui/colorlcd/model/model_select.cpp | 28 +- radio/src/gui/colorlcd/model/model_setup.cpp | 175 +++++-- radio/src/gui/colorlcd/model/output_edit.cpp | 2 +- .../src/gui/colorlcd/model/throttle_params.h | 30 -- radio/src/gui/colorlcd/model/trims_setup.cpp | 89 ---- radio/src/gui/colorlcd/model/trims_setup.h | 32 -- .../src/gui/colorlcd/module/module_setup.cpp | 11 +- .../src/gui/colorlcd/radio/preview_window.cpp | 12 - .../gui/colorlcd/radio/radio_calibration.cpp | 2 +- radio/src/gui/colorlcd/radio/radio_cfs.cpp | 168 +----- radio/src/gui/colorlcd/radio/radio_cfs.h | 27 +- .../src/gui/colorlcd/radio/radio_diaganas.cpp | 28 +- radio/src/gui/colorlcd/radio/radio_diaganas.h | 2 +- .../src/gui/colorlcd/radio/radio_hardware.cpp | 43 +- radio/src/gui/colorlcd/radio/radio_setup.cpp | 484 +++++++++--------- radio/src/gui/colorlcd/radio/radio_theme.cpp | 1 - .../gui/colorlcd/setup_menus/pagegroup.cpp | 57 +-- .../src/gui/colorlcd/setup_menus/pagegroup.h | 28 +- .../gui/colorlcd/setup_menus/quick_menu.cpp | 202 ++++---- .../src/gui/colorlcd/setup_menus/quick_menu.h | 56 +- .../colorlcd/setup_menus/quick_menu_data.cpp | 4 +- .../setup_menus/quick_menu_favorites.cpp | 2 +- .../colorlcd/setup_menus/quick_menu_group.cpp | 21 +- .../colorlcd/setup_menus/quick_menu_group.h | 6 +- radio/src/gui/colorlcd/startup_shutdown.cpp | 72 +-- radio/src/gui/colorlcd/startup_shutdown.h | 4 +- .../src/gui/colorlcd/themes/theme_manager.cpp | 12 - radio/src/gui/colorlcd/themes/theme_manager.h | 6 - radio/src/gui/colorlcd/widgets/gauge.cpp | 4 +- radio/src/gui/colorlcd/widgets/modelbmp.cpp | 6 +- radio/src/gui/colorlcd/widgets/outputs.cpp | 19 +- radio/src/gui/colorlcd/widgets/radio_info.cpp | 44 +- radio/src/gui/colorlcd/widgets/timer.cpp | 6 +- radio/src/gui/colorlcd/widgets/value.cpp | 6 +- radio/src/gui/common/stdlcd/draw_functions.h | 1 - radio/src/gui/gui_common.cpp | 22 +- radio/src/lua/lua_lvgl_widget.cpp | 4 +- radio/src/lua/lua_widget.h | 2 - radio/src/main.cpp | 88 ++-- radio/src/storage/modelslist.cpp | 2 +- radio/src/storage/modelslist.h | 27 +- radio/src/storage/storage_common.cpp | 7 + radio/src/switches.cpp | 16 +- radio/src/targets/pa01/battery_driver.cpp | 3 +- radio/src/targets/pl18/battery_driver.cpp | 3 +- radio/src/targets/st16/battery_driver.cpp | 3 +- radio/src/tasks.cpp | 10 +- tools/convert-gfx-list.csv | 2 - 125 files changed, 1701 insertions(+), 2054 deletions(-) create mode 100644 companion/.ci-trigger delete mode 100644 radio/.gitignore delete mode 100644 radio/src/bitmaps/320x240/mask_ui_bg_tile_top_left.png delete mode 100644 radio/src/bitmaps/320x240/mask_ui_bg_tile_top_right.png delete mode 100644 radio/src/bitmaps/480x272/mask_ui_bg_tile_top_left.png delete mode 100644 radio/src/bitmaps/480x272/mask_ui_bg_tile_top_right.png delete mode 100644 radio/src/bitmaps/800x480/mask_ui_bg_tile_top_left.png delete mode 100644 radio/src/bitmaps/800x480/mask_ui_bg_tile_top_right.png delete mode 100644 radio/src/bitmaps/img-src/mask_ui_bg_tile_top_left.svg delete mode 100644 radio/src/bitmaps/img-src/mask_ui_bg_tile_top_right.svg create mode 100644 radio/src/gui/colorlcd/libui/messaging.cpp create mode 100644 radio/src/gui/colorlcd/libui/messaging.h delete mode 100644 radio/src/gui/colorlcd/model/throttle_params.h delete mode 100644 radio/src/gui/colorlcd/model/trims_setup.cpp delete mode 100644 radio/src/gui/colorlcd/model/trims_setup.h diff --git a/companion/.ci-trigger b/companion/.ci-trigger new file mode 100644 index 000000000..1e6063a86 --- /dev/null +++ b/companion/.ci-trigger @@ -0,0 +1 @@ +Mon Jan 26 18:05:25 AEST 2026 diff --git a/radio/.gitignore b/radio/.gitignore deleted file mode 100644 index 7091e5b78..000000000 --- a/radio/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/gtest-1.6.0 diff --git a/radio/src/bitmaps/320x240/mask_ui_bg_tile_top_left.png b/radio/src/bitmaps/320x240/mask_ui_bg_tile_top_left.png deleted file mode 100644 index c140846fff4ed739e1431a7be494e1b5e8b570b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^%s?!_!2~2@ID+m1DYhhUcNd2LAh=-f^2tCE6;Bt( z5RRG2|Nj0qS5|&J*ScIHDmq$H!67hE@KjPtN{U1P14GqT_QqDZe>p()44$rjF6*2U FngG_DYhhUcNd2LAh=-f^2tCEeNPw1 z5Q(XGj~ntb81S%coNV=9DC?oDfV+z9sYUnByjyj5F3Z+c3=dWqe|8e8D`hmQ;k#Ag SYjX>zmBG{1&t;ucLK6UOOeKr} diff --git a/radio/src/bitmaps/480x272/mask_ui_bg_tile_top_right.png b/radio/src/bitmaps/480x272/mask_ui_bg_tile_top_right.png deleted file mode 100644 index ee33ffdc2da1757a4cb5363771aa1b631cf6d321..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^EI=&6!2~2#UAZ>_DYhhUcNd2LAh=-f^2tCELr)jS z5Q(YD51u~d<>1&+`1siW|NkWw90CG9TwLsaM&Mk(y!||z%0mq7H8nLnhj|znG?wt2 U=i93825M&TboFyt=akR{036LGNB{r; diff --git a/radio/src/bitmaps/800x480/mask_ui_bg_tile_top_left.png b/radio/src/bitmaps/800x480/mask_ui_bg_tile_top_left.png deleted file mode 100644 index 99a5474a488d4bac6bcc02d8d7531d5c3ca2b824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol-!2~4FK8}0@5=ioPcVYMsf(!O8p9~c7^mK6y z(U|!5#75o&1{^F0rYmW=6|^4VxHoaC#F6X^8xAbrtm8jHDO}ED(wlc3Gm2Mfd92%d th9^3-*RY_1A>~ - - - - - - - - - - diff --git a/radio/src/bitmaps/img-src/mask_ui_bg_tile_top_right.svg b/radio/src/bitmaps/img-src/mask_ui_bg_tile_top_right.svg deleted file mode 100644 index 1459a5fff..000000000 --- a/radio/src/bitmaps/img-src/mask_ui_bg_tile_top_right.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/radio/src/edgetx.cpp b/radio/src/edgetx.cpp index 847ca0354..2e40816c8 100644 --- a/radio/src/edgetx.cpp +++ b/radio/src/edgetx.cpp @@ -167,10 +167,6 @@ void checkValidMCU(void) #endif } -#if defined(SIMU) -static bool evalFSok = false; -#endif - void per10ms() { DEBUG_TIMER_START(debugTimerPer10ms); @@ -215,13 +211,8 @@ void per10ms() } #if defined(FUNCTION_SWITCHES) -#if defined(SIMU) - if (evalFSok) - evalFunctionSwitches(); -#else evalFunctionSwitches(); #endif -#endif #if defined(ROTARY_ENCODER_NAVIGATION) && !defined(COLORLCD) if (rotaryEncoderPollingCycle()) { @@ -766,14 +757,14 @@ void checkAll(bool isBootCheck) } dlg->setMessage(strKeys.c_str()); - dlg->setCloseCondition([tgtime]() { - if (tgtime >= get_tmr10ms() && keyDown()) { - return false; - } else { + MainWindow::instance()->blockUntilClose(true, [=]() { + if (dlg->deleted()) return true; + if ((tgtime < get_tmr10ms()) || !keyDown()) { + dlg->deleteLater(); return true; } + return false; }); - dlg->runForever(); LED_ERROR_END(); } #else @@ -846,9 +837,11 @@ void checkThrottleStick() } LED_ERROR_BEGIN(); auto dialog = new ThrottleWarnDialog(throttleNotIdle); - dialog->runForever(); + MainWindow::instance()->blockUntilClose(true, [=]() { + return dialog->deleted(); + }); + LED_ERROR_END(); } - LED_ERROR_END(); } #else void checkThrottleStick() @@ -917,6 +910,7 @@ void checkAlarm() // added by Gohst } } +#if !defined(COLORLCD) void alert(const char * title, const char * msg , uint8_t sound) { LED_ERROR_BEGIN(); @@ -958,6 +952,7 @@ void alert(const char * title, const char * msg , uint8_t sound) LED_ERROR_END(); } +#endif #if defined(GVARS) #if MAX_TRIMS == 8 @@ -1585,9 +1580,6 @@ void edgeTxInit() #if defined(FUNCTION_SWITCHES) setFSStartupPosition(); -#if defined(SIMU) - evalFSok = true; -#endif #endif #if defined(GUI) @@ -1619,6 +1611,8 @@ void edgeTxInit() resetBacklightTimeout(); + LED_ERROR_END(); + pulsesStart(); WDG_ENABLE(WDG_DURATION); } @@ -1660,6 +1654,12 @@ int main() modulePortInit(); pulsesInit(); +#if defined(COLORLCD) + // Do all lvgl init in case of fatal error on startup + extern void initLvgl(); + initLvgl(); +#endif + #if !defined(DISABLE_MCUCHECK) checkValidMCU(); #endif diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 935aa10e2..e03c060f2 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -74,10 +74,8 @@ set(GUI_SRC model/output_edit.cpp model/preflight_checks.cpp model/special_functions.cpp - model/throttle_params.cpp model/timer_setup.cpp model/trainer_setup.cpp - model/trims_setup.cpp module/bind_menu_d16.cpp module/custom_failsafe.cpp diff --git a/radio/src/gui/colorlcd/LvglWrapper.cpp b/radio/src/gui/colorlcd/LvglWrapper.cpp index 348d359cc..8f0f0d3b0 100644 --- a/radio/src/gui/colorlcd/LvglWrapper.cpp +++ b/radio/src/gui/colorlcd/LvglWrapper.cpp @@ -380,3 +380,8 @@ void LvglWrapper::run() } } } + +void initLvgl() +{ + LvglWrapper::instance(); +} diff --git a/radio/src/gui/colorlcd/bitmaps.cpp b/radio/src/gui/colorlcd/bitmaps.cpp index c03525bf7..b00c7804f 100644 --- a/radio/src/gui/colorlcd/bitmaps.cpp +++ b/radio/src/gui/colorlcd/bitmaps.cpp @@ -294,13 +294,6 @@ static const uint8_t mask_info_usb_plugged[] __FLASH = { // gfx for ui elements - -const uint8_t mask_round_title_left[]{ -#include "mask_ui_bg_tile_top_left.lbm" -}; -const uint8_t mask_round_title_right[]{ -#include "mask_ui_bg_tile_top_right.lbm" -}; const uint8_t mask_topleft_bg[] __FLASH = { #include "mask_ui_bg_topbar_left.lbm" }; @@ -502,8 +495,6 @@ static const _BuiltinIcon _builtinIcons[EDGETX_ICONS_COUNT] = { BI(ICON_MPLEX_MULTIPLY, mask_inline_multiply), BI(ICON_MPLEX_REPLACE, mask_inline_replace), - BI(ICON_ROUND_TITLE_LEFT, mask_round_title_left), - BI(ICON_ROUND_TITLE_RIGHT, mask_round_title_right), BI(ICON_MODEL_GRID_LARGE, mask_btn_grid_large), BI(ICON_MODEL_GRID_SMALL, mask_btn_grid_small), BI(ICON_MODEL_LIST_TWO, mask_btn_list_two), diff --git a/radio/src/gui/colorlcd/bitmaps.h b/radio/src/gui/colorlcd/bitmaps.h index e646484cf..5fe22c2d8 100644 --- a/radio/src/gui/colorlcd/bitmaps.h +++ b/radio/src/gui/colorlcd/bitmaps.h @@ -122,9 +122,6 @@ enum EdgeTxIcon { ICON_MPLEX_MULTIPLY, ICON_MPLEX_REPLACE, - ICON_ROUND_TITLE_LEFT, - ICON_ROUND_TITLE_RIGHT, - ICON_MODEL_GRID_LARGE, ICON_MODEL_GRID_SMALL, ICON_MODEL_LIST_TWO, diff --git a/radio/src/gui/colorlcd/controls/color_editor.cpp b/radio/src/gui/colorlcd/controls/color_editor.cpp index 8449bfa18..1891fab76 100644 --- a/radio/src/gui/colorlcd/controls/color_editor.cpp +++ b/radio/src/gui/colorlcd/controls/color_editor.cpp @@ -28,7 +28,20 @@ static const char* const RGBChars[MAX_BARS] = {"R", "G", "B"}; static const char* const HSVChars[MAX_BARS] = {"H", "S", "V"}; -typedef std::function getRGBFromPos; +// ColorTypes() +// A ColorType implements an editor for selecting a color. Currently we support +// HSV, RGB and SYS (choose from system defined colors) +class ColorType +{ + public: + ColorType() {} + virtual ~ColorType() {} + + virtual void setText() {}; + virtual uint32_t getRGB() { return 0; }; + + protected: +}; class ColorBar : public FormField { @@ -93,7 +106,7 @@ class ColorBar : public FormField rel_pos.y = point_act.y - obj_coords.y1; bar->value = bar->screenToValue(rel_pos.y); - lv_event_send(target->parent, LV_EVENT_VALUE_CHANGED, nullptr); + Messaging::send(Messaging::COLOR_CHANGED); } static void on_key(lv_event_t* e) @@ -114,7 +127,7 @@ class ColorBar : public FormField else bar->value -= accel; } - lv_event_send(obj->parent, LV_EVENT_VALUE_CHANGED, nullptr); + Messaging::send(Messaging::COLOR_CHANGED); } } else if (key == LV_KEY_RIGHT) { if (bar->value < bar->maxValue) { @@ -126,7 +139,7 @@ class ColorBar : public FormField else bar->value = bar->maxValue; } - lv_event_send(obj->parent, LV_EVENT_VALUE_CHANGED, nullptr); + Messaging::send(Messaging::COLOR_CHANGED); } } } @@ -189,22 +202,7 @@ class ColorBar : public FormField uint32_t maxValue = 0; uint32_t value = 0; - getRGBFromPos getRGB = nullptr; -}; - -// ColorTypes() -// A ColorType implements an editor for selecting a color. Currently we support -// HSV, RGB and SYS (choose from system defined colors) -class ColorType -{ - public: - ColorType() {} - virtual ~ColorType() {} - - virtual void setText() {}; - virtual uint32_t getRGB() { return 0; }; - - protected: + std::function getRGB = nullptr; }; // Color editor with three bars for selecting color value. Base class for HSV @@ -391,8 +389,7 @@ class ThemeColorType : public ColorType etx_bg_color(btn->getLvObj(), (LcdColorIndex)color); btn->setPressHandler([=]() { m_color = color; - lv_event_send(parent->getParent()->getParent()->getLvObj(), - LV_EVENT_VALUE_CHANGED, nullptr); + Messaging::send(Messaging::COLOR_CHANGED); return 0; }); } @@ -442,8 +439,7 @@ class FixedColorType : public ColorType etx_bg_color(btn->getLvObj(), (LcdColorIndex)color); btn->setPressHandler([=]() { m_color = color; - lv_event_send(parent->getParent()->getLvObj(), - LV_EVENT_VALUE_CHANGED, nullptr); + Messaging::send(Messaging::COLOR_CHANGED); return 0; }); } @@ -456,9 +452,8 @@ class FixedColorType : public ColorType ///////////////////////////////////////////////////////////////////////// ColorEditor::ColorEditor(Window* parent, const rect_t& rect, uint32_t color, std::function setValue, - std::function preview, COLOR_EDITOR_FMT fmt, COLOR_EDITOR_TYPE typ) : - Window(parent, rect), _setValue(std::move(setValue)), _preview(std::move(preview)), + Window(parent, rect), _setValue(std::move(setValue)), format(fmt) { if (format == ETX_RGB565) { @@ -475,10 +470,9 @@ ColorEditor::ColorEditor(Window* parent, const rect_t& rect, uint32_t color, _color = color; setColorEditorType(typ); - lv_obj_add_event_cb(lvobj, ColorEditor::value_changed, LV_EVENT_VALUE_CHANGED, - nullptr); + Messaging::send(Messaging::COLOR_PREVIEW, _color); - if (_preview) _preview(_color); + colorUpdateMsg.subscribe(Messaging::COLOR_CHANGED, [=](uint32_t param) { setRGB(); }); } void ColorEditor::setColorEditorType(COLOR_EDITOR_TYPE colorType) @@ -528,12 +522,5 @@ void ColorEditor::setRGB() _color = _colorType->getRGB(); // update bars & labels setText(); - if (_preview) _preview(_color); -} - -void ColorEditor::value_changed(lv_event_t* e) -{ - lv_obj_t* target = lv_event_get_target(e); - ColorEditor* edit = (ColorEditor*)lv_obj_get_user_data(target); - if (edit) edit->setRGB(); + Messaging::send(Messaging::COLOR_PREVIEW, _color); } diff --git a/radio/src/gui/colorlcd/controls/color_editor.h b/radio/src/gui/colorlcd/controls/color_editor.h index 27f6f01e5..d944ac8d0 100644 --- a/radio/src/gui/colorlcd/controls/color_editor.h +++ b/radio/src/gui/colorlcd/controls/color_editor.h @@ -21,6 +21,7 @@ #pragma once #include "window.h" +#include "messaging.h" class ColorType; @@ -39,7 +40,6 @@ class ColorEditor : public Window public: ColorEditor(Window* parent, const rect_t& rect, uint32_t color, std::function setValue = nullptr, - std::function preview = nullptr, COLOR_EDITOR_FMT fmt = ETX_RGB565, COLOR_EDITOR_TYPE typ = HSV_COLOR_EDITOR); #if defined(DEBUG_WINDOWS) @@ -57,11 +57,10 @@ class ColorEditor : public Window protected: ColorType* _colorType = nullptr; std::function _setValue; - std::function _preview; uint32_t _color; COLOR_EDITOR_FMT format; + Messaging colorUpdateMsg; void setText(); void setRGB(); - static void value_changed(lv_event_t* e); }; diff --git a/radio/src/gui/colorlcd/controls/color_picker.cpp b/radio/src/gui/colorlcd/controls/color_picker.cpp index 5f24e554b..5064efb5f 100644 --- a/radio/src/gui/colorlcd/controls/color_picker.cpp +++ b/radio/src/gui/colorlcd/controls/color_picker.cpp @@ -76,7 +76,6 @@ class ColorEditorPopup : public BaseDialog public: ColorEditorPopup(uint32_t color, std::function _setValue, - std::function _preview, COLOR_EDITOR_FMT fmt) : BaseDialog(STR_COLOR_PICKER, false, COLOR_EDIT_WIDTH, COLOR_EDIT_HEIGHT), origColor(color), setValue(std::move(_setValue)), format(fmt) @@ -85,7 +84,7 @@ class ColorEditorPopup : public BaseDialog auto line = form->newLine(grid); rect_t r{0, 0, CE_SZ, CE_SZ}; - auto cedit = new ColorEditor(line, r, color, [=](uint32_t c) { updateColor(c); }, _preview, format, THM_COLOR_EDITOR); + auto cedit = new ColorEditor(line, r, color, [=](uint32_t c) { updateColor(c); }, format, THM_COLOR_EDITOR); lv_obj_set_style_grid_cell_x_align(cedit->getLvObj(), LV_GRID_ALIGN_CENTER, 0); auto vbox = new Window(line, rect_t{}); @@ -183,25 +182,19 @@ class ColorEditorPopup : public BaseDialog }; ColorPicker::ColorPicker(Window* parent, const rect_t& rect, - std::function getValue, - std::function setValue, - std::function preview, + std::function _getValue, + std::function _setValue, COLOR_EDITOR_FMT fmt) : Button(parent, {rect.x, rect.y, rect.w == 0 ? ColorEditorPopup::COLOR_PAD_WIDTH : rect.w, EdgeTxStyles::UI_ELEMENT_HEIGHT}), - setValue(std::move(setValue)), preview(std::move(preview)), format(fmt) + getValue(std::move(_getValue)), setValue(std::move(_setValue)), format(fmt) { updateColor(getValue()); } void ColorPicker::onClicked() { - new ColorEditorPopup(getColor(), [=](uint32_t c) { setColor(c); }, preview, format); -} - -void ColorPicker::setColor(uint32_t c) -{ - setValue(c); - updateColor(c); + updateColor(getValue()); + new ColorEditorPopup(color, [=](uint32_t c) { setValue(c); updateColor(c); }, format); } void ColorPicker::updateColor(uint32_t c) diff --git a/radio/src/gui/colorlcd/controls/color_picker.h b/radio/src/gui/colorlcd/controls/color_picker.h index a39eda7bf..7e038eaea 100644 --- a/radio/src/gui/colorlcd/controls/color_picker.h +++ b/radio/src/gui/colorlcd/controls/color_picker.h @@ -27,6 +27,7 @@ class ColorPicker : public Button { uint32_t color; + std::function getValue; std::function setValue; std::function preview; COLOR_EDITOR_FMT format; @@ -37,11 +38,7 @@ public: ColorPicker(Window* parent, const rect_t& rect, std::function getValue, std::function setValue = nullptr, - std::function preview = nullptr, COLOR_EDITOR_FMT fmt = ETX_RGB565); - void setColor(uint32_t c); - uint32_t getColor() const { return color; } - void onClicked() override; }; diff --git a/radio/src/gui/colorlcd/controls/curve.cpp b/radio/src/gui/colorlcd/controls/curve.cpp index 45f49d76f..434a4b8b2 100644 --- a/radio/src/gui/colorlcd/controls/curve.cpp +++ b/radio/src/gui/colorlcd/controls/curve.cpp @@ -147,17 +147,6 @@ Curve::Curve(Window* parent, const rect_t& rect, dw = rect.w - dx * 2; dh = rect.h - dy * 2; - for (int i = 0; i < 17; i += 1) { - auto p = lv_obj_create(lvobj); - etx_solid_bg(p, COLOR_THEME_PRIMARY2_INDEX); - etx_obj_add_style(p, styles->circle, LV_PART_MAIN); - etx_obj_add_style(p, styles->border, LV_PART_MAIN); - etx_obj_add_style(p, styles->border_color[COLOR_THEME_SECONDARY1_INDEX], LV_PART_MAIN); - lv_obj_set_size(p, POS_PT_SZ, POS_PT_SZ); - lv_obj_add_flag(p, LV_OBJ_FLAG_HIDDEN); - pointDots[i] = p; - } - if (positionFunc) { posVLine = lv_line_create(lvobj); etx_obj_add_style(posVLine, styles->graph_position_line, LV_PART_MAIN); @@ -178,6 +167,8 @@ Curve::Curve(Window* parent, const rect_t& rect, updatePosition(); } + + curveUpdateMsg.subscribe(Messaging::CURVE_UPDATE, [=](uint32_t param) { update(); }); } coord_t Curve::getPointX(int x) const @@ -220,28 +211,6 @@ void Curve::updatePosition() } } -void Curve::addPoint(const point_t& point) -{ - int i = points.size(); - coord_t x = getPointX(point.x); - coord_t y = getPointY(point.y); - lv_obj_set_pos(pointDots[i], x - POS_PT_SZ / 2, y - POS_PT_SZ / 2); - lv_obj_clear_flag(pointDots[i], LV_OBJ_FLAG_HIDDEN); - - points.push_back(point); - - update(); -} - -void Curve::clearPoints() -{ - points.clear(); - for (int i = 0; i < 17; i += 1) - lv_obj_add_flag(pointDots[i], LV_OBJ_FLAG_HIDDEN); - - update(); -} - void Curve::update() { base.update(); diff --git a/radio/src/gui/colorlcd/controls/curve.h b/radio/src/gui/colorlcd/controls/curve.h index 680c9514e..625d62b1a 100644 --- a/radio/src/gui/colorlcd/controls/curve.h +++ b/radio/src/gui/colorlcd/controls/curve.h @@ -22,6 +22,7 @@ #pragma once #include "window.h" +#include "messaging.h" class StaticText; @@ -59,9 +60,6 @@ class Curve : public Window std::string getName() const override { return "Curve"; } #endif - void addPoint(const point_t& point); - void clearPoints(); - void update(); protected: @@ -71,13 +69,12 @@ class Curve : public Window int lastPos = 0; std::function valueFunc; std::function positionFunc; - std::list points; StaticText* positionValue = nullptr; lv_point_t posLinePoints[4]; lv_obj_t* posVLine = nullptr; lv_obj_t* posHLine = nullptr; lv_obj_t* posPoint = nullptr; - lv_obj_t* pointDots[17] = { nullptr }; + Messaging curveUpdateMsg; void updatePosition(); diff --git a/radio/src/gui/colorlcd/controls/curve_param.cpp b/radio/src/gui/colorlcd/controls/curve_param.cpp index c6b83475b..c94d8cef8 100644 --- a/radio/src/gui/colorlcd/controls/curve_param.cpp +++ b/radio/src/gui/colorlcd/controls/curve_param.cpp @@ -21,6 +21,7 @@ #include "curve_param.h" +#include "curve.h" #include "edgetx.h" #include "getset_helpers.h" #include "gvar_numberedit.h" @@ -30,9 +31,9 @@ #define SET_DIRTY() storageDirty(EE_MODEL) CurveChoice::CurveChoice(Window* parent, std::function getRefValue, - std::function setRefValue, std::function refreshView, mixsrc_t source) : + std::function setRefValue, mixsrc_t source) : Choice(parent, rect_t{}, -MAX_CURVES, MAX_CURVES, getRefValue, setRefValue), - source(source), refreshView(std::move(refreshView)) + source(source) { setTextHandler([](int value) { return getCurveString(value); }); } @@ -42,16 +43,15 @@ bool CurveChoice::onLongPress() if (modelCurvesEnabled()) { if (_getValue()) { lv_obj_clear_state(lvobj, LV_STATE_PRESSED); - ModelCurvesPage::pushEditCurve(abs(_getValue()) - 1, refreshView, source); + ModelCurvesPage::pushEditCurve(abs(_getValue()) - 1, source); } } return true; } CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, - std::function setRefValue, int16_t sourceMin, mixsrc_t source, - std::function refreshView) : - Window(parent, rect), ref(ref), refreshView(std::move(refreshView)) + std::function setRefValue, int16_t sourceMin, mixsrc_t source) : + Window(parent, rect), ref(ref) { padAll(PAD_TINY); lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW_WRAP); @@ -98,7 +98,7 @@ CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, v.isSource = false; v.value = newValue; setRefValue(v.rawValue); - }, refreshView, source); + }, source); update(); } @@ -133,8 +133,7 @@ void CurveParam::update() } act_field->show(); - if (refreshView) - refreshView(); + Messaging::send(Messaging::CURVE_UPDATE); auto act_obj = act_field->getLvObj(); if (has_focus) { diff --git a/radio/src/gui/colorlcd/controls/curve_param.h b/radio/src/gui/colorlcd/controls/curve_param.h index c614a980e..db8386b06 100644 --- a/radio/src/gui/colorlcd/controls/curve_param.h +++ b/radio/src/gui/colorlcd/controls/curve_param.h @@ -31,7 +31,7 @@ class CurveChoice : public Choice { public: CurveChoice(Window* parent, std::function getRefValue, - std::function setRefValue, std::function refreshView, + std::function setRefValue, mixsrc_t source); bool onLongPress() override; @@ -46,8 +46,7 @@ class CurveParam : public Window public: CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, std::function setRefValue, - int16_t sourceMin, mixsrc_t source, - std::function refreshView = nullptr); + int16_t sourceMin, mixsrc_t source); protected: // Curve @@ -59,7 +58,5 @@ class CurveParam : public Window Choice* cust_choice; Window* act_field = nullptr; - std::function refreshView; - void update(); }; diff --git a/radio/src/gui/colorlcd/controls/gvar_numberedit.cpp b/radio/src/gui/colorlcd/controls/gvar_numberedit.cpp index 4fac9bed3..f2b57c5ad 100644 --- a/radio/src/gui/colorlcd/controls/gvar_numberedit.cpp +++ b/radio/src/gui/colorlcd/controls/gvar_numberedit.cpp @@ -88,15 +88,6 @@ void GVarNumberEdit::switchGVarMode() #endif } -void GVarNumberEdit::onEvent(event_t event) -{ - if (event == EVT_KEY_LONG(KEY_ENTER)) { - switchGVarMode(); - } else { - Window::onEvent(event); - } -} - void GVarNumberEdit::update() { bool has_focus = act_field && act_field->hasFocus(); diff --git a/radio/src/gui/colorlcd/controls/gvar_numberedit.h b/radio/src/gui/colorlcd/controls/gvar_numberedit.h index 0190e49b0..938c44dbc 100644 --- a/radio/src/gui/colorlcd/controls/gvar_numberedit.h +++ b/radio/src/gui/colorlcd/controls/gvar_numberedit.h @@ -58,7 +58,6 @@ class GVarNumberEdit : public Window std::function setValue; int32_t voffset; - void onEvent(event_t event) override; void update(); static void value_changed(lv_event_t* e); diff --git a/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp b/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp index d00afa167..e8301e1d0 100644 --- a/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp +++ b/radio/src/gui/colorlcd/controls/switch_warn_dialog.cpp @@ -28,33 +28,20 @@ SwitchWarnDialog::SwitchWarnDialog() : FullScreenDialog(WARNING_TYPE_ALERT, STR_SWITCHWARN, "", STR_PRESS_ANY_KEY_TO_SKIP) { - last_bad_switches = 0xff; - last_bad_pots = 0x0; - setCloseCondition(std::bind(&SwitchWarnDialog::warningInactive, this)); lv_label_set_long_mode(messageLabel->getLvObj(), LV_LABEL_LONG_WRAP); AUDIO_ERROR_MESSAGE(AU_SWITCH_ALERT); } -bool SwitchWarnDialog::warningInactive() -{ - uint16_t bad_pots; - - if (!isSwitchWarningRequired(bad_pots)) return true; - - if (last_bad_switches != switches_states || last_bad_pots != bad_pots) { - last_bad_pots = bad_pots; - last_bad_switches = switches_states; - } - - return false; -} - void SwitchWarnDialog::checkEvents() { - if (!running) return; - FullScreenDialog::checkEvents(); + uint16_t bad_pots; + if (!isSwitchWarningRequired(bad_pots)) { + deleteLater(); + return; + }; + std::string warn_txt; for (int i = 0; i < switchGetMaxAllSwitches(); ++i) { if (SWITCH_WARNING_ALLOWED(i)) { @@ -95,13 +82,15 @@ ThrottleWarnDialog::ThrottleWarnDialog(const char* msg) : FullScreenDialog(WARNING_TYPE_ALERT, STR_THROTTLE_UPPERCASE, msg, STR_PRESS_ANY_KEY_TO_SKIP) { - setCloseCondition(std::bind(&ThrottleWarnDialog::warningInactive, this)); lv_label_set_long_mode(messageLabel->getLvObj(), LV_LABEL_LONG_WRAP); AUDIO_ERROR_MESSAGE(AU_THROTTLE_ALERT); } -bool ThrottleWarnDialog::warningInactive() +void ThrottleWarnDialog::checkEvents() { + FullScreenDialog::checkEvents(); + extern bool isThrottleWarningAlertNeeded(); - return !isThrottleWarningAlertNeeded(); + if (!isThrottleWarningAlertNeeded()) + deleteLater(); } diff --git a/radio/src/gui/colorlcd/controls/switch_warn_dialog.h b/radio/src/gui/colorlcd/controls/switch_warn_dialog.h index 4bb7c452e..cf0bed8b7 100644 --- a/radio/src/gui/colorlcd/controls/switch_warn_dialog.h +++ b/radio/src/gui/colorlcd/controls/switch_warn_dialog.h @@ -35,11 +35,6 @@ class SwitchWarnDialog : public FullScreenDialog #endif protected: - swarnstate_t last_bad_switches; - uint16_t last_bad_pots; - - bool warningInactive(); - void checkEvents() override; }; @@ -53,5 +48,5 @@ class ThrottleWarnDialog : public FullScreenDialog #endif protected: - bool warningInactive(); + void checkEvents() override; }; diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index 0e3f9e71c..183af767c 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -56,10 +56,10 @@ class Layout2x4 : public Layout lv_obj_t* panel1 = nullptr; lv_obj_t* panel2 = nullptr; - void checkEvents() override + void updateDecorations() override { + Layout::updateDecorations(); setPanels(); - Layout::checkEvents(); } void setPanels() diff --git a/radio/src/gui/colorlcd/layouts/layout6x1.cpp b/radio/src/gui/colorlcd/layouts/layout6x1.cpp index 98097a7a4..59e2479a0 100644 --- a/radio/src/gui/colorlcd/layouts/layout6x1.cpp +++ b/radio/src/gui/colorlcd/layouts/layout6x1.cpp @@ -49,10 +49,10 @@ class Layout6x1 : public Layout rect_t mainZone = {0, 0, 0, 0}; lv_obj_t* panel = nullptr; - void checkEvents() override + void updateDecorations() override { + Layout::updateDecorations(); setPanel(); - Layout::checkEvents(); } void setPanel() diff --git a/radio/src/gui/colorlcd/libui/bitmapbuffer_fileio.cpp b/radio/src/gui/colorlcd/libui/bitmapbuffer_fileio.cpp index bcbbbfe3e..e02b458ed 100644 --- a/radio/src/gui/colorlcd/libui/bitmapbuffer_fileio.cpp +++ b/radio/src/gui/colorlcd/libui/bitmapbuffer_fileio.cpp @@ -97,6 +97,8 @@ const stbi_io_callbacks stbCallbacks = {stbc_read, stbc_skip, stbc_eof}; BitmapBuffer *BitmapBuffer::loadBitmap(const char *filename, BitmapFormats fmt) { + if ((filename == nullptr) || (filename[0] == 0)) return nullptr; + FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ); if (result != FR_OK) { return nullptr; diff --git a/radio/src/gui/colorlcd/libui/button.cpp b/radio/src/gui/colorlcd/libui/button.cpp index 9f1aefe69..a7179a821 100644 --- a/radio/src/gui/colorlcd/libui/button.cpp +++ b/radio/src/gui/colorlcd/libui/button.cpp @@ -150,17 +150,21 @@ MomentaryButton::MomentaryButton(Window* parent, const rect_t& rect, std::string lv_obj_center(label); } -void MomentaryButton::onPressed() +bool MomentaryButton::customEventHandler(lv_event_code_t code) { - if (pressHandler) - pressHandler(); - lv_obj_add_state(lvobj, LV_STATE_CHECKED); - lv_obj_clear_state(lvobj, LV_STATE_PRESSED); -} - -void MomentaryButton::onReleased() -{ - if (releaseHandler) - releaseHandler(); - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + switch (code) { + case LV_EVENT_PRESSED: + if (pressHandler) + pressHandler(); + lv_obj_add_state(lvobj, LV_STATE_CHECKED); + lv_obj_clear_state(lvobj, LV_STATE_PRESSED); + return true; + case LV_EVENT_RELEASED: + if (releaseHandler) + releaseHandler(); + lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + return true; + default: + return false; + }; } diff --git a/radio/src/gui/colorlcd/libui/button.h b/radio/src/gui/colorlcd/libui/button.h index 3527a389c..2740bd8df 100644 --- a/radio/src/gui/colorlcd/libui/button.h +++ b/radio/src/gui/colorlcd/libui/button.h @@ -140,9 +140,6 @@ class MomentaryButton : public FormField std::string getName() const override { return "MomentaryButton"; } #endif - void onPressed() override; - void onReleased() override; - void setText(std::string value) { if (value != text) { @@ -161,4 +158,6 @@ class MomentaryButton : public FormField std::function releaseHandler; std::string text; lv_obj_t* label = nullptr; + + bool customEventHandler(lv_event_code_t code) override; }; diff --git a/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp index 037053c5b..497816d32 100644 --- a/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/libui/fullscreen_dialog.cpp @@ -23,16 +23,20 @@ #include "edgetx.h" #include "etx_lv_theme.h" +#include "hal/usb_driver.h" #include "hal/watchdog_driver.h" #include "mainwindow.h" #include "os/sleep.h" #include "static.h" +#include "theme_manager.h" #include "view_main.h" +//----------------------------------------------------------------------------- + FullScreenDialog::FullScreenDialog( uint8_t type, std::string title, std::string message, std::string action, const std::function& confirmHandler) : - Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}), + NavWindow(MainWindow::instance(), {0, 0, LCD_W, LCD_H}), type(type), title(std::move(title)), message(std::move(message)), @@ -129,75 +133,26 @@ bool FullScreenDialog::onLongPress() return false; } -void FullScreenDialog::onEvent(event_t event) +#if defined(HARDWARE_KEYS) +void FullScreenDialog::alertCancel() { // Buttons other than RTN or ENTER - if (type == WARNING_TYPE_ALERT) { + if (type == WARNING_TYPE_ALERT) closeDialog(); - killEvents(event); - } -} - -void FullScreenDialog::onCancel() { deleteLater(); } - -void FullScreenDialog::checkEvents() -{ - Window::checkEvents(); - if (closeCondition && closeCondition()) { - deleteLater(); - } -} - -void FullScreenDialog::deleteLater() -{ - if (_deleted) return; - - if (running) { - running = false; - } else { - Window::deleteLater(); - } } +void FullScreenDialog::onPressSYS() { alertCancel(); } +void FullScreenDialog::onPressMDL() { alertCancel(); } +void FullScreenDialog::onPressPGUP() { alertCancel(); } +void FullScreenDialog::onPressPGDN() { alertCancel(); } +void FullScreenDialog::onPressTELE() { alertCancel(); } +#endif void FullScreenDialog::setMessage(const char* text) { if (messageLabel) messageLabel->setText(text); } -void FullScreenDialog::runForever(bool checkPwr) -{ - running = true; - - // reset input devices to avoid - // RELEASED/CLICKED to be called in a loop - lv_indev_reset(nullptr, nullptr); - - while (running) { - resetBacklightTimeout(); - - if (checkPwr) { - auto check = pwrCheck(); - if (check == e_power_off) { - boardOff(); -#if defined(SIMU) - return; -#endif - } else if (check == e_power_press) { - WDG_RESET(); - sleep_ms(1); - continue; - } - } - - checkBacklight(); - WDG_RESET(); - - MainWindow::instance()->run(); - sleep_ms(10); - } - - deleteLater(); -} +//----------------------------------------------------------------------------- void raiseAlert(const char* title, const char* msg, const char* action, uint8_t sound) @@ -207,7 +162,9 @@ void raiseAlert(const char* title, const char* msg, const char* action, LED_ERROR_BEGIN(); auto dialog = new FullScreenDialog(WARNING_TYPE_ALERT, title ? title : "", msg ? msg : "", action ? action : ""); - dialog->runForever(); + MainWindow::instance()->blockUntilClose(true, [=]() { + return dialog->deleted(); + }); LED_ERROR_END(); } @@ -219,18 +176,15 @@ bool confirmationDialog(const char* title, const char* msg, bool checkPwr, auto dialog = new FullScreenDialog(WARNING_TYPE_CONFIRM, title ? title : "", msg ? msg : "", "", [&confirmed]() { confirmed = true; }); - if (closeCondition) { - dialog->setCloseCondition([&confirmed, &closeCondition]() { - if (closeCondition()) { - confirmed = true; - return true; - } else { - return false; - } - }); - } - dialog->runForever(checkPwr); + MainWindow::instance()->blockUntilClose(checkPwr, [&]() { + if (dialog->deleted()) return true; + if (closeCondition && closeCondition()) { + confirmed = true; + dialog->deleteLater(); + } + return confirmed; + }); return confirmed; } diff --git a/radio/src/gui/colorlcd/libui/fullscreen_dialog.h b/radio/src/gui/colorlcd/libui/fullscreen_dialog.h index 7b7f54a4b..6615e84d7 100644 --- a/radio/src/gui/colorlcd/libui/fullscreen_dialog.h +++ b/radio/src/gui/colorlcd/libui/fullscreen_dialog.h @@ -25,7 +25,9 @@ class StaticText; -class FullScreenDialog : public Window +//----------------------------------------------------------------------------- + +class FullScreenDialog : public NavWindow { public: FullScreenDialog(uint8_t type, std::string title, std::string message = "", @@ -41,23 +43,20 @@ class FullScreenDialog : public Window void setMessage(const char* text); - void onEvent(event_t event) override; - void onCancel() override; bool onLongPress() override; - - void deleteLater() override; - - void checkEvents() override; - - void setCloseCondition(std::function handler) - { - closeCondition = std::move(handler); - } - - void runForever(bool checkPwr = true); + void onCancel() override { deleteLater(); } void closeDialog(); +#if defined(HARDWARE_KEYS) + void alertCancel(); + void onPressSYS() override; + void onPressMDL() override; + void onPressPGUP() override; + void onPressPGDN() override; + void onPressTELE() override; +#endif + static LAYOUT_SIZE_SCALED(ALERT_FRAME_TOP, 50, 70) static LAYOUT_SIZE(ALERT_FRAME_HO, LAYOUT_SCALE(120), 2 * ALERT_FRAME_TOP) static constexpr coord_t ALERT_FRAME_HEIGHT = LCD_H - ALERT_FRAME_HO; @@ -80,10 +79,10 @@ class FullScreenDialog : public Window std::string title; std::string message; std::string action; - bool running = false; - std::function closeCondition; std::function confirmHandler; StaticText* messageLabel = nullptr; void build(); }; + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/libui/mainwindow.cpp b/radio/src/gui/colorlcd/libui/mainwindow.cpp index 21d6dcf15..ab5470b22 100644 --- a/radio/src/gui/colorlcd/libui/mainwindow.cpp +++ b/radio/src/gui/colorlcd/libui/mainwindow.cpp @@ -20,10 +20,12 @@ #include "board.h" #include "debug.h" +#include "edgetx.h" #include "keyboard_base.h" #include "layout.h" #include "LvglWrapper.h" #include "etx_lv_theme.h" +#include "os/sleep.h" #include "sdcard.h" #include "view_main.h" @@ -56,7 +58,7 @@ void MainWindow::emptyTrash() trash.clear(); } -void MainWindow::run() +void MainWindow::run(bool trash) { LvglWrapper::instance()->run(); @@ -79,7 +81,8 @@ void MainWindow::run() } } - emptyTrash(); + if (trash) + emptyTrash(); #if defined(DEBUG_WINDOWS) auto delta = timersGetMsTick() - start; @@ -97,11 +100,9 @@ void MainWindow::shutdown() LayoutFactory::deleteCustomScreens(); // clear layer stack first - for (Window* w = Window::topWindow(); w; w = Window::topWindow()) { + for (Window* w = Window::topWindow(); w; w = Window::topWindow()) w->deleteLater(); - } - children.clear(); clear(); emptyTrash(); @@ -128,3 +129,48 @@ bool MainWindow::setBackgroundImage(std::string& fileName) return false; } + +void MainWindow::blockUntilClose(bool checkPwr, std::function closeCondition, bool isError) +{ + // reset input devices to avoid + // RELEASED/CLICKED to be called in a loop + lv_indev_reset(nullptr, nullptr); + + if (isError) { + // refresh screen and turn backlight on + backlightEnable(BACKLIGHT_LEVEL_MAX); + // On startup error wait for power button to be released + while (pwrPressed()) { + WDG_RESET(); + run(false); + sleep_ms(10); + } + } + + while (!closeCondition()) { + if (checkPwr) { + auto check = pwrCheck(); + if (check == e_power_off) { + boardOff(); +#if defined(SIMU) + return; +#endif + } else if (check == e_power_press) { + WDG_RESET(); + sleep_ms(1); + continue; + } + } + + resetBacklightTimeout(); + if (isError) + backlightEnable(BACKLIGHT_LEVEL_MAX); + else + checkBacklight(); + + WDG_RESET(); + + run(false); + sleep_ms(10); + } +} diff --git a/radio/src/gui/colorlcd/libui/mainwindow.h b/radio/src/gui/colorlcd/libui/mainwindow.h index 54f0e589c..06f6fcff9 100644 --- a/radio/src/gui/colorlcd/libui/mainwindow.h +++ b/radio/src/gui/colorlcd/libui/mainwindow.h @@ -45,7 +45,7 @@ class MainWindow: public Window } #endif - void run(); + void run(bool trash = true); bool setBackgroundImage(std::string& fileName); @@ -53,6 +53,8 @@ class MainWindow: public Window void enableWidgetRefresh(bool state) { widgetRefreshEnable = state; } + void blockUntilClose(bool checkPwr, std::function closeCondition, bool isError = false); + protected: lv_obj_t* background = nullptr; const BitmapBuffer *backgroundBitmap = nullptr; diff --git a/radio/src/gui/colorlcd/libui/menu.cpp b/radio/src/gui/colorlcd/libui/menu.cpp index 13bbb89d1..645d97cbd 100644 --- a/radio/src/gui/colorlcd/libui/menu.cpp +++ b/radio/src/gui/colorlcd/libui/menu.cpp @@ -29,9 +29,6 @@ class MenuBody : public TableField { - friend class MenuWindowContent; - friend class Menu; - enum MENU_DIRECTION { DIRECTION_UP = 1, DIRECTION_DOWN = -1 }; class MenuLine @@ -132,17 +129,6 @@ class MenuBody : public TableField int count() const { return lines.size(); } - void onEvent(event_t event) override - { -#if defined(HARDWARE_KEYS) - if (event == EVT_KEY_BREAK(KEY_EXIT)) { - onCancel(); - } else { - TableField::onEvent(event); - } -#endif - } - void addLine(const MaskBitmap* icon_mask, const std::string& text, std::function onPress, std::function isChecked, bool update = true) @@ -197,7 +183,7 @@ class MenuBody : public TableField { Menu* menu = getParentMenu(); if (row < lines.size()) { - if (menu->multiple) { + if (menu->isMultiple()) { if (selectedIndex == (int)row) lines[row]->onPress(); else { @@ -300,11 +286,11 @@ static lv_obj_t* menu_content_create(lv_obj_t* parent) return etx_create(&menu_content_class, parent); } -class MenuWindowContent : public Window +class MenuWindowContent : public NavWindow { public: explicit MenuWindowContent(Menu* parent, coord_t popupWidth) : - Window(parent, rect_t{}, menu_content_create) + NavWindow(parent, rect_t{}, menu_content_create) { setWindowFlag(OPAQUE); @@ -348,6 +334,18 @@ class MenuWindowContent : public Window body->addLine(icon_mask, text, onPress, isChecked, update); } +#if defined(HARDWARE_KEYS) + void onPressPGUP() override + { + Messaging::send(Messaging::MENU_CHANGE_FILTER, -1); + } + + void onPressPGDN() override + { + Messaging::send(Messaging::MENU_CHANGE_FILTER, 1); + } +#endif + static LAYOUT_VAL_SCALED(MENUS_WIDTH, 200) protected: @@ -406,18 +404,10 @@ void Menu::addLine(const std::string &text, std::function onPress, addLine(nullptr, text, onPress, isChecked); } -void Menu::addLineBuffered(const MaskBitmap* icon_mask, const std::string& text, - std::function onPress, - std::function isChecked) -{ - content->addLine(icon_mask, text, std::move(onPress), std::move(isChecked), - false); -} - void Menu::addLineBuffered(const std::string &text, std::function onPress, std::function isChecked) { - addLineBuffered(nullptr, text, onPress, isChecked); + content->addLine(nullptr, text, std::move(onPress), std::move(isChecked), false); } void Menu::updateLines() @@ -432,16 +422,6 @@ void Menu::removeLines() updatePosition(); } -void Menu::onEvent(event_t event) -{ -#if defined(HARDWARE_KEYS) - if (toolbar && (event == EVT_KEY_BREAK(KEY_PAGEDN) || - event == EVT_KEY_BREAK(KEY_PAGEUP))) { - toolbar->onEvent(event); - } -#endif -} - void Menu::onCancel() { if (cancelHandler) cancelHandler(); @@ -474,3 +454,10 @@ unsigned Menu::count() const { return content->count(); } int Menu::selection() const { return content->selection(); } void Menu::select(int index) { content->setIndex(index); } + +void Menu::checkEvents() +{ + ModalWindow::checkEvents(); + if (waitHandler) + waitHandler(); +} diff --git a/radio/src/gui/colorlcd/libui/menu.h b/radio/src/gui/colorlcd/libui/menu.h index 77f007b6e..10d7f99b1 100644 --- a/radio/src/gui/colorlcd/libui/menu.h +++ b/radio/src/gui/colorlcd/libui/menu.h @@ -27,8 +27,6 @@ class MenuToolbar; class Menu : public ModalWindow { - friend class MenuBody; - public: explicit Menu(bool multiple = false, coord_t popupWidth = 0); @@ -51,10 +49,6 @@ class Menu : public ModalWindow void addLine(const std::string &text, std::function onPress, std::function isChecked = nullptr); - void addLineBuffered(const MaskBitmap *icon_mask, const std::string &text, - std::function onPress, - std::function isChecked = nullptr); - void addLineBuffered(const std::string &text, std::function onPress, std::function isChecked = nullptr); @@ -68,19 +62,13 @@ class Menu : public ModalWindow void select(int index); - void onEvent(event_t event) override; void onCancel() override; - - void checkEvents() override - { - ModalWindow::checkEvents(); - if (waitHandler) { - waitHandler(); - } - } + void checkEvents() override; void handleLongPress(); + bool isMultiple() const { return multiple; } + protected: bool multiple; MenuWindowContent *content; diff --git a/radio/src/gui/colorlcd/libui/menutoolbar.cpp b/radio/src/gui/colorlcd/libui/menutoolbar.cpp index b6f158df2..1856b51b9 100644 --- a/radio/src/gui/colorlcd/libui/menutoolbar.cpp +++ b/radio/src/gui/colorlcd/libui/menutoolbar.cpp @@ -80,6 +80,13 @@ MenuToolbar::MenuToolbar(Choice* choice, Menu* menu, const int columns) : addButton(STR_SELECT_MENU_ALL, choice->getMin(), choice->getMax(), nullptr, nullptr, true); + + changeFilterMsg.subscribe(Messaging::MENU_CHANGE_FILTER, [=](uint32_t param) { + if (param == 1) + nextFilter(); + else + prevFilter(); + }); } MenuToolbar::~MenuToolbar() { lv_group_del(group); } @@ -93,15 +100,16 @@ void MenuToolbar::resetFilter() } } -void MenuToolbar::onEvent(event_t event) +void MenuToolbar::nextFilter() { - if (event == EVT_KEY_BREAK(KEY_PAGEDN)) { - lv_group_focus_next(group); - } - else if (event == EVT_KEY_BREAK(KEY_PAGEUP)) { - lv_group_focus_prev(group); - } + lv_group_focus_next(group); + auto obj = lv_group_get_focused(group); + lv_event_send(obj, LV_EVENT_CLICKED, nullptr); +} +void MenuToolbar::prevFilter() +{ + lv_group_focus_prev(group); auto obj = lv_group_get_focused(group); lv_event_send(obj, LV_EVENT_CLICKED, nullptr); } diff --git a/radio/src/gui/colorlcd/libui/menutoolbar.h b/radio/src/gui/colorlcd/libui/menutoolbar.h index d011a09f8..548dc761a 100644 --- a/radio/src/gui/colorlcd/libui/menutoolbar.h +++ b/radio/src/gui/colorlcd/libui/menutoolbar.h @@ -21,6 +21,7 @@ #include "button.h" #include "choice.h" #include "listbox.h" +#include "messaging.h" class Menu; @@ -34,14 +35,14 @@ class MenuToolbarButton : public ButtonBase class MenuToolbar : public Window { - // friend Menu; - public: MenuToolbar(Choice* choice, Menu* menu, const int columns); ~MenuToolbar(); void resetFilter(); - void onEvent(event_t event) override; + + void nextFilter(); + void prevFilter(); virtual void longPress() {} @@ -54,6 +55,7 @@ class MenuToolbar : public Window int nxtBtnPos = 0; int filterColumns = 0; MenuToolbarButton* allBtn = nullptr; + Messaging changeFilterMsg; lv_group_t* group; diff --git a/radio/src/gui/colorlcd/libui/messaging.cpp b/radio/src/gui/colorlcd/libui/messaging.cpp new file mode 100644 index 000000000..2eaf4ea67 --- /dev/null +++ b/radio/src/gui/colorlcd/libui/messaging.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * libopenui - https://github.com/opentx/libopenui + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "messaging.h" + +#include +#include + +static std::list subscriptions; + +Messaging::~Messaging() +{ + unsubscribe(); +} + +void Messaging::subscribe(uint32_t _id, std::function cb) +{ + unsubscribe(); + id = _id; + callback = cb; + subscriptions.emplace_back(this); +} + +void Messaging::unsubscribe() +{ + if (id) { + auto s = std::find_if(subscriptions.begin(), subscriptions.end(), + [=](Messaging* lh) -> bool { return lh == this; }); + if (s != subscriptions.end()) subscriptions.erase(s); + } + + callback = nullptr; + id = 0; +} + +void Messaging::send(uint32_t id) +{ + send(id, 0); +} + +void Messaging::send(uint32_t msgId, uint32_t msgData) +{ + for (auto it = subscriptions.rbegin(); it != subscriptions.rend(); ++it) { + Messaging* m = *it; + if (m->id == msgId && m->callback) + m->callback(msgData); + } +} diff --git a/radio/src/gui/colorlcd/libui/messaging.h b/radio/src/gui/colorlcd/libui/messaging.h new file mode 100644 index 000000000..6bde89378 --- /dev/null +++ b/radio/src/gui/colorlcd/libui/messaging.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * libopenui - https://github.com/opentx/libopenui + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include +#include + +class Messaging +{ + public: + enum MessageId + { + COLOR_CHANGED = 1, + COLOR_PREVIEW, + MODULE_UPDATE, + CURVE_UPDATE, + CURVE_EDIT, + DECORATION_UPDATE, + QUICK_MENU_ITEM_SELECT, + MENU_CHANGE_FILTER, + REFRESH, + REFRESH_OUTPUTS_WIDGET, + }; + + Messaging() {} + ~Messaging(); + void subscribe(uint32_t id, std::function cb); + void unsubscribe(); + + uint32_t getUIntParam(); + bool getBoolParam(); + + static void send(uint32_t id); + static void send(uint32_t id, uint32_t data); + + protected: + uint32_t id = 0; + std::function callback = nullptr; +}; diff --git a/radio/src/gui/colorlcd/libui/page.cpp b/radio/src/gui/colorlcd/libui/page.cpp index c9b4f8061..5899d9681 100644 --- a/radio/src/gui/colorlcd/libui/page.cpp +++ b/radio/src/gui/colorlcd/libui/page.cpp @@ -87,7 +87,7 @@ Page::Page(EdgeTxIcon icon, PaddingSize padding, bool pauseRefresh) : #if VERSION_MAJOR == 2 addCustomButton(0, 0, [=]() { onCancel(); }); #else - addCustomButton(0, 0, [=]() { openMenu(); }); + addCustomButton(0, 0, [=]() { QuickMenu::openQuickMenu(); }); addCustomButton(LCD_W - EdgeTxStyles::MENU_HEADER_HEIGHT, 0, [=]() { onCancel(); }); #endif #endif @@ -104,31 +104,17 @@ Page::Page(EdgeTxIcon icon, PaddingSize padding, bool pauseRefresh) : pushLayer(true); body->padAll(padding); -} -void Page::openMenu() -{ - PageGroup* p = Window::pageGroup(); - QMPage qmPage = QM_NONE; - if (p) - qmPage = p->getCurrentTab()->pageId(); - QuickMenu::openQuickMenu( - [=](bool close) { - onCancel(); - if (p) { - while (!Window::topWindow()->isPageGroup()) { - Window::topWindow()->deleteLater(); - } - if (close) - Window::topWindow()->onCancel(); - } - }, p, qmPage); + quickMenuMsg.subscribe(Messaging::QUICK_MENU_ITEM_SELECT, + [=](uint32_t param) { + onCancel(); + }); } void Page::onCancel() { - QuickMenu::closeQuickMenu(); - deleteLater(); + if (!_deleted) + deleteLater(); } void Page::onClicked() { Keyboard::hide(false); } @@ -151,7 +137,7 @@ void Page::onPressSYS() { QMPage pg = g_eeGeneral.getKeyShortcut(EVT_KEY_BREAK(KEY_SYS)); if (pg == QM_OPEN_QUICK_MENU) { - if (!QuickMenu::isOpen()) openMenu(); + QuickMenu::openQuickMenu(); } else { auto p = navWindow(); if (p) { @@ -218,7 +204,7 @@ SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, bool header->setTitle2(subtitle); } -SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount) : +SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, const SetupLineDef* setupLines) : Page(icon, PAD_SMALL, true) { body->padBottom(PAD_LARGE * 2); @@ -226,14 +212,19 @@ SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, Setup header->setTitle(title); header->setTitle2(subtitle); - SetupLine::showLines(body, y, EDT_X, PAD_SMALL, setupLines, lineCount); + SetupLine::showLines(body, y, EDT_X, PAD_SMALL, setupLines); enableRefresh(); } -Window* SubPage::setupLine(const char* title, std::function createEdit, coord_t lblYOffset) +Window* SubPage::setupLine(const char* title, std::function createEdit, coord_t lblYOffset) { auto w = new SetupLine(body, y, EDT_X, PAD_SMALL, title, createEdit, lblYOffset); y += w->height(); return w; } + +void SubPage::useFlexLayout() +{ + body->setFlexLayout(); +} diff --git a/radio/src/gui/colorlcd/libui/page.h b/radio/src/gui/colorlcd/libui/page.h index d2a6d418e..97cda57de 100644 --- a/radio/src/gui/colorlcd/libui/page.h +++ b/radio/src/gui/colorlcd/libui/page.h @@ -22,6 +22,7 @@ #pragma once #include "static.h" +#include "messaging.h" class PageHeader : public Window { @@ -54,11 +55,10 @@ class Page : public NavWindow void enableRefresh(); - void openMenu(); - protected: PageHeader* header = nullptr; Window* body = nullptr; + Messaging quickMenuMsg; bool bubbleEvents() override { return false; } @@ -79,9 +79,11 @@ class SubPage : public Page { public: SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, bool pauseRefresh = false); - SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount); + SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, const SetupLineDef* setupLines); - Window* setupLine(const char* title, std::function createEdit, coord_t lblYOffset = 0); + Window* setupLine(const char* title, std::function createEdit, coord_t lblYOffset = 0); + + void useFlexLayout(); static LAYOUT_SIZE(EDT_X, LCD_W * 9 / 20, LCD_W * 8 / 20) diff --git a/radio/src/gui/colorlcd/libui/popups.cpp b/radio/src/gui/colorlcd/libui/popups.cpp index a23fbc631..5f70b2212 100644 --- a/radio/src/gui/colorlcd/libui/popups.cpp +++ b/radio/src/gui/colorlcd/libui/popups.cpp @@ -33,36 +33,11 @@ static void _run_popup_dialog(const char* title, const char* msg, const char* info = nullptr) { - bool running = true; - - resetBacklightTimeout(); - - // reset input devices to avoid - // RELEASED/CLICKED to be called in a loop - lv_indev_reset(nullptr, nullptr); - auto md = new MessageDialog(title, msg, info); - md->setCloseHandler([&]() { running = false; }); - while (running) { - // Allow power off while showing popup - auto check = pwrCheck(); - if (check == e_power_off) { - boardOff(); -#if defined(SIMU) - // Required so simulator exits cleanly when window closed - return; -#endif - } else if (check == e_power_press) { - WDG_RESET(); - sleep_ms(1); - continue; - } - checkBacklight(); - WDG_RESET(); - MainWindow::instance()->run(); - sleep_ms(20); - } + MainWindow::instance()->blockUntilClose(true, [=]() { + return md->deleted(); + }); } void POPUP_INFORMATION(const char* message) diff --git a/radio/src/gui/colorlcd/libui/table.cpp b/radio/src/gui/colorlcd/libui/table.cpp index ac206c38a..8980c1043 100644 --- a/radio/src/gui/colorlcd/libui/table.cpp +++ b/radio/src/gui/colorlcd/libui/table.cpp @@ -237,55 +237,6 @@ void TableField::adjustScroll() lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF); } -void TableField::selectNext(int16_t dir) -{ - lv_table_t* table = (lv_table_t*)lvobj; - - if (table->col_act == LV_TABLE_CELL_NONE || - table->row_act == LV_TABLE_CELL_NONE) { - if (table->col_cnt > 0 && table->row_cnt > 0) { - table->col_act = 0; - table->row_act = 0; - } - } else { - table->col_act += dir; - if (table->col_act >= table->col_cnt) { - table->col_act = 0; - table->row_act += dir; - - if (table->row_act >= table->row_cnt) { - table->col_act = LV_TABLE_CELL_NONE; - table->row_act = LV_TABLE_CELL_NONE; - - // wrap around - if (table->col_cnt > 0 && table->row_cnt > 0) { - if (dir < 0) { - table->col_act = table->col_cnt - 1; - table->row_act = table->row_cnt - 1; - } else { - table->col_act = 0; - table->row_act = 0; - } - } - } - } - } - - lv_obj_invalidate(lvobj); - adjustScroll(); -} - -void TableField::onEvent(event_t event) -{ - if (event == EVT_ROTARY_RIGHT) { - selectNext(1); - } else if (event == EVT_ROTARY_LEFT) { - selectNext(-1); - } else { - Window::onEvent(event); - } -} - int TableField::getSelected() const { uint16_t row, col; diff --git a/radio/src/gui/colorlcd/libui/table.h b/radio/src/gui/colorlcd/libui/table.h index 94bdcf172..7a35ba589 100644 --- a/radio/src/gui/colorlcd/libui/table.h +++ b/radio/src/gui/colorlcd/libui/table.h @@ -43,7 +43,6 @@ class TableField : public Window virtual void onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) {} void adjustScroll(); - void selectNext(int16_t dir); void setAutoEdit(); bool isAutoEdit() const { return autoedit; } @@ -63,7 +62,6 @@ class TableField : public Window lv_group_t* group = nullptr; lv_group_t* oldGroup = nullptr; - void onEvent(event_t event) override; bool onLongPress() override; void deleteLater() override; diff --git a/radio/src/gui/colorlcd/libui/view_text.cpp b/radio/src/gui/colorlcd/libui/view_text.cpp index 479ea5367..3605ea37c 100644 --- a/radio/src/gui/colorlcd/libui/view_text.cpp +++ b/radio/src/gui/colorlcd/libui/view_text.cpp @@ -25,6 +25,7 @@ #include "etx_lv_theme.h" #include "fullscreen_dialog.h" #include "lib_file.h" +#include "mainwindow.h" #include "menu.h" #include "sdcard.h" @@ -85,17 +86,11 @@ class TextViewer } } - void onEvent(event_t event) - { #if defined(HARDWARE_KEYS) + void changePage(int direction) + { if (int(bufSize) < fileLength) { - if (event == EVT_KEY_BREAK(KEY_PAGEDN)) { - offset += bufSize; - } - - if (event == EVT_KEY_BREAK(KEY_PAGEUP)) { - offset -= bufSize; - } + offset = offset + bufSize * direction; offset = std::max(offset, 0); offset = std::min(offset, fileLength - (int)bufSize); @@ -103,8 +98,8 @@ class TextViewer sdReadTextFileBlock(bufSize, offset); lv_label_set_text_static(lb, buffer); } - #endif } + #endif void build(Window* window) { @@ -268,15 +263,18 @@ void ViewTextWindow::onCancel() checkListOpen = false; } -void ViewTextWindow::onEvent(event_t event) -{ #if defined(HARDWARE_KEYS) - if (textViewer) textViewer->onEvent(event); - - if (event == EVT_KEY_BREAK(KEY_EXIT)) onCancel(); -#endif +void ViewTextWindow::onPressPGUP() +{ + if (textViewer) textViewer->changePage(-1); } +void ViewTextWindow::onPressPGDN() +{ + if (textViewer) textViewer->changePage(1); +} +#endif + class ViewChecklistWindow : public Page, public TextViewer { public: @@ -354,13 +352,6 @@ class ViewChecklistWindow : public Page, public TextViewer } } - void onEvent(event_t event) override - { -#if defined(HARDWARE_KEYS) - if (event == EVT_KEY_BREAK(KEY_EXIT)) onCancel(); -#endif - } - static void checkbox_event_handler(lv_event_t* e) { lv_obj_t* target = lv_event_get_target(e); @@ -494,7 +485,7 @@ static std::string getModelNotesFile() return std::string(""); } -void readModelNotes(bool fromMenu) +static Window* _readModelNotes(bool fromMenu) { std::string modelNotesName = getModelNotesFile(); if (!modelNotesName.empty()) { @@ -502,49 +493,29 @@ void readModelNotes(bool fromMenu) if (isFileAvailable(fullPath.c_str())) { if (fromMenu || !g_model.checklistInteractive) - new ViewTextWindow(std::string(MODELS_PATH), modelNotesName, ICON_MODEL); + return new ViewTextWindow(std::string(MODELS_PATH), modelNotesName, ICON_MODEL); else - new ViewChecklistWindow(std::string(MODELS_PATH), modelNotesName, ICON_MODEL); - } else { + return new ViewChecklistWindow(std::string(MODELS_PATH), modelNotesName, ICON_MODEL); } } + return nullptr; } -class CheckListDialog : public FullScreenDialog +void readModelNotes(bool fromMenu) { - public: - CheckListDialog() : - FullScreenDialog(WARNING_TYPE_ALERT, "", "", "") - { - LED_ERROR_BEGIN(); - checkListOpen = true; - setCloseCondition(std::bind(&CheckListDialog::warningInactive, this)); - readModelNotes(); - } - -#if defined(DEBUG_WINDOWS) - std::string getName() const override { return "CheckListDialog"; } -#endif - - protected: - - bool warningInactive() - { - if (!checkListOpen) { - LED_ERROR_END(); - deleteLater(); - } - return !checkListOpen; - } -}; + _readModelNotes(fromMenu); +} // Blocking version of readModelNotes. void readChecklist() { - std::string s = getModelNotesFile(); - if (!s.empty()) { - auto dialog = new CheckListDialog(); - dialog->runForever(); + auto dialog = _readModelNotes(false); + if (dialog) { + LED_ERROR_BEGIN(); + MainWindow::instance()->blockUntilClose(true, [=]() { + return dialog->deleted(); + }); + LED_ERROR_END(); } } diff --git a/radio/src/gui/colorlcd/libui/view_text.h b/radio/src/gui/colorlcd/libui/view_text.h index cc787002e..901e67c96 100644 --- a/radio/src/gui/colorlcd/libui/view_text.h +++ b/radio/src/gui/colorlcd/libui/view_text.h @@ -43,10 +43,13 @@ class ViewTextWindow : public Page void delayedInit() override; +#if defined(HARDWARE_KEYS) + void onPressPGUP() override; + void onPressPGDN() override; +#endif + protected: TextViewer* textViewer = nullptr; - - void onEvent(event_t event) override; }; class ModelNotesPage : public PageGroupItem diff --git a/radio/src/gui/colorlcd/libui/window.cpp b/radio/src/gui/colorlcd/libui/window.cpp index d1e79d135..90f3c2b86 100644 --- a/radio/src/gui/colorlcd/libui/window.cpp +++ b/radio/src/gui/colorlcd/libui/window.cpp @@ -74,10 +74,14 @@ void Layer::pop(Window* w) lv_group_t* prevGroup = stack.back().prevGroup; stack.pop_back(); w = back(); - if (prevGroup) { - w->assignLvGroup(prevGroup, true); - } else if (!stack.empty()) { - w->assignLvGroup(stack.back().group, true); + if (w) { + if (prevGroup) { + w->assignLvGroup(prevGroup, true); + } else if (!stack.empty()) { + w->assignLvGroup(stack.back().group, true); + } else { + lv_group_set_default(NULL); + } } else { lv_group_set_default(NULL); } @@ -165,6 +169,8 @@ void Window::eventHandler(lv_event_t *e) if (code == LV_EVENT_DELETE || deleted()) return; + if (customEventHandler(code)) return; + switch (code) { case LV_EVENT_SCROLL: { lv_coord_t scroll_x = lv_obj_get_scroll_x(target); @@ -172,7 +178,7 @@ void Window::eventHandler(lv_event_t *e) if (scrollHandler) scrollHandler(scroll_x, scroll_y); // exclude pointer based scrolling (only focus scrolling) - if (!lv_obj_is_scrolling(target) && !noForcedScroll) { + if (!lv_obj_is_scrolling(target) && ((windowFlags & NO_FORCED_SCROLL) == 0)) { lv_point_t *p = (lv_point_t *)lv_event_get_param(e); lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target); @@ -209,12 +215,6 @@ void Window::eventHandler(lv_event_t *e) TRACE("LONG PRESS[%p]", this); _longPressed = onLongPress(); break; - case LV_EVENT_PRESSED: - onPressed(); - break; - case LV_EVENT_RELEASED: - onReleased(); - break; default: break; } @@ -311,7 +311,7 @@ void Window::pushLayer(bool hideParent) if (!layerCreated) { parentHidden = hideParent; layerCreated = true; - if (parentHidden) Window::topWindow()->hide(); + if (parentHidden && Window::topWindow()) Window::topWindow()->hide(); Layer::push(this); } } @@ -320,7 +320,7 @@ void Window::popLayer() { if (layerCreated) { Layer::pop(this); - if (parentHidden) Window::topWindow()->show(); + if (parentHidden && Window::topWindow()) Window::topWindow()->show(); layerCreated = false; parentHidden = false; } @@ -642,7 +642,7 @@ NavWindow::NavWindow(Window *parent, const rect_t &rect, class SetupTextButton : public TextButton { public: - SetupTextButton(Window* parent, const rect_t& rect, PageButtonDef& entry) : + SetupTextButton(Window* parent, const rect_t& rect, const PageButtonDef& entry) : TextButton(parent, rect, STR_VAL(entry.title)) { setPressHandler([=] { @@ -660,14 +660,17 @@ class SetupTextButton : public TextButton }; SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, - PaddingSize padding, PageDefs pages, coord_t btnHeight) : + PaddingSize padding, const PageButtonDef* pages, coord_t btnHeight) : Window(parent, rect) { padAll(padding); coord_t buttonWidth = (width() - PAD_SMALL * (cols + 1) - PAD_TINY * 2) / cols; - int rows = (pages.size() + cols - 1) / cols; + int size = 0; + for (; pages[size].title; size += 1); + + int rows = (size + cols - 1) / cols; int height = rows * btnHeight + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; if (title) { height += EdgeTxStyles::STD_FONT_HEIGHT + PAD_TINY; @@ -678,12 +681,12 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha new Subtitle(this, title); int n = 0; - int remaining = pages.size(); + int remaining = size; coord_t yo = title ? EdgeTxStyles::STD_FONT_HEIGHT + PAD_TINY : 0; coord_t xw = buttonWidth + PAD_SMALL; coord_t xo = (width() - (cols * xw - PAD_SMALL)) / 2; coord_t x, y; - for (auto& entry : pages) { + for (int p = 0; p < size; p += 1) { if (remaining < cols && (n % cols == 0)) { coord_t space = ((cols - remaining) * xw) / (remaining + 1); xw += space; @@ -692,14 +695,14 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha x = xo + (n % cols) * xw; y = yo + (n / cols) * (btnHeight + PAD_MEDIUM); - new SetupTextButton(this, {x, y, buttonWidth, btnHeight}, entry); + new SetupTextButton(this, {x, y, buttonWidth, btnHeight}, pages[p]); n += 1; remaining -= 1; } } SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, - std::function createEdit, coord_t lblYOffset) : + std::function createEdit, coord_t lblYOffset) : Window(parent, {0, y, LCD_W - padding * 2, 0}) { padAll(PAD_ZERO); @@ -726,11 +729,11 @@ SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize paddin } } -coord_t SetupLine::showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, SetupLineDef* setupLines, int lineCount) +coord_t SetupLine::showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const SetupLineDef* setupLines) { Window* w; - for (int i = 0; i < lineCount; i += 1) { + for (int i = 0; setupLines[i].title || setupLines[i].createEdit; i += 1) { #if !defined(ALL_LANGS) w = new SetupLine(parent, y, col2, padding, setupLines[i].title, setupLines[i].createEdit); #else diff --git a/radio/src/gui/colorlcd/libui/window.h b/radio/src/gui/colorlcd/libui/window.h index 1012c0d6b..d23fd906d 100644 --- a/radio/src/gui/colorlcd/libui/window.h +++ b/radio/src/gui/colorlcd/libui/window.h @@ -25,6 +25,7 @@ #include "definitions.h" #include "edgetx_helpers.h" #include "etx_lv_theme.h" +#include "messaging.h" class FlexGridLayout; class FormLine; @@ -40,8 +41,11 @@ typedef lv_obj_t *(*LvglCreate)(lv_obj_t *); constexpr WindowFlags OPAQUE = 1u << 0u; constexpr WindowFlags NO_FOCUS = 1u << 1u; -constexpr WindowFlags NO_SCROLL = 1u << 2u; -constexpr WindowFlags NO_CLICK = 1u << 3u; +constexpr WindowFlags NO_SCROLL = 1u << 2u; +constexpr WindowFlags NO_CLICK = 1u << 3u; +constexpr WindowFlags NO_FORCED_SCROLL = 1u << 4u; + +//----------------------------------------------------------------------------- class Window { @@ -143,13 +147,10 @@ class Window virtual void onClicked(); virtual void onCancel(); virtual bool onLongPress(); - virtual void onPressed() {} - virtual void onReleased() {} + virtual void checkEvents(); void invalidate(); - virtual void checkEvents(); - void attach(Window *window); void detach(); @@ -164,10 +165,8 @@ class Window inline lv_obj_t *getLvObj() { return lvobj; } virtual bool isTopBar() { return false; } - virtual bool isWidgetsContainer() { return false; } virtual bool isNavWindow() { return false; } virtual bool isPageGroup() { return false; } - virtual bool isBubblePopup() { return false; } void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, @@ -182,8 +181,6 @@ class Window virtual void enable(bool enabled = true); void disable() { enable(false); } - void disableForcedScroll() { noForcedScroll = true; } - void pushLayer(bool hideParent = false); void popLayer(); static Window* topWindow(); @@ -204,7 +201,6 @@ class Window WindowFlags windowFlags = 0; LcdFlags textFlags = 0; - bool noForcedScroll = false; bool _deleted = false; @@ -222,12 +218,15 @@ class Window void eventHandler(lv_event_t *e); static void window_event_cb(lv_event_t *e); + virtual bool customEventHandler(lv_event_code_t code) { return false; } static void delayLoader(lv_event_t* e); void delayLoad(); virtual void delayedInit() {} }; +//----------------------------------------------------------------------------- + class NavWindow : public Window { public: @@ -260,39 +259,39 @@ struct PageButtonDef { std::function createPage; std::function isActive; std::function enabled; - - PageButtonDef( - STR_TYP title, - std::function createPage, - std::function isActive = nullptr, - std::function enabled = nullptr) : - title(title), createPage(std::move(createPage)), isActive(std::move(isActive)), enabled(std::move(enabled)) - {} }; +//----------------------------------------------------------------------------- + class SetupButtonGroup : public Window { public: - typedef std::list PageDefs; - SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, - PaddingSize padding, PageDefs pages, coord_t btnHeight = EdgeTxStyles::UI_ELEMENT_HEIGHT); + PaddingSize padding, const PageButtonDef* pages, coord_t btnHeight = EdgeTxStyles::UI_ELEMENT_HEIGHT); protected: }; +//----------------------------------------------------------------------------- + +class SetupLine; + struct SetupLineDef { STR_TYP title; - std::function createEdit; + std::function createEdit; }; class SetupLine : public Window { public: SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, - std::function createEdit, coord_t lblYOffset = 0); + std::function createEdit, coord_t lblYOffset = 0); - static coord_t showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, SetupLineDef* setupLines, int lineCount); + static coord_t showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const SetupLineDef* setupLines); + + Messaging setupMsg; protected: }; + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/lz4_fonts.h b/radio/src/gui/colorlcd/lz4_fonts.h index 6a90fc201..a5306946f 100644 --- a/radio/src/gui/colorlcd/lz4_fonts.h +++ b/radio/src/gui/colorlcd/lz4_fonts.h @@ -25,7 +25,7 @@ /* Structures for handling LZ4 compressed versions of LVGL fonts - Compressed data is uncompressed at runtime and these strucutres + Compressed data is uncompressed at runtime and these structures then used to rebuild the LVGL font structures. Note: this is dependant on the LVGL font structure formats. If diff --git a/radio/src/gui/colorlcd/mainview/layout.cpp b/radio/src/gui/colorlcd/mainview/layout.cpp index ad1789366..8641ea4c3 100644 --- a/radio/src/gui/colorlcd/mainview/layout.cpp +++ b/radio/src/gui/colorlcd/mainview/layout.cpp @@ -307,36 +307,29 @@ Layout::Layout(Window* parent, const LayoutFactory* factory, uint8_t* zoneMap) : WidgetsContainer(parent, {0, 0, LCD_W, LCD_H}, zoneCount), factory(factory), - decoration(new ViewMainDecoration(this)), zoneMap(zoneMap), screenNum(screenNum) { + decoration = new ViewMainDecoration(this); setWindowFlag(NO_FOCUS); - show(true); + decorationUpdateMsg.subscribe(Messaging::DECORATION_UPDATE, [=](uint32_t param) { updateDecorations(); }); + updateDecorations(); + zoneUpdateRequired = false; } -void Layout::setTrimsVisible(bool visible) +void Layout::updateDecorations() { - decoration->setTrimsVisible(visible); -} - -void Layout::setSlidersVisible(bool visible) -{ - decoration->setSlidersVisible(visible); -} - -void Layout::setFlightModeVisible(bool visible) -{ - decoration->setFlightModeVisible(visible); + // Set visible decoration + decoration->setSlidersVisible(hasSliders()); + decoration->setTrimsVisible(hasTrims()); + decoration->setFlightModeVisible(hasFlightMode()); + zoneUpdateRequired = true; } void Layout::show(bool visible) { - // Set visible decoration - setSlidersVisible(visible && hasSliders()); - setTrimsVisible(visible && hasTrims()); - setFlightModeVisible(visible && hasFlightMode()); - - if (visible) { + decoration->show(visible); + if (visible && zoneUpdateRequired) { + zoneUpdateRequired = false; // and update relevant windows updateZones(); } @@ -352,7 +345,11 @@ rect_t Layout::getMainZone() const zone.w -= 2 * PAD_LARGE; zone.h -= 2 * PAD_LARGE; } - return ViewMain::instance()->getMainZone(zone, hasTopbar()); + if (hasTopbar()) { + zone.y += EdgeTxStyles::MENU_HEADER_HEIGHT; + zone.h -= EdgeTxStyles::MENU_HEADER_HEIGHT; + } + return zone; } rect_t Layout::getZone(unsigned int index) const diff --git a/radio/src/gui/colorlcd/mainview/layout.h b/radio/src/gui/colorlcd/mainview/layout.h index 5fa76be9a..f14b85ecf 100644 --- a/radio/src/gui/colorlcd/mainview/layout.h +++ b/radio/src/gui/colorlcd/mainview/layout.h @@ -25,6 +25,7 @@ #include "dataconstants.h" #include "widget.h" #include "view_main_decoration.h" +#include "messaging.h" #include @@ -83,8 +84,6 @@ extern const LayoutOption defaultLayoutOptions[]; class Layout: public WidgetsContainer { - friend class LayoutFactory; - public: Layout(Window* parent, const LayoutFactory * factory, int screenNum, uint8_t zoneCount, uint8_t* zoneMap); @@ -114,12 +113,8 @@ class Layout: public WidgetsContainer virtual bool hasTrims() const; virtual bool isMirrored() const; - // Set decoration visibility - void setTrimsVisible(bool visible); - void setSlidersVisible(bool visible); - void setFlightModeVisible(bool visible); - // Updates settings for trims, sliders, pots, etc... + virtual void updateDecorations(); void show(bool visible = true) override; bool isLayout() override { return true; } @@ -127,11 +122,13 @@ class Layout: public WidgetsContainer void removeWidget(unsigned int index) override; protected: - const LayoutFactory * factory = nullptr; - std::unique_ptr decoration; + const LayoutFactory* factory = nullptr; + ViewMainDecoration* decoration = nullptr; uint8_t* zoneMap = nullptr; int screenNum; rect_t lastMainZone = {0,0,0,0}; + Messaging decorationUpdateMsg; + bool zoneUpdateRequired = false; // Last time we refreshed the window uint32_t lastRefresh = 0; diff --git a/radio/src/gui/colorlcd/mainview/screen_setup.cpp b/radio/src/gui/colorlcd/mainview/screen_setup.cpp index aa0a73ad0..7dd76932c 100644 --- a/radio/src/gui/colorlcd/mainview/screen_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/screen_setup.cpp @@ -51,7 +51,7 @@ class LayoutChoice : public Button LayoutFactorySetter setValue) : Button(parent, {0, 0, LayoutFactory::BM_W + PAD_LARGE + PAD_SMALL, LayoutFactory::BM_H + PAD_LARGE + PAD_SMALL}), getValue(std::move(getValue)), - _setValue(std::move(setValue)) + setValue(std::move(setValue)) { padAll(PAD_ZERO); canvas = lv_canvas_create(lvobj); @@ -64,7 +64,7 @@ class LayoutChoice : public Button auto menu = new Menu(); for (auto layout : LayoutFactory::getRegisteredLayouts()) { menu->addLine(layout->getBitmap(), layout->getName(), - [=]() { setValue(layout); }); + [=]() { if (setValue) setValue(layout); update(); }); } auto it = @@ -82,7 +82,7 @@ class LayoutChoice : public Button protected: lv_obj_t* canvas = nullptr; std::function getValue; - std::function _setValue; + std::function setValue; void update() { @@ -98,12 +98,6 @@ class LayoutChoice : public Button lv_coord_t h = bitmap->height; lv_canvas_set_buffer(canvas, (void*)&bitmap->data[0], w, h, LV_IMG_CF_ALPHA_8BIT); } - - void setValue(const LayoutFactory* layout) - { - if (_setValue) _setValue(layout); - update(); - } }; #if LANDSCAPE @@ -117,14 +111,8 @@ static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; ScreenSetupPage::ScreenSetupPage(unsigned index, const PageDef& pageDef) : - PageGroupItem(pageDef) + PageGroupItem(pageDef), customScreenIndex(index) { - update(index + QuickMenu::pageIndex(QM_UI_SCREEN1)); -} - -void ScreenSetupPage::update(uint8_t index) -{ - customScreenIndex = index - QuickMenu::pageIndex(QM_UI_SCREEN1); } void ScreenSetupPage::build(Window* window) @@ -271,12 +259,19 @@ void ScreenSetupPage::buildLayoutOptions() GET_DEFAULT(value->boolValue), [=](int newValue) { value->boolValue = newValue; + Messaging::send(Messaging::DECORATION_UPDATE); SET_DIRTY(); }); break; case LayoutOption::Color: - new ColorPicker(line, rect_t{}, GET_SET_DEFAULT(value->unsignedValue)); + new ColorPicker(line, rect_t{}, + GET_DEFAULT(value->unsignedValue), + [=](int newValue) { + value->unsignedValue = newValue; + Messaging::send(Messaging::DECORATION_UPDATE); + SET_DIRTY(); + }); break; default: diff --git a/radio/src/gui/colorlcd/mainview/screen_setup.h b/radio/src/gui/colorlcd/mainview/screen_setup.h index 7c1659ec5..ca0665653 100644 --- a/radio/src/gui/colorlcd/mainview/screen_setup.h +++ b/radio/src/gui/colorlcd/mainview/screen_setup.h @@ -45,8 +45,6 @@ class ScreenSetupPage : public PageGroupItem void build(Window* form) override; - void update(uint8_t index) override; - static void addScreen(); protected: diff --git a/radio/src/gui/colorlcd/mainview/topbar.cpp b/radio/src/gui/colorlcd/mainview/topbar.cpp index d869b2452..332406bd7 100644 --- a/radio/src/gui/colorlcd/mainview/topbar.cpp +++ b/radio/src/gui/colorlcd/mainview/topbar.cpp @@ -72,7 +72,7 @@ bool TopBarPersistentData::isWidget(int idx, const char* s) //----------------------------------------------------------------------------- SetupTopBarWidgetsPage::SetupTopBarWidgetsPage() : - Window(ViewMain::instance(), rect_t{}) + NavWindow(ViewMain::instance(), rect_t{}) { // remember focus pushLayer(); @@ -116,33 +116,15 @@ void SetupTopBarWidgetsPage::deleteLater() storageDirty(EE_MODEL); } -void SetupTopBarWidgetsPage::onEvent(event_t event) -{ -#if defined(HARDWARE_KEYS) - if (event == EVT_KEY_FIRST(KEY_PAGEUP) || event == EVT_KEY_FIRST(KEY_PAGEDN) || - event == EVT_KEY_FIRST(KEY_SYS) || event == EVT_KEY_FIRST(KEY_MODEL)) { - killEvents(event); - } else if (event == EVT_KEY_FIRST(KEY_TELE)) { - onCancel(); - } else { - Window::onEvent(event); - } -#else - Window::onEvent(event); -#endif -} - //----------------------------------------------------------------------------- -constexpr uint32_t TOPBAR_REFRESH = 1000 / 10; // 10 Hz - TopBar::TopBar(Window * parent) : WidgetsContainer(parent, {0, 0, LCD_W, EdgeTxStyles::MENU_HEADER_HEIGHT}, MAX_TOPBAR_ZONES) { setWindowFlag(NO_FOCUS); etx_solid_bg(lvobj, COLOR_THEME_SECONDARY1_INDEX); - headerIcon = new HeaderIcon(parent, ICON_EDGETX, [=]() { ViewMain::instance()->openMenu(); }); + headerIcon = new HeaderIcon(parent, ICON_EDGETX, [=]() { QuickMenu::openQuickMenu(); }); } unsigned int TopBar::getZonesCount() const @@ -203,15 +185,6 @@ coord_t TopBar::getVisibleHeight(float visible) const // 0.0 -> 1.0 return (coord_t)h; } -void TopBar::checkEvents() -{ - uint32_t now = lv_tick_get(); - if (now - lastRefresh >= TOPBAR_REFRESH) { - lastRefresh = now; - WidgetsContainer::checkEvents(); - } -} - void TopBar::removeWidget(unsigned int index) { if (index >= zoneCount) return; diff --git a/radio/src/gui/colorlcd/mainview/topbar.h b/radio/src/gui/colorlcd/mainview/topbar.h index 9be71762a..0a313d48d 100644 --- a/radio/src/gui/colorlcd/mainview/topbar.h +++ b/radio/src/gui/colorlcd/mainview/topbar.h @@ -27,7 +27,7 @@ class HeaderIcon; //----------------------------------------------------------------------------- -class SetupTopBarWidgetsPage : public Window +class SetupTopBarWidgetsPage : public NavWindow { public: explicit SetupTopBarWidgetsPage(); @@ -38,8 +38,19 @@ class SetupTopBarWidgetsPage : public Window void onClicked() override; void onCancel() override; - void onEvent(event_t event) override; void deleteLater() override; + +#if defined(HARDWARE_KEYS) + void onPressSYS() override {} + void onLongPressSYS() override {} + void onPressMDL() override {} + void onLongPressMDL() override {} + void onPressPGUP() override {} + void onPressPGDN() override {} + void onLongPressPGUP() override {} + void onLongPressPGDN() override {} + void onPressTELE() override { onCancel(); } +#endif }; //----------------------------------------------------------------------------- @@ -64,8 +75,6 @@ class TopBar: public WidgetsContainer void setEdgeTxButtonVisible(float visible); coord_t getVisibleHeight(float visible) const; // 0.0 -> 1.0 - void checkEvents() override; - bool isTopBar() override { return true; } void removeWidget(unsigned int index) override; diff --git a/radio/src/gui/colorlcd/mainview/view_logical_switches.cpp b/radio/src/gui/colorlcd/mainview/view_logical_switches.cpp index 953bf6935..2e81f0859 100644 --- a/radio/src/gui/colorlcd/mainview/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/mainview/view_logical_switches.cpp @@ -25,6 +25,7 @@ #include "edgetx.h" #include "etx_lv_theme.h" #include "quick_menu.h" +#include "static.h" #include "switches.h" #if PORTRAIT @@ -208,34 +209,6 @@ class LogicalSwitchDisplayFooter : public Window lv_obj_t* lsDelay = nullptr; }; -class LogicalSwitchDisplayButton : public TextButton -{ - public: - LogicalSwitchDisplayButton(Window* parent, const rect_t& rect, - std::string text, unsigned index) : - TextButton(parent, rect, std::move(text), nullptr), index(index) - { - } - - void checkEvents() override - { - bool newvalue = getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + index); - if (value != newvalue) { - if (newvalue) { - lv_obj_add_state(lvobj, LV_STATE_CHECKED); - } else { - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); - } - value = newvalue; - } - ButtonBase::checkEvents(); - } - - protected: - unsigned index = 0; - bool value = false; -}; - LogicalSwitchesViewPage::LogicalSwitchesViewPage() : PageGroupItem(STR_MONITOR_SWITCHES, QM_TOOLS_LS_MON) { @@ -273,24 +246,13 @@ void LogicalSwitchesViewPage::build(Window* window) strAppendSigned(&lsString[1], i + 1, 2); if (isActive) { - auto button = new LogicalSwitchDisplayButton( - window, {x, y, BTN_WIDTH, btnHeight}, lsString, i); - - button->setFocusHandler([=](bool focus) { - if (focus) { - footer->setIndex(i); - } - return 0; - }); + auto button = new TextButton(window, {x, y, BTN_WIDTH, btnHeight}, lsString); + button->setCheckHandler([=]() { button->check(getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + i)); }); + button->setFocusHandler([=](bool focus) { if (focus) { footer->setIndex(i); } }); } else { if (btnHeight > EdgeTxStyles::STD_FONT_HEIGHT) y += (btnHeight - EdgeTxStyles::STD_FONT_HEIGHT) / 2; - auto lbl = etx_label_create(window->getLvObj()); - lv_obj_set_size(lbl, BTN_WIDTH, btnHeight); - lv_obj_set_pos(lbl, x, y); - etx_obj_add_style(lbl, styles->text_align_center, LV_PART_MAIN); - etx_txt_color(lbl, COLOR_THEME_DISABLED_INDEX); - lv_label_set_text(lbl, lsString.c_str()); + new StaticText(window, {x, y, BTN_WIDTH, btnHeight}, lsString, COLOR_THEME_DISABLED_INDEX, CENTERED); } } } diff --git a/radio/src/gui/colorlcd/mainview/view_main.cpp b/radio/src/gui/colorlcd/mainview/view_main.cpp index c26905003..70e1fe3eb 100644 --- a/radio/src/gui/colorlcd/mainview/view_main.cpp +++ b/radio/src/gui/colorlcd/mainview/view_main.cpp @@ -108,14 +108,6 @@ ViewMain::ViewMain() : ViewMain::~ViewMain() { _instance = nullptr; } -void ViewMain::deleteLater() -{ - if (_deleted) return; - - NavWindow::deleteLater(); - QuickMenu::shutdownQuickMenu(); -} - void ViewMain::addMainView(WidgetsContainer* view, uint32_t viewId) { TRACE("addMainView(0x%p, %d)", view, viewId); @@ -141,18 +133,6 @@ unsigned ViewMain::getMainViewsCount() const return lv_obj_get_child_cnt(tile_view); } -rect_t ViewMain::getMainZone(rect_t zone, bool hasTopbar) const -{ - if (isVisible) { - auto visibleHeight = topbar->getVisibleHeight(hasTopbar ? 1.0 : 0.0); - zone.y += visibleHeight; - zone.h -= visibleHeight; - return zone; - } else { - return {0, 0, LCD_W, LCD_H}; - } -} - unsigned ViewMain::getCurrentMainView() const { return lv_obj_get_scroll_x(tile_view) / width(); @@ -248,9 +228,8 @@ void ViewMain::doKeyShortcut(event_t event) { QMPage pg = g_eeGeneral.getKeyShortcut(event); if (pg == QM_OPEN_QUICK_MENU) { - if (!QuickMenu::isOpen()) openMenu(); + QuickMenu::openQuickMenu(); } else { - QuickMenu::closeQuickMenu(); QuickMenu::openPage(pg); } } @@ -263,20 +242,18 @@ void ViewMain::onLongPressTELE() { doKeyShortcut(EVT_KEY_LONG(KEY_TELE)); } void ViewMain::onPressPGUP() { if (!widget_select) { - QuickMenu::closeQuickMenu(); previousMainView(); } } void ViewMain::onPressPGDN() { if (!widget_select) { - QuickMenu::closeQuickMenu(); nextMainView(); } } #endif -void ViewMain::onClicked() { openMenu(); } +void ViewMain::onClicked() { QuickMenu::openQuickMenu(); } void ViewMain::onCancel() { @@ -333,11 +310,6 @@ bool ViewMain::enableWidgetSelect(bool enable) return true; } -void ViewMain::openMenu() -{ - QuickMenu::openQuickMenu(); -} - void ViewMain::ws_timer(lv_timer_t* t) { ViewMain* view = (ViewMain*)t->user_data; diff --git a/radio/src/gui/colorlcd/mainview/view_main.h b/radio/src/gui/colorlcd/mainview/view_main.h index 7fcc66ad5..ad2270110 100644 --- a/radio/src/gui/colorlcd/mainview/view_main.h +++ b/radio/src/gui/colorlcd/mainview/view_main.h @@ -45,10 +45,6 @@ class ViewMain : public NavWindow void updateTopbarVisibility(); bool enableWidgetSelect(bool enable); - // Get the available space in the middle of the screen - // (without topbar) - rect_t getMainZone(rect_t zone, bool hasTopbar) const; - unsigned getMainViewsCount() const; unsigned getCurrentMainView() const; void setCurrentMainView(unsigned view); @@ -60,7 +56,6 @@ class ViewMain : public NavWindow void onClicked() override; void onCancel() override; - void openMenu(); bool onLongPress() override; void show(bool visible = true) override; @@ -85,8 +80,6 @@ class ViewMain : public NavWindow bool widget_select = false; lv_timer_t* widget_select_timer = nullptr; - void deleteLater() override; - // Set topbar visibility [0.0 -> 1.0] void setTopbarVisible(float visible); void setEdgeTxButtonVisible(float visible); diff --git a/radio/src/gui/colorlcd/mainview/view_main_decoration.cpp b/radio/src/gui/colorlcd/mainview/view_main_decoration.cpp index 229a46cb2..3bbcee6f6 100644 --- a/radio/src/gui/colorlcd/mainview/view_main_decoration.cpp +++ b/radio/src/gui/colorlcd/mainview/view_main_decoration.cpp @@ -30,23 +30,24 @@ #include "hal/adc_driver.h" -ViewMainDecoration::ViewMainDecoration(Window* parent, bool showTrims, bool showSliders, bool showFM) : - parent(parent), showTrims(showTrims), showSliders(showSliders), showFM(showFM) +ViewMainDecoration::ViewMainDecoration(Window* parent, bool calibration) : + Window(parent, {0, 0, parent->width(), parent->height()}) { - w_ml = layoutBox(parent, LV_ALIGN_LEFT_MID, LV_FLEX_FLOW_ROW_REVERSE); - w_mr = layoutBox(parent, LV_ALIGN_RIGHT_MID, LV_FLEX_FLOW_ROW); - w_bl = layoutBox(parent, LV_ALIGN_BOTTOM_LEFT, LV_FLEX_FLOW_COLUMN); - w_br = layoutBox(parent, LV_ALIGN_BOTTOM_RIGHT, LV_FLEX_FLOW_COLUMN); + w_ml = layoutBox(this, LV_ALIGN_LEFT_MID, LV_FLEX_FLOW_ROW_REVERSE); + w_mr = layoutBox(this, LV_ALIGN_RIGHT_MID, LV_FLEX_FLOW_ROW); + w_bl = layoutBox(this, LV_ALIGN_BOTTOM_LEFT, LV_FLEX_FLOW_COLUMN); + w_br = layoutBox(this, LV_ALIGN_BOTTOM_RIGHT, LV_FLEX_FLOW_COLUMN); - w_bc = layoutBox(parent, LV_ALIGN_BOTTOM_MID, LV_FLEX_FLOW_COLUMN); + w_bc = layoutBox(this, LV_ALIGN_BOTTOM_MID, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(w_bc->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); - if (this->showTrims) + if (!calibration) { createTrims(w_ml, w_mr, w_bl, w_br); - if (this->showFM) createFlightMode(w_bc); - if (this->showSliders) - createSliders(w_ml, w_mr, w_bl, w_bc, w_br); + } else { + showTrims = showFM = false; + } + createSliders(w_ml, w_mr, w_bl, w_bc, w_br); } Window* ViewMainDecoration::layoutBox(Window* parent, lv_align_t align, @@ -109,7 +110,7 @@ static bool canTrimShow(int idx) rect_t ViewMainDecoration::getMainZone() const { - coord_t x = 0, w = LCD_W, h = LCD_H; + coord_t x = 0, w = width(), h = height(); coord_t bh = 0; if (showSliders) { diff --git a/radio/src/gui/colorlcd/mainview/view_main_decoration.h b/radio/src/gui/colorlcd/mainview/view_main_decoration.h index 9441b9a4d..4421f4269 100644 --- a/radio/src/gui/colorlcd/mainview/view_main_decoration.h +++ b/radio/src/gui/colorlcd/mainview/view_main_decoration.h @@ -25,10 +25,10 @@ class MainViewTrim; -class ViewMainDecoration +class ViewMainDecoration : public Window { public: - ViewMainDecoration(Window* parent, bool showTrims = true, bool showSliders = true, bool showFM = true); + ViewMainDecoration(Window* parent, bool calibration = false); // Set decoration visibility void setTrimsVisible(bool visible); @@ -65,9 +65,9 @@ class ViewMainDecoration Window* w_bl; Window* w_bc; Window* w_br; - bool showTrims; - bool showSliders; - bool showFM; + bool showTrims = true; + bool showSliders = true; + bool showFM = true; Window* sliders[SLIDERS_MAX] = { 0 }; MainViewTrim* trims[TRIMS_MAX] = { 0 }; diff --git a/radio/src/gui/colorlcd/mainview/widget.cpp b/radio/src/gui/colorlcd/mainview/widget.cpp index 48d6ea661..bccb98295 100644 --- a/radio/src/gui/colorlcd/mainview/widget.cpp +++ b/radio/src/gui/colorlcd/mainview/widget.cpp @@ -160,8 +160,6 @@ Widget::Widget(const WidgetFactory* factory, Window* parent, const rect_t& rect, { setWindowFlag(NO_FOCUS | NO_SCROLL); - if (parent->isTopBar()) fsAllowed = false; - setPressHandler([&]() -> uint8_t { // When ViewMain is in "widget select mode", // the widget is added to a focus group @@ -172,16 +170,16 @@ Widget::Widget(const WidgetFactory* factory, Window* parent, const rect_t& rect, void Widget::openMenu() { - if (fsAllowed && ViewMain::instance()->isAppMode()) + if (!parent->isTopBar() && ViewMain::instance()->isAppMode()) { setFullscreen(true); return; } - if (hasOptions() || fsAllowed) { + if (hasOptions() || !parent->isTopBar()) { Menu* menu = new Menu(); menu->setTitle(getFactory()->getDisplayName()); - if (fsAllowed) { + if (!parent->isTopBar()) { menu->addLine(STR_WIDGET_FULLSCREEN, [&]() { setFullscreen(true); }); } if (hasOptions()) { @@ -209,11 +207,12 @@ void Widget::update() {} void Widget::setFullscreen(bool enable) { - if (!fsAllowed || (enable == fullscreen)) return; + if (!!parent->isTopBar() || (enable == fullscreen)) return; fullscreen = enable; // Show or hide ViewMain widgets and decorations + Messaging::send(Messaging::DECORATION_UPDATE); ViewMain::instance()->show(!enable); // Leave Fullscreen Mode diff --git a/radio/src/gui/colorlcd/mainview/widget.h b/radio/src/gui/colorlcd/mainview/widget.h index 2a80c90c2..2da507ccb 100644 --- a/radio/src/gui/colorlcd/mainview/widget.h +++ b/radio/src/gui/colorlcd/mainview/widget.h @@ -126,7 +126,6 @@ class Widget : public ButtonBase int screenNum; int zoneNum; bool fullscreen = false; - bool fsAllowed = true; bool closeFS = false; lv_obj_t* focusBorder = nullptr; lv_style_t borderStyle; diff --git a/radio/src/gui/colorlcd/mainview/widgets_container.h b/radio/src/gui/colorlcd/mainview/widgets_container.h index 05bc870ad..7fb9a8990 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_container.h +++ b/radio/src/gui/colorlcd/mainview/widgets_container.h @@ -45,7 +45,6 @@ class WidgetsContainer: public Window virtual bool isLayout() { return false; } virtual bool isAppMode() const { return false; } - bool isWidgetsContainer() override { return true; } void deleteLater() override; diff --git a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp index 6becb8e25..0b9223f82 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp @@ -115,7 +115,7 @@ void SetupWidgetsPageSlot::addNewWidget(WidgetsContainer* container, } SetupWidgetsPage::SetupWidgetsPage(uint8_t customScreenIdx) : - Window(ViewMain::instance(), rect_t{}), customScreenIdx(customScreenIdx) + NavWindow(ViewMain::instance(), rect_t{}), customScreenIdx(customScreenIdx) { pushLayer(); @@ -172,20 +172,3 @@ void SetupWidgetsPage::deleteLater() storageDirty(EE_MODEL); } - -void SetupWidgetsPage::onEvent(event_t event) -{ -#if defined(HARDWARE_KEYS) - if (event == EVT_KEY_FIRST(KEY_PAGEUP) || - event == EVT_KEY_FIRST(KEY_PAGEDN) || event == EVT_KEY_FIRST(KEY_SYS) || - event == EVT_KEY_FIRST(KEY_MODEL)) { - killEvents(event); - } else if (event == EVT_KEY_FIRST(KEY_TELE)) { - onCancel(); - } else { - Window::onEvent(event); - } -#else - Window::onEvent(event); -#endif -} diff --git a/radio/src/gui/colorlcd/mainview/widgets_setup.h b/radio/src/gui/colorlcd/mainview/widgets_setup.h index 9db629c62..3c9f4cd86 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_setup.h +++ b/radio/src/gui/colorlcd/mainview/widgets_setup.h @@ -26,7 +26,7 @@ class ScreenMenu; class WidgetsContainer; -class SetupWidgetsPage : public Window +class SetupWidgetsPage : public NavWindow { public: SetupWidgetsPage(uint8_t customScreenIdx); @@ -41,11 +41,21 @@ class SetupWidgetsPage : public Window void onClicked() override; void onCancel() override; +#if defined(HARDWARE_KEYS) + void onPressSYS() override {} + void onLongPressSYS() override {} + void onPressMDL() override {} + void onLongPressMDL() override {} + void onPressPGUP() override {} + void onPressPGDN() override {} + void onLongPressPGUP() override {} + void onLongPressPGDN() override {} +#endif + protected: uint8_t customScreenIdx; unsigned savedView = 0; - void onEvent(event_t event) override; void deleteLater() override; }; diff --git a/radio/src/gui/colorlcd/model/curveedit.cpp b/radio/src/gui/colorlcd/model/curveedit.cpp index 1fb98c22a..f39c47e1f 100644 --- a/radio/src/gui/colorlcd/model/curveedit.cpp +++ b/radio/src/gui/colorlcd/model/curveedit.cpp @@ -38,25 +38,32 @@ static const lv_coord_t default_row_dsc[] = {LV_GRID_CONTENT, class CurveEdit : public Curve { public: - CurveEdit(Window* parent, const rect_t& rect, uint8_t index) : + CurveEdit(Window* parent, const rect_t& rect, uint8_t index, mixsrc_t source) : Curve(parent, rect, [=](int x) -> int { return applyCustomCurve(x, index); }, - [=]() -> int { return getValue(currentSource); } - ), + [=]() -> int { return getValue(currentSource); }), index(index), - current(0) + current(0), + currentSource(source) { setWindowFlag(NO_FOCUS); - updatePreview(); - } - void setCurrentSource(mixsrc_t source) - { - currentSource = source; - if (source) - lockSource = true; - else - lockSource = false; + lockSource = currentSource; + + for (int i = 0; i < 17; i += 1) { + auto p = lv_obj_create(lvobj); + etx_solid_bg(p, COLOR_THEME_PRIMARY2_INDEX); + etx_obj_add_style(p, styles->circle, LV_PART_MAIN); + etx_obj_add_style(p, styles->border, LV_PART_MAIN); + etx_obj_add_style(p, styles->border_color[COLOR_THEME_SECONDARY1_INDEX], LV_PART_MAIN); + lv_obj_set_size(p, POS_PT_SZ, POS_PT_SZ); + lv_obj_add_flag(p, LV_OBJ_FLAG_HIDDEN); + pointDots[i] = p; + } + + previewUpdateMsg.subscribe(Messaging::CURVE_EDIT, [=](uint32_t param) { updatePreview(); }); + + updatePreview(); } void updatePreview() @@ -66,6 +73,25 @@ class CurveEdit : public Curve for (uint8_t i = 0; i < 5 + curve.points; i++) { addPoint(getPoint(index, i)); } + update(); + } + + void clearPoints() + { + points.clear(); + for (int i = 0; i < 17; i += 1) + lv_obj_add_flag(pointDots[i], LV_OBJ_FLAG_HIDDEN); + } + + void addPoint(const point_t& point) + { + int i = points.size(); + coord_t x = getPointX(point.x); + coord_t y = getPointY(point.y); + lv_obj_set_pos(pointDots[i], x - POS_PT_SZ / 2, y - POS_PT_SZ / 2); + lv_obj_clear_flag(pointDots[i], LV_OBJ_FLAG_HIDDEN); + + points.push_back(point); } protected: @@ -73,15 +99,18 @@ class CurveEdit : public Curve uint8_t current; mixsrc_t currentSource = 0; bool lockSource = false; + std::list points; + lv_obj_t* pointDots[17] = { nullptr }; + Messaging previewUpdateMsg; - void checkEvents(void) override + void checkEvents() override { if (!lockSource) { int16_t val = getMovedSource(MIXSRC_FIRST_STICK); if (val > 0) currentSource = val; } - Window::checkEvents(); + Curve::checkEvents(); } }; @@ -98,12 +127,6 @@ class CurveDataEdit : public Window padBottom(PAD_SMALL); } - void setCurveEdit(CurveEdit* _curveEdit) - { - curveEdit = _curveEdit; - update(); - } - void update() { clear(); @@ -137,7 +160,6 @@ class CurveDataEdit : public Window protected: uint8_t index; - CurveEdit* curveEdit; NumberEdit* numEditX[16]; void curvePointsRow(Window* parent, coord_t y, int start, int count, @@ -195,7 +217,7 @@ class CurveDataEdit : public Window numEditX[px + 1]->setMin(newValue); } SET_DIRTY(); - curveEdit->updatePreview(); + Messaging::send(Messaging::CURVE_EDIT); }, CENTERED); } @@ -232,16 +254,15 @@ class CurveDataEdit : public Window [=](int32_t newValue) { points[i + start] = newValue; SET_DIRTY(); - curveEdit->updatePreview(); + Messaging::send(Messaging::CURVE_EDIT); }, CENTERED); } } }; -CurveEditWindow::CurveEditWindow(uint8_t index, - std::function refreshView) : - Page(ICON_MODEL_CURVES, PAD_ZERO), index(index), refreshView(refreshView) +CurveEditWindow::CurveEditWindow(uint8_t index, mixsrc_t source) : + Page(ICON_MODEL_CURVES, PAD_ZERO), index(index), source(source) { buildBody(body); buildHeader(header); @@ -306,7 +327,7 @@ void CurveEditWindow::buildBody(Window* window) auto smooth = new TextButton(iLine, rect_t{0, 0, EdgeTxStyles::EDIT_FLD_WIDTH_NARROW, 0}, STR_SMOOTH, [=]() { g_model.curves[index].smooth = !g_model.curves[index].smooth; - curveEdit->updatePreview(); + Messaging::send(Messaging::CURVE_EDIT); return g_model.curves[index].smooth; }); smooth->check(g_model.curves[index].smooth); @@ -336,7 +357,7 @@ void CurveEditWindow::buildBody(Window* window) curve.type = newValue; } SET_DIRTY(); - curveEdit->updatePreview(); + Messaging::send(Messaging::CURVE_EDIT); if (curveDataEdit) { curveDataEdit->update(); } @@ -364,7 +385,7 @@ void CurveEditWindow::buildBody(Window* window) } curve.points = newValue; SET_DIRTY(); - curveEdit->updatePreview(); + Messaging::send(Messaging::CURVE_EDIT); if (curveDataEdit) { curveDataEdit->update(); } @@ -385,22 +406,16 @@ void CurveEditWindow::buildBody(Window* window) 0, 0, box->width(), box->height() - (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2) * 2}, index); + curveDataEdit->update(); // Curve editor lv_obj_set_flex_align(line->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); - curveEdit = new CurveEdit(line, {0, 0, CurveDataEdit::CURVE_WIDTH, CurveDataEdit::CURVE_WIDTH}, index); - - curveDataEdit->setCurveEdit(curveEdit); + new CurveEdit(line, {0, 0, CurveDataEdit::CURVE_WIDTH, CurveDataEdit::CURVE_WIDTH}, index, source); } void CurveEditWindow::onCancel() { - if (refreshView) refreshView(); + Messaging::send(Messaging::CURVE_UPDATE); Page::onCancel(); } - -void CurveEditWindow::setCurrentSource(mixsrc_t source) -{ - curveEdit->setCurrentSource(source); -} diff --git a/radio/src/gui/colorlcd/model/curveedit.h b/radio/src/gui/colorlcd/model/curveedit.h index 25f30554c..b85732e62 100644 --- a/radio/src/gui/colorlcd/model/curveedit.h +++ b/radio/src/gui/colorlcd/model/curveedit.h @@ -25,23 +25,18 @@ #include "page.h" #include "window.h" -class NumberEdit; -class CurveEdit; class CurveDataEdit; class CurveEditWindow : public Page { public: CurveEditWindow(uint8_t index, - std::function refreshView = nullptr); - - void setCurrentSource(mixsrc_t source); + mixsrc_t source = 0); protected: uint8_t index; - CurveEdit* curveEdit = nullptr; CurveDataEdit* curveDataEdit = nullptr; - std::function refreshView = nullptr; + mixsrc_t source = 0; void buildHeader(Window* window); void buildBody(Window* window); diff --git a/radio/src/gui/colorlcd/model/function_switches.cpp b/radio/src/gui/colorlcd/model/function_switches.cpp index 73c45bb61..d061daf00 100644 --- a/radio/src/gui/colorlcd/model/function_switches.cpp +++ b/radio/src/gui/colorlcd/model/function_switches.cpp @@ -41,19 +41,60 @@ const char* edgetx_fs_manual_url = "https://edgetx.gitbook.io/edgetx-user-manual/b-and-w-radios/model-select/" "setup#function-switches"; -class FunctionSwitch : public Window +//----------------------------------------------------------------------------- + +FunctionSwitchBase::FunctionSwitchBase(Window* parent, uint8_t sw) : + Window(parent, {0, 0, LCD_W - PAD_SMALL * 2, ROW_H}), + switchIndex(sw) +{ + padAll(PAD_TINY); + + std::string s(CHAR_SWITCH); + s += switchGetDefaultName(switchIndex); + + new StaticText(this, {PAD_LARGE, PAD_MEDIUM, SW_W, EdgeTxStyles::STD_FONT_HEIGHT}, s); + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) +#if NARROW_LAYOUT + offLabel = new StaticText(this, {C1_X - C1_W - PAD_TINY, C1_Y + COLLBL_YO, C1_W, 0}, STR_OFF, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); + onLabel = new StaticText(this, {C2_X - C2_W - PAD_TINY, C2_Y + COLLBL_YO, C2_W, 0}, STR_ON_ONE_SWITCHES[0], COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); +#endif + + overrideLabel = new StaticText(this, {OVRLBL_X, C1_Y + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_LARGE, OVRLBL_W, 0}, + STR_LUA_OVERRIDE, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); +#endif +} + +void FunctionSwitchBase::setLEDState(uint8_t typ) +{ +#if defined(FUNCTION_SWITCHES_RGB_LEDS) +#if NARROW_LAYOUT + offLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + onLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); +#endif + offColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + onColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + overrideLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + onOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + offOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); + if (typ != SWITCH_NONE && typ != SWITCH_GLOBAL) + setHeight(ROW_H); + else + setHeight(ROW_HS); +#endif +} + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) +uint8_t FunctionSwitchBase::activeSwitch = 0; +#endif + +//----------------------------------------------------------------------------- + +class FunctionSwitch : public FunctionSwitchBase { public: - FunctionSwitch(Window* parent, uint8_t sw) : - Window(parent, {0, 0, LCD_W - PAD_SMALL * 2, ROW_H}), switchIndex(sw) + FunctionSwitch(Window* parent, uint8_t sw) : FunctionSwitchBase(parent, sw) { - padAll(PAD_TINY); - - std::string s(CHAR_SWITCH); - s += switchGetDefaultName(switchIndex); - - new StaticText(this, {PAD_LARGE, PAD_MEDIUM, SW_W, EdgeTxStyles::STD_FONT_HEIGHT}, s); - new ModelTextEdit(this, {NM_X, 0, NM_W, 0}, g_model.cfsName(switchIndex), LEN_SWITCH_NAME); @@ -118,17 +159,13 @@ class FunctionSwitch : public Window }); #if defined(FUNCTION_SWITCHES_RGB_LEDS) -#if NARROW_LAYOUT - offLabel = new StaticText(this, {C1_X - C1_W - PAD_TINY, C1_Y + COLLBL_YO, C1_W, 0}, STR_OFF, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); - onLabel = new StaticText(this, {C2_X - C2_W - PAD_TINY, C2_Y + COLLBL_YO, C2_W, 0}, STR_ON_ONE_SWITCHES[0], COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); -#endif - offValue = g_model.cfsOffColor(switchIndex); onValue = g_model.cfsOnColor(switchIndex); offColor = new ColorPicker( this, {C1_X, C1_Y, C1_W, 0}, [=]() -> int { // getValue + activeSwitch = switchIndex; return g_model.cfsOffColor(switchIndex).getColor() | RGB888_FLAG; }, [=](int newValue) { // setValue @@ -142,12 +179,12 @@ class FunctionSwitch : public Window offValue = g_model.cfsOffColor(switchIndex); setFSEditOverride(-1, 0); SET_DIRTY(); - }, - [=](int newValue) { previewColor(newValue); }, ETX_RGB888); + }, ETX_RGB888); onColor = new ColorPicker( this, {C2_X, C2_Y, C2_W, 0}, [=]() -> int { // getValue + activeSwitch = switchIndex; return g_model.cfsOnColor(switchIndex).getColor() | RGB888_FLAG; }, [=](int newValue) { // setValue @@ -161,11 +198,8 @@ class FunctionSwitch : public Window onValue = g_model.cfsOnColor(switchIndex); setFSEditOverride(-1, 0); SET_DIRTY(); - }, - [=](int newValue) { previewColor(newValue); }, ETX_RGB888); + }, ETX_RGB888); - overrideLabel = new StaticText(this, {OVRLBL_X, C1_Y + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_LARGE, OVRLBL_W, 0}, - STR_LUA_OVERRIDE, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); offOverride = new ToggleSwitch(this, {OVROFF_X, C1_Y + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE, 0, 0}, [=]() { return g_model.cfsOffColorLuaOverride(switchIndex); }, [=](bool v) { g_model.cfsSetOffColorLuaOverride(switchIndex, v); }); @@ -177,104 +211,15 @@ class FunctionSwitch : public Window setState(); } -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - static LAYOUT_VAL_SCALED(SW_W, 70) - static constexpr coord_t NM_X = SW_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(NM_W, 60) - static constexpr coord_t TP_X = NM_X + NM_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(TP_W, 78) - static constexpr coord_t GR_X = TP_X + TP_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(GR_W, 84) - static constexpr coord_t ST_X = GR_X + GR_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(ST_W, 60) - static constexpr coord_t ROW_HS = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE * 2; -#if NARROW_LAYOUT - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 3 + PAD_OUTLINE * 4; - static constexpr coord_t C1_X = GR_X; - static constexpr coord_t C1_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; - static LAYOUT_VAL_SCALED(C1_W, 40) - static constexpr coord_t C2_X = ST_X; - static constexpr coord_t C2_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; - static LAYOUT_VAL_SCALED(C2_W, 40) - static constexpr coord_t OVRLBL_X = NM_X; - static constexpr coord_t OVRLBL_W = NM_W + TP_W; - static constexpr coord_t OVROFF_X = C1_X; - static constexpr coord_t COLLBL_YO = PAD_MEDIUM; -#else - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 2 + PAD_OUTLINE * 3; - static constexpr coord_t C1_X = ST_X + ST_W + PAD_SMALL; - static constexpr coord_t C1_Y = 0; - static LAYOUT_VAL_SCALED(C1_W, 40) - static constexpr coord_t C2_X = C1_X + C1_W + PAD_SMALL; - static constexpr coord_t C2_Y = 0; - static LAYOUT_VAL_SCALED(C2_W, 40) - static constexpr coord_t OVRLBL_X = GR_X; - static constexpr coord_t OVRLBL_W = GR_W + ST_W - PAD_LARGE; - static constexpr coord_t OVROFF_X = C1_X - PAD_MEDIUM * 2; - static constexpr coord_t COLLBL_YO = PAD_SMALL; -#endif -#else - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE * 2; - static constexpr coord_t SW_W = (LCD_W - PAD_SMALL * 2 - PAD_TINY * 4) / 5; - static constexpr coord_t NM_X = SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(NM_W, 80) - static constexpr coord_t TP_X = NM_X + SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(TP_W, 86) - static constexpr coord_t GR_X = TP_X + SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(GR_W, 94) - static constexpr coord_t ST_X = GR_X + SW_W + PAD_LARGE * 2 + PAD_SMALL; - static LAYOUT_VAL_SCALED(ST_W, 70) -#endif - protected: - uint8_t switchIndex; - Choice* typeChoice = nullptr; Choice* groupChoice = nullptr; - Choice* startChoice = nullptr; -#if defined(FUNCTION_SWITCHES_RGB_LEDS) -#if defined(NARROW_LAYOUT) - StaticText *offLabel = nullptr; - StaticText *onLabel = nullptr; -#endif - ColorPicker* offColor = nullptr; - ColorPicker* onColor = nullptr; - RGBLedColor offValue; - RGBLedColor onValue; - StaticText* overrideLabel = nullptr; - ToggleSwitch* onOverride = nullptr; - ToggleSwitch* offOverride = nullptr; -#endif - int lastType = -1; - -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - void previewColor(int newValue) - { - // Convert color index to RGB - newValue = color32ToRGB(newValue); - setFSEditOverride(switchIndex, newValue); - } -#endif void setState() { uint8_t typ = g_model.cfsType(switchIndex); startChoice->show(typ == SWITCH_2POS && g_model.cfsGroup(switchIndex) == 0); groupChoice->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); -#if defined(FUNCTION_SWITCHES_RGB_LEDS) -#if NARROW_LAYOUT - offLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - onLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); -#endif - offColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - onColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - overrideLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - onOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - offOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - if (typ != SWITCH_NONE && typ != SWITCH_GLOBAL) - setHeight(ROW_H); - else - setHeight(ROW_HS); -#endif + setLEDState(typ); } void checkEvents() override @@ -288,6 +233,8 @@ class FunctionSwitch : public Window } }; +//----------------------------------------------------------------------------- + class SwitchGroup : public Window { public: @@ -360,14 +307,24 @@ class SwitchGroup : public Window Choice* startChoice; }; -ModelFunctionSwitches::ModelFunctionSwitches() : Page(ICON_MODEL_SETUP) +//----------------------------------------------------------------------------- + +FunctionSwitchesBase::FunctionSwitchesBase(EdgeTxIcon icon, const char* title) : Page(icon) { - header->setTitle(STR_MAIN_MENU_MODEL_SETTINGS); + header->setTitle(title); header->setTitle2(STR_FUNCTION_SWITCHES); body->padAll(PAD_TINY); body->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); +#if defined(FUNCTION_SWITCHES_RGB_LEDS) + previewMsg.subscribe(Messaging::COLOR_PREVIEW, [=](uint32_t param) { + // Convert color index to RGB + uint32_t c = color32ToRGB(param); + setFSEditOverride(FunctionSwitchBase::editSwitch(), c); + }); +#endif + auto box = new Window(body, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}); new StaticText(box, {0, 0, FunctionSwitch::SW_W, 0}, STR_SWITCHES); new StaticText(box, {FunctionSwitch::NM_X + PAD_OUTLINE, 0, FunctionSwitch::NM_W, 0}, STR_NAME, COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); @@ -380,20 +337,14 @@ ModelFunctionSwitches::ModelFunctionSwitches() : Page(ICON_MODEL_SETUP) new StaticText(box, {FunctionSwitch::C1_X + PAD_OUTLINE, 0, FunctionSwitch::C1_W, 0}, STR_OFF, COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); new StaticText(box, {FunctionSwitch::C2_X + PAD_OUTLINE, 0, FunctionSwitch::C2_W, 0}, STR_ON_ONE_SWITCHES[0], COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); #endif +} - for (uint8_t i = 0; i < switchGetMaxSwitches(); i += 1) { - if (switchIsCustomSwitch(i)) - new FunctionSwitch(body, i); - } - - for (uint8_t i = 1; i <= 3; i += 1) { - groupLines[i - 1] = new SwitchGroup(body, i); - } - +void FunctionSwitchesBase::addQRCode() +{ #if defined(HARDWARE_TOUCH) body->padBottom(PAD_LARGE); - box = new Window(body, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}); + auto box = new Window(body, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}); new StaticText(box, rect_t{}, STR_MORE_INFO); @@ -403,25 +354,48 @@ ModelFunctionSwitches::ModelFunctionSwitches() : Page(ICON_MODEL_SETUP) lv_qrcode_update(qr, edgetx_fs_manual_url, strlen(edgetx_fs_manual_url)); lv_obj_set_pos(qr, (LCD_W - 150) / 2, EdgeTxStyles::STD_FONT_HEIGHT); #endif +} + +void FunctionSwitchesBase::checkEvents() +{ + setState(); + Page::checkEvents(); +} + +//----------------------------------------------------------------------------- + +ModelFunctionSwitches::ModelFunctionSwitches() : FunctionSwitchesBase(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS) +{ + for (uint8_t i = 0; i < switchGetMaxSwitches(); i += 1) { + if (switchIsCustomSwitch(i)) + new FunctionSwitch(body, i); + } + + for (uint8_t i = 1; i <= 3; i += 1) { + groupLines[i - 1] = new SwitchGroup(body, i); + } + + addQRCode(); setState(); } void ModelFunctionSwitches::setState() { - int cnt = 0; for (int i = 0; i < 3; i += 1) { - cnt += getSwitchCountInFSGroup(i + 1); groupLines[i]->show(firstSwitchInGroup(i + 1) >= 0); groupLines[i]->refresh(); } - startupHeader->show(cnt != NUM_FUNCTIONS_SWITCHES); -} - -void ModelFunctionSwitches::checkEvents() -{ - setState(); - Page::checkEvents(); + bool showStartHeader = false; + for (uint8_t i = 0; i < switchGetMaxSwitches(); i += 1) { + if (switchIsCustomSwitch(i) && g_model.cfsType(i) == SWITCH_2POS && g_model.cfsGroup(i) == 0) { + showStartHeader = true; + break; + } + } + startupHeader->show(showStartHeader); } #endif + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/model/function_switches.h b/radio/src/gui/colorlcd/model/function_switches.h index 2f42e18d4..962c00bbc 100644 --- a/radio/src/gui/colorlcd/model/function_switches.h +++ b/radio/src/gui/colorlcd/model/function_switches.h @@ -23,22 +23,132 @@ #if defined(FUNCTION_SWITCHES) +#include "edgetx.h" #include "page.h" +#include "messaging.h" +class Choice; +class ColorPicker; +class StaticText; class SwitchGroup; +class ToggleSwitch; -class ModelFunctionSwitches : public Page +//----------------------------------------------------------------------------- + +class FunctionSwitchBase : public Window +{ + public: + FunctionSwitchBase(Window* parent, uint8_t sw); + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) + static uint8_t editSwitch() { return activeSwitch; } +#endif + +#if defined(FUNCTION_SWITCHES_RGB_LEDS) + static LAYOUT_VAL_SCALED(SW_W, 70) + static constexpr coord_t NM_X = SW_W + PAD_SMALL; + static LAYOUT_VAL_SCALED(NM_W, 60) + static constexpr coord_t TP_X = NM_X + NM_W + PAD_SMALL; + static LAYOUT_VAL_SCALED(TP_W, 78) + static constexpr coord_t GR_X = TP_X + TP_W + PAD_SMALL; + static LAYOUT_VAL_SCALED(GR_W, 84) + static constexpr coord_t ST_X = GR_X + GR_W + PAD_SMALL; + static LAYOUT_VAL_SCALED(ST_W, 60) + static constexpr coord_t ROW_HS = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE * 2; +#if NARROW_LAYOUT + static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 3 + PAD_OUTLINE * 4; + static constexpr coord_t C1_X = GR_X; + static constexpr coord_t C1_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; + static LAYOUT_VAL_SCALED(C1_W, 40) + static constexpr coord_t C2_X = ST_X; + static constexpr coord_t C2_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; + static LAYOUT_VAL_SCALED(C2_W, 40) + static constexpr coord_t OVRLBL_X = NM_X; + static constexpr coord_t OVRLBL_W = NM_W + TP_W; + static constexpr coord_t OVROFF_X = C1_X; + static constexpr coord_t COLLBL_YO = PAD_MEDIUM; +#else + static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 2 + PAD_OUTLINE * 3; + static constexpr coord_t C1_X = ST_X + ST_W + PAD_SMALL; + static constexpr coord_t C1_Y = 0; + static LAYOUT_VAL_SCALED(C1_W, 40) + static constexpr coord_t C2_X = C1_X + C1_W + PAD_SMALL; + static constexpr coord_t C2_Y = 0; + static LAYOUT_VAL_SCALED(C2_W, 40) + static constexpr coord_t OVRLBL_X = GR_X; + static constexpr coord_t OVRLBL_W = GR_W + ST_W - PAD_LARGE; + static constexpr coord_t OVROFF_X = C1_X - PAD_MEDIUM * 2; + static constexpr coord_t COLLBL_YO = PAD_SMALL; +#endif +#else + static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE * 2; + static constexpr coord_t SW_W = (LCD_W - PAD_SMALL * 2 - PAD_TINY * 4) / 5; + static constexpr coord_t NM_X = SW_W + PAD_TINY; + static LAYOUT_VAL_SCALED(NM_W, 80) + static constexpr coord_t TP_X = NM_X + SW_W + PAD_TINY; + static LAYOUT_VAL_SCALED(TP_W, 86) + static constexpr coord_t GR_X = TP_X + SW_W + PAD_TINY; + static LAYOUT_VAL_SCALED(GR_W, 94) + static constexpr coord_t ST_X = GR_X + SW_W + PAD_LARGE * 2 + PAD_SMALL; + static LAYOUT_VAL_SCALED(ST_W, 70) +#endif + + protected: + uint8_t switchIndex; + Choice* typeChoice = nullptr; + Choice* startChoice = nullptr; +#if defined(FUNCTION_SWITCHES_RGB_LEDS) +#if defined(NARROW_LAYOUT) + StaticText *offLabel = nullptr; + StaticText *onLabel = nullptr; +#endif + ColorPicker* offColor = nullptr; + ColorPicker* onColor = nullptr; + RGBLedColor offValue; + RGBLedColor onValue; + StaticText* overrideLabel = nullptr; + ToggleSwitch* onOverride = nullptr; + ToggleSwitch* offOverride = nullptr; + static uint8_t activeSwitch; +#endif + int lastType = -1; + + void setLEDState(uint8_t typ); +}; + +//----------------------------------------------------------------------------- + +class FunctionSwitchesBase : public Page +{ + public: + FunctionSwitchesBase(EdgeTxIcon icon, const char* title); + + void addQRCode(); + + protected: + BitmapBuffer* qrcode = nullptr; + StaticText* startupHeader = nullptr; +#if defined(FUNCTION_SWITCHES_RGB_LEDS) + Messaging previewMsg; +#endif + + virtual void setState() = 0; + void checkEvents() override; +}; + +//----------------------------------------------------------------------------- + +class ModelFunctionSwitches : public FunctionSwitchesBase { public: ModelFunctionSwitches(); protected: SwitchGroup* groupLines[3] = {nullptr}; - BitmapBuffer* qrcode = nullptr; - StaticText* startupHeader = nullptr; - void setState(); - void checkEvents() override; + void setState() override; }; #endif + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/model/input_edit.cpp b/radio/src/gui/colorlcd/model/input_edit.cpp index 5b8548fc4..eb342a9e2 100644 --- a/radio/src/gui/colorlcd/model/input_edit.cpp +++ b/radio/src/gui/colorlcd/model/input_edit.cpp @@ -47,7 +47,7 @@ static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; class InputEditAdvanced : public Page { public: - InputEditAdvanced(InputEditWindow* parent, uint8_t input_n, uint8_t index) : Page(ICON_MODEL_INPUTS) + InputEditAdvanced(uint8_t input_n, uint8_t index) : Page(ICON_MODEL_INPUTS) { std::string title2(getSourceString(MIXSRC_FIRST_INPUT + input_n)); header->setTitle(STR_MENUINPUTS); @@ -66,7 +66,7 @@ class InputEditAdvanced : public Page [=]() -> int16_t { return 4 - input->mode; }, [=](int16_t newValue) { input->mode = 4 - newValue; - parent->previewUpdate(); + Messaging::send(Messaging::CURVE_UPDATE); SET_DIRTY(); }); @@ -218,10 +218,7 @@ void InputEditWindow::buildBody(Window* form) input->curve.value = newValue; updatePreview = true; SET_DIRTY(); - }, MIXSRC_FIRST, input->srcRaw, - [=]() { - updatePreview = true; - }); + }, MIXSRC_FIRST, input->srcRaw); lv_obj_set_style_grid_cell_x_align(param->getLvObj(), LV_GRID_ALIGN_STRETCH, 0); @@ -229,7 +226,7 @@ void InputEditWindow::buildBody(Window* form) line->padAll(PAD_LARGE); auto btn = new TextButton(line, rect_t{}, LV_SYMBOL_SETTINGS, [=]() -> uint8_t { - new InputEditAdvanced(this, this->input, index); + new InputEditAdvanced(this->input, index); return 0; }); lv_obj_set_width(btn->getLvObj(), lv_pct(100)); @@ -237,6 +234,8 @@ void InputEditWindow::buildBody(Window* form) void InputEditWindow::checkEvents() { + if (_deleted) return; + ExpoData* input = expoAddress(index); getvalue_t val; @@ -292,7 +291,7 @@ void InputEditWindow::checkEvents() if (updatePreview) { updatePreview = false; - if (preview) preview->update(); + Messaging::send(Messaging::CURVE_UPDATE); } Page::checkEvents(); diff --git a/radio/src/gui/colorlcd/model/input_edit.h b/radio/src/gui/colorlcd/model/input_edit.h index acdf2e624..d89283662 100644 --- a/radio/src/gui/colorlcd/model/input_edit.h +++ b/radio/src/gui/colorlcd/model/input_edit.h @@ -30,8 +30,6 @@ class InputEditWindow : public Page public: InputEditWindow(int8_t input, uint8_t index); - void previewUpdate() { updatePreview = true; } - static LAYOUT_SIZE(CURVE_W_LANDSCAPE, 138, 120) static LAYOUT_ORIENTATION_SCALED(INPUT_EDIT_CURVE_WIDTH, CURVE_W_LANDSCAPE, 176) static LAYOUT_ORIENTATION(INPUT_EDIT_CURVE_HEIGHT, INPUT_EDIT_CURVE_WIDTH, LAYOUT_SCALE(132)) diff --git a/radio/src/gui/colorlcd/model/model_curves.cpp b/radio/src/gui/colorlcd/model/model_curves.cpp index b3855c7e0..48f2622cd 100644 --- a/radio/src/gui/colorlcd/model/model_curves.cpp +++ b/radio/src/gui/colorlcd/model/model_curves.cpp @@ -43,7 +43,7 @@ class CurveButton : public Button s = strAppend(s, ":"); strAppend(s, g_model.curves[index].name, LEN_CURVE_NAME); } - title = new StaticText(this, {PAD_SMALL, -1, width() - PAD_MEDIUM * 2, EdgeTxStyles::STD_FONT_HEIGHT}, buf, + title = new StaticText(this, {0, 0, lv_pct(100), EdgeTxStyles::STD_FONT_HEIGHT}, buf, COLOR_THEME_SECONDARY1_INDEX, CENTERED | FONT(BOLD)); etx_txt_color(title->getLvObj(), COLOR_THEME_PRIMARY2_INDEX, LV_PART_MAIN | LV_STATE_USER_1); @@ -51,13 +51,6 @@ class CurveButton : public Button etx_solid_bg(title->getLvObj(), COLOR_THEME_FOCUS_INDEX, LV_PART_MAIN | LV_STATE_USER_1); - hdrLeft = new StaticIcon(this, 0, 0, ICON_ROUND_TITLE_LEFT, - COLOR_THEME_SECONDARY2_INDEX); - auto mask = getBuiltinIcon(ICON_ROUND_TITLE_RIGHT); - hdrRight = new StaticIcon(this, width() - mask->width - PAD_BORDER * 2, 0, - ICON_ROUND_TITLE_RIGHT, - COLOR_THEME_SECONDARY2_INDEX); - // Preview preview = new CurveRenderer( this, @@ -83,19 +76,13 @@ class CurveButton : public Button uint8_t index; StaticText *title; CurveRenderer *preview; - StaticIcon *hdrLeft = nullptr; - StaticIcon *hdrRight = nullptr; void checkEvents() override { if (hasFocus()) { lv_obj_add_state(title->getLvObj(), LV_STATE_USER_1); - hdrLeft->setColor(COLOR_THEME_FOCUS_INDEX); - hdrRight->setColor(COLOR_THEME_FOCUS_INDEX); } else { lv_obj_clear_state(title->getLvObj(), LV_STATE_USER_1); - hdrLeft->setColor(COLOR_THEME_SECONDARY2_INDEX); - hdrRight->setColor(COLOR_THEME_SECONDARY2_INDEX); } } }; @@ -117,7 +104,7 @@ ModelCurvesPage::ModelCurvesPage(const PageDef& pageDef) : PageGroupItem(pageDef // can be called from any other screen to edit a curve. // currently called from model_mixes.cpp on longpress. -void ModelCurvesPage::pushEditCurve(int index, std::function refreshView, mixsrc_t source) +void ModelCurvesPage::pushEditCurve(int index, mixsrc_t source) { if (!isCurveUsed(index)) { CurveHeader &curve = g_model.curves[index]; @@ -125,8 +112,7 @@ void ModelCurvesPage::pushEditCurve(int index, std::function refresh initPoints(curve, points); } - auto cv = new CurveEditWindow(index, refreshView); - cv->setCurrentSource(source); + new CurveEditWindow(index, source); } void ModelCurvesPage::rebuild(Window *window) diff --git a/radio/src/gui/colorlcd/model/model_curves.h b/radio/src/gui/colorlcd/model/model_curves.h index 8fe364fc6..d3f041377 100644 --- a/radio/src/gui/colorlcd/model/model_curves.h +++ b/radio/src/gui/colorlcd/model/model_curves.h @@ -29,7 +29,7 @@ class ModelCurvesPage : public PageGroupItem public: ModelCurvesPage(const PageDef& pageDef); - static void pushEditCurve(int index, std::function refreshView, mixsrc_t source); + static void pushEditCurve(int index, mixsrc_t source); virtual void build(Window* window) override; diff --git a/radio/src/gui/colorlcd/model/model_outputs.cpp b/radio/src/gui/colorlcd/model/model_outputs.cpp index 7ddd89937..b749a728c 100644 --- a/radio/src/gui/colorlcd/model/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model/model_outputs.cpp @@ -28,6 +28,7 @@ #include "getset_helpers.h" #include "list_line_button.h" #include "menu.h" +#include "messaging.h" #include "output_edit.h" #include "toggleswitch.h" @@ -109,6 +110,8 @@ class OutputLineButton : public ListLineButton setHeight(CH_LINE_H); padAll(PAD_ZERO); + refreshMsg.subscribe(Messaging::REFRESH, [=](uint32_t param) { refresh(); }); + delayLoad(); } @@ -187,6 +190,7 @@ class OutputLineButton : public ListLineButton protected: int value = -10000; + Messaging refreshMsg; bool isActive() const override { return false; } @@ -232,8 +236,7 @@ void ModelOutputsPage::build(Window* window) STR_TRIMS2OFFSETS, STR_ADD_ALL_TRIMS_TO_SUBTRIMS, [=] { moveTrimsToOffsets(); - for (int i = 0; i < outputButtons.size(); i += 1) - outputButtons[i]->refresh(); + Messaging::send(Messaging::REFRESH); }); return 0; }); @@ -247,8 +250,6 @@ void ModelOutputsPage::build(Window* window) lv_obj_set_pos(btn->getLvObj(), TRIMB_X, TRIMB_Y + (ch * (OutputLineButton::CH_LINE_H + PAD_TINY))); btn->setWidth(TRIMB_W); - outputButtons.emplace_back(btn); - LimitData* output = limitAddress(ch); btn->setPressHandler([=]() -> uint8_t { Menu* menu = new Menu(); diff --git a/radio/src/gui/colorlcd/model/model_select.cpp b/radio/src/gui/colorlcd/model/model_select.cpp index da77e2427..3f370a789 100644 --- a/radio/src/gui/colorlcd/model/model_select.cpp +++ b/radio/src/gui/colorlcd/model/model_select.cpp @@ -71,7 +71,7 @@ class ModelButton : public Button delayLoad(); } - void addDetails() + void delayedInit() override { coord_t w = width() - PAD_SMALL * 2; @@ -98,16 +98,18 @@ class ModelButton : public Button } lv_label_set_long_mode(modelName->getLvObj(), LV_LABEL_LONG_DOT); - checkEvents(); + bool chk = (modelCell == modelslist.getCurrentModel()); + if (chk != checked()) { + check(chk); + if (chk) + lv_obj_add_state(modelName->getLvObj(), LV_STATE_USER_1); + else + lv_obj_clear_state(modelName->getLvObj(), LV_STATE_USER_1); + } lv_obj_update_layout(lvobj); } - void delayedInit() override - { - addDetails(); - } - const char *modelFilename() { return modelCell->modelFilename; } ModelCell *getModelCell() const { return modelCell; } @@ -169,18 +171,6 @@ class ModelButton : public Button COLOR_THEME_SECONDARY1_INDEX, CENTERED | font); } - void checkEvents() override - { - bool chk = (modelCell == modelslist.getCurrentModel()); - if (chk != checked()) { - check(chk); - if (chk) - lv_obj_add_state(modelName->getLvObj(), LV_STATE_USER_1); - else - lv_obj_clear_state(modelName->getLvObj(), LV_STATE_USER_1); - } - } - void onClicked() override { setFocused(); diff --git a/radio/src/gui/colorlcd/model/model_setup.cpp b/radio/src/gui/colorlcd/model/model_setup.cpp index effd3ea4d..41b736e05 100644 --- a/radio/src/gui/colorlcd/model/model_setup.cpp +++ b/radio/src/gui/colorlcd/model/model_setup.cpp @@ -24,6 +24,7 @@ #include #include "button_matrix.h" +#include "dialog.h" #include "edgetx.h" #include "etx_lv_theme.h" #include "filechoice.h" @@ -33,12 +34,12 @@ #include "model_heli.h" #include "module_setup.h" #include "preflight_checks.h" +#include "sourcechoice.h" #include "storage/modelslist.h" #include "textedit.h" -#include "throttle_params.h" #include "timer_setup.h" +#include "toggleswitch.h" #include "trainer_setup.h" -#include "trims_setup.h" #if defined(FUNCTION_SWITCHES) #include "function_switches.h" @@ -69,7 +70,7 @@ static void viewOption(Window* parent, coord_t x, coord_t y, lbl->show(getValue() == 0); } -static SetupLineDef viewOptionsPageSetupLines[] = { +const static SetupLineDef viewOptionsPageSetupLines[] = { { STR_DEF(STR_RADIO_MENU_TABS), nullptr, }, @@ -172,6 +173,7 @@ static SetupLineDef viewOptionsPageSetupLines[] = { g_eeGeneral.modelTelemetryDisabled); } }, + {nullptr, nullptr}, }; struct CenterBeepsMatrix : public ButtonMatrix { @@ -254,7 +256,7 @@ struct CenterBeepsMatrix : public ButtonMatrix { uint8_t ana_idx[MAX_ANALOG_INPUTS]; }; -static SetupLineDef otherPageSetupLines[] = { +const static SetupLineDef otherPageSetupLines[] = { { STR_DEF(STR_JITTER_FILTER), [](Window* parent, coord_t x, coord_t y) { @@ -272,9 +274,116 @@ static SetupLineDef otherPageSetupLines[] = { parent->setHeight(bm->height() + PAD_SMALL); } }, + {nullptr, nullptr}, }; -static SetupLineDef setupLines[] = { +const static SetupLineDef throttleParamsSetupLines[] = { + { + // Throttle reversed + STR_DEF(STR_THROTTLEREVERSE), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.throttleReversed)); + } + }, + { + // Throttle source + STR_DEF(STR_TTRACE), + [](Window* parent, coord_t x, coord_t y) { + auto sc = new SourceChoice(parent, {x, y, 0, 0}, 0, MIXSRC_LAST_CH, + []() {return throttleSource2Source(g_model.thrTraceSrc); }, + [](int16_t src) { + int16_t val = source2ThrottleSource(src); + if (val >= 0) { + g_model.thrTraceSrc = val; + SET_DIRTY(); + } + }); + sc->setAvailableHandler(isThrottleSourceAvailable); + } + }, + { + // Throttle trim + STR_DEF(STR_TTRIM), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.thrTrim)); + } + }, + { + // Throttle trim source + STR_DEF(STR_TTRIM_SW), + [](Window* parent, coord_t x, coord_t y) { + new SourceChoice( + parent, {x, y, 0, 0}, MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM, + []() { return g_model.getThrottleStickTrimSource(); }, + [](int16_t src) { + g_model.setThrottleStickTrimSource(src); + SET_DIRTY(); + }); + } + }, + {nullptr, nullptr}, +}; + +#if defined(USE_HATS_AS_KEYS) +static LAYOUT_VAL_SCALED(HATSMODE_W, 120) +#endif + +const static SetupLineDef trimsSetupLines[] = { + { + // Reset trims + nullptr, + [](Window* parent, coord_t x, coord_t y) { + new TextButton(parent, {PAD_TINY, y, LCD_W - PAD_MEDIUM * 2, 0}, STR_RESET_BTN, []() -> uint8_t { + for (auto &fm : g_model.flightModeData) memclear(&fm.trim, sizeof(fm.trim)); + SET_DIRTY(); + AUDIO_WARNING1(); + return 0; + }); + } + }, +#if defined(USE_HATS_AS_KEYS) + { + // Hats mode for NV14/EL18 + STR_DEF(STR_HATSMODE), + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, HATSMODE_W, 0}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, HATSMODE_GLOBAL, + GET_SET_DEFAULT(g_model.hatsMode)); + new TextButton(parent, {x + HATSMODE_W + PAD_SMALL, y, 0, 0}, "?", [=]() { + new MessageDialog(STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + LEFT); + return 0; + }); + } + }, +#endif + { + // Trim step + STR_DEF(STR_TRIMINC), + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VTRIMINC, -2, 2, + GET_SET_DEFAULT(g_model.trimInc)); + } + }, + { + // Extended trims + STR_DEF(STR_ETRIMS), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.extendedTrims)); + } + }, + { + // Display trims + // TODO: move to "Screen setup" ? + STR_DEF(STR_DISPLAY_TRIMS), + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VDISPLAYTRIMS, 0, 2, + GET_SET_DEFAULT(g_model.displayTrims)); + } + }, + {nullptr, nullptr}, +}; + +const static SetupLineDef setupLines[] = { { // Model name STR_DEF(STR_MODELNAME), @@ -342,35 +451,39 @@ static SetupLineDef setupLines[] = { }, false, STR_BITMAP); } }, + {nullptr, nullptr}, +}; + +const static PageButtonDef modelSetupButtons[] = { + // Modules + {STR_DEF(STR_INTERNALRF), []() { new ModulePage(INTERNAL_MODULE); }, []() { return g_model.moduleData[INTERNAL_MODULE].type > 0; }}, + {STR_DEF(STR_EXTERNALRF), []() { new ModulePage(EXTERNAL_MODULE); }, []() { return g_model.moduleData[EXTERNAL_MODULE].type > 0; }}, + {STR_DEF(STR_TRAINER), []() { new TrainerPage(); }, []() { return g_model.trainerData.mode > 0; }}, + // Timer buttons + {STR_DEF(STR_TIMER_1), []() { new TimerWindow(0); }, []() { return g_model.timers[0].mode > 0; }}, + {STR_DEF(STR_TIMER_2), []() { new TimerWindow(1); }, []() { return g_model.timers[1].mode > 0; }}, + {STR_DEF(STR_TIMER_3), []() { new TimerWindow(2); }, []() { return g_model.timers[2].mode > 0; }}, + + {STR_DEF(STR_PREFLIGHT), []() { new PreflightChecks(); }}, + {STR_DEF(STR_TRIMS), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_TRIMS, trimsSetupLines); }}, + {STR_DEF(STR_THROTTLE_LABEL), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_THROTTLE_LABEL, throttleParamsSetupLines); }}, + {STR_DEF(STR_ENABLED_FEATURES), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_ENABLED_FEATURES, viewOptionsPageSetupLines); }}, +#if defined(USBJ_EX) + {STR_DEF(STR_USBJOYSTICK_LABEL), []() { new ModelUSBJoystickPage(); }}, +#endif +#if defined(FUNCTION_SWITCHES) + {STR_DEF(STR_FUNCTION_SWITCHES), []() { new ModelFunctionSwitches(); }}, +#endif + {STR_DEF(STR_MENU_OTHER), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_MENU_OTHER, otherPageSetupLines); }}, +#if defined(HELI) + {STR_DEF(STR_MENUHELISETUP), []() { return new ModelHeliPage(); }, nullptr, modelHeliEnabled}, +#endif + {nullptr}, }; void ModelSetupPage::build(Window * window) { - coord_t y = SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); + coord_t y = SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines); - new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { - // Modules - {STR_DEF(STR_INTERNALRF), []() { new ModulePage(INTERNAL_MODULE); }, []() { return g_model.moduleData[INTERNAL_MODULE].type > 0; }}, - {STR_DEF(STR_EXTERNALRF), []() { new ModulePage(EXTERNAL_MODULE); }, []() { return g_model.moduleData[EXTERNAL_MODULE].type > 0; }}, - {STR_DEF(STR_TRAINER), []() { new TrainerPage(); }, []() { return g_model.trainerData.mode > 0; }}, - // Timer buttons - {STR_DEF(STR_TIMER_1), []() { new TimerWindow(0); }, []() { return g_model.timers[0].mode > 0; }}, - {STR_DEF(STR_TIMER_2), []() { new TimerWindow(1); }, []() { return g_model.timers[1].mode > 0; }}, - {STR_DEF(STR_TIMER_3), []() { new TimerWindow(2); }, []() { return g_model.timers[2].mode > 0; }}, - - {STR_DEF(STR_PREFLIGHT), []() { new PreflightChecks(); }}, - {STR_DEF(STR_TRIMS), []() { new TrimsSetup(); }}, - {STR_DEF(STR_THROTTLE_LABEL), []() { new ThrottleParams(); }}, - {STR_DEF(STR_ENABLED_FEATURES), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, -#if defined(USBJ_EX) - {STR_DEF(STR_USBJOYSTICK_LABEL), []() { new ModelUSBJoystickPage(); }}, -#endif -#if defined(FUNCTION_SWITCHES) - {STR_DEF(STR_FUNCTION_SWITCHES), []() { new ModelFunctionSwitches(); }}, -#endif - {STR_DEF(STR_MENU_OTHER), []() { new SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_MENU_OTHER, otherPageSetupLines, DIM(otherPageSetupLines)); }}, -#if defined(HELI) - {STR_DEF(STR_MENUHELISETUP), []() { return new ModelHeliPage(); }, nullptr, modelHeliEnabled}, -#endif - }, BTN_H); + new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, modelSetupButtons, BTN_H); } diff --git a/radio/src/gui/colorlcd/model/output_edit.cpp b/radio/src/gui/colorlcd/model/output_edit.cpp index 4c9e34c76..6a47ea9d9 100644 --- a/radio/src/gui/colorlcd/model/output_edit.cpp +++ b/radio/src/gui/colorlcd/model/output_edit.cpp @@ -185,7 +185,7 @@ void OutputEditWindow::buildBody(Window *form) // Curve new StaticText(line, rect_t{}, STR_CURVE); - new CurveChoice(line, GET_SET_DEFAULT(output->curve), nullptr, channel + MIXSRC_FIRST_CH); + new CurveChoice(line, GET_SET_DEFAULT(output->curve), channel + MIXSRC_FIRST_CH); // PPM center line = form->newLine(grid); diff --git a/radio/src/gui/colorlcd/model/throttle_params.h b/radio/src/gui/colorlcd/model/throttle_params.h deleted file mode 100644 index 0d481b904..000000000 --- a/radio/src/gui/colorlcd/model/throttle_params.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#pragma once - -#include "page.h" - -class ThrottleParams : public SubPage -{ - public: - ThrottleParams(); -}; diff --git a/radio/src/gui/colorlcd/model/trims_setup.cpp b/radio/src/gui/colorlcd/model/trims_setup.cpp deleted file mode 100644 index f18d99b85..000000000 --- a/radio/src/gui/colorlcd/model/trims_setup.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "trims_setup.h" - -#include "button.h" -#include "choice.h" -#include "dialog.h" -#include "edgetx.h" -#include "getset_helpers.h" -#include "toggleswitch.h" - -#define SET_DIRTY() storageDirty(EE_MODEL) - -static SetupLineDef setupLines[] = { - { - // Reset trims - nullptr, - [](Window* parent, coord_t x, coord_t y) { - new TextButton(parent, {PAD_TINY, y, LCD_W - PAD_MEDIUM * 2, 0}, STR_RESET_BTN, []() -> uint8_t { - for (auto &fm : g_model.flightModeData) memclear(&fm.trim, sizeof(fm.trim)); - SET_DIRTY(); - AUDIO_WARNING1(); - return 0; - }); - } - }, -#if defined(USE_HATS_AS_KEYS) - { - // Hats mode for NV14/EL18 - STR_DEF(STR_HATSMODE), - [](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, TrimsSetup::HATSMODE_W, 0}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, HATSMODE_GLOBAL, - GET_SET_DEFAULT(g_model.hatsMode)); - new TextButton(parent, {x + TrimsSetup::HATSMODE_W + PAD_SMALL, y, 0, 0}, "?", [=]() { - new MessageDialog(STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", - LEFT); - return 0; - }); - } - }, -#endif - { - // Trim step - STR_DEF(STR_TRIMINC), - [](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, 0}, STR_VTRIMINC, -2, 2, - GET_SET_DEFAULT(g_model.trimInc)); - } - }, - { - // Extended trims - STR_DEF(STR_ETRIMS), - [](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.extendedTrims)); - } - }, - { - // Display trims - // TODO: move to "Screen setup" ? - STR_DEF(STR_DISPLAY_TRIMS), - [](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, 0}, STR_VDISPLAYTRIMS, 0, 2, - GET_SET_DEFAULT(g_model.displayTrims)); - } - }, -}; - -TrimsSetup::TrimsSetup() : SubPage(ICON_MODEL_SETUP, STR_MAIN_MENU_MODEL_SETTINGS, STR_TRIMS, setupLines, DIM(setupLines)) -{ -} diff --git a/radio/src/gui/colorlcd/model/trims_setup.h b/radio/src/gui/colorlcd/model/trims_setup.h deleted file mode 100644 index 750ff4809..000000000 --- a/radio/src/gui/colorlcd/model/trims_setup.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) EdgeTX - * - * Based on code named - * opentx - https://github.com/opentx/opentx - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#pragma once - -#include "page.h" - -class TrimsSetup : public SubPage -{ - public: - TrimsSetup(); - - static LAYOUT_VAL_SCALED(HATSMODE_W, 120) -}; diff --git a/radio/src/gui/colorlcd/module/module_setup.cpp b/radio/src/gui/colorlcd/module/module_setup.cpp index a3cea7b53..36f7a3102 100644 --- a/radio/src/gui/colorlcd/module/module_setup.cpp +++ b/radio/src/gui/colorlcd/module/module_setup.cpp @@ -30,6 +30,7 @@ #include "etx_lv_theme.h" #include "form.h" #include "getset_helpers.h" +#include "messaging.h" #include "mixer_scheduler.h" #include "os/sleep.h" #include "ppm_settings.h" @@ -120,6 +121,7 @@ class ModuleWindow : public Window setFlexLayout(); updateModule(); lv_obj_add_event_cb(lvobj, ModuleWindow::mw_refresh_cb, LV_EVENT_REFRESH, this); + moduleUpdateMsg.subscribe(Messaging::MODULE_UPDATE, [=](uint32_t param) { updateLayout(); }); } void updateModule() @@ -513,6 +515,7 @@ class ModuleWindow : public Window FailsafeChoice* fsChoice = nullptr; Choice* rfPower = nullptr; StaticText* idUnique = nullptr; + Messaging moduleUpdateMsg; void startRSSIDialog(std::function closeHandler = nullptr) { @@ -631,8 +634,7 @@ class ModuleSubTypeChoice : public Choice #endif } - if (moduleWindow) - moduleWindow->updateLayout(); + Messaging::send(Messaging::MODULE_UPDATE); } void updateLayout() @@ -732,11 +734,8 @@ class ModuleSubTypeChoice : public Choice } } - void setModuleWindow(ModuleWindow* w) { moduleWindow = w; } - protected: uint8_t moduleIdx; - ModuleWindow* moduleWindow = nullptr; }; ModulePage::ModulePage(uint8_t moduleIdx) : Page(ICON_MODEL_SETUP) @@ -772,8 +771,6 @@ ModulePage::ModulePage(uint8_t moduleIdx) : Page(ICON_MODEL_SETUP) auto subTypeChoice = new ModuleSubTypeChoice(box, moduleIdx); auto moduleWindow = new ModuleWindow(body, moduleIdx); - subTypeChoice->setModuleWindow(moduleWindow); - // This needs to be after moduleWindow has been created moduleChoice->setSetValueHandler([=](int32_t newValue) { setModuleType(moduleIdx, newValue); diff --git a/radio/src/gui/colorlcd/radio/preview_window.cpp b/radio/src/gui/colorlcd/radio/preview_window.cpp index 1d165ce73..5e9943389 100644 --- a/radio/src/gui/colorlcd/radio/preview_window.cpp +++ b/radio/src/gui/colorlcd/radio/preview_window.cpp @@ -83,10 +83,6 @@ class ThemedCheckBox : public ToggleSwitch setFocusHandler([](bool focus) {}); } -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override { return parent->onEvent(event); } -#endif - protected: bool checked; }; @@ -103,10 +99,6 @@ class ThemedButton : public TextButton setWindowFlag(NO_FOCUS | NO_CLICK); setPressHandler([=]() { return isChecked; }); } - -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override { parent->onEvent(event); } -#endif }; class ThemedTextEdit : public TextEdit @@ -120,10 +112,6 @@ class ThemedTextEdit : public TextEdit preview(edited, editText, strlen(editText)); } -#if defined(HARDWARE_KEYS) - void onEvent(event_t event) override { parent->onEvent(event); } -#endif - protected: char editText[50]; }; diff --git a/radio/src/gui/colorlcd/radio/radio_calibration.cpp b/radio/src/gui/colorlcd/radio/radio_calibration.cpp index b7db72eef..fb7818bee 100644 --- a/radio/src/gui/colorlcd/radio/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio/radio_calibration.cpp @@ -106,7 +106,7 @@ void RadioCalibrationPage::buildBody(Window *window) 3, 2); } - std::unique_ptr deco(new ViewMainDecoration(window, false, true, false)); + new ViewMainDecoration(window, true); axisBtn = new TextButton(window, {AXIS_X, PAD_LARGE, AXIS_W, 0}, STR_STICKS, [=]() -> uint8_t { diff --git a/radio/src/gui/colorlcd/radio/radio_cfs.cpp b/radio/src/gui/colorlcd/radio/radio_cfs.cpp index 2b4555965..0bcf000fe 100644 --- a/radio/src/gui/colorlcd/radio/radio_cfs.cpp +++ b/radio/src/gui/colorlcd/radio/radio_cfs.cpp @@ -37,19 +37,13 @@ extern const char* _fct_sw_start[]; extern const char* edgetx_fs_manual_url; -class RadioFunctionSwitch : public Window +//----------------------------------------------------------------------------- + +class RadioFunctionSwitch : public FunctionSwitchBase { public: - RadioFunctionSwitch(Window* parent, uint8_t sw) : - Window(parent, {0, 0, LCD_W - PAD_SMALL * 2, ROW_H}), switchIndex(sw) + RadioFunctionSwitch(Window* parent, uint8_t sw) : FunctionSwitchBase(parent, sw) { - padAll(PAD_TINY); - - std::string s(CHAR_SWITCH); - s += switchGetDefaultName(switchIndex); - - new StaticText(this, {PAD_LARGE, PAD_MEDIUM, SW_W, EdgeTxStyles::STD_FONT_HEIGHT}, s); - new ModelTextEdit(this, {NM_X, 0, NM_W, 0}, g_eeGeneral.switchName(switchIndex), LEN_SWITCH_NAME); @@ -87,17 +81,13 @@ class RadioFunctionSwitch : public Window }); #if defined(FUNCTION_SWITCHES_RGB_LEDS) -#if NARROW_LAYOUT - new StaticText(this, {C1_X - C1_W - PAD_TINY, C1_Y + COLLBL_YO, C1_W, 0}, STR_OFF, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); - new StaticText(this, {C2_X - C2_W - PAD_TINY, C2_Y + COLLBL_YO, C2_W, 0}, STR_ON_ONE_SWITCHES[0], COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); -#endif - offValue = g_eeGeneral.switchOffColor(switchIndex); onValue = g_eeGeneral.switchOnColor(switchIndex); offColor = new ColorPicker( this, {C1_X, C1_Y, C1_W, 0}, [=]() -> int { // getValue + activeSwitch = switchIndex; return g_eeGeneral.switchOffColor(switchIndex).getColor() | RGB888_FLAG; }, [=](int newValue) { // setValue @@ -111,12 +101,12 @@ class RadioFunctionSwitch : public Window offValue = g_eeGeneral.switchOffColor(switchIndex); setFSEditOverride(-1, 0); SET_DIRTY(); - }, - [=](int newValue) { previewColor(newValue); }, ETX_RGB888); + }, ETX_RGB888); onColor = new ColorPicker( this, {C2_X, C2_Y, C2_W, 0}, [=]() -> int { // getValue + activeSwitch = switchIndex; return g_eeGeneral.switchOnColor(switchIndex).getColor() | RGB888_FLAG; }, [=](int newValue) { // setValue @@ -130,11 +120,8 @@ class RadioFunctionSwitch : public Window onValue = g_eeGeneral.switchOnColor(switchIndex); setFSEditOverride(-1, 0); SET_DIRTY(); - }, - [=](int newValue) { previewColor(newValue); }, ETX_RGB888); + }, ETX_RGB888); - overrideLabel = new StaticText(this, {OVRLBL_X, C1_Y + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_LARGE, OVRLBL_W, 0}, - STR_LUA_OVERRIDE, COLOR_THEME_PRIMARY1_INDEX, FONT(XS) | RIGHT); offOverride = new ToggleSwitch(this, {OVROFF_X, C1_Y + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE, 0, 0}, [=]() { return g_eeGeneral.cfsOffColorLuaOverride(switchIndex); }, [=](bool v) { g_eeGeneral.cfsSetOffColorLuaOverride(switchIndex, v); }); @@ -146,93 +133,13 @@ class RadioFunctionSwitch : public Window setState(); } -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - static LAYOUT_VAL_SCALED(SW_W, 70) - static constexpr coord_t NM_X = SW_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(NM_W, 60) - static constexpr coord_t TP_X = NM_X + NM_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(TP_W, 78) - static constexpr coord_t GR_X = TP_X + TP_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(GR_W, 84) - static constexpr coord_t ST_X = GR_X + GR_W + PAD_SMALL; - static LAYOUT_VAL_SCALED(ST_W, 60) -#if NARROW_LAYOUT - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 3 + PAD_OUTLINE * 4; - static constexpr coord_t C1_X = GR_X; - static constexpr coord_t C1_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; - static LAYOUT_VAL_SCALED(C1_W, 40) - static constexpr coord_t C2_X = ST_X; - static constexpr coord_t C2_Y = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE; - static LAYOUT_VAL_SCALED(C2_W, 40) - static constexpr coord_t OVRLBL_X = NM_X; - static constexpr coord_t OVRLBL_W = NM_W + TP_W; - static constexpr coord_t OVROFF_X = C1_X; - static constexpr coord_t COLLBL_YO = PAD_MEDIUM; -#else - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT * 2 + PAD_OUTLINE * 3; - static constexpr coord_t C1_X = ST_X + ST_W + PAD_SMALL; - static constexpr coord_t C1_Y = 0; - static LAYOUT_VAL_SCALED(C1_W, 40) - static constexpr coord_t C2_X = C1_X + C1_W + PAD_SMALL; - static constexpr coord_t C2_Y = 0; - static LAYOUT_VAL_SCALED(C2_W, 40) - static constexpr coord_t OVRLBL_X = GR_X; - static constexpr coord_t OVRLBL_W = GR_W + ST_W - PAD_LARGE; - static constexpr coord_t OVROFF_X = C1_X - PAD_MEDIUM * 2; - static constexpr coord_t COLLBL_YO = PAD_SMALL; -#endif -#else - static constexpr coord_t ROW_H = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_OUTLINE * 2; - static constexpr coord_t SW_W = (LCD_W - PAD_SMALL * 2 - PAD_TINY * 4) / 5; - static constexpr coord_t NM_X = SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(NM_W, 80) - static constexpr coord_t TP_X = NM_X + SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(TP_W, 86) - static constexpr coord_t GR_X = TP_X + SW_W + PAD_TINY; - static LAYOUT_VAL_SCALED(GR_W, 94) - static constexpr coord_t ST_X = GR_X + SW_W + PAD_LARGE * 2 + PAD_SMALL; - static LAYOUT_VAL_SCALED(ST_W, 70) -#endif - protected: - uint8_t switchIndex; - Choice* typeChoice = nullptr; - Choice* startChoice = nullptr; -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - ColorPicker* offColor = nullptr; - ColorPicker* onColor = nullptr; - RGBLedColor offValue; - RGBLedColor onValue; - StaticText* overrideLabel = nullptr; - ToggleSwitch* onOverride = nullptr; - ToggleSwitch* offOverride = nullptr; -#endif - int lastType = -1; - -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - void previewColor(int newValue) - { - // Convert color index to RGB - newValue = color32ToRGB(newValue); - setFSEditOverride(switchIndex, newValue); - } -#endif void setState() { uint8_t typ = g_eeGeneral.switchType(switchIndex); startChoice->show(typ == SWITCH_2POS && g_eeGeneral.switchGroup(switchIndex) == 0); -#if defined(FUNCTION_SWITCHES_RGB_LEDS) - offColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - onColor->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - overrideLabel->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - onOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - offOverride->show(typ != SWITCH_NONE && typ != SWITCH_GLOBAL); - if (typ != SWITCH_NONE && typ != SWITCH_GLOBAL) - setHeight(ROW_H); - else - setHeight(ROW_H - EdgeTxStyles::UI_ELEMENT_HEIGHT - PAD_OUTLINE); -#endif + setLEDState(typ); } void checkEvents() override @@ -246,61 +153,32 @@ class RadioFunctionSwitch : public Window } }; -RadioFunctionSwitches::RadioFunctionSwitches() : Page(ICON_RADIO_HARDWARE) +//----------------------------------------------------------------------------- + +RadioFunctionSwitches::RadioFunctionSwitches() : FunctionSwitchesBase(ICON_RADIO_HARDWARE, STR_HARDWARE) { - header->setTitle(STR_HARDWARE); - header->setTitle2(STR_FUNCTION_SWITCHES); - - body->padAll(PAD_TINY); - body->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); - - auto box = new Window(body, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}); - new StaticText(box, {0, 0, RadioFunctionSwitch::SW_W, 0}, STR_SWITCHES); - new StaticText(box, {RadioFunctionSwitch::NM_X + PAD_OUTLINE, 0, RadioFunctionSwitch::NM_W, 0}, STR_NAME, COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); - new StaticText(box, {RadioFunctionSwitch::TP_X + PAD_OUTLINE, 0, RadioFunctionSwitch::TP_W, 0}, STR_SWITCH_TYPE, - COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); - startupHeader = new StaticText(box, {RadioFunctionSwitch::ST_X + PAD_OUTLINE, 0, RadioFunctionSwitch::ST_W, 0}, STR_SWITCH_STARTUP, - COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); -#if defined(FUNCTION_SWITCHES_RGB_LEDS) && !NARROW_LAYOUT - new StaticText(box, {RadioFunctionSwitch::C1_X + PAD_OUTLINE, 0, RadioFunctionSwitch::C1_W, 0}, STR_OFF, COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); - new StaticText(box, {RadioFunctionSwitch::C2_X + PAD_OUTLINE, 0, RadioFunctionSwitch::C2_W, 0}, STR_ON_ONE_SWITCHES[0], COLOR_THEME_PRIMARY1_INDEX, FONT(XS)); -#endif - for (uint8_t i = 0; i < switchGetMaxSwitches(); i += 1) { if (switchIsCustomSwitch(i)) new RadioFunctionSwitch(body, i); } -#if defined(HARDWARE_TOUCH) - body->padBottom(PAD_LARGE); - - box = new Window(body, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}); - - new StaticText(box, rect_t{}, STR_MORE_INFO); - - auto qr = lv_qrcode_create(box->getLvObj(), 150, - makeLvColor(COLOR_THEME_SECONDARY1), - makeLvColor(COLOR_THEME_SECONDARY3)); - lv_qrcode_update(qr, edgetx_fs_manual_url, strlen(edgetx_fs_manual_url)); - lv_obj_set_pos(qr, (LCD_W - 150) / 2, EdgeTxStyles::STD_FONT_HEIGHT); -#endif + addQRCode(); setState(); } void RadioFunctionSwitches::setState() { - // int cnt = 0; - // for (int i = 1; i <= 3; i += 1) { - // cnt += getSwitchCountInFSGroup(i); - // } - // startupHeader->show(cnt != NUM_FUNCTIONS_SWITCHES); -} - -void RadioFunctionSwitches::checkEvents() -{ - setState(); - Page::checkEvents(); + bool showStartHeader = false; + for (uint8_t i = 0; i < switchGetMaxSwitches(); i += 1) { + if (switchIsCustomSwitch(i) && g_eeGeneral.switchType(i) == SWITCH_2POS) { + showStartHeader = true; + break; + } + } + startupHeader->show(showStartHeader); } #endif + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/radio/radio_cfs.h b/radio/src/gui/colorlcd/radio/radio_cfs.h index fa86257fd..fe3f540a6 100644 --- a/radio/src/gui/colorlcd/radio/radio_cfs.h +++ b/radio/src/gui/colorlcd/radio/radio_cfs.h @@ -19,24 +19,19 @@ * GNU General Public License for more details. */ - #pragma once +#pragma once - #if defined(FUNCTION_SWITCHES) +#if defined(FUNCTION_SWITCHES) - #include "page.h" +#include "function_switches.h" - class RadioFunctionSwitches : public Page - { - public: - RadioFunctionSwitches(); +class RadioFunctionSwitches : public FunctionSwitchesBase +{ + public: + RadioFunctionSwitches(); - protected: - BitmapBuffer* qrcode = nullptr; - StaticText* startupHeader = nullptr; + protected: + void setState() override; +}; - void setState(); - void checkEvents() override; - }; - - #endif - \ No newline at end of file +#endif diff --git a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp index a2396fc7b..560230ed4 100644 --- a/radio/src/gui/colorlcd/radio/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio/radio_diaganas.cpp @@ -159,8 +159,6 @@ class AnaViewWindow : public Window #endif } - void checkEvents() override { Window::checkEvents(); } - protected: FlexGridLayout grid; FormLine* line = nullptr; @@ -479,8 +477,8 @@ class AnaMinMaxViewWindow : public AnaViewWindow class AnaCalibratedViewPage : public PageGroupItem { public: - AnaCalibratedViewPage(QMPage qmPage) : - PageGroupItem(STR_ANADIAGS_CALIB, qmPage) + AnaCalibratedViewPage() : + PageGroupItem(STR_ANADIAGS_CALIB) { icon = ICON_STATS; } @@ -495,8 +493,8 @@ class AnaCalibratedViewPage : public PageGroupItem class AnaFilteredDevViewPage : public PageGroupItem { public: - AnaFilteredDevViewPage(QMPage qmPage) : - PageGroupItem(STR_ANADIAGS_FILTRAWDEV, qmPage) + AnaFilteredDevViewPage() : + PageGroupItem(STR_ANADIAGS_FILTRAWDEV) { icon = ICON_STATS; } @@ -511,8 +509,8 @@ class AnaFilteredDevViewPage : public PageGroupItem class AnaUnfilteredRawViewPage : public PageGroupItem { public: - AnaUnfilteredRawViewPage(QMPage qmPage) : - PageGroupItem(STR_ANADIAGS_UNFILTRAW, qmPage) + AnaUnfilteredRawViewPage() : + PageGroupItem(STR_ANADIAGS_UNFILTRAW) { icon = ICON_STATS; } @@ -527,8 +525,8 @@ class AnaUnfilteredRawViewPage : public PageGroupItem class AnaMinMaxViewPage : public PageGroupItem { public: - AnaMinMaxViewPage(QMPage qmPage) : - PageGroupItem(STR_ANADIAGS_MINMAX, qmPage) + AnaMinMaxViewPage() : + PageGroupItem(STR_ANADIAGS_MINMAX) { icon = ICON_STATS; } @@ -540,11 +538,11 @@ class AnaMinMaxViewPage : public PageGroupItem } }; -RadioAnalogsDiagsViewPageGroup::RadioAnalogsDiagsViewPageGroup(QMPage qmPage) : +RadioAnalogsDiagsViewPageGroup::RadioAnalogsDiagsViewPageGroup() : TabsGroup(ICON_STATS, STR_ANALOGS_BTN) { - addTab(new AnaCalibratedViewPage(qmPage)); - addTab(new AnaFilteredDevViewPage(qmPage)); - addTab(new AnaUnfilteredRawViewPage(qmPage)); - addTab(new AnaMinMaxViewPage(qmPage)); + addTab(new AnaCalibratedViewPage()); + addTab(new AnaFilteredDevViewPage()); + addTab(new AnaUnfilteredRawViewPage()); + addTab(new AnaMinMaxViewPage()); } diff --git a/radio/src/gui/colorlcd/radio/radio_diaganas.h b/radio/src/gui/colorlcd/radio/radio_diaganas.h index 701d2efcf..fbffaade1 100644 --- a/radio/src/gui/colorlcd/radio/radio_diaganas.h +++ b/radio/src/gui/colorlcd/radio/radio_diaganas.h @@ -26,5 +26,5 @@ class RadioAnalogsDiagsViewPageGroup : public TabsGroup { public: - RadioAnalogsDiagsViewPageGroup(QMPage qmPage); + RadioAnalogsDiagsViewPageGroup(); }; diff --git a/radio/src/gui/colorlcd/radio/radio_hardware.cpp b/radio/src/gui/colorlcd/radio/radio_hardware.cpp index ca50b359a..998483430 100644 --- a/radio/src/gui/colorlcd/radio/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio/radio_hardware.cpp @@ -93,7 +93,7 @@ class BatCalEdit : public NumberEdit } }; -static SetupLineDef setupLines[] = { +const static SetupLineDef setupLines[] = { { // Batt meter range - Range 3.0v to 16v STR_DEF(STR_BATTERY_RANGE), @@ -159,13 +159,34 @@ static SetupLineDef setupLines[] = { } }, #endif + {nullptr, nullptr}, +}; + +const static PageButtonDef calibrationButtons[] = { + {STR_DEF(STR_MENUCALIBRATION), []() { new RadioCalibrationPage(); }}, + {STR_DEF(STR_STICKS), []() { new HWInputDialog(STR_STICKS); }}, + {STR_DEF(STR_POTS), []() { new HWInputDialog(STR_POTS, HWPots::POTS_WINDOW_WIDTH); }}, + {STR_DEF(STR_SWITCHES), []() { new HWInputDialog(STR_SWITCHES, HWSwitches::SW_WINDOW_WIDTH); }}, +#if defined(FUNCTION_SWITCHES) + {STR_DEF(STR_FUNCTION_SWITCHES), []() { new RadioFunctionSwitches(); }}, +#endif + {nullptr}, +}; + +const static PageButtonDef debugButtons[] = { + {STR_DEF(STR_ANALOGS_BTN), []() { new RadioAnalogsDiagsViewPageGroup(); }}, + {STR_DEF(STR_KEYS_BTN), []() { new RadioKeyDiagsPage(); }}, +#if defined(FUNCTION_SWITCHES) + {STR_DEF(STR_FS_BTN), []() { new RadioCustSwitchesDiagsPage(); }}, +#endif + {nullptr}, }; void RadioHardwarePage::build(Window* window) { window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); - SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); + SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines); FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); @@ -188,22 +209,8 @@ void RadioHardwarePage::build(Window* window) new SerialConfigWindow(window, grid); // Calibration - new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_INPUTS, BTN_COLS, PAD_ZERO, { - {STR_DEF(STR_MENUCALIBRATION), []() { new RadioCalibrationPage(); }}, - {STR_DEF(STR_STICKS), []() { new HWInputDialog(STR_STICKS); }}, - {STR_DEF(STR_POTS), []() { new HWInputDialog(STR_POTS, HWPots::POTS_WINDOW_WIDTH); }}, - {STR_DEF(STR_SWITCHES), []() { new HWInputDialog(STR_SWITCHES, HWSwitches::SW_WINDOW_WIDTH); }}, -#if defined(FUNCTION_SWITCHES) - {STR_DEF(STR_FUNCTION_SWITCHES), []() { new RadioFunctionSwitches(); }}, -#endif - }); + new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_INPUTS, BTN_COLS, PAD_ZERO, calibrationButtons); // Debugs - new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_DEBUG, FS_BTN_COLS, PAD_ZERO, { - {STR_DEF(STR_ANALOGS_BTN), [=]() { new RadioAnalogsDiagsViewPageGroup(qmPageId); }}, - {STR_DEF(STR_KEYS_BTN), []() { new RadioKeyDiagsPage(); }}, -#if defined(FUNCTION_SWITCHES) - {STR_DEF(STR_FS_BTN), []() { new RadioCustSwitchesDiagsPage(); }}, -#endif - }); + new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_DEBUG, FS_BTN_COLS, PAD_ZERO, debugButtons); } diff --git a/radio/src/gui/colorlcd/radio/radio_setup.cpp b/radio/src/gui/colorlcd/radio/radio_setup.cpp index 53c4740ea..1f1eb0233 100644 --- a/radio/src/gui/colorlcd/radio/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio/radio_setup.cpp @@ -48,6 +48,36 @@ static const lv_coord_t col_two_dsc[] = {LV_GRID_FR(19), LV_GRID_FR(21), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; +class DateNumberEdit : public NumberEdit +{ + public: + DateNumberEdit(Window* parent, coord_t x, coord_t y, int vmin, int vmax, bool leading0, + std::function getValue, + std::function setValue) : + NumberEdit(parent, {x, y, DT_EDT_W, 0}, vmin, vmax, + getValue, + [=](int32_t newValue) { + setValue(newValue); + SET_DIRTY(); + }) + { + lastValue = this->getValue(); + if (leading0) + setDisplayHandler([](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); +} + + static LAYOUT_ORIENTATION(DT_EDT_W, EdgeTxStyles::EDIT_FLD_WIDTH_NARROW, LAYOUT_SCALE(52)) + + protected: + int32_t lastValue; + + void checkEvents() override + { + if (lastValue != getValue()) + update(); + } +}; + class DateTimeWindow : public Window { public: @@ -62,42 +92,19 @@ class DateTimeWindow : public Window { Window::checkEvents(); - if (seconds && (get_tmr10ms() - lastRefresh >= 10)) { + if (get_tmr10ms() - lastRefresh >= 10) { lastRefresh = get_tmr10ms(); - gettime(&m_tm); - if (m_tm.tm_year != m_last_tm.tm_year) - year->update(); - if (m_tm.tm_mon != m_last_tm.tm_mon) - month->update(); - if (m_tm.tm_mday != m_last_tm.tm_mday) - day->update(); - if (m_tm.tm_hour != m_last_tm.tm_hour) - hour->update(); - if (m_tm.tm_min != m_last_tm.tm_min) - minutes->update(); - if (m_tm.tm_sec != m_last_tm.tm_sec) - seconds->update(); - m_last_tm = m_tm; } } - // Absolute layout for date/time setion due to slow performance - // of lv_textarea in a flex layout. - static LAYOUT_ORIENTATION(DT_EDT_W, EdgeTxStyles::EDIT_FLD_WIDTH_NARROW, LAYOUT_SCALE(52)) static constexpr coord_t DT_Y2 = PAD_TINY + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM; protected: bool init = false; struct gtm m_tm; - struct gtm m_last_tm; tmr10ms_t lastRefresh = 0; - NumberEdit* year = nullptr; - NumberEdit* month = nullptr; NumberEdit* day = nullptr; - NumberEdit* hour = nullptr; - NumberEdit* minutes = nullptr; - NumberEdit* seconds = nullptr; int8_t daysInMonth() { @@ -127,71 +134,54 @@ class DateTimeWindow : public Window void build() { gettime(&m_tm); - m_last_tm = m_tm; // Date new StaticText(this, rect_t{PAD_TINY, PAD_TINY + PAD_MEDIUM, SubPage::EDT_X - PAD_TINY - PAD_SMALL, EdgeTxStyles::STD_FONT_HEIGHT}, STR_DATE); - year = new NumberEdit( - this, rect_t{SubPage::EDT_X, PAD_TINY, DT_EDT_W, 0}, 2023, 2037, + new DateNumberEdit(this, SubPage::EDT_X, PAD_TINY, 2023, 2037, false, [=]() -> int32_t { return TM_YEAR_BASE + m_tm.tm_year; }, [=](int32_t newValue) { - m_last_tm.tm_year = m_tm.tm_year = newValue - TM_YEAR_BASE; + m_tm.tm_year = newValue - TM_YEAR_BASE; setDaysInMonth(); SET_LOAD_DATETIME(&m_tm); }); - month = new NumberEdit( - this, rect_t{SubPage::EDT_X + DT_EDT_W + PAD_TINY, PAD_TINY, DT_EDT_W, 0}, 1, 12, + new DateNumberEdit(this, SubPage::EDT_X + DateNumberEdit::DT_EDT_W + PAD_TINY, PAD_TINY, 1, 12, false, [=]() -> int32_t { return 1 + m_tm.tm_mon; }, [=](int32_t newValue) { - m_last_tm.tm_mon = m_tm.tm_mon = newValue - 1; + m_tm.tm_mon = newValue - 1; setDaysInMonth(); SET_LOAD_DATETIME(&m_tm); }); - month->setDisplayHandler( - [](int32_t value) { return formatNumberAsString(value, LEADING0); }); - day = new NumberEdit( - this, rect_t{SubPage::EDT_X + 2 * DT_EDT_W + PAD_SMALL, PAD_TINY, DT_EDT_W, 0}, 1, - daysInMonth(), [=]() -> int32_t { return m_tm.tm_mday; }, + day = new DateNumberEdit(this, SubPage::EDT_X + 2 * DateNumberEdit::DT_EDT_W + PAD_SMALL, PAD_TINY, 1, daysInMonth(), true, + [=]() -> int32_t { return m_tm.tm_mday; }, [=](int32_t newValue) { - m_last_tm.tm_mday = m_tm.tm_mday = newValue; + m_tm.tm_mday = newValue; SET_LOAD_DATETIME(&m_tm); }); - day->setDisplayHandler( - [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); // Time new StaticText(this, rect_t{PAD_TINY, DT_Y2 + PAD_MEDIUM, SubPage::EDT_X - PAD_TINY - PAD_SMALL, EdgeTxStyles::STD_FONT_HEIGHT}, STR_TIME); - hour = new NumberEdit( - this, rect_t{SubPage::EDT_X, DT_Y2, DT_EDT_W, 0}, 0, 23, + new DateNumberEdit(this, SubPage::EDT_X, DT_Y2, 0, 23, true, [=]() -> int32_t { return m_tm.tm_hour; }, [=](int32_t newValue) { - m_last_tm.tm_hour = m_tm.tm_hour = newValue; + m_tm.tm_hour = newValue; SET_LOAD_DATETIME(&m_tm); }); - hour->setDisplayHandler( - [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); - minutes = new NumberEdit( - this, rect_t{SubPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y2, DT_EDT_W, 0}, 0, 59, + new DateNumberEdit(this, SubPage::EDT_X + DateNumberEdit::DT_EDT_W + PAD_TINY, DT_Y2, 0, 59, true, [=]() -> int32_t { return m_tm.tm_min; }, [=](int32_t newValue) { - m_last_tm.tm_min = m_tm.tm_min = newValue; + m_tm.tm_min = newValue; SET_LOAD_DATETIME(&m_tm); }); - minutes->setDisplayHandler( - [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); - seconds = new NumberEdit( - this, rect_t{SubPage::EDT_X + DT_EDT_W * 2 + PAD_SMALL, DT_Y2, DT_EDT_W, 0}, 0, 59, + new DateNumberEdit(this, SubPage::EDT_X + DateNumberEdit::DT_EDT_W * 2 + PAD_SMALL, DT_Y2, 0, 59, true, [=]() -> int32_t { return m_tm.tm_sec; }, [=](int32_t newValue) { - m_last_tm.tm_sec = m_tm.tm_sec = newValue; + m_tm.tm_sec = newValue; SET_LOAD_DATETIME(&m_tm); }); - seconds->setDisplayHandler( - [](int value) { return formatNumberAsString(value, LEADING0, 2); }); } }; @@ -201,6 +191,7 @@ class ControlTextOverride : public StaticText ControlTextOverride(Window* parent, coord_t x, coord_t y, FunctionsActive func) : StaticText(parent, {x + XO, y + PAD_MEDIUM, 0, 0}, STR_SF_OVERRIDDEN, COLOR_THEME_WARNING_INDEX, FONT_SZ), func(func) { + hide(); } void checkEvents() override @@ -216,7 +207,7 @@ class ControlTextOverride : public StaticText }; #if defined(AUDIO) -static SetupLineDef soundPageSetupLines[] = { +const static SetupLineDef soundPageSetupLines[] = { { // Beeps mode STR_DEF(STR_MODE), @@ -302,11 +293,12 @@ static SetupLineDef soundPageSetupLines[] = { } }, #endif + {nullptr, nullptr}, }; #endif #if defined(VARIO) -static SetupLineDef varioPageSetupLines[] = { +const static SetupLineDef varioPageSetupLines[] = { { // Vario volume STR_DEF(STR_VOLUME), @@ -354,11 +346,12 @@ static SetupLineDef varioPageSetupLines[] = { edit->setSuffix("ms"); } }, + {nullptr, nullptr}, }; #endif #if defined(HAPTIC) -static SetupLineDef hapticPageSetupLines[] = { +const static SetupLineDef hapticPageSetupLines[] = { { // Haptic mode STR_DEF(STR_MODE), @@ -383,10 +376,11 @@ static SetupLineDef hapticPageSetupLines[] = { GET_SET_DEFAULT(g_eeGeneral.hapticStrength)))->setPos(x, y); } }, + {nullptr, nullptr}, }; #endif -static SetupLineDef alarmsPageSetupLines[] = { +const static SetupLineDef alarmsPageSetupLines[] = { { // Battery warning STR_DEF(STR_BATTERYWARNING), @@ -451,124 +445,133 @@ static SetupLineDef alarmsPageSetupLines[] = { GET_SET_INVERTED(g_eeGeneral.disableTrainerPoweroffAlarm)); } }, + {nullptr, nullptr}, }; -class BacklightPage : public SubPage +class BacklightSlider : public Slider { public: - BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_BACKLIGHT_LABEL, true) + BacklightSlider(Window* parent, coord_t x, coord_t y, + std::function getValue, + std::function setValue) : + Slider(parent, LV_PCT(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, + getValue, setValue) { - body->setFlexLayout(); - - // Backlight mode - setupLine(STR_MODE, [=](Window* parent, coord_t x, coord_t y) { - auto blMode = new Choice( - parent, {x, y, 0, 0}, STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, - GET_DEFAULT(g_eeGeneral.backlightMode), [=](int32_t newValue) { - g_eeGeneral.backlightMode = newValue; - updateBacklightControls(); - SET_DIRTY(); - }); - - blMode->setAvailableHandler( - [=](int newValue) { return newValue != e_backlight_mode_off; }); - }); - - // Delay - backlightTimeout = setupLine(STR_BACKLIGHT_TIMER, [=](Window* parent, coord_t x, coord_t y) { - auto edit = - new NumberEdit(parent, {x, y, EdgeTxStyles::EDIT_FLD_WIDTH_NARROW, 0}, 5, 600, - GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), - SET_VALUE(g_eeGeneral.lightAutoOff, newValue / 5)); - edit->setStep(5); - edit->setSuffix("s"); - }); - - // Backlight ON bright - setupLine(STR_BLONBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { - backlightOnSlider = new Slider( - parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, - [=]() -> int32_t { - return BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; - }, - [=](int32_t newValue) { - if (newValue >= g_eeGeneral.blOffBright || - g_eeGeneral.backlightMode == e_backlight_mode_on) { - g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - newValue; - } else { - g_eeGeneral.backlightBright = - BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; - backlightOnSlider->update(); - } - SET_DIRTY(); - }); - backlightOnSlider->setPos(x, y); - }); - - // Backlight OFF bright - setupLine(STR_BLOFFBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { - backlightOffSlider = new Slider( - parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, - BACKLIGHT_LEVEL_MAX, GET_DEFAULT(g_eeGeneral.blOffBright), - [=](int32_t newValue) { - int32_t onBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; - if (newValue <= onBright || - g_eeGeneral.backlightMode == e_backlight_mode_off) { - g_eeGeneral.blOffBright = newValue; - } else { - g_eeGeneral.blOffBright = onBright; - backlightOffSlider->update(); - } - SET_DIRTY(); - }); - backlightOffSlider->setPos(x, y); - }); - -#if defined(KEYS_BACKLIGHT_GPIO) - // Keys backlight - setupLine(STR_KEYS_BACKLIGHT, [=](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, 0}, - GET_SET_DEFAULT(g_eeGeneral.keysBacklight)); - }); -#endif - - // Backlight/Brightness source - setupLine(STR_CONTROL, [=](Window* parent, coord_t x, coord_t y) { - auto choice = new SourceChoice(parent, {x, y, 0, 0}, MIXSRC_NONE, MIXSRC_LAST_SWITCH, - GET_SET_DEFAULT(g_eeGeneral.backlightSrc), true); - choice->setAvailableHandler(isSourceAvailableForBacklightOrVolume); - new ControlTextOverride(parent, x, y, FUNCTION_BACKLIGHT); - }); - - // Flash beep - setupLine(STR_ALARM, [=](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); - }); - - updateBacklightControls(); - - enableRefresh(); + setPos(x, y); + updateMsg.subscribe(Messaging::REFRESH, [=](uint32_t param) { update(); }); } protected: - Window* backlightTimeout = nullptr; - Slider* backlightOffSlider = nullptr; - Slider* backlightOnSlider = nullptr; - - void updateBacklightControls() - { - int32_t onBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; - if (onBright < g_eeGeneral.blOffBright) - g_eeGeneral.backlightBright = - BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; - - backlightTimeout->show(g_eeGeneral.backlightMode != e_backlight_mode_on); - - resetBacklightTimeout(); - } + Messaging updateMsg; }; -static SetupLineDef gpsPageSetupLines[] = { +const static SetupLineDef backlightSetupLines[] = { + { + // Backlight mode + STR_DEF(STR_MODE), + [](Window* parent, coord_t x, coord_t y) { + auto blMode = new Choice( + parent, {x, y, 0, 0}, STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, + GET_DEFAULT(g_eeGeneral.backlightMode), [=](int32_t newValue) { + g_eeGeneral.backlightMode = newValue; + Messaging::send(Messaging::REFRESH); + SET_DIRTY(); + }); + + blMode->setAvailableHandler( + [=](int newValue) { return newValue != e_backlight_mode_off; }); + } + }, + { + // Delay + STR_DEF(STR_BACKLIGHT_TIMER), + [](SetupLine* parent, coord_t x, coord_t y) { + parent->setupMsg.subscribe(Messaging::REFRESH, [=](uint32_t param) { + parent->show(g_eeGeneral.backlightMode != e_backlight_mode_on); + resetBacklightTimeout(); + }); + auto edit = + new NumberEdit(parent, {x, y, EdgeTxStyles::EDIT_FLD_WIDTH_NARROW, 0}, 5, 600, + GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), + SET_VALUE(g_eeGeneral.lightAutoOff, newValue / 5)); + edit->setStep(5); + edit->setSuffix("s"); + parent->show(g_eeGeneral.backlightMode != e_backlight_mode_on); + } + }, + { + // Backlight ON bright + STR_DEF(STR_BLONBRIGHTNESS), + [](Window* parent, coord_t x, coord_t y) { + new BacklightSlider( + parent, x, y, + [=]() -> int32_t { + return BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; + }, + [=](int32_t newValue) { + if (newValue >= g_eeGeneral.blOffBright || + g_eeGeneral.backlightMode == e_backlight_mode_on) { + g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - newValue; + } else { + g_eeGeneral.backlightBright = + BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; + Messaging::send(Messaging::REFRESH); + } + SET_DIRTY(); + }); +} + }, + { + // Backlight OFF bright + STR_DEF(STR_BLOFFBRIGHTNESS), + [](Window* parent, coord_t x, coord_t y) { + new BacklightSlider( + parent, x, y, + GET_DEFAULT(g_eeGeneral.blOffBright), + [=](int32_t newValue) { + int32_t onBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; + if (newValue <= onBright || + g_eeGeneral.backlightMode == e_backlight_mode_off) { + g_eeGeneral.blOffBright = newValue; + } else { + g_eeGeneral.blOffBright = onBright; + Messaging::send(Messaging::REFRESH); + } + SET_DIRTY(); + }); +} + }, +#if defined(KEYS_BACKLIGHT_GPIO) + { + // Keys backlight + STR_DEF(STR_KEYS_BACKLIGHT), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.keysBacklight)); + } + }, +#endif + { + // Backlight/Brightness source + STR_DEF(STR_CONTROL), + [](Window* parent, coord_t x, coord_t y) { + auto choice = new SourceChoice(parent, {x, y, 0, 0}, MIXSRC_NONE, MIXSRC_LAST_SWITCH, + GET_SET_DEFAULT(g_eeGeneral.backlightSrc), true); + choice->setAvailableHandler(isSourceAvailableForBacklightOrVolume); + new ControlTextOverride(parent, x, y, FUNCTION_BACKLIGHT); + } + }, + { + // Flash beep + STR_DEF(STR_ALARM), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); + } + }, + {nullptr, nullptr}, +}; + +const static SetupLineDef gpsPageSetupLines[] = { { // Timezone STR_DEF(STR_TIMEZONE), @@ -601,6 +604,7 @@ static SetupLineDef gpsPageSetupLines[] = { GET_SET_DEFAULT(g_eeGeneral.gpsFormat)); } }, + {nullptr, nullptr}, }; static void viewOption(Window* parent, coord_t x, coord_t y, @@ -616,7 +620,7 @@ static void viewOption(Window* parent, coord_t x, coord_t y, } } -static SetupLineDef viewOptionsPageSetupLines[] = { +const static SetupLineDef viewOptionsPageSetupLines[] = { { STR_DEF(STR_RADIO_MENU_TABS), nullptr, }, @@ -719,61 +723,65 @@ static SetupLineDef viewOptionsPageSetupLines[] = { g_model.modelTelemetryDisabled); } }, + {nullptr, nullptr}, }; -class ManageModelsSetupPage : public SubPage -{ - public: - ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_MAIN_MENU_RADIO_SETTINGS, STR_MANAGE_MODELS, true) +const static SetupLineDef manageModelsSetupLines[] = { { - body->setFlexLayout(); - // Model quick select - setupLine(STR_MODEL_QUICK_SELECT, [=](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, 0}, - GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect)); - }); - - // Label single/multi select - setupLine(STR_LABELS_SELECT, [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, 0}, STR_LABELS_SELECT_MODE, 0, 1, - GET_DEFAULT(g_eeGeneral.labelSingleSelect), - [=](int newValue) { - g_eeGeneral.labelSingleSelect = newValue; - modelslabels.clearFilter(); - SET_DIRTY(); - }); - }); - - // Label multi select matching mode - multiSelectMatch = setupLine(STR_LABELS_MATCH, [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, 0}, STR_LABELS_MATCH_MODE, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.labelMultiMode)); - }); - - // Favorites multi select matching mode - favSelectMatch = setupLine(STR_FAV_MATCH, [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, 0}, STR_FAV_MATCH_MODE, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.favMultiMode)); - }); - - checkEvents(); - - enableRefresh(); - } - - void checkEvents() override + STR_DEF(STR_MODEL_QUICK_SELECT), + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect)); + } + }, { - multiSelectMatch->show(!g_eeGeneral.labelSingleSelect); - favSelectMatch->show(!g_eeGeneral.labelSingleSelect && (g_eeGeneral.labelMultiMode != 0)); - } - - protected: - Window* multiSelectMatch = nullptr; - Window* favSelectMatch = nullptr; + // Label single/multi select + STR_DEF(STR_LABELS_SELECT), + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_LABELS_SELECT_MODE, 0, 1, + GET_DEFAULT(g_eeGeneral.labelSingleSelect), + [=](int newValue) { + g_eeGeneral.labelSingleSelect = newValue; + modelslabels.clearFilter(); + Messaging::send(Messaging::REFRESH); + SET_DIRTY(); + }); + } + }, + { + // Label multi select matching mode + STR_DEF(STR_LABELS_MATCH), + [](SetupLine* parent, coord_t x, coord_t y) { + parent->setupMsg.subscribe(Messaging::REFRESH, [=](uint32_t param) { + parent->show(!g_eeGeneral.labelSingleSelect); + }); + new Choice(parent, {x, y, 0, 0}, STR_LABELS_MATCH_MODE, 0, 1, + GET_DEFAULT(g_eeGeneral.labelMultiMode), + [=](int newValue) { + g_eeGeneral.labelMultiMode = newValue; + Messaging::send(Messaging::REFRESH); + SET_DIRTY(); + }); + parent->show(!g_eeGeneral.labelSingleSelect); + } + }, + { + // Favorites multi select matching mode + STR_DEF(STR_FAV_MATCH), + [](SetupLine* parent, coord_t x, coord_t y) { + parent->setupMsg.subscribe(Messaging::REFRESH, [=](uint32_t param) { + parent->show(!g_eeGeneral.labelSingleSelect && (g_eeGeneral.labelMultiMode != 0)); + }); + new Choice(parent, {x, y, 0, 0}, STR_FAV_MATCH_MODE, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.favMultiMode)); + parent->show(!g_eeGeneral.labelSingleSelect && (g_eeGeneral.labelMultiMode != 0)); + } + }, + {nullptr, nullptr}, }; -static SetupLineDef setupLines[] = { +const static SetupLineDef setupLines[] = { { // Splash screen STR_DEF(STR_SPLASHSCREEN), @@ -1013,6 +1021,7 @@ static SetupLineDef setupLines[] = { }); } }, + {nullptr, nullptr}, }; RadioSetupPage::RadioSetupPage(const PageDef& pageDef) : PageGroupItem(pageDef, PAD_TINY) {} @@ -1028,6 +1037,28 @@ static bool hasShortcutKeys() } #endif +const static PageButtonDef radioSetupButtons[] = { +#if defined(AUDIO) + {STR_DEF(STR_SOUND_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_SOUND_LABEL, soundPageSetupLines); }}, +#endif +#if defined(VARIO) + {STR_DEF(STR_VARIO), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_VARIO, varioPageSetupLines); }}, +#endif +#if defined(HAPTIC) + {STR_DEF(STR_HAPTIC_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_HAPTIC_LABEL, hapticPageSetupLines); }}, +#endif + {STR_DEF(STR_ALARMS_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_ALARMS_LABEL, alarmsPageSetupLines); }}, + {STR_DEF(STR_BACKLIGHT_LABEL), []() { (new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_BACKLIGHT_LABEL, backlightSetupLines))->useFlexLayout(); }}, + {STR_DEF(STR_GPS), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_GPS, gpsPageSetupLines); }}, + {STR_DEF(STR_ENABLED_FEATURES), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_ENABLED_FEATURES, viewOptionsPageSetupLines); }}, + {STR_DEF(STR_MAIN_MENU_MANAGE_MODELS), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_MANAGE_MODELS, manageModelsSetupLines); }}, +#if VERSION_MAJOR > 2 + {STR_DEF(STR_KEY_SHORTCUTS), []() { new QMKeyShortcutsPage(); }, nullptr, []() { return hasShortcutKeys(); }}, + {STR_DEF(STR_QUICK_MENU_FAVORITES), []() { new QMFavoritesPage(); }, nullptr}, +#endif + {nullptr}, +}; + void RadioSetupPage::build(Window* window) { coord_t y = 0; @@ -1038,27 +1069,8 @@ void RadioSetupPage::build(Window* window) y += w->height() + padding; // Sub-pages - w = new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { -#if defined(AUDIO) - {STR_DEF(STR_SOUND_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_SOUND_LABEL, soundPageSetupLines, DIM(soundPageSetupLines)); }}, -#endif -#if defined(VARIO) - {STR_DEF(STR_VARIO), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_VARIO, varioPageSetupLines, DIM(varioPageSetupLines)); }}, -#endif -#if defined(HAPTIC) - {STR_DEF(STR_HAPTIC_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_HAPTIC_LABEL, hapticPageSetupLines, DIM(hapticPageSetupLines)); }}, -#endif - {STR_DEF(STR_ALARMS_LABEL), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_ALARMS_LABEL, alarmsPageSetupLines, DIM(alarmsPageSetupLines)); }}, - {STR_DEF(STR_BACKLIGHT_LABEL), []() { new BacklightPage(); }}, - {STR_DEF(STR_GPS), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_GPS, gpsPageSetupLines, DIM(gpsPageSetupLines)); }}, - {STR_DEF(STR_ENABLED_FEATURES), []() { new SubPage(ICON_RADIO_SETUP, STR_MAIN_MENU_RADIO_SETTINGS, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, - {STR_DEF(STR_MAIN_MENU_MANAGE_MODELS), []() { new ManageModelsSetupPage(); }}, -#if VERSION_MAJOR > 2 - {STR_DEF(STR_KEY_SHORTCUTS), []() { new QMKeyShortcutsPage(); }, nullptr, [=]() { return hasShortcutKeys(); }}, - {STR_DEF(STR_QUICK_MENU_FAVORITES), []() { new QMFavoritesPage(); }, nullptr}, -#endif - }, BTN_H); + w = new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, radioSetupButtons, BTN_H); y += w->height() + padding; - SetupLine::showLines(window, y, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); + SetupLine::showLines(window, y, SubPage::EDT_X, padding, setupLines); } diff --git a/radio/src/gui/colorlcd/radio/radio_theme.cpp b/radio/src/gui/colorlcd/radio/radio_theme.cpp index fa973147d..45554438c 100644 --- a/radio/src/gui/colorlcd/radio/radio_theme.cpp +++ b/radio/src/gui/colorlcd/radio/radio_theme.cpp @@ -284,7 +284,6 @@ class ColorEditPage : public Page } setHexStr(rgb); }); - _colorEditor->setColorEditorType(HSV_COLOR_EDITOR); _activeTab = 1; r.w = COLOR_BOX_WIDTH; diff --git a/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp b/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp index 17e8b3afd..b229fd0e1 100644 --- a/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp +++ b/radio/src/gui/colorlcd/setup_menus/pagegroup.cpp @@ -278,19 +278,7 @@ class PageGroupHeader : public PageGroupHeaderBase menu->setCurrentTab(idx); } - void updateLayout() - { - for (uint8_t i = 0; i < pages.size(); i += 1) { - pages[i]->update(i); - } - } - protected: - void checkEvents() override - { - updateLayout(); - PageGroupHeaderBase::checkEvents(); - } }; //----------------------------------------------------------------------------- @@ -311,6 +299,12 @@ PageGroupBase::PageGroupBase(coord_t bodyY, EdgeTxIcon icon) : lv_obj_add_event_cb(lvobj, on_draw_begin, LV_EVENT_COVER_CHECK, nullptr); lv_obj_add_event_cb(lvobj, on_draw_end, LV_EVENT_DRAW_POST_END, nullptr); #endif + + quickMenuMsg.subscribe(Messaging::QUICK_MENU_ITEM_SELECT, + [=](uint32_t param) { + if (param) + onCancel(); + }); } void PageGroupBase::checkEvents() @@ -327,8 +321,9 @@ void PageGroupBase::onClicked() { Keyboard::hide(false); } void PageGroupBase::onCancel() { - QuickMenu::closeQuickMenu(); - deleteLater(); + if (!_deleted) { + deleteLater(); + } } uint8_t PageGroupBase::tabCount() const @@ -399,7 +394,7 @@ void PageGroupBase::doKeyShortcut(event_t event) { QMPage pg = g_eeGeneral.getKeyShortcut(event); if (pg == QM_OPEN_QUICK_MENU) { - if (!QuickMenu::isOpen()) openMenu(); + QuickMenu::openQuickMenu(); } else { if (QuickMenu::subMenuIcon(pg) == icon) { setCurrentTab(QuickMenu::pageIndex(pg)); @@ -454,7 +449,7 @@ PageGroup::PageGroup(EdgeTxIcon icon, const char* title, const PageDef* pages) : #if VERSION_MAJOR == 2 addCustomButton(0, 0, [=]() { onCancel(); }); #else - addCustomButton(0, 0, [=]() { openMenu(); }); + addCustomButton(0, 0, [=]() { QuickMenu::openQuickMenu(); }); addCustomButton(LCD_W - EdgeTxStyles::MENU_HEADER_HEIGHT, 0, [=]() { onCancel(); }); #endif #endif @@ -465,15 +460,6 @@ PageGroup::PageGroup(EdgeTxIcon icon, const char* title, const PageDef* pages) : }); } -void PageGroup::openMenu() -{ - QuickMenu::openQuickMenu( - [=](bool close) { - if (close) - onCancel(); - }, this, currentTab->pageId()); -} - //----------------------------------------------------------------------------- class TabsGroupHeader : public PageGroupHeaderBase @@ -545,31 +531,12 @@ TabsGroup::TabsGroup(EdgeTxIcon icon, const char* parentLabel) : #if VERSION_MAJOR == 2 addCustomButton(0, 0, [=]() { onCancel(); }); #else - addCustomButton(0, 0, [=]() { openMenu(); }); + addCustomButton(0, 0, [=]() { QuickMenu::openQuickMenu(); }); addCustomButton(LCD_W - EdgeTxStyles::MENU_HEADER_HEIGHT, 0, [=]() { onCancel(); }); #endif #endif } -void TabsGroup::openMenu() -{ - PageGroup* p = Window::pageGroup(); - QMPage qmPage = QM_NONE; - if (p) - qmPage = p->getCurrentTab()->pageId(); - QuickMenu::openQuickMenu( - [=](bool close) { - onCancel(); - if (p) { - while (!Window::topWindow()->isPageGroup()) { - Window::topWindow()->deleteLater(); - } - if (close) - Window::topWindow()->onCancel(); - } - }, p, qmPage); -} - void TabsGroup::hidePageButtons() { ((TabsGroupHeader*)header)->hidePageButtons(); diff --git a/radio/src/gui/colorlcd/setup_menus/pagegroup.h b/radio/src/gui/colorlcd/setup_menus/pagegroup.h index b109007ab..02ba9dd17 100644 --- a/radio/src/gui/colorlcd/setup_menus/pagegroup.h +++ b/radio/src/gui/colorlcd/setup_menus/pagegroup.h @@ -22,6 +22,7 @@ #pragma once #include "bitmaps.h" +#include "messaging.h" #include "quick_menu.h" class HeaderIcon; @@ -31,6 +32,7 @@ class PageGroupBase; class SelectedTabIcon; class PageGroupIconButton; #endif + //----------------------------------------------------------------------------- enum PageDefAction { @@ -53,24 +55,6 @@ struct PageDef { extern PageDef favoritesMenuItems[]; #endif -enum QMTopDefAction { - QM_SUBMENU, - QM_ACTION -}; - -struct QMTopDef { - EdgeTxIcon icon; - STR_TYP qmTitle; - STR_TYP title; - QMTopDefAction pageAction; - QMPage qmPage; - const PageDef* subMenuItems; - std::function action; - std::function enabled; -}; - -extern const QMTopDef qmTopItems[]; - //----------------------------------------------------------------------------- class PageGroupItem @@ -101,7 +85,6 @@ class PageGroupItem PaddingSize getPadding() const { return padding; } - virtual void update(uint8_t index) {} virtual void cleanup() {} QMPage pageId() const { return qmPageId; } @@ -191,8 +174,7 @@ class PageGroupBase : public NavWindow Window* body = nullptr; PageGroupItem* currentTab = nullptr; EdgeTxIcon icon; - - virtual void openMenu() = 0; + Messaging quickMenuMsg; void checkEvents() override; @@ -241,8 +223,6 @@ class PageGroup : public PageGroupBase static constexpr coord_t PAGE_GROUP_BODY_Y = PAGE_GROUP_TOP_BAR_H + PAGE_GROUP_ALT_TITLE_H; protected: - - void openMenu() override; }; //----------------------------------------------------------------------------- @@ -270,6 +250,4 @@ class TabsGroup : public PageGroupBase static constexpr coord_t TABS_GROUP_BODY_Y = TABS_GROUP_TOP_BAR_H + TABS_GROUP_ALT_TITLE_H; protected: - - void openMenu() override; }; diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp index 7c6e8ad1d..7f7276a04 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu.cpp @@ -33,65 +33,80 @@ //----------------------------------------------------------------------------- +bool QMMainDef::isSubMenu(QMPage page) const +{ + for (int i = 0; subMenuItems[i].icon < EDGETX_ICONS_COUNT; i += 1) + if (subMenuItems[i].qmPage == page) + return true; + return false; +} + +bool QMMainDef::isSubMenu(QMPage page, EdgeTxIcon curIcon) const +{ + if ((icon != EDGETX_ICONS_COUNT) && (icon != curIcon)) + return false; + return isSubMenu(page); +} + +int QMMainDef::getIndex(QMPage n) const +{ + for (int i = 0; subMenuItems[i].icon < EDGETX_ICONS_COUNT; i += 1) + if (subMenuItems[i].qmPage == n) + return i; + return -1; +} + +//----------------------------------------------------------------------------- + #if VERSION_MAJOR > 2 class QuickSubMenu { public: - QuickSubMenu(Window* parent, QuickMenu* quickMenu, - EdgeTxIcon icon, const char* title, const char* parentTitle, - const PageDef* items): - parent(parent), quickMenu(quickMenu), - icon(icon), title(title), parentTitle(parentTitle), items(items) + QuickSubMenu(Window* parent, QuickMenu* quickMenu, const QMMainDef* mainDef): + parent(parent), quickMenu(quickMenu), mainDef(mainDef) {} bool isSubMenu(QMPage n) { - for (int i = 0; items[i].icon < EDGETX_ICONS_COUNT; i += 1) - if (items[i].qmPage == n) return true; - return false; + return mainDef->isSubMenu(n); } bool isSubMenu(QMPage n, EdgeTxIcon curIcon) { - if ((icon != EDGETX_ICONS_COUNT) && (icon != curIcon)) return false; - return isSubMenu(n); + return mainDef->isSubMenu(n, curIcon); } bool isSubMenu(ButtonBase* b) { - for (int i = 0; items[i].icon < EDGETX_ICONS_COUNT; i += 1) - if (menuButton == b) return true; - return false; + return menuButton == b; } int getIndex(QMPage n) { - for (int i = 0; items[i].icon < EDGETX_ICONS_COUNT; i += 1) - if (items[i].qmPage == n) return i; - return -1; + return mainDef->getIndex(n); } ButtonBase* addButton() { - menuButton = quickMenu->getTopMenu()->addButton(icon, title, + menuButton = quickMenu->getTopMenu()->addButton(mainDef->icon, STR_VAL(mainDef->qmTitle), [=]() -> uint8_t { activate(); return 0; + }, + mainDef->enabled, + [=](bool focus) { + if (!quickMenu->deleted()) { + if (focus) { + if (!subMenu) buildSubMenu(); + quickMenu->getTopMenu()->setCurrent(menuButton); + } + if (subMenu) + subMenu->show(focus); + if (!focus && quickMenu->getTopMenu()) + quickMenu->getTopMenu()->setGroup(); + } }); - menuButton->setFocusHandler([=](bool focus) { - if (!quickMenu->deleted()) { - if (focus) { - if (!subMenu) buildSubMenu(); - quickMenu->getTopMenu()->setCurrent(menuButton); - } - if (subMenu) - subMenu->show(focus); - if (!focus && quickMenu->getTopMenu()) - quickMenu->getTopMenu()->setGroup(); - } - }); - return menuButton; } @@ -129,11 +144,11 @@ class QuickSubMenu { subMenu = new QuickMenuGroup(parent); - for (int i = 0; items[i].icon < EDGETX_ICONS_COUNT; i += 1) { - subMenu->addButton(items[i].icon, STR_VAL(items[i].qmTitle), + for (int i = 0; mainDef->subMenuItems[i].icon < EDGETX_ICONS_COUNT; i += 1) { + subMenu->addButton(mainDef->subMenuItems[i].icon, STR_VAL(mainDef->subMenuItems[i].qmTitle), std::bind(&QuickSubMenu::onPress, this, i), - items[i].enabled, - [=]() { QuickMenu::setCurrentPage(items[i].qmPage, icon); }); + mainDef->subMenuItems[i].enabled, + [=](bool focus) { if (focus) QuickMenu::setCurrentPage(mainDef->subMenuItems[i].qmPage, mainDef->icon); }); } doLayout(); @@ -143,15 +158,15 @@ class QuickSubMenu uint8_t onPress(int n) { - if (items[n].pageAction == PAGE_CREATE) { + if (mainDef->subMenuItems[n].pageAction == PAGE_CREATE) { quickMenu->getTopMenu()->clearFocus(); int pgIdx = getPageNumber(n); - if (quickMenu->getPageGroup() && quickMenu->getPageGroup()->hasSubMenu(items[n].qmPage)) { + if (quickMenu->getPageGroup() && quickMenu->getPageGroup()->hasSubMenu(mainDef->subMenuItems[n].qmPage)) { quickMenu->onSelect(false); quickMenu->getPageGroup()->setCurrentTab(pgIdx); } else { quickMenu->onSelect(true); - auto pg = new PageGroup(icon, parentTitle, items); + auto pg = new PageGroup(mainDef->icon, STR_VAL(mainDef->title), mainDef->subMenuItems); pg->setCurrentTab(pgIdx); } } else { @@ -160,7 +175,7 @@ class QuickSubMenu quickMenu->getTopMenu()->clearFocus(); enableSubMenu(); onSelect(true); - items[n].action(); + mainDef->subMenuItems[n].action(); } return 0; } @@ -174,7 +189,7 @@ class QuickSubMenu { int pageNumber = 0; for (int i = 0; i < iconNumber; i += 1) - if (items[i].pageAction == PAGE_CREATE) + if (mainDef->subMenuItems[i].pageAction == PAGE_CREATE) pageNumber += 1; return pageNumber; } @@ -185,13 +200,18 @@ class QuickSubMenu subMenu->doLayout(QuickMenu::QM_SUB_COLS); } + void clearSubMenu() + { + if (subMenu) { + subMenu->deleteLater(); + subMenu = nullptr; + } + } + protected: Window* parent; QuickMenu* quickMenu; - EdgeTxIcon icon; - const char* title; - const char* parentTitle; - const PageDef* items; + const QMMainDef* mainDef; QuickMenuGroup* subMenu = nullptr; ButtonBase* menuButton = nullptr; }; @@ -200,34 +220,26 @@ class QuickSubMenu //----------------------------------------------------------------------------- QuickMenu* QuickMenu::instance = nullptr; -QMPage QuickMenu::curPage = QM_NONE; -EdgeTxIcon QuickMenu::curIcon = EDGETX_ICONS_COUNT; -QuickMenu* QuickMenu::openQuickMenu(std::function selectHandler, - PageGroupBase* pageGroup, QMPage curPage) +void QuickMenu::openQuickMenu() { - if (!instance) { + if (instance && instance->isVisible()) + return; + + if (!instance) instance = new QuickMenu(); - } - instance->openQM(selectHandler, pageGroup, curPage); - return instance; -} -void QuickMenu::closeQuickMenu() -{ - if (instance) - instance->closeQM(); -} + QMPage qmPage = QM_NONE; + PageGroup* p = Window::pageGroup(); + if (p) + qmPage = p->getCurrentTab()->pageId(); -bool QuickMenu::isOpen() -{ - return instance && instance->isVisible(); + instance->openQM(p, qmPage); } void QuickMenu::shutdownQuickMenu() { if (instance) instance->deleteLater(); - instance = nullptr; } QuickMenu::QuickMenu() : @@ -261,42 +273,32 @@ QuickMenu::QuickMenu() : #if VERSION_MAJOR > 2 box = new Window(this, {QM_SUB_X, QM_SUB_Y, QM_SUB_W, QM_SUB_H}); - int f = 0; - for (int i = 0; i < MAX_QM_FAVORITES; i += 1) { - if (g_eeGeneral.qmFavorites[i].shortcut != QM_NONE) { - setupFavorite((QMPage)g_eeGeneral.qmFavorites[i].shortcut, f); - f += 1; - } - } - favoritesMenuItems[f].icon = EDGETX_ICONS_COUNT; + updateFavorites(); #endif for (int i = 0; qmTopItems[i].icon != EDGETX_ICONS_COUNT; i += 1) { - if ((qmTopItems[i].enabled == nullptr) || qmTopItems[i].enabled()) { - if (qmTopItems[i].pageAction == QM_ACTION) { - mainMenu->addButton(qmTopItems[i].icon, STR_VAL(qmTopItems[i].qmTitle), [=]() { onSelect(true); qmTopItems[i].action(); }); + if (qmTopItems[i].pageAction == QM_ACTION) { + mainMenu->addButton(qmTopItems[i].icon, STR_VAL(qmTopItems[i].qmTitle), + [=]() { onSelect(true); qmTopItems[i].action(); }, qmTopItems[i].enabled); #if VERSION_MAJOR > 2 - } else { - auto sub = new QuickSubMenu(box, this, qmTopItems[i].icon, STR_VAL(qmTopItems[i].qmTitle), STR_VAL(qmTopItems[i].title), qmTopItems[i].subMenuItems); - sub->addButton(); - subMenus.emplace_back(sub); + } else { + auto sub = new QuickSubMenu(box, this, &qmTopItems[i]); + sub->addButton(); + subMenus.emplace_back(sub); #endif - } } } } void QuickMenu::deleteLater() { - if (_deleted) return; - - instance = nullptr; - - NavWindow::deleteLater(); + if (!_deleted) { + instance = nullptr; + NavWindow::deleteLater(); + } } -void QuickMenu::openQM(std::function selectHandler, - PageGroupBase* newPageGroup, QMPage newCurPage) +void QuickMenu::openQM(PageGroupBase* newPageGroup, QMPage newCurPage) { pushLayer(); @@ -310,7 +312,6 @@ void QuickMenu::openQM(std::function selectHandler, subMenus[i]->doLayout(); #endif - this->selectHandler = std::move(selectHandler); show(); lv_obj_move_foreground(lvobj); if (newPageGroup) { @@ -458,6 +459,27 @@ std::vector QuickMenu::menuPageNames(bool forFavorites) } #if VERSION_MAJOR > 2 +void QuickMenu::resetFavorites() +{ + if (instance) + instance->updateFavorites(); +} + +void QuickMenu::updateFavorites() +{ + int f = 0; + for (int i = 0; i < MAX_QM_FAVORITES; i += 1) { + if (g_eeGeneral.qmFavorites[i].shortcut != QM_NONE) { + setupFavorite((QMPage)g_eeGeneral.qmFavorites[i].shortcut, f); + f += 1; + } + } + favoritesMenuItems[f].icon = EDGETX_ICONS_COUNT; + + if (subMenus.size() >= 1) + subMenus[0]->clearSubMenu(); +} + void QuickMenu::setupFavorite(QMPage page, int f) { PageDef& fav = favoritesMenuItems[f]; @@ -497,8 +519,10 @@ void QuickMenu::setupFavorite(QMPage page, int f) void QuickMenu::setCurrentPage(QMPage newPage, EdgeTxIcon newIcon) { - curPage = newPage; - curIcon = newIcon; + if (instance) { + instance->curPage = newPage; + instance->curIcon = newIcon; + } } void QuickMenu::focusMainMenu() @@ -513,9 +537,8 @@ void QuickMenu::focusMainMenu() void QuickMenu::onSelect(bool close) { - if (selectHandler) selectHandler(close); - selectHandler = nullptr; closeQM(); + Messaging::send(Messaging::QUICK_MENU_ITEM_SELECT, close); } void QuickMenu::closeQM() @@ -523,7 +546,6 @@ void QuickMenu::closeQM() if (isVisible()) { popLayer(); hide(); - selectHandler = nullptr; } } diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu.h b/radio/src/gui/colorlcd/setup_menus/quick_menu.h index bee497cf3..db2b95c26 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu.h +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu.h @@ -27,16 +27,44 @@ #include "quick_menu_group.h" #include "quick_menu_def.h" -std::string replaceAll(std::string str, const std::string& from, const std::string& to); +//----------------------------------------------------------------------------- -class PageGroup; class PageGroupBase; -class ButtonBase; struct PageDef; #if VERSION_MAJOR > 2 class QuickSubMenu; #endif +//----------------------------------------------------------------------------- + +enum QMMainDefAction { + QM_SUBMENU, + QM_ACTION +}; + +struct QMMainDef { + EdgeTxIcon icon; + STR_TYP qmTitle; + STR_TYP title; + QMMainDefAction pageAction; + QMPage qmPage; + const PageDef* subMenuItems; + std::function action; + std::function enabled; + + bool isSubMenu(QMPage page) const; + bool isSubMenu(QMPage page, EdgeTxIcon curIcon) const; + int getIndex(QMPage page) const; +}; + +extern const QMMainDef qmTopItems[]; + +//----------------------------------------------------------------------------- + +std::string replaceAll(std::string str, const std::string& from, const std::string& to); + +//----------------------------------------------------------------------------- + #define GRP_W(n) ((QuickMenuGroup::QM_BUTTON_WIDTH + PAD_MEDIUM) * n - PAD_MEDIUM + PAD_OUTLINE * 2) #if LANDSCAPE #define GRP_H(n) ((QuickMenuGroup::QM_BUTTON_HEIGHT + PAD_MEDIUM) * n - PAD_MEDIUM + PAD_OUTLINE * 2) @@ -62,10 +90,7 @@ class QuickMenu : public NavWindow PageGroupBase* getPageGroup() const { return pageGroup; } QuickMenuGroup* getTopMenu() const { return mainMenu; } - static QuickMenu* openQuickMenu(std::function selectHandler = nullptr, - PageGroupBase* pageGroup = nullptr, QMPage curPage = QM_NONE); - static void closeQuickMenu(); - static bool isOpen(); + static void openQuickMenu(); static void shutdownQuickMenu(); static void openPage(QMPage page); @@ -73,6 +98,10 @@ class QuickMenu : public NavWindow static int pageIndex(QMPage page); static std::vector menuPageNames(bool forFavorites); +#if VERSION_MAJOR > 2 + static void resetFavorites(); +#endif + #if defined(HARDWARE_KEYS) void doKeyShortcut(event_t event); void onPressSYS() override; @@ -119,18 +148,16 @@ class QuickMenu : public NavWindow protected: static QuickMenu* instance; - std::function selectHandler = nullptr; bool inSubMenu = false; QuickMenuGroup* mainMenu = nullptr; #if VERSION_MAJOR > 2 std::vector subMenus; #endif PageGroupBase* pageGroup = nullptr; - static QMPage curPage; - static EdgeTxIcon curIcon; + QMPage curPage = QM_NONE; + EdgeTxIcon curIcon = EDGETX_ICONS_COUNT; - void openQM(std::function selectHandler, - PageGroupBase* pageGroup, QMPage curPage); + void openQM(PageGroupBase* pageGroup, QMPage curPage); void closeQM(); void focusMainMenu(); @@ -140,6 +167,9 @@ class QuickMenu : public NavWindow static void selected(); #if VERSION_MAJOR > 2 - static void setupFavorite(QMPage page, int f); + void updateFavorites(); + void setupFavorite(QMPage page, int f); #endif }; + +//----------------------------------------------------------------------------- diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_data.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu_data.cpp index 5009f1967..ff5d80596 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_data.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_data.cpp @@ -117,7 +117,7 @@ const PageDef statsMenuItems[] = { { EDGETX_ICONS_COUNT } }; -const QMTopDef qmTopItems[] = { +const QMMainDef qmTopItems[] = { { ICON_MODEL_SELECT, STR_DEF(STR_QM_MANAGE_MODELS), STR_DEF(STR_MANAGE_MODELS), QM_ACTION, QM_MANAGE_MODELS, nullptr, []() { new ModelLabelsWindow(); }}, { ICON_MODEL_NOTES, STR_DEF(STR_MAIN_MENU_MODEL_NOTES), STR_DEF(STR_MAIN_MENU_MODEL_NOTES), QM_ACTION, QM_NONE, nullptr, []() { QuickMenu::openPage(QM_MODEL_NOTES); }, modelHasNotes}, { ICON_MONITOR, STR_DEF(STR_QM_CHAN_MON), STR_DEF(STR_QM_CHAN_MON), QM_ACTION, QM_TOOLS_CHAN_MON, nullptr, []() { new ChannelsViewMenu(); } @@ -183,7 +183,7 @@ const PageDef toolsMenuItems[] = { { EDGETX_ICONS_COUNT } }; -const QMTopDef qmTopItems[] = { +const QMMainDef qmTopItems[] = { { ICON_QM_FAVORITES, STR_DEF(STR_FAVORITE_LABEL), STR_DEF(STR_FAVORITE_LABEL), QM_SUBMENU, QM_NONE, favoritesMenuItems, nullptr, []() { return favoritesMenuItems[0].icon != EDGETX_ICONS_COUNT; }}, { ICON_MODEL_SELECT, STR_DEF(STR_QM_MANAGE_MODELS), STR_DEF(STR_MANAGE_MODELS), QM_ACTION, QM_MANAGE_MODELS, nullptr, []() { new ModelLabelsWindow(); }}, { ICON_MODEL, STR_DEF(STR_QM_MODEL_SETUP), STR_DEF(STR_MAIN_MENU_MODEL_SETTINGS), QM_SUBMENU, QM_NONE, modelMenuItems}, diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_favorites.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu_favorites.cpp index 0e6753b47..bbc54d8c9 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_favorites.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_favorites.cpp @@ -67,7 +67,7 @@ void QMFavoritesPage::onCancel() if (changed) { // Delete quick menu, and close parent page group (in case it is Favorites group) - QuickMenu::shutdownQuickMenu(); + QuickMenu::resetFavorites(); QuickMenu::setCurrentPage(QM_NONE); Window::topWindow()->onCancel(); } diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp index 6cb13db1c..3618e3b9e 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.cpp @@ -127,6 +127,11 @@ class QuickMenuButton : public ButtonBase return true; } + void show(bool vis) override + { + ButtonBase::show(vis && isVisible()); + } + protected: StaticIcon* iconPtr = nullptr; StaticText* textPtr = nullptr; @@ -143,22 +148,18 @@ QuickMenuGroup::QuickMenuGroup(Window* parent) : ButtonBase* QuickMenuGroup::addButton(EdgeTxIcon icon, const char* title, std::function pressHandler, std::function visibleHandler, - std::function focusHandler) + std::function focusHandler) { ButtonBase* b = new QuickMenuButton(this, icon, title, [=]() { pressHandler(); return 0; }, visibleHandler); b->setLongPressHandler([=]() { pressHandler(); return 0; }); btns.push_back(b); if (group) lv_group_add_obj(group, b->getLvObj()); b->setFocusHandler([=](bool focus) { - if (focus) { + if (focus) curBtn = b; - if (focusHandler) focusHandler(); - } + if (focusHandler) + focusHandler(focus); }); - if (btns.size() == 1) { - curBtn = b; - if (focusHandler) focusHandler(); - } return b; } @@ -238,11 +239,9 @@ void QuickMenuGroup::doLayout(int cols) coord_t x = (n % cols) * (QM_BUTTON_WIDTH + PAD_MEDIUM); coord_t y = (n / cols) * (QM_BUTTON_HEIGHT + PAD_MEDIUM); lv_obj_set_pos(btns[i]->getLvObj(), x, y); - btns[i]->show(); n += 1; - } else { - btns[i]->hide(); } + btns[i]->show(); } } diff --git a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h index 70f542fdd..3baa56a26 100644 --- a/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h +++ b/radio/src/gui/colorlcd/setup_menus/quick_menu_group.h @@ -40,7 +40,7 @@ class QuickMenuGroup : public Window ButtonBase* addButton(EdgeTxIcon icon, const char* title, std::function pressHandler, std::function visibleHandler = nullptr, - std::function focusHandler = nullptr); + std::function focusHandler = nullptr); void setGroup(); void setFocus(); @@ -55,6 +55,8 @@ class QuickMenuGroup : public Window void prevEntry(); ButtonBase* getFocusedButton(); + void deleteLater() override; + #if PORTRAIT static LAYOUT_VAL_SCALED(QM_BUTTON_WIDTH, 72) #else @@ -69,6 +71,4 @@ class QuickMenuGroup : public Window std::vector btns; ButtonBase* curBtn = nullptr; lv_group_t* group = nullptr; - - void deleteLater() override; }; diff --git a/radio/src/gui/colorlcd/startup_shutdown.cpp b/radio/src/gui/colorlcd/startup_shutdown.cpp index df1cf7791..c675b6b32 100644 --- a/radio/src/gui/colorlcd/startup_shutdown.cpp +++ b/radio/src/gui/colorlcd/startup_shutdown.cpp @@ -81,8 +81,8 @@ void drawSplash() #endif } - // Force screen refresh - lv_refr_now(nullptr); + // Refresh to show splash screen + MainWindow::instance()->run(); } static tmr10ms_t splashStartTime = 0; @@ -110,25 +110,17 @@ void waitSplash() if (splashStartTime) { inactivityCheckInputs(); splashStartTime += SPLASH_TIMEOUT; - while (splashStartTime >= get_tmr10ms()) { - MainWindow::instance()->run(); - WDG_RESET(); - checkSpeakerVolume(); - checkBacklight(); - sleep_ms(10); + + MainWindow::instance()->blockUntilClose(true, [=]() { + if (splashStartTime < get_tmr10ms()) + return true; auto evt = getEvent(); if (evt || inactivityCheckInputs()) { if (evt) killEvents(evt); - break; + return true; } -#if defined(SIMU) - // Allow simulator to exit if closed while splash showing - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - break; - } -#endif // defined(SIMU) - } + return false; + }); // Reset timer so special/global functions set to !1x don't get triggered START_SILENCE_PERIOD(); @@ -217,49 +209,23 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, MainWindow::instance()->run(); } -void drawFatalErrorScreen(const char* message) +Window* drawFatalErrorScreen(const char* message) { - static Window* fatalErrorWindow = nullptr; + auto w = new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); + w->setWindowFlag(OPAQUE); + etx_solid_bg(w->getLvObj(), COLOR_BLACK_INDEX); - if (!fatalErrorWindow) { - fatalErrorWindow = - new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); - fatalErrorWindow->setWindowFlag(OPAQUE); - etx_solid_bg(fatalErrorWindow->getLvObj(), COLOR_BLACK_INDEX); + new StaticText(w, rect_t{0, LCD_H / 2 - EdgeTxStyles::STD_FONT_HEIGHT, LCD_W, EdgeTxStyles::STD_FONT_HEIGHT * 2}, + message, COLOR_WHITE_INDEX, FONT(XL) | CENTERED); - new StaticText(fatalErrorWindow, rect_t{0, LCD_H / 2 - EdgeTxStyles::STD_FONT_HEIGHT, LCD_W, EdgeTxStyles::STD_FONT_HEIGHT * 2}, - message, COLOR_WHITE_INDEX, FONT(XL) | CENTERED); - } - - backlightEnable(100); - MainWindow::instance()->run(); + return w; } void runFatalErrorScreen(const char* message) { - lcdInitDisplayDriver(); - drawFatalErrorScreen(message); - // On startup wait for power button to be released - while (pwrPressed()) { - WDG_RESET(); - } - - while (true) { - drawFatalErrorScreen(message); - WDG_RESET(); - - // loop as long as PWR button is pressed - while (true) { - uint32_t pwr_check = pwrCheck(); - if (pwr_check == e_power_off) { - boardOff(); - return; // only happens in SIMU, required for proper shutdown - } else if (pwr_check == e_power_on) { - break; - } - WDG_RESET(); - } - } + MainWindow::instance()->blockUntilClose(true, []() { + return false; + }, true); } diff --git a/radio/src/gui/colorlcd/startup_shutdown.h b/radio/src/gui/colorlcd/startup_shutdown.h index 6d8d72e1f..b4f9a82a1 100644 --- a/radio/src/gui/colorlcd/startup_shutdown.h +++ b/radio/src/gui/colorlcd/startup_shutdown.h @@ -23,7 +23,9 @@ #include -void drawFatalErrorScreen(const char* message); +class Window; + +Window* drawFatalErrorScreen(const char* message); void runFatalErrorScreen(const char* message); // Screen templates diff --git a/radio/src/gui/colorlcd/themes/theme_manager.cpp b/radio/src/gui/colorlcd/themes/theme_manager.cpp index fa070b4b0..0bea14b4e 100644 --- a/radio/src/gui/colorlcd/themes/theme_manager.cpp +++ b/radio/src/gui/colorlcd/themes/theme_manager.cpp @@ -586,15 +586,3 @@ HeaderBackIcon::HeaderBackIcon(Window* parent, std::function action) : } #endif } - -UsbSDConnected::UsbSDConnected() : - Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}) -{ - setWindowFlag(OPAQUE); - - etx_solid_bg(lvobj, COLOR_THEME_PRIMARY1_INDEX); - new HeaderDateTime(this, LCD_W - TopBar::HDR_DATE_XO, PAD_MEDIUM); - - auto icon = new StaticIcon(this, 0, 0, ICON_USB_PLUGGED, COLOR_THEME_PRIMARY2_INDEX); - lv_obj_center(icon->getLvObj()); -} diff --git a/radio/src/gui/colorlcd/themes/theme_manager.h b/radio/src/gui/colorlcd/themes/theme_manager.h index 9a24525fd..7e3dff8bd 100644 --- a/radio/src/gui/colorlcd/themes/theme_manager.h +++ b/radio/src/gui/colorlcd/themes/theme_manager.h @@ -211,9 +211,3 @@ class HeaderBackIcon : public StaticIcon if (action) action(); } }; - -class UsbSDConnected : public Window -{ - public: - UsbSDConnected(); -}; diff --git a/radio/src/gui/colorlcd/widgets/gauge.cpp b/radio/src/gui/colorlcd/widgets/gauge.cpp index 4ae9fd563..b0b1b11b2 100644 --- a/radio/src/gui/colorlcd/widgets/gauge.cpp +++ b/radio/src/gui/colorlcd/widgets/gauge.cpp @@ -105,12 +105,10 @@ class GaugeWidget : public Widget DynamicNumber* valueText = nullptr; lv_obj_t* bar = nullptr; - void checkEvents() override + void foreground() override { if (!loaded) return; - Widget::checkEvents(); - auto newValue = getGuageValue(); if (lastValue != newValue) { lastValue = newValue; diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index 5e252eeb0..e5d81a992 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -50,15 +50,13 @@ class ModelBitmapWidget : public Widget image = new StaticBitmap(this, {0, 0, width(), height()}); image->hide(); - checkEvents(); + foreground(); } - void checkEvents() override + void foreground() override { if (!loaded) return; - Widget::checkEvents(); - if (getHash() != deps_hash) { update(); } diff --git a/radio/src/gui/colorlcd/widgets/outputs.cpp b/radio/src/gui/colorlcd/widgets/outputs.cpp index 8e3c5a86e..6b8998fdf 100644 --- a/radio/src/gui/colorlcd/widgets/outputs.cpp +++ b/radio/src/gui/colorlcd/widgets/outputs.cpp @@ -19,9 +19,10 @@ * GNU General Public License for more details. */ -#include "edgetx.h" #include "widget.h" -#include + +#include "edgetx.h" +#include "messaging.h" constexpr int16_t OUTPUT_INVALID_VALUE = INT16_MIN; @@ -38,6 +39,8 @@ class ChannelValue : public Window { setWindowFlag(NO_FOCUS | NO_CLICK); + refreshMsg.subscribe(Messaging::REFRESH_OUTPUTS_WIDGET, [=](uint32_t param) { refresh(); }); + delayLoad(); } @@ -79,7 +82,7 @@ class ChannelValue : public Window lv_line_set_points(divLine, divPoints, 2); etx_obj_add_style(divLine, styles->div_line, LV_PART_MAIN); - checkEvents(); + refresh(); } void setChannel() @@ -94,7 +97,7 @@ class ChannelValue : public Window lv_label_set_text(chanLabel, s); } - void checkEvents() override + void refresh() { if (!loaded) return; @@ -136,8 +139,6 @@ class ChannelValue : public Window chanHasName = hasName; setChannel(); } - - Window::checkEvents(); } static LAYOUT_VAL_SCALED(ROW_HEIGHT, 16) @@ -155,6 +156,7 @@ class ChannelValue : public Window lv_obj_t* chanLabel = nullptr; lv_point_t divPoints[2]; lv_obj_t* bar = nullptr; + Messaging refreshMsg; }; class OutputsWidget : public Widget @@ -225,6 +227,11 @@ class OutputsWidget : public Widget } } + void foreground() override + { + Messaging::send(Messaging::REFRESH_OUTPUTS_WIDGET); + } + static const WidgetOption options[]; protected: diff --git a/radio/src/gui/colorlcd/widgets/radio_info.cpp b/radio/src/gui/colorlcd/widgets/radio_info.cpp index 976b531e2..0ac951177 100644 --- a/radio/src/gui/colorlcd/widgets/radio_info.cpp +++ b/radio/src/gui/colorlcd/widgets/radio_info.cpp @@ -19,28 +19,19 @@ * GNU General Public License for more details. */ -#include "hal/usb_driver.h" -#include "edgetx.h" -#include "theme_manager.h" #include "widget.h" + +#include "edgetx.h" +#include "hal/usb_driver.h" #include "layout.h" +#include "theme_manager.h" -class TopBarWidget : public Widget -{ - public: - TopBarWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, - int screenNum, int zoneNum) : - Widget(factory, parent, rect, screenNum, zoneNum) - { - } -}; - -class RadioInfoWidget : public TopBarWidget +class RadioInfoWidget : public Widget { public: RadioInfoWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum) : - TopBarWidget(factory, parent, rect, screenNum, zoneNum) + Widget(factory, parent, rect, screenNum, zoneNum) { // Logs logsIcon = new StaticIcon(this, W_LOG_X, PAD_THREE, ICON_DOT, @@ -108,7 +99,7 @@ class RadioInfoWidget : public TopBarWidget etx_bg_color(rssiBars[i], COLOR_THEME_PRIMARY2_INDEX, LV_STATE_USER_1); } - checkEvents(); + foreground(); } void update() override @@ -121,10 +112,8 @@ class RadioInfoWidget : public TopBarWidget etx_bg_color_from_flags(batteryFill, widgetData->options[0].value.unsignedValue, LV_STATE_USER_2); } - void checkEvents() override + void foreground() override { - TopBarWidget::checkEvents(); - usbIcon->show(usbPlugged()); if (getSelectedUsbMode() == USB_UNSELECTED_MODE) usbIcon->setColor(COLOR_THEME_PRIMARY3_INDEX); @@ -240,18 +229,23 @@ const WidgetOption RadioInfoWidget::options[] = { BaseWidgetFactory RadioInfoWidget("Radio Info", RadioInfoWidget::options, STR_RADIO_INFO_WIDGET); -class DateTimeWidget : public TopBarWidget +class DateTimeWidget : public Widget { public: DateTimeWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum) : - TopBarWidget(factory, parent, rect, screenNum, zoneNum) + Widget(factory, parent, rect, screenNum, zoneNum) { coord_t x = rect.w - HeaderDateTime::HDR_DATE_WIDTH - DT_XO; dateTime = new HeaderDateTime(this, x, PAD_THREE); update(); } + void foreground() override + { + Widget::checkEvents(); + } + void update() override { auto widgetData = getPersistentData(); @@ -283,12 +277,12 @@ BaseWidgetFactory DateTimeWidget("Date Time", #if defined(INTERNAL_GPS) -class InternalGPSWidget : public TopBarWidget +class InternalGPSWidget : public Widget { public: InternalGPSWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum) : - TopBarWidget(factory, parent, rect, screenNum, zoneNum) + Widget(factory, parent, rect, screenNum, zoneNum) { icon = new StaticIcon(this, width() / 2 - PAD_LARGE - PAD_TINY, ICON_H, @@ -299,10 +293,8 @@ class InternalGPSWidget : public TopBarWidget COLOR_THEME_PRIMARY2_INDEX, CENTERED | FONT(XS)); } - void checkEvents() override + void foreground() override { - TopBarWidget::checkEvents(); - bool hasGPS = serialGetModePort(UART_MODE_GPS) >= 0; numSats->show(hasGPS && (gpsData.numSat > 0)); diff --git a/radio/src/gui/colorlcd/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp index 1740dbd7d..f3a95cc4f 100644 --- a/radio/src/gui/colorlcd/widgets/timer.cpp +++ b/radio/src/gui/colorlcd/widgets/timer.cpp @@ -102,15 +102,13 @@ class TimerWidget : public Widget lv_obj_add_flag(timerArc, LV_OBJ_FLAG_HIDDEN); update(); - checkEvents(); + foreground(); } - void checkEvents() override + void foreground() override { if (!loaded) return; - Widget::checkEvents(); - auto widgetData = getPersistentData(); uint32_t index = widgetData->options[0].value.unsignedValue; diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index b8ef48dbf..7a6e2e535 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -71,15 +71,13 @@ class ValueWidget : public Widget lv_label_set_text(value, ""); update(); - checkEvents(); + foreground(); } - void checkEvents() override + void foreground() override { if (!loaded) return; - Widget::checkEvents(); - bool changed = false; auto widgetData = getPersistentData(); diff --git a/radio/src/gui/common/stdlcd/draw_functions.h b/radio/src/gui/common/stdlcd/draw_functions.h index fe4ab6665..fb0fbec78 100644 --- a/radio/src/gui/common/stdlcd/draw_functions.h +++ b/radio/src/gui/common/stdlcd/draw_functions.h @@ -43,7 +43,6 @@ void drawFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att = 0); void drawStartupAnimation(uint32_t duration, uint32_t totalDuration); void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message); void drawSleepBitmap(); -void drawFatalErrorScreen(const char * message); void runFatalErrorScreen(const char * message); void lcdDrawMMM(coord_t x, coord_t y, LcdFlags flags=0); diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 6d58240b3..706d33b7d 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -721,32 +721,28 @@ bool isSourceAvailableInResetSpecialFunction(int index) class AntennaSelectionMenu : public Menu { - bool& done; - -public: - AntennaSelectionMenu(bool& done) : Menu(), done(done) { + public: + AntennaSelectionMenu() : Menu() + { setTitle(STR_ANTENNA); addLine(STR_USE_INTERNAL_ANTENNA, [] { globalData.externalAntennaEnabled = false; }); addLine(STR_USE_EXTERNAL_ANTENNA, [] { globalData.externalAntennaEnabled = true; }); - setCloseHandler([=]() { this->done = true; }); setCloseWhenClickOutside(false); } -protected: + + protected: void onCancel() override {} }; static void runAntennaSelectionMenu() { - bool finished = false; - new AntennaSelectionMenu(finished); + auto menu = new AntennaSelectionMenu(); - while (!finished) { - WDG_RESET(); - MainWindow::instance()->run(); - sleep_ms(20); - } + MainWindow::instance()->blockUntilClose(true, [=]() { + return menu->deleted(); + }); } #else void onAntennaSelection(const char* result) diff --git a/radio/src/lua/lua_lvgl_widget.cpp b/radio/src/lua/lua_lvgl_widget.cpp index 96edc184a..d0ce15b07 100644 --- a/radio/src/lua/lua_lvgl_widget.cpp +++ b/radio/src/lua/lua_lvgl_widget.cpp @@ -1500,7 +1500,7 @@ void LvglWidgetBox::build(lua_State *L) { window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); - window->disableForcedScroll(); + window->setWindowFlag(NO_FORCED_SCROLL); window->setScrollHandler([=](coord_t x, coord_t y) { pcallFuncWith2Int(L, scrolledFunction, 0, x, y); }); lv_obj_add_flag(window->getLvObj(), LV_OBJ_FLAG_EVENT_BUBBLE); if (luaScriptManager->isWidget() && !luaScriptManager->isFullscreen()) { @@ -2472,7 +2472,7 @@ void LvglWidgetPage::build(lua_State *L) showBackButton, prevActionFunction != LUA_REFNIL, nextActionFunction != LUA_REFNIL); window = page->getBody(); - window->disableForcedScroll(); + window->setWindowFlag(NO_FORCED_SCROLL); window->setScrollHandler([=](coord_t x, coord_t y) { pcallFuncWith2Int(L, scrolledFunction, 0, x, y); }); if (setFlex()) { lv_flex_align_t align1 = (align.flags & RIGHT) ? LV_FLEX_ALIGN_END : (align.flags & CENTERED) ? LV_FLEX_ALIGN_CENTER : LV_FLEX_ALIGN_START; diff --git a/radio/src/lua/lua_widget.h b/radio/src/lua/lua_widget.h index 638c07eb4..88ec8750c 100644 --- a/radio/src/lua/lua_widget.h +++ b/radio/src/lua/lua_widget.h @@ -104,8 +104,6 @@ class LuaScriptManager : public LuaEventHandler class LuaWidget : public Widget, public LuaScriptManager { - friend class LuaWidgetFactory; - public: LuaWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, int screenNum, int zoneNum, int zoneRectDataRef, diff --git a/radio/src/main.cpp b/radio/src/main.cpp index dc102e810..8abf1cff2 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -141,6 +141,23 @@ void closeUsbMenu() #endif #if defined(COLORLCD) +class UsbSDConnected : public Window +{ + public: + UsbSDConnected() : Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}) + { + setWindowFlag(OPAQUE); + + pushLayer(false); + + etx_solid_bg(lvobj, COLOR_THEME_PRIMARY1_INDEX); + new HeaderDateTime(this, LCD_W - TopBar::HDR_DATE_XO, PAD_MEDIUM); + + auto icon = new StaticIcon(this, 0, 0, ICON_USB_PLUGGED, COLOR_THEME_PRIMARY2_INDEX); + lv_obj_center(icon->getLvObj()); + } +}; + static UsbSDConnected* usbConnectedWindow = nullptr; #endif @@ -193,11 +210,21 @@ void handleUsbConnection() usbStop(); TRACE("USB stopped"); if (getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { - edgeTxResume(); #if defined(COLORLCD) usbConnectedWindow->deleteLater(); usbConnectedWindow = nullptr; + // In case the SD card is removed during the session + if (!SD_CARD_PRESENT()) { + // Blocks until shutdown or SD card inserted + auto w = drawFatalErrorScreen(STR_NO_SDCARD); + MainWindow::instance()->blockUntilClose(true, []() { + return SD_CARD_PRESENT(); + }, true); + w->deleteLater(); + } + edgeTxResume(); #else + edgeTxResume(); pushEvent(EVT_ENTRY); #endif } else if (getSelectedUsbMode() == USB_SERIAL_MODE) { @@ -350,8 +377,6 @@ void guiMain(event_t evt) } #endif - MainWindow::instance()->run(); - bool mainViewRequested = (mainRequestFlags & (1u << REQUEST_MAIN_VIEW)); if (mainViewRequested) { auto viewMain = ViewMain::instance(); @@ -369,6 +394,20 @@ void guiMain(event_t evt) writeScreenshot(); mainRequestFlags &= ~(1u << REQUEST_SCREENSHOT); } + + // For color screens show a popup deferred from another task + show_ui_popup(); + // Show GVAR popup + if (gvarDisplayTimer > 0) { + char s[30], *p; + p = strAppendStringWithIndex(s, STR_GV, gvarLastChanged + 1); + p = strAppend(p, " ", 1); + p = strAppend(p, g_model.gvars[gvarLastChanged].name, LEN_GVAR_NAME); + p = strAppend(p, " = ", 3); + p = strAppendSigned(p, GVAR_VALUE(gvarLastChanged, getGVarFlightMode(mixerCurrentFlightMode, gvarLastChanged))); + POPUP_BUBBLE(s, gvarDisplayTimer * 10, 200); + gvarDisplayTimer = 0; + } } #elif defined(GUI) @@ -510,10 +549,14 @@ void perMain() checkHatsAsKeys(); #endif +#if defined(COLORLCD) + MainWindow::instance()->run(); +#endif + #if defined(RTC_BACKUP_RAM) if (UNEXPECTED_SHUTDOWN()) { #if defined(COLORLCD) - drawFatalErrorScreen(STR_EMERGENCY_MODE); + backlightEnable(BACKLIGHT_LEVEL_MAX); #else lcdClear(); menuMainView(0); @@ -523,26 +566,18 @@ void perMain() } #endif - if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) && - SD_CARD_PRESENT() && !sdMounted()) { - sdMount(); - } + if (!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) { + if (SD_CARD_PRESENT() && !sdMounted()) + sdMount(); - // In case the SD card is removed during the session - if ((!usbPlugged() || (getSelectedUsbMode() == USB_UNSELECTED_MODE)) && - !SD_CARD_PRESENT() && !UNEXPECTED_SHUTDOWN()) { - // TODO: implement for b/w -#if defined(COLORLCD) - drawFatalErrorScreen(STR_NO_SDCARD); - return; -#endif + // In case the SD card is removed during the session + if (!SD_CARD_PRESENT()) { + // TODO: implement for b/w + } } if (usbPlugged() && getSelectedUsbMode() == USB_MASS_STORAGE_MODE) { -#if defined(COLORLCD) - MainWindow::instance()->run(); - usbConnectedWindow->checkEvents(); -#else +#if !defined(COLORLCD) // disable access to menus lcdClear(); menuMainView(0); @@ -567,19 +602,6 @@ void perMain() DEBUG_TIMER_START(debugTimerGuiMain); #if defined(COLORLCD) guiMain(0); - // For color screens show a popup deferred from another task - show_ui_popup(); - // Show GVAR popup - if (gvarDisplayTimer > 0) { - char s[30], *p; - p = strAppendStringWithIndex(s, STR_GV, gvarLastChanged + 1); - p = strAppend(p, " ", 1); - p = strAppend(p, g_model.gvars[gvarLastChanged].name, LEN_GVAR_NAME); - p = strAppend(p, " = ", 3); - p = strAppendSigned(p, GVAR_VALUE(gvarLastChanged, getGVarFlightMode(mixerCurrentFlightMode, gvarLastChanged))); - POPUP_BUBBLE(s, gvarDisplayTimer * 10, 200); - gvarDisplayTimer = 0; - } #else guiMain(evt); #endif diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 4ffe397e3..990a62ab9 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -1293,7 +1293,7 @@ const char *ModelsList::save(LabelsVector newOrder) f_puts("\r\n", &file); f_close(&file); - modelslabels._isDirty = false; + modelslabels.resetDirty(); return NULL; } diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index b26575753..c3d334400 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -139,6 +139,7 @@ class ModelMap : protected std::multimap } std::string getBulletLabelString(ModelCell *, const char *noresults = ""); void setDirty(bool save = false); + void resetDirty() { _isDirty = false; } bool isDirty() { return _isDirty; } // Currently selected labels in the GUI @@ -163,18 +164,6 @@ class ModelMap : protected std::multimap const std::string &from, const std::string &to); - protected: - ModelsSortBy _sortOrder = DEFAULT_MODEL_SORT; - bool _isDirty = true; - std::set filtlbls; - std::string currentlabel = ""; - - void updateModelCell(ModelCell *); - bool removeModels( - ModelCell *); // Should only be called from ModelsList remove model - bool updateModelFile(ModelCell *); - void sortModelsBy(ModelsVector &mv, ModelsSortBy sortby); - void clear() { _isDirty = true; @@ -182,6 +171,18 @@ class ModelMap : protected std::multimap std::multimap::clear(); } + void updateModelCell(ModelCell *); + bool removeModels(ModelCell *); + + protected: + ModelsSortBy _sortOrder = DEFAULT_MODEL_SORT; + bool _isDirty = true; + std::set filtlbls; + std::string currentlabel = ""; + + bool updateModelFile(ModelCell *); + void sortModelsBy(ModelsVector &mv, ModelsSortBy sortby); + int getIndexByLabel(const std::string &str) { auto a = std::find(labels.begin(), labels.end(), str); @@ -196,8 +197,6 @@ class ModelMap : protected std::multimap return std::string(); } - friend class ModelsList; - private: LabelsVector labels; // Storage space for discovered labels }; diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index a39d894de..16b6bf7e0 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -118,6 +118,13 @@ void postRadioSettingsLoad() serialSetMode(port_nr, UART_MODE_NONE); } #endif + +#if defined(COLORLCD) + // Ensure ON brightness >= OFF brightness + if ((BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright) < g_eeGeneral.blOffBright) + g_eeGeneral.backlightBright = + BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; +#endif } static bool sortMixerLines() diff --git a/radio/src/switches.cpp b/radio/src/switches.cpp index 23bd912b8..d202e37dd 100644 --- a/radio/src/switches.cpp +++ b/radio/src/switches.cpp @@ -95,6 +95,10 @@ uint8_t isSwitch3Pos(uint8_t idx) return IS_CONFIG_3POS(idx); } +#if defined(SIMU) +bool evalFSok = false; +#endif + void setFSStartupPosition() { for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { @@ -118,6 +122,10 @@ void setFSStartupPosition() } } } + +#if defined(SIMU) + evalFSok = true; +#endif } void setFSLogicalState(uint8_t index, uint8_t value) @@ -159,6 +167,10 @@ bool isFSGroupUsed(uint8_t index) void evalFunctionSwitches() { +#if defined(SIMU) + if (!evalFSok) return; +#endif + for (uint8_t i = 0; i < switchGetMaxSwitches(); i++) { if (switchIsCustomSwitch(i)) { if (g_model.getSwitchType(i) == SWITCH_NONE) { @@ -884,7 +896,9 @@ void checkSwitches() LED_ERROR_BEGIN(); auto dialog = new SwitchWarnDialog(); - dialog->runForever(); + MainWindow::instance()->blockUntilClose(true, [=]() { + return dialog->deleted(); + }); LED_ERROR_END(); } #elif defined(GUI) diff --git a/radio/src/targets/pa01/battery_driver.cpp b/radio/src/targets/pa01/battery_driver.cpp index 4b8ad5765..506540956 100644 --- a/radio/src/targets/pa01/battery_driver.cpp +++ b/radio/src/targets/pa01/battery_driver.cpp @@ -344,7 +344,8 @@ void handle_battery_charge(uint32_t last_press_time) if (!lcdInited) { lcdInited = true; backlightInit(); - lcdInitDisplayDriver(); + // Ensure lvgl is initialised before creating windows + LvglWrapper::instance(); } if (updateTime == 0 || ((timersGetMsTick() - updateTime) >= 500)) diff --git a/radio/src/targets/pl18/battery_driver.cpp b/radio/src/targets/pl18/battery_driver.cpp index 026316730..5ec4ee6df 100644 --- a/radio/src/targets/pl18/battery_driver.cpp +++ b/radio/src/targets/pl18/battery_driver.cpp @@ -480,7 +480,8 @@ void handle_battery_charge(uint32_t last_press_time) if (!lcdInited) { lcdInited = true; backlightInit(); - lcdInitDisplayDriver(); + // Ensure lvgl is initialised before creating windows + LvglWrapper::instance(); } if (updateTime == 0 || ((timersGetMsTick() - updateTime) >= 500)) diff --git a/radio/src/targets/st16/battery_driver.cpp b/radio/src/targets/st16/battery_driver.cpp index e37c51799..1c95d1eef 100644 --- a/radio/src/targets/st16/battery_driver.cpp +++ b/radio/src/targets/st16/battery_driver.cpp @@ -337,7 +337,8 @@ void handle_battery_charge(uint32_t last_press_time) if (!lcdInited) { lcdInited = true; backlightInit(); - lcdInitDisplayDriver(); + // Ensure lvgl is initialised before creating windows + LvglWrapper::instance(); } if (updateTime == 0 || ((timersGetMsTick() - updateTime) >= 500)) diff --git a/radio/src/tasks.cpp b/radio/src/tasks.cpp index 9e6e28bf5..f5b5896d4 100644 --- a/radio/src/tasks.cpp +++ b/radio/src/tasks.cpp @@ -33,7 +33,6 @@ #include "tasks/mixer_task.h" #if defined(COLORLCD) -#include "LvglWrapper.h" #include "startup_shutdown.h" #endif @@ -55,14 +54,15 @@ bool perMainEnabled = true; static void menusTask() { -#if defined(COLORLCD) - LvglWrapper::instance(); -#endif - edgeTxInit(); mixerTaskInit(); +#if defined(COLORLCD) && defined(RTC_BACKUP_RAM) + if (UNEXPECTED_SHUTDOWN()) + drawFatalErrorScreen(STR_EMERGENCY_MODE); +#endif + #if defined(PWR_BUTTON_PRESS) while (task_running()) { uint32_t pwr_check = pwrCheck(); diff --git a/tools/convert-gfx-list.csv b/tools/convert-gfx-list.csv index 14b4fdee8..11176773c 100644 --- a/tools/convert-gfx-list.csv +++ b/tools/convert-gfx-list.csv @@ -71,8 +71,6 @@ mask_inline_multiply,17,17,2026-01-13 11:36:16 mask_inline_replace,17,17,2026-01-13 11:36:16 mask_menu_edgetx,122,25,2026-01-13 11:36:16 mask_menu_favs,30,30,2026-01-13 11:36:16 -mask_ui_bg_tile_top_left,4,20,2026-01-13 11:36:16 -mask_ui_bg_tile_top_right,4,20,2026-01-13 11:36:16 mask_ui_bg_topbar_left,45,45,2026-01-13 11:36:16 mask_ui_bg_topbar_right,45,45,2026-01-13 11:36:16 mask_ui_btn_close,30,30,2026-01-13 11:36:16