From c1c600cf3550305af1cb89c0456d8be71c8f440b Mon Sep 17 00:00:00 2001 From: philmoz Date: Mon, 30 Sep 2024 09:41:48 +1000 Subject: [PATCH] feat(color): enhancements and fixes for Lua LVGL API (#5558) New: line and triangle primitives message popup dialog expose buttons for for font, alignment, color, timer, switch and source pickers generic popup dialog that can be filled with other things lvgl.isAppMode() function to let widgets know when app mode is active additional containers for creating layouts (box and setting) flex layouts Fixes: colors are updated when theme changes fix memory leaks when stand alone scripts close run stand alone scripts in a separate Lua state and use lua_pcallk() to prevent cross boundary errors (color only) --- .../gui/colorlcd/controls/color_picker.cpp | 6 +- .../gui/colorlcd/controls/sourcechoice.cpp | 2 +- .../gui/colorlcd/controls/switchchoice.cpp | 2 +- radio/src/gui/colorlcd/libui/choice.cpp | 2 +- radio/src/gui/colorlcd/libui/dialog.cpp | 35 +- radio/src/gui/colorlcd/libui/dialog.h | 16 +- radio/src/gui/colorlcd/libui/etx_lv_theme.cpp | 27 +- radio/src/gui/colorlcd/libui/etx_lv_theme.h | 6 + radio/src/gui/colorlcd/libui/filechoice.cpp | 4 +- radio/src/gui/colorlcd/libui/menu.cpp | 4 +- radio/src/gui/colorlcd/libui/menu.h | 2 +- radio/src/gui/colorlcd/libui/modal_window.cpp | 5 +- radio/src/gui/colorlcd/libui/modal_window.h | 2 +- radio/src/gui/colorlcd/libui/popups.cpp | 2 +- .../gui/colorlcd/mainview/screen_setup.cpp | 2 +- .../src/gui/colorlcd/mainview/view_about.cpp | 2 +- .../gui/colorlcd/mainview/view_main_menu.cpp | 2 +- radio/src/gui/colorlcd/mainview/widget.cpp | 6 +- .../gui/colorlcd/mainview/widget_settings.cpp | 6 +- .../gui/colorlcd/mainview/widgets_setup.cpp | 4 +- .../src/gui/colorlcd/mainview/widgets_setup.h | 6 +- radio/src/gui/colorlcd/model/model_curves.cpp | 8 +- radio/src/gui/colorlcd/model/model_gvars.cpp | 2 +- radio/src/gui/colorlcd/model/model_inputs.cpp | 6 +- .../colorlcd/model/model_logical_switches.cpp | 6 +- .../colorlcd/model/model_mixer_scripts.cpp | 2 +- radio/src/gui/colorlcd/model/model_mixes.cpp | 6 +- .../src/gui/colorlcd/model/model_outputs.cpp | 4 +- radio/src/gui/colorlcd/model/model_select.cpp | 26 +- radio/src/gui/colorlcd/model/model_setup.cpp | 2 +- .../gui/colorlcd/model/model_telemetry.cpp | 4 +- .../gui/colorlcd/model/model_usbjoystick.cpp | 2 +- .../gui/colorlcd/model/special_functions.cpp | 6 +- .../gui/colorlcd/model/trainer_bluetooth.cpp | 4 +- radio/src/gui/colorlcd/model/trims_setup.cpp | 2 +- .../gui/colorlcd/module/access_settings.cpp | 195 +- .../src/gui/colorlcd/module/access_settings.h | 4 +- .../src/gui/colorlcd/module/bind_menu_d16.cpp | 4 +- radio/src/gui/colorlcd/module/bind_menu_d16.h | 2 +- .../src/gui/colorlcd/module/module_setup.cpp | 14 +- .../gui/colorlcd/module/multi_rfprotos.cpp | 4 +- .../src/gui/colorlcd/module/multi_rfprotos.h | 2 +- radio/src/gui/colorlcd/radio/hw_bluetooth.cpp | 4 +- radio/src/gui/colorlcd/radio/hw_inputs.cpp | 2 +- .../gui/colorlcd/radio/radio_sdmanager.cpp | 27 +- radio/src/gui/colorlcd/radio/radio_setup.cpp | 2 +- radio/src/gui/colorlcd/radio/radio_theme.cpp | 16 +- radio/src/gui/colorlcd/radio/radio_tools.cpp | 2 +- .../src/gui/colorlcd/radio/radio_version.cpp | 6 +- radio/src/gui/colorlcd/standalone_lua.cpp | 218 ++- radio/src/gui/colorlcd/standalone_lua.h | 15 +- radio/src/gui/gui_common.cpp | 2 +- radio/src/keys.h | 1 - radio/src/lua/api_colorlcd_lvgl.cpp | 252 ++- radio/src/lua/interface.cpp | 44 +- radio/src/lua/lua_api.h | 3 + radio/src/lua/lua_lvgl_widget.cpp | 1620 +++++++++++------ radio/src/lua/lua_lvgl_widget.h | 502 +++-- radio/src/lua/lua_widget.cpp | 66 +- radio/src/lua/lua_widget.h | 59 +- radio/src/lua/lua_widget_factory.h | 1 + radio/src/lua/widgets.cpp | 13 +- radio/src/main.cpp | 2 +- 63 files changed, 2124 insertions(+), 1181 deletions(-) diff --git a/radio/src/gui/colorlcd/controls/color_picker.cpp b/radio/src/gui/colorlcd/controls/color_picker.cpp index d57d796f4..4a679f561 100644 --- a/radio/src/gui/colorlcd/controls/color_picker.cpp +++ b/radio/src/gui/colorlcd/controls/color_picker.cpp @@ -61,9 +61,9 @@ class ColorEditorPopup : public BaseDialog } public: - ColorEditorPopup(Window* parent, uint32_t color, + ColorEditorPopup(uint32_t color, std::function _setValue) : - BaseDialog(parent, STR_COLOR_PICKER, false, COLOR_EDIT_WIDTH, + BaseDialog(STR_COLOR_PICKER, false, COLOR_EDIT_WIDTH, LV_SIZE_CONTENT) { FlexGridLayout grid(col_dsc, row_dsc); @@ -170,7 +170,7 @@ ColorPicker::ColorPicker(Window* parent, const rect_t& rect, void ColorPicker::onClicked() { - new ColorEditorPopup(this, getColor(), [=](uint32_t c) { setColor(c); }); + new ColorEditorPopup(getColor(), [=](uint32_t c) { setColor(c); }); } void ColorPicker::setColor(uint32_t c) diff --git a/radio/src/gui/colorlcd/controls/sourcechoice.cpp b/radio/src/gui/colorlcd/controls/sourcechoice.cpp index 82d34fcc9..0ac19cc96 100644 --- a/radio/src/gui/colorlcd/controls/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/controls/sourcechoice.cpp @@ -186,7 +186,7 @@ void SourceChoice::openMenu() inverted = getIntValue() < 0; inMenu = true; - auto menu = new Menu(this); + auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); auto tb = new SourceChoiceMenuToolbar(this, menu); diff --git a/radio/src/gui/colorlcd/controls/switchchoice.cpp b/radio/src/gui/colorlcd/controls/switchchoice.cpp index 8a1b0b6d3..c7c305134 100644 --- a/radio/src/gui/colorlcd/controls/switchchoice.cpp +++ b/radio/src/gui/colorlcd/controls/switchchoice.cpp @@ -131,7 +131,7 @@ void SwitchChoice::openMenu() { setEditMode(true); // this needs to be done first before menu is created. - auto menu = new Menu(this); + auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); inverted = _getValue() < 0; diff --git a/radio/src/gui/colorlcd/libui/choice.cpp b/radio/src/gui/colorlcd/libui/choice.cpp index 166a94f2a..431c3e6ff 100644 --- a/radio/src/gui/colorlcd/libui/choice.cpp +++ b/radio/src/gui/colorlcd/libui/choice.cpp @@ -215,7 +215,7 @@ void Choice::openMenu() { setEditMode(true); // this needs to be done first before menu is created. - auto menu = new Menu(this); + auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); fillMenu(menu); diff --git a/radio/src/gui/colorlcd/libui/dialog.cpp b/radio/src/gui/colorlcd/libui/dialog.cpp index b8123bd62..c542335e8 100644 --- a/radio/src/gui/colorlcd/libui/dialog.cpp +++ b/radio/src/gui/colorlcd/libui/dialog.cpp @@ -28,21 +28,22 @@ class BaseDialogForm : public Window { public: - BaseDialogForm(Window* parent, lv_coord_t width) : Window(parent, rect_t{}) + BaseDialogForm(Window* parent, lv_coord_t width, bool flexLayout) : Window(parent, rect_t{}) { etx_scrollbar(lvobj); - padAll(PAD_MEDIUM); - setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_MEDIUM, width, LV_SIZE_CONTENT); + padAll(PAD_TINY); + if (flexLayout) + setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO, width, LV_SIZE_CONTENT); } protected: void onClicked() override { Keyboard::hide(false); } }; -BaseDialog::BaseDialog(Window* parent, const char* title, +BaseDialog::BaseDialog(const char* title, bool closeIfClickedOutside, lv_coord_t width, - lv_coord_t maxHeight) : - ModalWindow(parent, closeIfClickedOutside) + lv_coord_t maxHeight, bool flexLayout) : + ModalWindow(closeIfClickedOutside) { auto content = new Window(this, rect_t{}); content->setWindowFlag(OPAQUE); @@ -56,7 +57,7 @@ BaseDialog::BaseDialog(Window* parent, const char* title, header->padAll(PAD_SMALL); header->show(title != nullptr); - form = new BaseDialogForm(content, width); + form = new BaseDialogForm(content, width, flexLayout); if (maxHeight != LV_SIZE_CONTENT) lv_obj_set_style_max_height(form->getLvObj(), maxHeight - EdgeTxStyles::UI_ELEMENT_HEIGHT, LV_PART_MAIN); } @@ -68,9 +69,9 @@ void BaseDialog::setTitle(const char* title) //----------------------------------------------------------------------------- -ProgressDialog::ProgressDialog(Window* parent, const char* title, +ProgressDialog::ProgressDialog(const char* title, std::function onClose) : - BaseDialog(parent, title, false), onClose(std::move(onClose)) + BaseDialog(title, false), onClose(std::move(onClose)) { progress = new Progress(form, rect_t{0, 0, LV_PCT(100), 32}); updateProgress(0); @@ -96,10 +97,10 @@ void ProgressDialog::closeDialog() //----------------------------------------------------------------------------- -MessageDialog::MessageDialog(Window* parent, const char* title, +MessageDialog::MessageDialog(const char* title, const char* message, const char* info, LcdFlags messageFlags, LcdFlags infoFlags) : - BaseDialog(parent, title, true) + BaseDialog(title, true) { messageWidget = new StaticText(form, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}, message, COLOR_THEME_PRIMARY1_INDEX, messageFlags); @@ -115,9 +116,9 @@ void MessageDialog::onClicked() { deleteLater(); } //----------------------------------------------------------------------------- DynamicMessageDialog::DynamicMessageDialog( - Window* parent, const char* title, std::function textHandler, + const char* title, std::function textHandler, const char* message, const int lineHeight, LcdColorIndex color, LcdFlags textFlags) : - BaseDialog(parent, title, true) + BaseDialog(title, true) { messageWidget = new StaticText(form, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}, message, COLOR_THEME_PRIMARY1_INDEX, CENTERED); @@ -130,11 +131,11 @@ void DynamicMessageDialog::onClicked() { deleteLater(); } //----------------------------------------------------------------------------- -ConfirmDialog::ConfirmDialog(Window* parent, const char* title, +ConfirmDialog::ConfirmDialog(const char* title, const char* message, std::function confirmHandler, std::function cancelHandler) : - BaseDialog(parent, title, false), + BaseDialog(title, false), confirmHandler(std::move(confirmHandler)), cancelHandler(std::move(cancelHandler)) { @@ -168,9 +169,9 @@ void ConfirmDialog::onCancel() //----------------------------------------------------------------------------- -LabelDialog::LabelDialog(Window *parent, const char *label, int length, const char* title, +LabelDialog::LabelDialog(const char *label, int length, const char* title, std::function _saveHandler) : - ModalWindow(parent, false), saveHandler(std::move(_saveHandler)) + ModalWindow(false), saveHandler(std::move(_saveHandler)) { assert(length <= MAX_LABEL_LENGTH); diff --git a/radio/src/gui/colorlcd/libui/dialog.h b/radio/src/gui/colorlcd/libui/dialog.h index abd90c646..bcb00765e 100644 --- a/radio/src/gui/colorlcd/libui/dialog.h +++ b/radio/src/gui/colorlcd/libui/dialog.h @@ -33,9 +33,10 @@ class Progress; class BaseDialog : public ModalWindow { public: - BaseDialog(Window* parent, const char* title, bool closeIfClickedOutside, + BaseDialog(const char* title, bool closeIfClickedOutside, lv_coord_t width = DIALOG_DEFAULT_WIDTH, - lv_coord_t maxHeight = DIALOG_DEFAULT_HEIGHT); + lv_coord_t maxHeight = DIALOG_DEFAULT_HEIGHT, + bool flexLayout = true); void setTitle(const char* title); @@ -52,8 +53,7 @@ class BaseDialog : public ModalWindow class ProgressDialog : public BaseDialog { public: - ProgressDialog(Window* parent, const char* title, - std::function onClose); + ProgressDialog(const char* title, std::function onClose); void updateProgress(int percentage); void setTitle(std::string title); @@ -74,7 +74,7 @@ class ProgressDialog : public BaseDialog class MessageDialog : public BaseDialog { public: - MessageDialog(Window* parent, const char* title, const char* message, + MessageDialog(const char* title, const char* message, const char* info = nullptr, LcdFlags messageFlags = CENTERED, LcdFlags infoFlags = CENTERED); @@ -94,7 +94,7 @@ class MessageDialog : public BaseDialog class DynamicMessageDialog : public BaseDialog { public: - DynamicMessageDialog(Window* parent, const char* title, + DynamicMessageDialog(const char* title, std::function textHandler, const char* message = "", const int lineHeight = EdgeTxStyles::PAGE_LINE_HEIGHT, @@ -117,7 +117,7 @@ class DynamicMessageDialog : public BaseDialog class ConfirmDialog : public BaseDialog { public: - ConfirmDialog(Window* parent, const char* title, const char* message, + ConfirmDialog(const char* title, const char* message, std::function confirmHandler, std::function cancelHandler = nullptr); @@ -133,7 +133,7 @@ class ConfirmDialog : public BaseDialog class LabelDialog : public ModalWindow { public: - LabelDialog(Window *parent, const char *label, int length, const char* title, + LabelDialog(const char *label, int length, const char* title, std::function _saveHandler = nullptr); static constexpr int MAX_LABEL_LENGTH = 255; diff --git a/radio/src/gui/colorlcd/libui/etx_lv_theme.cpp b/radio/src/gui/colorlcd/libui/etx_lv_theme.cpp index 98b1f788b..9be774bae 100644 --- a/radio/src/gui/colorlcd/libui/etx_lv_theme.cpp +++ b/radio/src/gui/colorlcd/libui/etx_lv_theme.cpp @@ -236,6 +236,7 @@ EdgeTxStyles::EdgeTxStyles() lv_style_set_img_recolor_opa(&img_color[i], LV_OPA_COVER); lv_style_init(&border_color[i]); lv_style_init(&arc_color[i]); + lv_style_init(&line_color[i]); } lv_style_init(&outline_color_light); lv_style_init(&outline_color_normal); @@ -310,6 +311,7 @@ void EdgeTxStyles::applyColors() lv_style_set_img_recolor(&img_color[i], c); lv_style_set_border_color(&border_color[i], c); lv_style_set_arc_color(&arc_color[i], c); + lv_style_set_line_color(&line_color[i], c); } lv_style_set_line_color(&graph_border, makeLvColor(COLOR_THEME_SECONDARY2)); @@ -466,12 +468,33 @@ void etx_arc_color(lv_obj_t* obj, LcdColorIndex colorIdx, etx_obj_add_style(obj, styles->arc_color[colorIdx], selector); } +void etx_remove_line_color(lv_obj_t* obj, lv_style_selector_t selector) +{ + // Remove styles + for (int i = 0; i < TOTAL_COLOR_COUNT; i += 1) + lv_obj_remove_style(obj, &styles->line_color[i], selector); +} + +void etx_line_color(lv_obj_t* obj, LcdColorIndex colorIdx, + lv_style_selector_t selector) +{ + // Remove old style first + etx_remove_line_color(obj, selector); + etx_obj_add_style(obj, styles->line_color[colorIdx], selector); +} + +void etx_remove_img_color(lv_obj_t* obj, lv_style_selector_t selector) +{ + // Remove styles + for (int i = 0; i < TOTAL_COLOR_COUNT; i += 1) + lv_obj_remove_style(obj, &styles->img_color[i], selector); +} + void etx_img_color(lv_obj_t* obj, LcdColorIndex colorIdx, lv_style_selector_t selector) { // Remove old style first - for (int i = 0; i < TOTAL_COLOR_COUNT; i += 1) - lv_obj_remove_style(obj, &styles->img_color[i], selector); + etx_remove_img_color(obj, selector); etx_obj_add_style(obj, styles->img_color[colorIdx], selector); } diff --git a/radio/src/gui/colorlcd/libui/etx_lv_theme.h b/radio/src/gui/colorlcd/libui/etx_lv_theme.h index fa3fb3e78..39dc37758 100644 --- a/radio/src/gui/colorlcd/libui/etx_lv_theme.h +++ b/radio/src/gui/colorlcd/libui/etx_lv_theme.h @@ -111,6 +111,11 @@ void etx_remove_arc_color(lv_obj_t* obj, lv_style_selector_t selector = LV_PART_ void etx_arc_color(lv_obj_t* obj, LcdColorIndex colorIdx, lv_style_selector_t selector = LV_PART_MAIN); +void etx_remove_line_color(lv_obj_t* obj, lv_style_selector_t selector = LV_PART_MAIN); +void etx_line_color(lv_obj_t* obj, LcdColorIndex colorIdx, + lv_style_selector_t selector = LV_PART_MAIN); + +void etx_remove_img_color(lv_obj_t* obj, lv_style_selector_t selector = LV_PART_MAIN); void etx_img_color(lv_obj_t* obj, LcdColorIndex colorIdx, lv_style_selector_t selector = LV_PART_MAIN); @@ -145,6 +150,7 @@ class EdgeTxStyles lv_style_t img_color[TOTAL_COLOR_COUNT]; lv_style_t border_color[TOTAL_COLOR_COUNT]; lv_style_t arc_color[TOTAL_COLOR_COUNT]; + lv_style_t line_color[TOTAL_COLOR_COUNT]; lv_style_t outline_color_light; lv_style_t outline_color_normal; lv_style_t outline_color_focus; diff --git a/radio/src/gui/colorlcd/libui/filechoice.cpp b/radio/src/gui/colorlcd/libui/filechoice.cpp index 0f5baf8da..94fa282ba 100644 --- a/radio/src/gui/colorlcd/libui/filechoice.cpp +++ b/radio/src/gui/colorlcd/libui/filechoice.cpp @@ -182,7 +182,7 @@ void FileChoice::openMenu() if (fileCount > 0) { setEditMode(true); // this needs to be done first before menu is created. - auto menu = new Menu(this); + auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); auto tb = new FileChoiceMenuToolbar(this, menu); @@ -192,6 +192,6 @@ void FileChoice::openMenu() menu->setCloseHandler([=]() { setEditMode(false); }); } else { - new MessageDialog(this, STR_SDCARD, STR_NO_FILES_ON_SD); + new MessageDialog(STR_SDCARD, STR_NO_FILES_ON_SD); } } diff --git a/radio/src/gui/colorlcd/libui/menu.cpp b/radio/src/gui/colorlcd/libui/menu.cpp index f5066d7d8..de5cc14dd 100644 --- a/radio/src/gui/colorlcd/libui/menu.cpp +++ b/radio/src/gui/colorlcd/libui/menu.cpp @@ -364,8 +364,8 @@ class MenuWindowContent : public Window //----------------------------------------------------------------------------- -Menu::Menu(Window* parent, bool multiple) : - ModalWindow(parent, true), +Menu::Menu(bool multiple) : + ModalWindow(true), multiple(multiple), content(new MenuWindowContent(this)) { diff --git a/radio/src/gui/colorlcd/libui/menu.h b/radio/src/gui/colorlcd/libui/menu.h index c7867f75c..6ebdefe14 100644 --- a/radio/src/gui/colorlcd/libui/menu.h +++ b/radio/src/gui/colorlcd/libui/menu.h @@ -29,7 +29,7 @@ class Menu : public ModalWindow friend class MenuBody; public: - explicit Menu(Window *parent, bool multiple = false); + explicit Menu(bool multiple = false); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "Menu"; } diff --git a/radio/src/gui/colorlcd/libui/modal_window.cpp b/radio/src/gui/colorlcd/libui/modal_window.cpp index aa135f82e..973964753 100644 --- a/radio/src/gui/colorlcd/libui/modal_window.cpp +++ b/radio/src/gui/colorlcd/libui/modal_window.cpp @@ -20,6 +20,7 @@ #include "layer.h" #include "etx_lv_theme.h" +#include "mainwindow.h" // Modal overlay style (for dimming background) void modal_window_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) @@ -45,8 +46,8 @@ static lv_obj_t* modal_create(lv_obj_t* parent) return etx_create(&modal_window_class, parent); } -ModalWindow::ModalWindow(Window* parent, bool closeWhenClickOutside) : - Window(parent->getFullScreenWindow(), {0, 0, LCD_W, LCD_H}, modal_create), +ModalWindow::ModalWindow(bool closeWhenClickOutside) : + Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, modal_create), closeWhenClickOutside(closeWhenClickOutside) { Layer::push(this); diff --git a/radio/src/gui/colorlcd/libui/modal_window.h b/radio/src/gui/colorlcd/libui/modal_window.h index a4e9bd02f..a8eafd485 100644 --- a/radio/src/gui/colorlcd/libui/modal_window.h +++ b/radio/src/gui/colorlcd/libui/modal_window.h @@ -23,7 +23,7 @@ class ModalWindow : public Window { public: - explicit ModalWindow(Window* parent, bool closeWhenClickOutside = false); + explicit ModalWindow(bool closeWhenClickOutside = false); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "ModalWindow"; } diff --git a/radio/src/gui/colorlcd/libui/popups.cpp b/radio/src/gui/colorlcd/libui/popups.cpp index 1af160dd9..131ba71b6 100644 --- a/radio/src/gui/colorlcd/libui/popups.cpp +++ b/radio/src/gui/colorlcd/libui/popups.cpp @@ -38,7 +38,7 @@ static void _run_popup_dialog(const char* title, const char* msg, // RELEASED/CLICKED to be called in a loop lv_indev_reset(nullptr, nullptr); - auto md = new MessageDialog(MainWindow::instance(), title, msg, info); + auto md = new MessageDialog(title, msg, info); md->setCloseHandler([&]() { running = false; }); while (running) { // Allow power off while showing popup diff --git a/radio/src/gui/colorlcd/mainview/screen_setup.cpp b/radio/src/gui/colorlcd/mainview/screen_setup.cpp index ebf36dcda..244696495 100644 --- a/radio/src/gui/colorlcd/mainview/screen_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/screen_setup.cpp @@ -56,7 +56,7 @@ class LayoutChoice : public Button void onPress() override { - auto menu = new Menu(parent); + auto menu = new Menu(); for (auto layout : LayoutFactory::getRegisteredLayouts()) { menu->addLine(layout->getBitmap(), layout->getName(), [=]() { setValue(layout); }); diff --git a/radio/src/gui/colorlcd/mainview/view_about.cpp b/radio/src/gui/colorlcd/mainview/view_about.cpp index 0b51f61a0..6d75c432f 100644 --- a/radio/src/gui/colorlcd/mainview/view_about.cpp +++ b/radio/src/gui/colorlcd/mainview/view_about.cpp @@ -39,7 +39,7 @@ const std::string copyright_str = "Copyright (C) " BUILD_YEAR " EdgeTX"; const std::string edgetx_url = "https://edgetx.org"; AboutUs::AboutUs() : - BaseDialog(MainWindow::instance(), STR_ABOUT_US, true, 220, LV_SIZE_CONTENT) + BaseDialog(STR_ABOUT_US, true, 220, LV_SIZE_CONTENT) { new StaticText(form, {0, 0, LV_PCT(100), LV_SIZE_CONTENT}, about_str + "\n" + copyright_str, diff --git a/radio/src/gui/colorlcd/mainview/view_main_menu.cpp b/radio/src/gui/colorlcd/mainview/view_main_menu.cpp index 55b79fa61..e79062a34 100644 --- a/radio/src/gui/colorlcd/mainview/view_main_menu.cpp +++ b/radio/src/gui/colorlcd/mainview/view_main_menu.cpp @@ -128,7 +128,7 @@ ViewMainMenu::ViewMainMenu(Window* parent, std::function closeHandler) : carousel->addButton( ICON_MODEL_TELEMETRY, STR_MAIN_MENU_RESET_TELEMETRY, [=]() -> uint8_t { deleteLater(); - Menu* resetMenu = new Menu(parent); + Menu* resetMenu = new Menu(); resetMenu->addLine(STR_RESET_FLIGHT, []() { flightReset(); }); resetMenu->addLine(STR_RESET_TIMER1, []() { timerReset(0); }); resetMenu->addLine(STR_RESET_TIMER2, []() { timerReset(1); }); diff --git a/radio/src/gui/colorlcd/mainview/widget.cpp b/radio/src/gui/colorlcd/mainview/widget.cpp index 41c6a60c7..b7b7c3ff4 100644 --- a/radio/src/gui/colorlcd/mainview/widget.cpp +++ b/radio/src/gui/colorlcd/mainview/widget.cpp @@ -58,11 +58,7 @@ void Widget::openMenu() } if (getOptions() || fsAllowed) { - // Widgets are placed on a full screen window which is underneath the main - // view menu bar Find the parent of this so that when the popup loads it - // covers the main view menu - Window* w = parent->getFullScreenWindow()->getParent(); - Menu* menu = new Menu(w ? w : this); + Menu* menu = new Menu(); menu->setTitle(getFactory()->getDisplayName()); if (fsAllowed) { menu->addLine(STR_WIDGET_FULLSCREEN, [&]() { setFullscreen(true); }); diff --git a/radio/src/gui/colorlcd/mainview/widget_settings.cpp b/radio/src/gui/colorlcd/mainview/widget_settings.cpp index 0e978f501..198969d91 100644 --- a/radio/src/gui/colorlcd/mainview/widget_settings.cpp +++ b/radio/src/gui/colorlcd/mainview/widget_settings.cpp @@ -43,11 +43,9 @@ static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; WidgetSettings::WidgetSettings(Widget* w) : - BaseDialog(ViewMain::instance(), w->getFactory()->getDisplayName(), true), widget(w) + BaseDialog(w->getFactory()->getDisplayName(), true), widget(w) { - FlexGridLayout grid(line_col_dsc, line_row_dsc); - form->padAll(PAD_SMALL); - form->padRow(PAD_ZERO); + FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); uint8_t optIdx = 0; auto opt = widget->getOptions(); diff --git a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp index 4d3f30d8b..9acef2611 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_setup.cpp +++ b/radio/src/gui/colorlcd/mainview/widgets_setup.cpp @@ -37,7 +37,7 @@ SetupWidgetsPageSlot::SetupWidgetsPageSlot(Window* parent, const rect_t& rect, { setPressHandler([=]() -> uint8_t { if (container->getWidget(slotIndex)) { - Menu* menu = new Menu(parent); + Menu* menu = new Menu(); menu->addLine(STR_SELECT_WIDGET, [=]() { addNewWidget(container, slotIndex); }); auto widget = container->getWidget(slotIndex); @@ -95,7 +95,7 @@ void SetupWidgetsPageSlot::addNewWidget(WidgetsContainer* container, auto w = container->getWidget((slotIndex)); if (w) cur = w->getFactory()->getDisplayName(); - Menu* menu = new Menu(parent); + Menu* menu = new Menu(); menu->setTitle(STR_SELECT_WIDGET); int selected = -1; int index = 0; diff --git a/radio/src/gui/colorlcd/mainview/widgets_setup.h b/radio/src/gui/colorlcd/mainview/widgets_setup.h index 5d185f882..f9e37db73 100644 --- a/radio/src/gui/colorlcd/mainview/widgets_setup.h +++ b/radio/src/gui/colorlcd/mainview/widgets_setup.h @@ -40,12 +40,13 @@ class SetupWidgetsPage : public Window void onClicked() override; void onCancel() override; - void deleteLater(bool detach = true, bool trash = true) override; protected: uint8_t customScreenIdx; unsigned savedView = 0; + void onEvent(event_t event) override; + void deleteLater(bool detach = true, bool trash = true) override; }; class SetupWidgetsPageSlot : public ButtonBase @@ -55,6 +56,9 @@ class SetupWidgetsPageSlot : public ButtonBase WidgetsContainer* container, uint8_t slotIndex); protected: + WidgetsContainer* container = nullptr; + uint8_t slotIndex = 0; + bool openSettings = false; lv_style_t borderStyle; lv_point_t borderPts[5]; lv_obj_t* border; diff --git a/radio/src/gui/colorlcd/model/model_curves.cpp b/radio/src/gui/colorlcd/model/model_curves.cpp index 3e692fa6b..3feede692 100644 --- a/radio/src/gui/colorlcd/model/model_curves.cpp +++ b/radio/src/gui/colorlcd/model/model_curves.cpp @@ -142,7 +142,7 @@ void ModelCurvesPage::editCurve(Window *window, uint8_t curve) void ModelCurvesPage::presetMenu(Window *window, uint8_t index) { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->setTitle(STR_CURVE_PRESET); for (int angle = -45; angle <= 45; angle += 15) { char label[16]; @@ -169,7 +169,7 @@ void ModelCurvesPage::presetMenu(Window *window, uint8_t index) void ModelCurvesPage::newCV(Window *window, bool presetCV) { - Menu *menu = new Menu(Layer::back()); + Menu *menu = new Menu(); menu->setTitle(STR_CURVE); char s[6] = "CVxx"; @@ -195,7 +195,7 @@ void ModelCurvesPage::newCV(Window *window, bool presetCV) void ModelCurvesPage::plusPopup(Window *window) { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->setTitle(STR_NEW); menu->addLine(STR_EDIT, [=]() { newCV(window, false); }); menu->addLine(STR_CURVE_PRESET, [=]() { newCV(window, true); }); @@ -235,7 +235,7 @@ void ModelCurvesPage::build(Window *window) auto button = new CurveButton(line, rect_t{0, 0, CurveButton::CURVE_BTN_W, CurveButton::CURVE_BTH_H}, index); button->setPressHandler([=]() -> uint8_t { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->setTitle(STR_CURVE); menu->addLine(STR_EDIT, [=]() { editCurve(window, index); }); menu->addLine(STR_CURVE_PRESET, [=]() { presetMenu(window, index); }); diff --git a/radio/src/gui/colorlcd/model/model_gvars.cpp b/radio/src/gui/colorlcd/model/model_gvars.cpp index 817514431..1bc986647 100644 --- a/radio/src/gui/colorlcd/model/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model/model_gvars.cpp @@ -561,7 +561,7 @@ void ModelGVarsPage::build(Window* window) auto button = new GVarButton(window, index); lv_obj_set_pos(button->getLvObj(), 0, yo + index * (GVarButton::BTN_H + PAD_TINY)); button->setPressHandler([=]() { - Menu* menu = new Menu(window); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { Window* editWindow = new GVarEditWindow(index); editWindow->setCloseHandler([=]() { rebuild(window); }); diff --git a/radio/src/gui/colorlcd/model/model_inputs.cpp b/radio/src/gui/colorlcd/model/model_inputs.cpp index 677ffd138..702afc229 100644 --- a/radio/src/gui/colorlcd/model/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model/model_inputs.cpp @@ -202,7 +202,7 @@ ModelInputsPage::ModelInputsPage() : InputMixPageBase(STR_MENUINPUTS, ICON_MODEL bool ModelInputsPage::reachExposLimit() { if (getExposCount() >= MAX_EXPOS) { - new MessageDialog(form, STR_WARNING, STR_NOFREEEXPO); + new MessageDialog(STR_WARNING, STR_NOFREEEXPO); return true; } return false; @@ -233,7 +233,7 @@ InputMixButtonBase* ModelInputsPage::createLineButton(InputMixGroupBase* group, uint8_t input = group->getMixSrc() - MIXSRC_FIRST_INPUT; button->setPressHandler([=]() -> uint8_t { - Menu* menu = new Menu(form); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { uint8_t idx = button->getIndex(); editInput(input, idx); @@ -287,7 +287,7 @@ void ModelInputsPage::addLineButton(uint8_t index) void ModelInputsPage::newInput() { - Menu* menu = new Menu(Layer::back()); + Menu* menu = new Menu(); menu->setTitle(STR_MENU_INPUTS); uint8_t chn = 0; diff --git a/radio/src/gui/colorlcd/model/model_logical_switches.cpp b/radio/src/gui/colorlcd/model/model_logical_switches.cpp index bab7bf2e1..66a0f80cf 100644 --- a/radio/src/gui/colorlcd/model/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model/model_logical_switches.cpp @@ -560,7 +560,7 @@ void ModelLogicalSwitchesPage::rebuild(Window* window) void ModelLogicalSwitchesPage::newLS(Window* window, bool pasteLS) { - Menu* menu = new Menu(Layer::back()); + Menu* menu = new Menu(); menu->setTitle(STR_MENU_LOGICAL_SWITCHES); // search for unused switches @@ -593,7 +593,7 @@ void ModelLogicalSwitchesPage::newLS(Window* window, bool pasteLS) void ModelLogicalSwitchesPage::plusPopup(Window* window) { if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) { - Menu* menu = new Menu(window); + Menu* menu = new Menu(); menu->addLine(STR_NEW, [=]() { newLS(window, false); }); menu->addLine(STR_PASTE, [=]() { newLS(window, true); }); } else { @@ -619,7 +619,7 @@ void ModelLogicalSwitchesPage::build(Window* window) auto button = new LogicalSwitchButton(window, i); button->setPressHandler([=]() { - Menu* menu = new Menu(window); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { Window* lsWindow = new LogicalSwitchEditPage(i); lsWindow->setCloseHandler([=]() { diff --git a/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp index f69292c0b..ee7bd28fd 100644 --- a/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model/model_mixer_scripts.cpp @@ -301,7 +301,7 @@ void ModelMixerScriptsPage::build(Window* window, int8_t focusIdx) auto button = new ScriptLineButton(window, *sd, runtimeData, idx); button->setPressHandler([=]() -> uint8_t { - Menu* const menu = new Menu(window); + Menu* const menu = new Menu(); menu->addLine(STR_EDIT, [=]() { editLine(window, idx); }); if (runtimeData != nullptr) { diff --git a/radio/src/gui/colorlcd/model/model_mixes.cpp b/radio/src/gui/colorlcd/model/model_mixes.cpp index e82cf8edb..e1ff73594 100644 --- a/radio/src/gui/colorlcd/model/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model/model_mixes.cpp @@ -231,7 +231,7 @@ ModelMixesPage::ModelMixesPage() : InputMixPageBase(STR_MIXES, ICON_MODEL_MIXER) bool ModelMixesPage::reachMixesLimit() { if (getMixCount() >= MAX_MIXERS) { - new MessageDialog(form, STR_WARNING, STR_NOFREEMIXER); + new MessageDialog(STR_WARNING, STR_NOFREEMIXER); return true; } return false; @@ -264,7 +264,7 @@ InputMixButtonBase* ModelMixesPage::createLineButton(InputMixGroupBase *group, u uint8_t ch = group->getMixSrc() - MIXSRC_FIRST_CH; button->setPressHandler([=]() -> uint8_t { - Menu *menu = new Menu(form); + Menu *menu = new Menu(); menu->addLine(STR_EDIT, [=]() { uint8_t idx = button->getIndex(); editMix(ch, idx); @@ -318,7 +318,7 @@ void ModelMixesPage::addLineButton(uint8_t index) void ModelMixesPage::newMix() { - Menu* menu = new Menu(Layer::back()); + Menu* menu = new Menu(); menu->setTitle(STR_MENU_CHANNELS); uint8_t index = 0; diff --git a/radio/src/gui/colorlcd/model/model_outputs.cpp b/radio/src/gui/colorlcd/model/model_outputs.cpp index 2bd49b90d..3dd678d3a 100644 --- a/radio/src/gui/colorlcd/model/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model/model_outputs.cpp @@ -240,7 +240,7 @@ void ModelOutputsPage::build(Window* window) new TextButton(window, {ADD_TRIMS_X, ADD_TRIMS_Y, ADD_TRIMS_W, ADD_TRIMS_H}, STR_ADD_ALL_TRIMS_TO_SUBTRIMS, [=]() { new ConfirmDialog( - window, STR_TRIMS2OFFSETS, STR_ADD_ALL_TRIMS_TO_SUBTRIMS, + STR_TRIMS2OFFSETS, STR_ADD_ALL_TRIMS_TO_SUBTRIMS, [=] { moveTrimsToOffsets(); }); @@ -258,7 +258,7 @@ void ModelOutputsPage::build(Window* window) LimitData* output = limitAddress(ch); btn->setPressHandler([=]() -> uint8_t { - Menu* menu = new Menu(window); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { editOutput(ch, btn); }); menu->addLine(STR_RESET, [=]() { output->min = 0; diff --git a/radio/src/gui/colorlcd/model/model_select.cpp b/radio/src/gui/colorlcd/model/model_select.cpp index 06895fb70..489e9c2cd 100644 --- a/radio/src/gui/colorlcd/model/model_select.cpp +++ b/radio/src/gui/colorlcd/model/model_select.cpp @@ -334,7 +334,7 @@ class ModelsPageBody : public Window void openMenu() { - Menu *menu = new Menu(this); + Menu *menu = new Menu(); menu->setTitle(focusedModel->modelName); if (g_eeGeneral.modelQuickSelect || focusedModel != modelslist.getCurrentModel()) { @@ -393,7 +393,7 @@ class ModelsPageBody : public Window void duplicateModel(ModelCell *model) { new ConfirmDialog( - parent, STR_DUPLICATE_MODEL, + STR_DUPLICATE_MODEL, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { storageFlushCurrentModel(); storageCheck(true); @@ -422,7 +422,7 @@ class ModelsPageBody : public Window void deleteModel(ModelCell *model) { new ConfirmDialog( - parent, STR_DELETE_MODEL, + STR_DELETE_MODEL, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { modelslist.removeModel(model); if (refreshLabels != nullptr) refreshLabels(); @@ -437,7 +437,7 @@ class ModelsPageBody : public Window // dont display menu if there will be no labels if (labels.size()) { - auto menu = new Menu(getParent(), true); + auto menu = new Menu(true); menu->setTitle(model->modelName); menu->setCloseHandler([=]() { if (isDirty) { @@ -466,7 +466,7 @@ class ModelsPageBody : public Window void saveAsTemplate(ModelCell *model) { new ConfirmDialog( - parent, STR_SAVE_TEMPLATE, + STR_SAVE_TEMPLATE, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { storageDirty(EE_MODEL); storageCheck(true); @@ -487,7 +487,7 @@ class ModelsPageBody : public Window snprintf(templatePath, FF_MAX_LFN, "%s%c%s", persFolder, '/', modelName); if (isFileAvailable(templatePath)) { - new ConfirmDialog(parent, STR_FILE_EXISTS, STR_ASK_OVERWRITE, [=] { + new ConfirmDialog(STR_FILE_EXISTS, STR_ASK_OVERWRITE, [=] { sdCopyFile(model->modelFilename, MODELS_PATH, modelName, persFolder); }); @@ -668,7 +668,7 @@ void ModelLabelsWindow::newModel() void ModelLabelsWindow::newLabel() { tmpLabel[0] = '\0'; - new LabelDialog(this, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string label) { + new LabelDialog(tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string label) { int newlabindex = modelslabels.addLabel(label); if (newlabindex >= 0) { std::set newset; @@ -690,7 +690,7 @@ void ModelLabelsWindow::buildHead(Window *hdr) // new model button new TextButton(hdr, {LCD_W - NEW_BTN_W - PAD_LARGE, PAD_MEDIUM, NEW_BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_NEW, [=]() { - auto menu = new Menu(this); + auto menu = new Menu(); menu->setTitle(STR_CREATE_NEW); menu->addLine(STR_NEW_MODEL, [=]() { newModel(); }); menu->addLine(STR_NEW_LABEL, [=]() { newLabel(); }); @@ -811,16 +811,16 @@ void ModelLabelsWindow::buildBody(Window *window) std::string selectedLabel = labels.at(selected); if (selectedLabel != STR_UNLABELEDMODEL) { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->setTitle(selectedLabel); menu->addLine(STR_RENAME_LABEL, [=]() { auto oldLabel = labels[selected]; strncpy(tmpLabel, oldLabel.c_str(), LABEL_LENGTH); tmpLabel[LABEL_LENGTH] = '\0'; - new LabelDialog(this, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string newLabel) { + new LabelDialog(tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string newLabel) { if (newLabel.size() > 0) { auto rndialog = - new ProgressDialog(this, STR_RENAME_LABEL, [=]() {}); + new ProgressDialog(STR_RENAME_LABEL, [=]() {}); modelslabels.renameLabel( oldLabel, newLabel, [=](const char *name, int percentage) { rndialog->setTitle(std::string(STR_RENAME_LABEL) + " " + @@ -838,9 +838,9 @@ void ModelLabelsWindow::buildBody(Window *window) menu->addLine(STR_DELETE_LABEL, [=]() { auto labelToDelete = labels[selected]; new ConfirmDialog( - parent, STR_DELETE_LABEL, labelToDelete.c_str(), [=]() { + STR_DELETE_LABEL, labelToDelete.c_str(), [=]() { auto deldialog = - new ProgressDialog(this, STR_DELETE_LABEL, [=]() {}); + new ProgressDialog(STR_DELETE_LABEL, [=]() {}); modelslabels.removeLabel( labelToDelete, [=](const char *name, int percentage) { deldialog->setTitle(std::string(STR_DELETE_LABEL) + " " + diff --git a/radio/src/gui/colorlcd/model/model_setup.cpp b/radio/src/gui/colorlcd/model/model_setup.cpp index 38e9b9a70..3930c153d 100644 --- a/radio/src/gui/colorlcd/model/model_setup.cpp +++ b/radio/src/gui/colorlcd/model/model_setup.cpp @@ -300,7 +300,7 @@ static SetupLineDef setupLines[] = { auto curmod = modelslist.getCurrentModel(); TextButton* btn = new TextButton(parent, {x, y, 0, 0}, modelslabels.getBulletLabelString(curmod, STR_UNLABELEDMODEL)); btn->setPressHandler([=]() { - Menu *menu = new Menu(MainWindow::instance(), true); + Menu *menu = new Menu(true); menu->setTitle(STR_LABELS); for (auto &label : modelslabels.getLabels()) { menu->addLineBuffered( diff --git a/radio/src/gui/colorlcd/model/model_telemetry.cpp b/radio/src/gui/colorlcd/model/model_telemetry.cpp index 99b429dfb..2ae074196 100644 --- a/radio/src/gui/colorlcd/model/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model/model_telemetry.cpp @@ -836,7 +836,7 @@ void ModelTelemetryPage::buildSensorList(int8_t focusSensorIndex) if (!first) first = button; button->setPressHandler([=]() -> uint8_t { - Menu* menu = new Menu(window); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { editSensor(window, idx); }); menu->addLine(STR_COPY, [=]() { auto newIndex = availableTelemetryIndex(); @@ -944,7 +944,7 @@ void ModelTelemetryPage::build(Window* window) // Delete all sensors button deleteAll = new TextButton(line, rect_t{}, STR_DELETE_ALL_SENSORS, [=]() -> uint8_t { - new ConfirmDialog(window, STR_DELETE_ALL_SENSORS, STR_CONFIRMDELETE, + new ConfirmDialog(STR_DELETE_ALL_SENSORS, STR_CONFIRMDELETE, [=]() { for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) { delTelemetryIndex(i); diff --git a/radio/src/gui/colorlcd/model/model_usbjoystick.cpp b/radio/src/gui/colorlcd/model/model_usbjoystick.cpp index a60f5c383..79a525e14 100644 --- a/radio/src/gui/colorlcd/model/model_usbjoystick.cpp +++ b/radio/src/gui/colorlcd/model/model_usbjoystick.cpp @@ -562,7 +562,7 @@ ModelUSBJoystickPage::ModelUSBJoystickPage() : Page(ICON_MODEL_USB) if (cch->mode == USBJOYS_CH_NONE) { editChannel(ch, btn); } else { - Menu* menu = new Menu(parent); + Menu* menu = new Menu(); menu->addLine(STR_EDIT, [=]() { editChannel(ch, btn); }); menu->addLine(STR_CLEAR, [=]() { memset(cch, 0, sizeof(USBJoystickChData)); diff --git a/radio/src/gui/colorlcd/model/special_functions.cpp b/radio/src/gui/colorlcd/model/special_functions.cpp index 49005e8d7..9d956dca8 100644 --- a/radio/src/gui/colorlcd/model/special_functions.cpp +++ b/radio/src/gui/colorlcd/model/special_functions.cpp @@ -720,7 +720,7 @@ void FunctionsPage::rebuild(Window *window) void FunctionsPage::newSF(Window *window, bool pasteSF) { - Menu *menu = new Menu(Layer::back()); + Menu *menu = new Menu(); menu->setTitle(title); // search for unused switches @@ -769,7 +769,7 @@ void FunctionsPage::editSpecialFunction(Window *window, uint8_t index, void FunctionsPage::plusPopup(Window *window) { if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->addLine(STR_NEW, [=]() { newSF(window, false); }); menu->addLine(STR_PASTE, [=]() { newSF(window, true); }); } else { @@ -810,7 +810,7 @@ void FunctionsPage::build(Window *window) }); button->setPressHandler([=]() { - Menu *menu = new Menu(window); + Menu *menu = new Menu(); menu->addLine(STR_EDIT, [=]() { editSpecialFunction(window, i, button); }); if (isActive) { diff --git a/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp b/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp index dd66784c0..b5758e09d 100644 --- a/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp +++ b/radio/src/gui/colorlcd/model/trainer_bluetooth.cpp @@ -42,7 +42,7 @@ public: }; BTDiscoverMenu::BTDiscoverMenu() : - Menu(Layer::back()) + Menu() { setTitle(STR_BT_SELECT_DEVICE); // TODO: set minimum height @@ -132,7 +132,7 @@ void BluetoothTrainerWindow::refresh() } } else if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_END) { if(reusableBuffer.moduleSetup.bt.devicesCount == 0) { - new MessageDialog(this, STR_BLUETOOTH, STR_BLUETOOTH_NODEVICES); + new MessageDialog(STR_BLUETOOTH, STR_BLUETOOTH_NODEVICES); bluetooth.state = BLUETOOTH_STATE_OFF; } } else if (bluetooth.distantAddr[0]) { diff --git a/radio/src/gui/colorlcd/model/trims_setup.cpp b/radio/src/gui/colorlcd/model/trims_setup.cpp index 2699a514c..6f9864e6d 100644 --- a/radio/src/gui/colorlcd/model/trims_setup.cpp +++ b/radio/src/gui/colorlcd/model/trims_setup.cpp @@ -46,7 +46,7 @@ static SetupLineDef setupLines[] = { 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(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + new MessageDialog(STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", LEFT); return 0; }); diff --git a/radio/src/gui/colorlcd/module/access_settings.cpp b/radio/src/gui/colorlcd/module/access_settings.cpp index d6c8c27d3..19e176f9e 100644 --- a/radio/src/gui/colorlcd/module/access_settings.cpp +++ b/radio/src/gui/colorlcd/module/access_settings.cpp @@ -34,7 +34,7 @@ namespace pxx2 class BindRxChoiceMenu : public Menu { public: - BindRxChoiceMenu(Window* parent, uint8_t moduleIdx, uint8_t receiverIdx); + BindRxChoiceMenu(uint8_t moduleIdx, uint8_t receiverIdx); protected: uint8_t moduleIdx; @@ -44,11 +44,71 @@ class BindRxChoiceMenu : public Menu class BindWaitDialog : public BaseDialog { public: - BindWaitDialog(Window* parent, uint8_t moduleIdx, uint8_t receiverIdx); + BindWaitDialog(uint8_t moduleIdx, uint8_t receiverIdx) : + BaseDialog(STR_BIND, true), + moduleIdx(moduleIdx), + receiverIdx(receiverIdx) + { + new StaticText(form, rect_t{}, STR_WAITING_FOR_RX); - void checkEvents() override; + setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + } - void deleteLater(bool detach = true, bool trash = true) override; + void checkEvents() override + { + auto& bindInfo = getPXX2BindInformationBuffer(); + + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { + // returned to normal after bind + if (bindInfo.step > BIND_INIT) { + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + deleteLater(); + if (bindInfo.step == BIND_OK) { + POPUP_INFORMATION(STR_REG_OK); + setPXX2ReceiverUsed(moduleIdx, receiverIdx); + } + return; + } + + // pre-bind phase: fetching info for R9M + auto& modSetup = getPXX2ModuleSetupBuffer(); + switch (bindInfo.step) { + case BIND_MODULE_TX_INFORMATION_REQUEST: + if (modSetup.moduleInformation.information.variant == PXX2_VARIANT_EU) { + // In EU mode we will need the power of the module to know if + // telemetry can be proposed + bindInfo.step = BIND_MODULE_TX_SETTINGS_REQUEST; + #if defined(SIMU) + modSetup.moduleSettings.txPower = 14; + #else + moduleState[moduleIdx].readModuleSettings(&modSetup.moduleSettings); + #endif + } else { + bindInfo.step = 0; + moduleState[moduleIdx].startBind(&bindInfo); + } + break; + case BIND_MODULE_TX_SETTINGS_REQUEST: + // We just receive the module settings (for TX power) + bindInfo.step = 0; + moduleState[moduleIdx].startBind(&bindInfo); + break; + } + return; + } + + if (bindInfo.step == BIND_INIT && bindInfo.candidateReceiversCount > 0) { + // prevent module mode being reset to NORMAL before exiting + setCloseHandler(nullptr); + deleteLater(); + + // ... and create RX choice dialog + new BindRxChoiceMenu(moduleIdx, receiverIdx); + return; + } + + BaseDialog::checkEvents(); + } protected: uint8_t moduleIdx; @@ -58,7 +118,7 @@ class BindWaitDialog : public BaseDialog class RxOptions : public BaseDialog { public: - RxOptions(Window* parent, uint8_t moduleIdx, uint8_t rxIdx); + RxOptions(uint8_t moduleIdx, uint8_t rxIdx); void checkEvents() override; protected: @@ -84,8 +144,7 @@ class RxOptions : public BaseDialog void writeSettings(); }; -static void startBindWaitDialog(Window* parent, uint8_t moduleIdx, - uint8_t receiverIdx) +static void startBindWaitDialog(uint8_t moduleIdx, uint8_t receiverIdx) { auto& bindInfo = getPXX2BindInformationBuffer(); #if defined(SIMU) @@ -96,32 +155,32 @@ static void startBindWaitDialog(Window* parent, uint8_t moduleIdx, bindInfo.step = BIND_OK; setPXX2ReceiverUsed(moduleIdx, receiverIdx); moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; - new MessageDialog(parent, STR_BIND, STR_BIND_OK); + new MessageDialog(STR_BIND, STR_BIND_OK); #else bindInfo.step = BIND_START; - new BindWaitDialog(parent, moduleIdx, receiverIdx); + new BindWaitDialog(moduleIdx, receiverIdx); #endif } -static void startFlexBindWaitDialog(Window* parent, uint8_t moduleIdx, +static void startFlexBindWaitDialog(uint8_t moduleIdx, uint8_t receiverIdx, uint8_t flex) { auto& bindInfo = getPXX2BindInformationBuffer(); bindInfo.flexMode = flex; - startBindWaitDialog(parent, moduleIdx, receiverIdx); + startBindWaitDialog(moduleIdx, receiverIdx); } -static void startLBTBindWaitDialog(Window* parent, uint8_t moduleIdx, +static void startLBTBindWaitDialog(uint8_t moduleIdx, uint8_t receiverIdx, uint8_t lbt) { auto& bindInfo = getPXX2BindInformationBuffer(); bindInfo.lbtMode = lbt; - startBindWaitDialog(parent, moduleIdx, receiverIdx); + startBindWaitDialog(moduleIdx, receiverIdx); } -BindRxChoiceMenu::BindRxChoiceMenu(Window* parent, uint8_t moduleIdx, +BindRxChoiceMenu::BindRxChoiceMenu(uint8_t moduleIdx, uint8_t receiverIdx) : - Menu(parent), moduleIdx(moduleIdx), receiverIdx(receiverIdx) + Menu(), moduleIdx(moduleIdx), receiverIdx(receiverIdx) { const auto& bindInfo = getPXX2BindInformationBuffer(); auto receiversCount = min(bindInfo.candidateReceiversCount, @@ -138,26 +197,26 @@ BindRxChoiceMenu::BindRxChoiceMenu(Window* parent, uint8_t moduleIdx, auto& modSetup = getPXX2ModuleSetupBuffer(); if (modSetup.moduleSettings.txPower <= 14) { // with telemetry - startLBTBindWaitDialog(parent, moduleIdx, receiverIdx, 1); + startLBTBindWaitDialog(moduleIdx, receiverIdx, 1); } else { // without telemetry - startLBTBindWaitDialog(parent, moduleIdx, receiverIdx, 2); + startLBTBindWaitDialog(moduleIdx, receiverIdx, 2); } return; } else if (isModuleR9MAccess(moduleIdx) && modInfo.information.variant == PXX2_VARIANT_FLEX) { bindInfo.step = BIND_RX_NAME_SELECTED; - auto flexMenu = new Menu(parent); + auto flexMenu = new Menu(); flexMenu->addLine(STR_FLEX_868, [=]() { - startFlexBindWaitDialog(parent, moduleIdx, receiverIdx, 0); + startFlexBindWaitDialog(moduleIdx, receiverIdx, 0); }); flexMenu->addLine(STR_FLEX_915, [=]() { - startFlexBindWaitDialog(parent, moduleIdx, receiverIdx, 1); + startFlexBindWaitDialog(moduleIdx, receiverIdx, 1); }); return; } - startBindWaitDialog(parent, moduleIdx, receiverIdx); + startBindWaitDialog(moduleIdx, receiverIdx); }); } @@ -170,78 +229,6 @@ static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -BindWaitDialog::BindWaitDialog(Window* parent, uint8_t moduleIdx, - uint8_t receiverIdx) : - BaseDialog(parent, STR_BIND, true), - moduleIdx(moduleIdx), - receiverIdx(receiverIdx) -{ - new StaticText(form, rect_t{}, STR_WAITING_FOR_RX); - - setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); -} - -void BindWaitDialog::deleteLater(bool detach, bool trash) -{ - BaseDialog::deleteLater(detach, trash); -} - -void BindWaitDialog::checkEvents() -{ - auto& bindInfo = getPXX2BindInformationBuffer(); - - if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { - // returned to normal after bind - if (bindInfo.step > BIND_INIT) { - removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); - deleteLater(); - if (bindInfo.step == BIND_OK) { - POPUP_INFORMATION(STR_REG_OK); - setPXX2ReceiverUsed(moduleIdx, receiverIdx); - } - return; - } - - // pre-bind phase: fetching info for R9M - auto& modSetup = getPXX2ModuleSetupBuffer(); - switch (bindInfo.step) { - case BIND_MODULE_TX_INFORMATION_REQUEST: - if (modSetup.moduleInformation.information.variant == PXX2_VARIANT_EU) { - // In EU mode we will need the power of the module to know if - // telemetry can be proposed - bindInfo.step = BIND_MODULE_TX_SETTINGS_REQUEST; -#if defined(SIMU) - modSetup.moduleSettings.txPower = 14; -#else - moduleState[moduleIdx].readModuleSettings(&modSetup.moduleSettings); -#endif - } else { - bindInfo.step = 0; - moduleState[moduleIdx].startBind(&bindInfo); - } - break; - case BIND_MODULE_TX_SETTINGS_REQUEST: - // We just receive the module settings (for TX power) - bindInfo.step = 0; - moduleState[moduleIdx].startBind(&bindInfo); - break; - } - return; - } - - if (bindInfo.step == BIND_INIT && bindInfo.candidateReceiversCount > 0) { - // prevent module mode being reset to NORMAL before exiting - setCloseHandler(nullptr); - deleteLater(); - - // ... and create RX choice dialog - new BindRxChoiceMenu(Layer::back(), moduleIdx, receiverIdx); - return; - } - - BaseDialog::checkEvents(); -} - ReceiverButton::ReceiverButton(Window* parent, rect_t rect, uint8_t moduleIdx, uint8_t receiverIdx) : TextButton(parent, rect, STR_BIND, @@ -256,13 +243,13 @@ uint8_t ReceiverButton::pressBind() if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] == '\0') { startBind(); } else { - auto menu = new Menu(parent); + auto menu = new Menu(); menu->addLine(STR_BIND, [=]() { startBind(); return 0; }); menu->addLine(STR_OPTIONS, [=]() { - new RxOptions(this, moduleIdx, receiverIdx); + new RxOptions(moduleIdx, receiverIdx); return 0; }); menu->addLine(STR_SHARE, [=]() { @@ -276,7 +263,7 @@ uint8_t ReceiverButton::pressBind() memclear(&modSetup, sizeof(modSetup)); modSetup.resetReceiverIndex = receiverIdx; modSetup.resetReceiverFlags = 0x01; - new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_DELETE, [=]() { + new ConfirmDialog(STR_RECEIVER, STR_RECEIVER_DELETE, [=]() { moduleState[moduleIdx].mode = MODULE_MODE_RESET; removePXX2Receiver(moduleIdx, receiverIdx); }); @@ -287,7 +274,7 @@ uint8_t ReceiverButton::pressBind() memclear(&modSetup, sizeof(modSetup)); modSetup.resetReceiverIndex = receiverIdx; modSetup.resetReceiverFlags = 0xFF; - new ConfirmDialog(parent, STR_RECEIVER, STR_RECEIVER_RESET, [=]() { + new ConfirmDialog(STR_RECEIVER, STR_RECEIVER_RESET, [=]() { moduleState[moduleIdx].mode = MODULE_MODE_RESET; removePXX2Receiver(moduleIdx, receiverIdx); }); @@ -321,7 +308,7 @@ void ReceiverButton::startBind() moduleState[moduleIdx].startBind(&bindInfo); } - new BindWaitDialog(parent, moduleIdx, receiverIdx); + new BindWaitDialog(moduleIdx, receiverIdx); } void ReceiverButton::checkEvents() @@ -342,8 +329,8 @@ void ReceiverButton::checkEvents() TextButton::checkEvents(); } -RegisterDialog::RegisterDialog(Window* parent, uint8_t moduleIdx) : - BaseDialog(parent, STR_REGISTER, true, DIALOG_DEFAULT_WIDTH, +RegisterDialog::RegisterDialog(uint8_t moduleIdx) : + BaseDialog(STR_REGISTER, true, DIALOG_DEFAULT_WIDTH, LV_SIZE_CONTENT), moduleIdx(moduleIdx) { @@ -439,8 +426,8 @@ void RegisterDialog::checkEvents() BaseDialog::checkEvents(); } -ModuleOptions::ModuleOptions(Window* parent, uint8_t moduleIdx) : - BaseDialog(parent, STR_MODULE_OPTIONS, true), moduleIdx(moduleIdx) +ModuleOptions::ModuleOptions(uint8_t moduleIdx) : + BaseDialog(STR_MODULE_OPTIONS, true), moduleIdx(moduleIdx) { new StaticText(form, rect_t{}, STR_WAITING_FOR_MODULE); @@ -632,8 +619,8 @@ void ModuleOptions::writeSettings() } } -RxOptions::RxOptions(Window* parent, uint8_t moduleIdx, uint8_t rxIdx) : - BaseDialog(parent, STR_RECEIVER_OPTIONS, true), +RxOptions::RxOptions(uint8_t moduleIdx, uint8_t rxIdx) : + BaseDialog(STR_RECEIVER_OPTIONS, true), moduleIdx(moduleIdx), receiverIdx(rxIdx) { diff --git a/radio/src/gui/colorlcd/module/access_settings.h b/radio/src/gui/colorlcd/module/access_settings.h index 9f67356f1..4018c3481 100644 --- a/radio/src/gui/colorlcd/module/access_settings.h +++ b/radio/src/gui/colorlcd/module/access_settings.h @@ -47,7 +47,7 @@ class ReceiverButton : public TextButton class RegisterDialog : public BaseDialog { public: - RegisterDialog(Window* parent, uint8_t moduleIdx); + RegisterDialog(uint8_t moduleIdx); void start(); void checkEvents() override; @@ -65,7 +65,7 @@ class RegisterDialog : public BaseDialog class ModuleOptions : public BaseDialog { public: - ModuleOptions(Window* parent, uint8_t moduleIdx); + ModuleOptions(uint8_t moduleIdx); void checkEvents() override; protected: diff --git a/radio/src/gui/colorlcd/module/bind_menu_d16.cpp b/radio/src/gui/colorlcd/module/bind_menu_d16.cpp index ca4d740a4..fd1f0e2e3 100644 --- a/radio/src/gui/colorlcd/module/bind_menu_d16.cpp +++ b/radio/src/gui/colorlcd/module/bind_menu_d16.cpp @@ -26,10 +26,10 @@ #include "telemetry/multi.h" #endif -BindChoiceMenu::BindChoiceMenu(Window *parent, uint8_t moduleIdx, +BindChoiceMenu::BindChoiceMenu(uint8_t moduleIdx, std::function onPress, std::function onCancelFn) : - Menu(parent), moduleIdx(moduleIdx), onPressHandler(std::move(onPress)) + Menu(), moduleIdx(moduleIdx), onPressHandler(std::move(onPress)) { if (isTelemAllowedOnBind(moduleIdx)) { addLine(STR_BINDING_1_8_TELEM_ON, [=]() { onSelect(Bind_1_8_TELEM_ON); }); diff --git a/radio/src/gui/colorlcd/module/bind_menu_d16.h b/radio/src/gui/colorlcd/module/bind_menu_d16.h index 46a2f57b7..269b2d249 100644 --- a/radio/src/gui/colorlcd/module/bind_menu_d16.h +++ b/radio/src/gui/colorlcd/module/bind_menu_d16.h @@ -38,6 +38,6 @@ class BindChoiceMenu : public Menu void onSelect(BindChanMode mode); public: - BindChoiceMenu(Window *parent, uint8_t moduleIdx, + BindChoiceMenu(uint8_t moduleIdx, std::function onPress, std::function onCancel); }; diff --git a/radio/src/gui/colorlcd/module/module_setup.cpp b/radio/src/gui/colorlcd/module/module_setup.cpp index 959fd0047..f3ad81e6c 100644 --- a/radio/src/gui/colorlcd/module/module_setup.cpp +++ b/radio/src/gui/colorlcd/module/module_setup.cpp @@ -246,7 +246,7 @@ class ModuleWindow : public Window if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx) || IS_R9_MULTI(moduleIdx)) { new BindChoiceMenu( - Layer::back(), moduleIdx, [=]() { bindButton->check(true); }, + moduleIdx, [=]() { bindButton->check(true); }, [=]() { bindButton->check(false); }); return 0; } @@ -316,7 +316,7 @@ class ModuleWindow : public Window if (isModuleISRM(moduleIdx)) { auto options = new TextButton(box, rect_t{}, LV_SYMBOL_SETTINGS); options->setPressHandler([=]() { - new pxx2::ModuleOptions(Layer::back(), moduleIdx); + new pxx2::ModuleOptions(moduleIdx); return 0; }); } @@ -336,7 +336,7 @@ class ModuleWindow : public Window registerButton = new TextButton(box, rect_t{}, STR_REGISTER); registerButton->setPressHandler([=]() -> uint8_t { - new pxx2::RegisterDialog(Layer::back(), moduleIdx); + new pxx2::RegisterDialog(moduleIdx); return 0; }); @@ -354,7 +354,7 @@ class ModuleWindow : public Window auto options = new TextButton(box, rect_t{}, LV_SYMBOL_SETTINGS); options->setPressHandler([=]() { - new pxx2::ModuleOptions(Layer::back(), moduleIdx); + new pxx2::ModuleOptions(moduleIdx); return 0; }); @@ -506,7 +506,7 @@ class ModuleWindow : public Window void startRSSIDialog(std::function closeHandler = nullptr) { auto rssiDialog = new DynamicMessageDialog( - parent, STR_RANGE_TEST, + STR_RANGE_TEST, [=]() { return std::to_string((int)TELEMETRY_RSSI()) + getRxStatLabels()->unit; }, @@ -664,7 +664,7 @@ class ModuleSubTypeChoice : public Choice protos->triggerScan(); if (protos->isScanning()) { - new RfScanDialog(parent, protos, [=]() { updateLayout(); }); + new RfScanDialog(protos, [=]() { updateLayout(); }); } else { TRACE("!protos->isScanning()"); } @@ -685,7 +685,7 @@ class ModuleSubTypeChoice : public Choice { #if defined(MULTIMODULE) if (isModuleMultimodule(moduleIdx)) { - auto menu = new Menu(this); + auto menu = new Menu(); if (menuTitle) menu->setTitle(menuTitle); menu->setCloseHandler([=]() { setEditMode(false); }); diff --git a/radio/src/gui/colorlcd/module/multi_rfprotos.cpp b/radio/src/gui/colorlcd/module/multi_rfprotos.cpp index 8c3d4684b..2fce5244a 100644 --- a/radio/src/gui/colorlcd/module/multi_rfprotos.cpp +++ b/radio/src/gui/colorlcd/module/multi_rfprotos.cpp @@ -28,9 +28,9 @@ // TODO: translation const char* const RFSCAN_TITLE = "MPM: Scanning protocols..."; -RfScanDialog::RfScanDialog(Window* parent, MultiRfProtocols* protos, +RfScanDialog::RfScanDialog(MultiRfProtocols* protos, std::function onClose) : - ProgressDialog(parent, RFSCAN_TITLE, onClose), + ProgressDialog(RFSCAN_TITLE, onClose), protos(protos) { } diff --git a/radio/src/gui/colorlcd/module/multi_rfprotos.h b/radio/src/gui/colorlcd/module/multi_rfprotos.h index 353d4d393..de8a5e66a 100644 --- a/radio/src/gui/colorlcd/module/multi_rfprotos.h +++ b/radio/src/gui/colorlcd/module/multi_rfprotos.h @@ -31,7 +31,7 @@ class RfScanDialog : public ProgressDialog uint32_t lastUpdate = 0; public: - RfScanDialog(Window* parent, MultiRfProtocols* protos, + RfScanDialog(MultiRfProtocols* protos, std::function onClose); void showProgress(); diff --git a/radio/src/gui/colorlcd/radio/hw_bluetooth.cpp b/radio/src/gui/colorlcd/radio/hw_bluetooth.cpp index c26818b68..0ce6b7640 100644 --- a/radio/src/gui/colorlcd/radio/hw_bluetooth.cpp +++ b/radio/src/gui/colorlcd/radio/hw_bluetooth.cpp @@ -33,7 +33,7 @@ static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; class BTDetailsDialog : public BaseDialog { public: - BTDetailsDialog(Window* parent) : BaseDialog(parent, STR_BLUETOOTH, true) + BTDetailsDialog() : BaseDialog(STR_BLUETOOTH, true) { FlexGridLayout grid(col_dsc, row_dsc); @@ -86,7 +86,7 @@ BluetoothConfigWindow::BluetoothConfigWindow(Window* parent, FlexGridLayout& gri settingsBtn = new TextButton(box, rect_t{}, LV_SYMBOL_SETTINGS, [=]() -> uint8_t { - new BTDetailsDialog(parent); + new BTDetailsDialog(); return 0; }); settingsBtn->show(g_eeGeneral.bluetoothMode != BLUETOOTH_OFF); diff --git a/radio/src/gui/colorlcd/radio/hw_inputs.cpp b/radio/src/gui/colorlcd/radio/hw_inputs.cpp index 248098758..56ae449a9 100644 --- a/radio/src/gui/colorlcd/radio/hw_inputs.cpp +++ b/radio/src/gui/colorlcd/radio/hw_inputs.cpp @@ -267,7 +267,7 @@ HWSwitches::HWSwitches(Window* parent) : template HWInputDialog::HWInputDialog(const char* title) : - BaseDialog(Layer::back(), title, true) + BaseDialog(title, true) { new T(form); } diff --git a/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp index d1a99c335..9bd285f85 100644 --- a/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio/radio_sdmanager.cpp @@ -95,8 +95,8 @@ ModuleCallback onUpdateStateChangedCallbackFor(FrskyOtaFlashDialog* dialog); class FrskyOtaFlashDialog : public BaseDialog { public: - explicit FrskyOtaFlashDialog(Window* parent, const char* title) : - BaseDialog(parent, title, true) + explicit FrskyOtaFlashDialog(const char* title) : + BaseDialog(title, true) { new StaticText(form, rect_t{}, STR_WAITING_FOR_RX); } @@ -137,8 +137,7 @@ class FrskyOtaFlashDialog : public BaseDialog *tmp++ = '.'; tmp = strAppendUnsigned(tmp, reusableBuffer.sdManager.otaUpdateInformation.receiverInformation.swVersion.revision); - updateConfirmDialog = new ConfirmDialog(Layer::back(), - getPXX2ReceiverName(modelId), + updateConfirmDialog = new ConfirmDialog(getPXX2ReceiverName(modelId), std::string(reusableBuffer.sdManager.otaReceiverVersion).c_str(), [=]() { onUpdateConfirmation(); }, [=]() { deleteLater(); }); @@ -156,7 +155,7 @@ class FrskyOtaFlashDialog : public BaseDialog if (reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversCount > 0) { if (reusableBuffer.sdManager.otaUpdateInformation.candidateReceiversCount != popupReceiversCount) { if (rxChoiceMenu == nullptr) { - rxChoiceMenu = new Menu(Layer::back()); + rxChoiceMenu = new Menu(); rxChoiceMenu->setTitle(STR_PXX2_SELECT_RX); rxChoiceMenu->setCancelHandler([=]() { // Seems menu didn't delete itself before call cancelHandler(). @@ -293,8 +292,7 @@ void RadioSdManagerPage::dirAction(const char* path, const char* name, { if (strcmp(name, "..") == 0) return; - auto window = Layer::back(); - auto menu = new Menu(window); + auto menu = new Menu(); menu->addLine(STR_RENAME_FILE, [=]() { uint8_t nameLength; uint8_t extLength; @@ -308,7 +306,7 @@ void RadioSdManagerPage::dirAction(const char* path, const char* name, std::string extension(""); if (ext) extension = ext; - new LabelDialog(Layer::back(), fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) { + new LabelDialog(fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) { label += extension; f_rename((const TCHAR *)name, (const TCHAR *)label.c_str()); browser->refresh(); @@ -316,7 +314,7 @@ void RadioSdManagerPage::dirAction(const char* path, const char* name, }); menu->addLine(STR_DELETE_FILE, [=]() { if (f_unlink(fullpath) != FR_OK) { - new MessageDialog(window, STR_DELETE_FILE, STR_DEL_DIR_NOT_EMPTY); + new MessageDialog(STR_DELETE_FILE, STR_DEL_DIR_NOT_EMPTY); } browser->refresh(); }); @@ -325,8 +323,7 @@ void RadioSdManagerPage::dirAction(const char* path, const char* name, void RadioSdManagerPage::fileAction(const char* path, const char* name, const char* fullpath) { - auto window = Layer::back(); - auto menu = new Menu(window); + auto menu = new Menu(); const char* ext = getFileExtension(name); if (ext) { if (!strcasecmp(ext, SOUNDS_EXT)) { @@ -376,7 +373,7 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, char buf[64]; sprintf(buf, " %s %dkB. %s", STR_FILE_SIZE, fileLength / 1024, STR_FILE_OPEN); - new ConfirmDialog(window, STR_WARNING, buf, + new ConfirmDialog(STR_WARNING, buf, [=] { new ViewTextWindow(path, name, ICON_RADIO_SD_MANAGER); }); } else { new ViewTextWindow(path, name, ICON_RADIO_SD_MANAGER); @@ -434,7 +431,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, information.productId)) menu->addLine(STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA, [=]() { auto dialog = new FrskyOtaFlashDialog( - Layer::back(), STR_FLASH_RECEIVER_BY_INTERNAL_MODULE_OTA); dialog->flash(fullpath, INTERNAL_MODULE); }); @@ -443,7 +439,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, information.productId)) menu->addLine(STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA, [=]() { auto dialog = new FrskyOtaFlashDialog( - Layer::back(), STR_FLASH_RECEIVER_BY_EXTERNAL_MODULE_OTA); dialog->flash(fullpath, EXTERNAL_MODULE); }); @@ -453,7 +448,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, menu->addLine(STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA, [=]() { auto dialog = new FrskyOtaFlashDialog( - Layer::back(), STR_FLASH_FLIGHT_CONTROLLER_BY_INTERNAL_MODULE_OTA); dialog->flash(fullpath, INTERNAL_MODULE); }); @@ -461,7 +455,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, menu->addLine(STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA, [=]() { auto dialog = new FrskyOtaFlashDialog( - Layer::back(), STR_FLASH_FLIGHT_CONTROLLER_BY_EXTERNAL_MODULE_OTA); dialog->flash(fullpath, EXTERNAL_MODULE); }); @@ -527,7 +520,7 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, std::string extension(""); if (ext) extension = ext; - new LabelDialog(Layer::back(), fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) { + new LabelDialog(fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) { label += extension; f_rename((const TCHAR *)name, (const TCHAR *)label.c_str()); browser->refresh(); diff --git a/radio/src/gui/colorlcd/radio/radio_setup.cpp b/radio/src/gui/colorlcd/radio/radio_setup.cpp index ac0f1b8a6..819934483 100644 --- a/radio/src/gui/colorlcd/radio/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio/radio_setup.cpp @@ -877,7 +877,7 @@ static SetupLineDef setupLines[] = { new Choice(parent, {x, y, 120, 0}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, HATSMODE_SWITCHABLE, GET_SET_DEFAULT(g_eeGeneral.hatsMode)); new TextButton(parent, {x + 120 + PAD_MEDIUM, y, 0, 0}, "?", [=]() { - new MessageDialog(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + new MessageDialog(STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", LEFT); return 0; }); diff --git a/radio/src/gui/colorlcd/radio/radio_theme.cpp b/radio/src/gui/colorlcd/radio/radio_theme.cpp index a2a0cdefd..82ee7bf4d 100644 --- a/radio/src/gui/colorlcd/radio/radio_theme.cpp +++ b/radio/src/gui/colorlcd/radio/radio_theme.cpp @@ -99,9 +99,9 @@ class ThemeDetailsDialog : public BaseDialog { public: ThemeDetailsDialog( - Window *parent, ThemeFile theme, + ThemeFile theme, std::function saveHandler = nullptr) : - BaseDialog(parent, STR_EDIT_THEME_DETAILS, false, DIALOG_DEFAULT_WIDTH, + BaseDialog(STR_EDIT_THEME_DETAILS, false, DIALOG_DEFAULT_WIDTH, LV_SIZE_CONTENT), theme(theme), saveHandler(saveHandler) @@ -346,8 +346,7 @@ class ThemeEditPage : public Page void onCancel() override { if (_dirty) { - new ConfirmDialog( - this, STR_SAVE_THEME, _theme.getName().c_str(), + new ConfirmDialog(STR_SAVE_THEME, _theme.getName().c_str(), [=]() { if (saveHandler != nullptr) { saveHandler(_theme); @@ -391,7 +390,7 @@ class ThemeEditPage : public Page // save and cancel rect_t r = {LCD_W - (ColorEditPage::BUTTON_WIDTH + 5), PAD_MEDIUM, ColorEditPage::BUTTON_WIDTH, 0}; new TextButton(window, r, STR_DETAILS, [=]() { - new ThemeDetailsDialog(page, _theme, [=](ThemeFile t) { + new ThemeDetailsDialog(_theme, [=](ThemeFile t) { _theme.setAuthor(t.getAuthor()); _theme.setInfo(t.getInfo()); _theme.setName(t.getName()); @@ -480,7 +479,7 @@ void ThemeSetupPage::checkEvents() void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp) { - auto menu = new Menu(listBox, false); + auto menu = new Menu(false); // you can't activate the active theme if (listBox->getSelected() != tp->getThemeIndex()) { @@ -529,7 +528,7 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp) menu->addLine(STR_DUPLICATE, [=]() { ThemeFile newTheme; - new ThemeDetailsDialog(window, newTheme, [=](ThemeFile theme) { + new ThemeDetailsDialog(newTheme, [=](ThemeFile theme) { if (!theme.getName().empty()) { char name[SELECTED_THEME_NAME_LEN + 1]; int n = 0; @@ -565,8 +564,7 @@ void ThemeSetupPage::displayThemeMenu(Window *window, ThemePersistance *tp) if (listBox->getSelected() != 0 && listBox->getSelected() != tp->getThemeIndex()) { menu->addLine(STR_DELETE, [=]() { - new ConfirmDialog( - window, STR_DELETE_THEME, + new ConfirmDialog(STR_DELETE_THEME, tp->getThemeByIndex(listBox->getSelected())->getName().c_str(), [=] { tp->deleteThemeByIndex(listBox->getSelected()); listBox->setNames(tp->getNames()); diff --git a/radio/src/gui/colorlcd/radio/radio_tools.cpp b/radio/src/gui/colorlcd/radio/radio_tools.cpp index ffe5148d8..abac43a6c 100644 --- a/radio/src/gui/colorlcd/radio/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio/radio_tools.cpp @@ -101,7 +101,7 @@ static void run_lua_tool(Window* parent, const std::string& path) *((char*)getBasename(toolPath) - 1) = '\0'; f_chdir(toolPath); - luaExec(path.c_str()); + luaExecStandalone(path.c_str()); } // LUA scripts in TOOLS diff --git a/radio/src/gui/colorlcd/radio/radio_version.cpp b/radio/src/gui/colorlcd/radio/radio_version.cpp index affc73b6c..826ae4d4f 100644 --- a/radio/src/gui/colorlcd/radio/radio_version.cpp +++ b/radio/src/gui/colorlcd/radio/radio_version.cpp @@ -73,8 +73,8 @@ class VersionDialog : public BaseDialog StaticText* ext_rx_status; public: - VersionDialog(Window* parent) : - BaseDialog(parent, STR_MODULES_RX_VERSION, true) + VersionDialog() : + BaseDialog(STR_MODULES_RX_VERSION, true) { #if defined(PXX2) memclear(&reusableBuffer.hardwareAndSettings.modules, @@ -378,7 +378,7 @@ void RadioVersionPage::build(Window* window) // Module and receivers versions auto btn = new TextButton(window, rect_t{}, STR_MODULES_RX_VERSION); btn->setPressHandler([=]() -> uint8_t { - new VersionDialog(window); + new VersionDialog(); return 0; }); lv_obj_set_width(btn->getLvObj(), lv_pct(100)); diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index bec2d3c5f..1244cc85c 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -24,13 +24,110 @@ #include "translations.h" #include "view_main.h" #include "dma2d.h" +#include "lua/lua_event.h" + +lua_State *lsStandalone = nullptr; + +#if defined(LUA_ALLOCATOR_TRACER) +LuaMemTracer lsStandaloneTrace; + +static void luaStandaloneHook(lua_State * L, lua_Debug *ar) +{ + else if (ar->event == LUA_HOOKLINE) { + lua_getinfo(L, "nSl", ar); + LuaMemTracer * tracer = &lsStandaloneTrace; + if (tracer->alloc || tracer->free) { + TRACE("LT: [+%u,-%u] %s:%d", tracer->alloc, tracer->free, tracer->script, tracer->lineno); + } + tracer->script = ar->source; + tracer->lineno = ar->currentline; + tracer->alloc = 0; + tracer->free = 0; + } +} +#endif // #if defined(LUA_ALLOCATOR_TRACER) + +static void luaStandaloneInit() +{ + luaClose(&lsStandalone); + +#if defined(USE_BIN_ALLOCATOR) + lsStandalone = lua_newstate(bin_l_alloc, nullptr); //we use our own allocator! +#elif defined(LUA_ALLOCATOR_TRACER) + memclear(&lsStandaloneTrace, sizeof(lsStandaloneTrace)); + lsStandaloneTrace.script = "lua_newstate(scripts)"; + lsStandalone = lua_newstate(tracer_alloc, &lsStandaloneTrace); //we use tracer allocator +#else + lsStandalone = lua_newstate(l_alloc, nullptr); //we use Lua default allocator +#endif + if (lsStandalone) { + // install our panic handler + extern int custom_lua_atpanic(lua_State *L); + lua_atpanic(lsStandalone, &custom_lua_atpanic); + +#if defined(LUA_ALLOCATOR_TRACER) + lua_sethook(L, luaStandaloneHook, LUA_MASKLINE); +#endif + + // protect libs and constants registration + PROTECT_LUA() { + luaRegisterLibraries(lsStandalone); + } + else { + // if we got panic during registration + // we disable Lua for this session + luaClose(&lsStandalone); + lsStandalone = 0; + } + UNPROTECT_LUA(); + } +} + +void luaExecStandalone(const char * filename) +{ + if (lsStandalone == NULL) + luaStandaloneInit(); + + PROTECT_LUA() { + if (luaLoadScriptFileToState(lsStandalone, filename, LUA_SCRIPT_LOAD_MODE) == SCRIPT_OK) { + if (lua_pcall(lsStandalone, 0, 1, 0) == LUA_OK && lua_istable(lsStandalone, -1)) { + int initFunction = LUA_REFNIL, runFunction = LUA_REFNIL; + bool lvglLayout = false; + + for (lua_pushnil(lsStandalone); lua_next(lsStandalone, -2); lua_pop(lsStandalone, 1)) { + const char * key = lua_tostring(lsStandalone, -2); + if (!strcmp(key, "init")) { + initFunction = luaL_ref(lsStandalone, LUA_REGISTRYINDEX); + lua_pushnil(lsStandalone); + } else if (!strcmp(key, "run")) { + runFunction = luaL_ref(lsStandalone, LUA_REGISTRYINDEX); + lua_pushnil(lsStandalone); + } else if (!strcasecmp(key, "useLvgl")) { + lvglLayout = lua_toboolean(lsStandalone, -1); + } + } + + StandaloneLuaWindow::setup(lvglLayout, initFunction, runFunction); + } + else { + TRACE("luaLoadFile(%s): Error parsing script: %s", filename, lua_tostring(lsStandalone, -1)); + } + } + } + else { + // error while loading Lua widget/theme, + // do not disable whole Lua state, just ingnore bad script + return; + } + UNPROTECT_LUA(); +} // singleton instance StandaloneLuaWindow* StandaloneLuaWindow::_instance; -StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl) : +StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl, int initFn, int runFn) : Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}), - useLvgl(useLvgl) + useLvgl(useLvgl), initFunction(initFn), runFunction(runFn) { setWindowFlag(OPAQUE); @@ -45,6 +142,7 @@ StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl) : lv_obj_set_size(lbl, LCD_W, LCD_H); etx_solid_bg(lbl, COLOR_THEME_PRIMARY1_INDEX); etx_txt_color(lbl, COLOR_THEME_PRIMARY2_INDEX); + etx_font(lbl, FONT_XL_INDEX); lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); lv_obj_set_style_pad_top(lbl, (LCD_H - EdgeTxStyles::PAGE_LINE_HEIGHT) / 2, LV_PART_MAIN); lv_label_set_text(lbl, STR_LOADING); @@ -69,12 +167,22 @@ StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl) : setupHandler(this); attach(); + + lua_gc(lsStandalone, LUA_GCCOLLECT, 0); + + // Pause function and mixer scripts + prevLuaState = luaState; + luaState = INTERPRETER_PAUSED; + +#if defined(USE_HATS_AS_KEYS) + setTransposeHatsForLUA(true); +#endif } -void StandaloneLuaWindow::setup(bool useLvgl) +void StandaloneLuaWindow::setup(bool useLvgl, int initFn, int runFn) { if (_instance == nullptr) - _instance = new StandaloneLuaWindow(useLvgl); + _instance = new StandaloneLuaWindow(useLvgl, initFn, runFn); } StandaloneLuaWindow* StandaloneLuaWindow::instance() @@ -102,6 +210,11 @@ void StandaloneLuaWindow::deleteLater(bool detach, bool trash) { if (_deleted) return; + if (initFunction != LUA_REFNIL) luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction); + if (runFunction != LUA_REFNIL) luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction); + lua_settop(lsStandalone, 0); + luaLcdBuffer = nullptr; + if (lcdBuffer) delete lcdBuffer; lcdBuffer = nullptr; @@ -118,6 +231,12 @@ void StandaloneLuaWindow::deleteLater(bool detach, bool trash) _instance = nullptr; } +#if defined(USE_HATS_AS_KEYS) + setTransposeHatsForLUA(false); +#endif + + luaState = prevLuaState; + Window::deleteLater(detach, trash); } @@ -125,38 +244,78 @@ void StandaloneLuaWindow::checkEvents() { Window::checkEvents(); + if (initFunction != LUA_REFNIL) { + lua_rawgeti(lsStandalone, LUA_REGISTRYINDEX, initFunction); + if (lua_pcall(lsStandalone, 0, 0, 0) != LUA_OK) { + luaShowError(); + } + luaL_unref(lsStandalone, LUA_REGISTRYINDEX, initFunction); + initFunction = LUA_REFNIL; + return; + } + // Set global LUA LCD buffer luaLcdBuffer = lcdBuffer; + luaLcdAllowed = !useLvglLayout(); - if (luaState != INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { - if (luaTask(true)) { - if (useLvglLayout() && !hasError) { - // if LUA finished a full cycle, - // call update functions - PROTECT_LUA() { - if (!callRefs(lsScripts)) { - luaShowError(); - } - } else { - luaShowError(); - } - UNPROTECT_LUA(); + LuaEventData evt; + // Pull a new event from the buffer + luaNextEvent(&evt); + if (evt.event == EVT_KEY_LONG(KEY_EXIT)) { + killEvents(evt.event); + deleteLater(); + } else { + if (runFunction != LUA_REFNIL) { + lua_rawgeti(lsStandalone, LUA_REGISTRYINDEX, runFunction); + + lua_pushunsigned(lsStandalone, evt.event); + int inputsCount = 1; + +#if defined(HARDWARE_TOUCH) + if (IS_TOUCH_EVENT(evt.event)) { + luaPushTouchEventTable(lsStandalone, &evt); + inputsCount += 1; + } +#endif + + if (lua_pcall(lsStandalone, inputsCount, 1, 0) != LUA_OK) { + luaShowError(); } else { - // if LUA finished a full cycle, - // invalidate to display the screen buffer - invalidate(); + if (lua_isnumber(lsStandalone, -1)) { + int scriptResult = lua_tointeger(lsStandalone, -1); + lua_pop(lsStandalone, 1); /* pop returned value */ + if (scriptResult != 0) { + deleteLater(); + } else { + if (useLvglLayout() && !hasError) { + PROTECT_LUA() { + if (!callRefs(lsStandalone)) { + luaShowError(); + } + } else { + luaShowError(); + } + UNPROTECT_LUA(); + } else { + invalidate(); + } + } + } else if (lua_isstring(lsStandalone, -1)) { + char nextScript[FF_MAX_LFN+1]; + strncpy(nextScript, lua_tostring(lsStandalone, -1), FF_MAX_LFN); + nextScript[FF_MAX_LFN] = '\0'; + _instance = nullptr; + lua_settop(lsStandalone, 0); + deleteLater(); + luaExecStandalone(nextScript); + } } } } - if (luaState == INTERPRETER_RELOAD_PERMANENT_SCRIPTS) { - // Script does not run anymore... - TRACE("LUA standalone script exited: deleting window!"); - deleteLater(); - } - // Kill global LUA LCD buffer luaLcdBuffer = nullptr; + luaLcdAllowed = false; } void StandaloneLuaWindow::onClicked() { Keyboard::hide(false); LuaEventHandler::onClicked(); } @@ -212,19 +371,22 @@ bool StandaloneLuaWindow::displayPopup(event_t event, uint8_t type, void StandaloneLuaWindow::clear() { - clearRefs(lsScripts); + clearRefs(lsStandalone); Window::clear(); } void StandaloneLuaWindow::luaShowError() { + if (runFunction != LUA_REFNIL) luaL_unref(lsStandalone, LUA_REGISTRYINDEX, runFunction); + runFunction = LUA_REFNIL; hasError = true; extern void luaError(lua_State *L, uint8_t error); - luaError(lsScripts, SCRIPT_SYNTAX_ERROR); + luaError(lsStandalone, SCRIPT_SYNTAX_ERROR); } void StandaloneLuaWindow::showError(bool firstCall, const char* title, const char* msg) { + runFunction = LUA_REFNIL; hasError = true; if (!errorModal) { lv_obj_set_scroll_dir(lvobj, LV_DIR_NONE); diff --git a/radio/src/gui/colorlcd/standalone_lua.h b/radio/src/gui/colorlcd/standalone_lua.h index e6f9e99ae..398c4961d 100644 --- a/radio/src/gui/colorlcd/standalone_lua.h +++ b/radio/src/gui/colorlcd/standalone_lua.h @@ -26,15 +26,17 @@ #include "lua/lua_api.h" #include "lua/lua_widget.h" +extern void luaExecStandalone(const char * filename); + class StandaloneLuaWindow : public Window, public LuaEventHandler, public LuaLvglManager { static StandaloneLuaWindow* _instance; - explicit StandaloneLuaWindow(bool useLvgl); + explicit StandaloneLuaWindow(bool useLvgl, int initFn, int runFn); public: static StandaloneLuaWindow* instance(); - static void setup(bool useLvgl); + static void setup(bool useLvgl, int initFn, int runFn); void attach(); void deleteLater(bool detach = true, bool trash = true) override; @@ -46,10 +48,11 @@ public: bool displayPopup(event_t event, uint8_t type, const char* text, const char* info, bool& result); - Window* getCurrentParent() const override { return tempParent ? tempParent : (Window*)this; } + Window* getCurrentParent() const override { return (tempParent && tempParent->getWindow()) ? tempParent->getWindow() : (Window*)this; } void clear() override; bool useLvglLayout() const override { return useLvgl; } + bool isAppMode() const override { return false; } void luaShowError() override; @@ -68,6 +71,9 @@ protected: lv_obj_t* errorMsg = nullptr; bool hasError = false; bool useLvgl = false; + int initFunction = LUA_REFNIL; + int runFunction = LUA_REFNIL; + uint8_t prevLuaState; // GFX BitmapBuffer *lcdBuffer = nullptr; @@ -76,9 +82,6 @@ protected: void popupPaint(BitmapBuffer* dc, coord_t x, coord_t y, coord_t w, coord_t h, const char* text, const char* info); - // run LUA code - void runLua(event_t evt); - void onEvent(event_t evt) override; void checkEvents() override; void onClicked() override; diff --git a/radio/src/gui/gui_common.cpp b/radio/src/gui/gui_common.cpp index 490194e98..d2783cd6a 100644 --- a/radio/src/gui/gui_common.cpp +++ b/radio/src/gui/gui_common.cpp @@ -648,7 +648,7 @@ class AntennaSelectionMenu : public Menu bool& done; public: - AntennaSelectionMenu(bool& done) : Menu(MainWindow::instance()), done(done) { + AntennaSelectionMenu(bool& done) : Menu(), done(done) { setTitle(STR_ANTENNA); addLine(STR_USE_INTERNAL_ANTENNA, [] { globalData.externalAntennaEnabled = false; }); diff --git a/radio/src/keys.h b/radio/src/keys.h index 2f55d5853..4c6d77926 100644 --- a/radio/src/keys.h +++ b/radio/src/keys.h @@ -200,7 +200,6 @@ bool rotaryEncoderPollingCycle(); void setHatsAsKeys(bool val); bool getHatsAsKeys(); void setTransposeHatsForLUA(bool val); -bool getTransposeHatsForLUA(); #endif struct InactivityData diff --git a/radio/src/lua/api_colorlcd_lvgl.cpp b/radio/src/lua/api_colorlcd_lvgl.cpp index a4b4bebfa..6278c454b 100644 --- a/radio/src/lua/api_colorlcd_lvgl.cpp +++ b/radio/src/lua/api_colorlcd_lvgl.cpp @@ -44,16 +44,16 @@ static int luaLvglObj(lua_State *L, std::function create, b return 1; } -static int luaLvglObjEx(lua_State *L, std::function create, bool standalone = false) +static int luaLvglObjEx(lua_State *L, std::function create, bool standalone = false) { if (luaLvglManager && (!standalone || !luaLvglManager->isWidget())) { - LvglWidgetObject* p = nullptr; - Window* prevParent = nullptr; + LvglWidgetObjectBase* p = nullptr; + LvglWidgetObjectBase* prevParent = nullptr; if (lua_gettop(L) == 2) { - p = LvglWidgetObject::checkLvgl(L, 1); + p = LvglWidgetObjectBase::checkLvgl(L, 1); if (p) { prevParent = luaLvglManager->getTempParent(); - luaLvglManager->setTempParent(p->getWindow()); + luaLvglManager->setTempParent(p); } } @@ -71,32 +71,27 @@ static int luaLvglObjEx(lua_State *L, std::function create, return 1; } -static int luaLvglConfirm(lua_State *L) +static int luaLvglPopup(lua_State *L, std::function create) { - auto obj = new LvglWidgetConfirmDialog(); + auto obj = create(); obj->getParams(L, 1); obj->build(L); return 0; } -LvglWidgetObject *LvglWidgetObject::checkLvgl(lua_State *L, int index) -{ - LvglWidgetObject **p = - (LvglWidgetObject **)luaL_checkudata(L, index, LVGL_METATABLE); - if (p) return *p; - return nullptr; -} - static int luaDestroyLvglWidget(lua_State *L) { - auto p = LvglWidgetObject::checkLvgl(L, 1); - if (p) delete p; + auto p = LvglWidgetObjectBase::checkLvgl(L, 1); + if (p) { + p->clearRefs(L); + delete p; + } return 0; } static int luaLvglSet(lua_State *L) { - auto p = LvglWidgetObject::checkLvgl(L, 1); + auto p = LvglWidgetObjectBase::checkLvgl(L, 1); if (p) { p->update(L); } @@ -107,9 +102,10 @@ static int luaLvglClear(lua_State *L) { if (luaLvglManager) { if (lua_gettop(L) == 1) { - auto p = LvglWidgetObject::checkLvgl(L, 1); - if (p) - p->getWindow()->clear(); + auto p = LvglWidgetObjectBase::checkLvgl(L, 1); + if (p) { + p->clear(); + } } else { luaLvglManager->clear(); } @@ -120,7 +116,7 @@ static int luaLvglClear(lua_State *L) static int luaLvglShow(lua_State *L) { - auto p = LvglWidgetObject::checkLvgl(L, 1); + auto p = LvglWidgetObjectBase::checkLvgl(L, 1); if (p) { p->show(); } @@ -129,7 +125,7 @@ static int luaLvglShow(lua_State *L) static int luaLvglHide(lua_State *L) { - auto p = LvglWidgetObject::checkLvgl(L, 1); + auto p = LvglWidgetObjectBase::checkLvgl(L, 1); if (p) { p->hide(); } @@ -164,50 +160,72 @@ static void buildLvgl(lua_State *L, int srcIndex, int refIndex) luaL_checktype(L, srcIndex, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, srcIndex - 1); lua_pop(L, 1)) { LvglWidgetParams p(L, -1); - LvglWidgetObject *lvobj = nullptr; + LvglWidgetObjectBase *obj = nullptr; if (strcasecmp(p.type, "label") == 0) - lvobj = new LvglWidgetLabel(); + obj = new LvglWidgetLabel(); else if (strcasecmp(p.type, "rectangle") == 0) - lvobj = new LvglWidgetRectangle(); + obj = new LvglWidgetRectangle(); else if (strcasecmp(p.type, "circle") == 0) - lvobj = new LvglWidgetCircle(); + obj = new LvglWidgetCircle(); else if (strcasecmp(p.type, "arc") == 0) - lvobj = new LvglWidgetArc(); + obj = new LvglWidgetArc(); + else if (strcasecmp(p.type, "hline") == 0) + obj = new LvglWidgetHLine(); + else if (strcasecmp(p.type, "vline") == 0) + obj = new LvglWidgetVLine(); + else if (strcasecmp(p.type, "line") == 0) + obj = new LvglWidgetLine(); + else if (strcasecmp(p.type, "triangle") == 0) + obj = new LvglWidgetTriangle(); else if (strcasecmp(p.type, "image") == 0) - lvobj = new LvglWidgetImage(); + obj = new LvglWidgetImage(); else if (strcasecmp(p.type, "qrcode") == 0) - lvobj = new LvglWidgetQRCode(); - // else if (strcasecmp(p.type, "meter") == 0) - // lvobj = new LvglWidgetMeter(); + obj = new LvglWidgetQRCode(); + else if (strcasecmp(p.type, "box") == 0) + obj = new LvglWidgetBox(); + else if (strcasecmp(p.type, "setting") == 0) + obj = new LvglWidgetSetting(); else if (!luaLvglManager->isWidget()) { if (strcasecmp(p.type, "button") == 0) - lvobj = new LvglWidgetTextButton(); + obj = new LvglWidgetTextButton(); else if (strcasecmp(p.type, "toggle") == 0) - lvobj = new LvglWidgetToggleSwitch(); + obj = new LvglWidgetToggleSwitch(); else if (strcasecmp(p.type, "textEdit") == 0) - lvobj = new LvglWidgetTextEdit(); + obj = new LvglWidgetTextEdit(); else if (strcasecmp(p.type, "numberEdit") == 0) - lvobj = new LvglWidgetNumberEdit(); + obj = new LvglWidgetNumberEdit(); else if (strcasecmp(p.type, "choice") == 0) - lvobj = new LvglWidgetChoice(); + obj = new LvglWidgetChoice(); else if (strcasecmp(p.type, "slider") == 0) - lvobj = new LvglWidgetSlider(); + obj = new LvglWidgetSlider(); else if (strcasecmp(p.type, "page") == 0) - lvobj = new LvglWidgetPage(); + obj = new LvglWidgetPage(); + else if (strcasecmp(p.type, "font") == 0) + obj = new LvglWidgetFontPicker(); + else if (strcasecmp(p.type, "align") == 0) + obj = new LvglWidgetAlignPicker(); + else if (strcasecmp(p.type, "color") == 0) + obj = new LvglWidgetColorPicker(); + else if (strcasecmp(p.type, "timer") == 0) + obj = new LvglWidgetTimerPicker(); + else if (strcasecmp(p.type, "switch") == 0) + obj = new LvglWidgetSwitchPicker(); + else if (strcasecmp(p.type, "source") == 0) + obj = new LvglWidgetSourcePicker(); } - if (lvobj) { - lvobj->getParams(L, -1); - lvobj->build(L); - auto ref = lvobj->getRef(L); + if (obj) { + obj->getParams(L, -1); + obj->build(L); + auto ref = obj->getRef(L); if (p.name) { lua_pushstring(L, p.name); lua_rawgeti(L, LUA_REGISTRYINDEX, ref); lua_settable(L, refIndex - 4); } - if (p.hasChildren) { + if (p.hasChildren && obj->getWindow()) { lua_getfield(L, -1, "children"); auto prevParent = luaLvglManager->getTempParent(); - luaLvglManager->setTempParent(lvobj->getWindow()); + luaLvglManager->setTempParent(obj); buildLvgl(L, -1, refIndex - 3); lua_pop(L, 1); luaLvglManager->setTempParent(prevParent); @@ -219,13 +237,13 @@ static void buildLvgl(lua_State *L, int srcIndex, int refIndex) static int luaLvglBuild(lua_State *L) { if (luaLvglManager) { - LvglWidgetObject* p = nullptr; - Window* prevParent = nullptr; + LvglWidgetObjectBase* p = nullptr; + LvglWidgetObjectBase* prevParent = nullptr; if (lua_gettop(L) == 2) { - p = LvglWidgetObject::checkLvgl(L, 1); + p = LvglWidgetObjectBase::checkLvgl(L, 1); if (p) { prevParent = luaLvglManager->getTempParent(); - luaLvglManager->setTempParent(p->getWindow()); + luaLvglManager->setTempParent(p); } } @@ -241,40 +259,116 @@ static int luaLvglBuild(lua_State *L) return 1; } -LROT_BEGIN(lvgllib, NULL, LROT_MASK_GC_INDEX) -LROT_FUNCENTRY(__gc, luaDestroyLvglWidget) -LROT_TABENTRY(__index, lvgllib) -LROT_FUNCENTRY(clear, luaLvglClear) -LROT_FUNCENTRY(build, luaLvglBuild) -// Objects - widgets and standalone -LROT_FUNCENTRY(label, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetLabel(); }); }) -LROT_FUNCENTRY(rectangle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetRectangle(); }); }) -LROT_FUNCENTRY(circle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetCircle(); }); }) -LROT_FUNCENTRY(arc, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetArc(); }); }) -LROT_FUNCENTRY(image, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetImage(); }); }) -LROT_FUNCENTRY(qrcode, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetQRCode(); }); }) -// LROT_FUNCENTRY(meter, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetMeter(); }); }) -// Objects - standalone scripts only -LROT_FUNCENTRY(button, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextButton(); }, true); }) -LROT_FUNCENTRY(toggle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetToggleSwitch(); }, true); }) -LROT_FUNCENTRY(textEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextEdit(); }, true); }) -LROT_FUNCENTRY(numberEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetNumberEdit(); }, true); }) -LROT_FUNCENTRY(choice, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetChoice(); }, true); }) -LROT_FUNCENTRY(slider, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSlider(); }, true); }) -// Containers -LROT_FUNCENTRY(page, [](lua_State* L) { return luaLvglObj(L, []() { return new LvglWidgetPage(); }, true); }) -// Dialogs -LROT_FUNCENTRY(confirm, luaLvglConfirm) -// Manipulation functions -LROT_FUNCENTRY(set, luaLvglSet) -LROT_FUNCENTRY(show, luaLvglShow) -LROT_FUNCENTRY(hide, luaLvglHide) -LROT_END(lvgllib, NULL, LROT_MASK_GC_INDEX) +static int luaLvglIsAppMode(lua_State *L) +{ + if (luaLvglManager) { + lua_pushboolean(L, luaLvglManager->isAppMode()); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// lvgl functions +LROT_BEGIN(lvgllib, NULL, 0) + LROT_FUNCENTRY(clear, luaLvglClear) + LROT_FUNCENTRY(build, luaLvglBuild) + LROT_FUNCENTRY(isAppMode, luaLvglIsAppMode) + // Objects - widgets and standalone + LROT_FUNCENTRY(label, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetLabel(); }); }) + LROT_FUNCENTRY(rectangle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetRectangle(); }); }) + LROT_FUNCENTRY(hline, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetHLine(); }); }) + LROT_FUNCENTRY(vline, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetVLine(); }); }) + LROT_FUNCENTRY(line, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetLine(); }); }) + LROT_FUNCENTRY(triangle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTriangle(); }); }) + LROT_FUNCENTRY(circle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetCircle(); }); }) + LROT_FUNCENTRY(arc, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetArc(); }); }) + LROT_FUNCENTRY(image, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetImage(); }); }) + LROT_FUNCENTRY(qrcode, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetQRCode(); }); }) + // Objects - standalone scripts only + LROT_FUNCENTRY(button, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextButton(); }, true); }) + LROT_FUNCENTRY(toggle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetToggleSwitch(); }, true); }) + LROT_FUNCENTRY(textEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextEdit(); }, true); }) + LROT_FUNCENTRY(numberEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetNumberEdit(); }, true); }) + LROT_FUNCENTRY(choice, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetChoice(); }, true); }) + LROT_FUNCENTRY(slider, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSlider(); }, true); }) + LROT_FUNCENTRY(font, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetFontPicker(); }, true); }) + LROT_FUNCENTRY(align, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetAlignPicker(); }, true); }) + LROT_FUNCENTRY(color, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetColorPicker(); }, true); }) + LROT_FUNCENTRY(timer, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTimerPicker(); }, true); }) + LROT_FUNCENTRY(switch, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSwitchPicker(); }, true); }) + LROT_FUNCENTRY(source, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSourcePicker(); }, true); }) + // Containers + LROT_FUNCENTRY(box, [](lua_State* L) { return luaLvglObj(L, []() { return new LvglWidgetBox(); }, true); }) + LROT_FUNCENTRY(setting, [](lua_State* L) { return luaLvglObj(L, []() { return new LvglWidgetSetting(); }, true); }) + LROT_FUNCENTRY(page, [](lua_State* L) { return luaLvglObj(L, []() { return new LvglWidgetPage(); }, true); }) + LROT_FUNCENTRY(dialog, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetDialog(); }, true); }) + // Dialogs + LROT_FUNCENTRY(confirm, [](lua_State* L) { return luaLvglPopup(L, []() { return new LvglWidgetConfirmDialog(); }); }) + LROT_FUNCENTRY(message, [](lua_State* L) { return luaLvglPopup(L, []() { return new LvglWidgetMessageDialog(); }); }) + // Object manipulation functions + LROT_FUNCENTRY(set, luaLvglSet) + LROT_FUNCENTRY(show, luaLvglShow) + LROT_FUNCENTRY(hide, luaLvglHide) + LROT_NUMENTRY(FLOW_ROW, LV_FLEX_FLOW_ROW) + LROT_NUMENTRY(FLOW_COLUMN, LV_FLEX_FLOW_COLUMN) + LROT_NUMENTRY(PAD_TINY, PAD_TINY) + LROT_NUMENTRY(PAD_SMALL, PAD_SMALL) + LROT_NUMENTRY(PAD_MEDIUM, PAD_MEDIUM) + LROT_NUMENTRY(PAD_LARGE, PAD_LARGE) +LROT_END(lvgllib, NULL, 0) + +// Metatable for simple objects (line, arc, label) +LROT_BEGIN(lvgl_base_mt, NULL, LROT_MASK_GC_INDEX) + LROT_FUNCENTRY(__gc, luaDestroyLvglWidget) + LROT_TABENTRY(__index, lvgl_base_mt) + // Object manipulation functions + LROT_FUNCENTRY(set, luaLvglSet) + LROT_FUNCENTRY(show, luaLvglShow) + LROT_FUNCENTRY(hide, luaLvglHide) +LROT_END(lvgl_base_mt, NULL, LROT_MASK_GC_INDEX) + +// Metatable for complex objects +LROT_BEGIN(lvgl_mt, NULL, LROT_MASK_GC_INDEX) + LROT_FUNCENTRY(__gc, luaDestroyLvglWidget) + LROT_TABENTRY(__index, lvgl_mt) + LROT_FUNCENTRY(clear, luaLvglClear) + LROT_FUNCENTRY(build, luaLvglBuild) + // Objects - widgets and standalone + LROT_FUNCENTRY(label, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetLabel(); }); }) + LROT_FUNCENTRY(rectangle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetRectangle(); }); }) + LROT_FUNCENTRY(hline, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetHLine(); }); }) + LROT_FUNCENTRY(vline, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetVLine(); }); }) + LROT_FUNCENTRY(line, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetLine(); }); }) + LROT_FUNCENTRY(triangle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTriangle(); }); }) + LROT_FUNCENTRY(circle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetCircle(); }); }) + LROT_FUNCENTRY(arc, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetArc(); }); }) + LROT_FUNCENTRY(image, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetImage(); }); }) + LROT_FUNCENTRY(qrcode, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetQRCode(); }); }) + // Objects - standalone scripts only + LROT_FUNCENTRY(button, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextButton(); }, true); }) + LROT_FUNCENTRY(toggle, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetToggleSwitch(); }, true); }) + LROT_FUNCENTRY(textEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTextEdit(); }, true); }) + LROT_FUNCENTRY(numberEdit, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetNumberEdit(); }, true); }) + LROT_FUNCENTRY(choice, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetChoice(); }, true); }) + LROT_FUNCENTRY(slider, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSlider(); }, true); }) + LROT_FUNCENTRY(font, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetFontPicker(); }, true); }) + LROT_FUNCENTRY(align, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetAlignPicker(); }, true); }) + LROT_FUNCENTRY(color, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetColorPicker(); }, true); }) + LROT_FUNCENTRY(timer, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetTimerPicker(); }, true); }) + LROT_FUNCENTRY(switch, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSwitchPicker(); }, true); }) + LROT_FUNCENTRY(source, [](lua_State* L) { return luaLvglObjEx(L, []() { return new LvglWidgetSourcePicker(); }, true); }) + // Object manipulation functions + LROT_FUNCENTRY(set, luaLvglSet) + LROT_FUNCENTRY(show, luaLvglShow) + LROT_FUNCENTRY(hide, luaLvglHide) +LROT_END(lvgl_mt, NULL, LROT_MASK_GC_INDEX) extern "C" { LUALIB_API int luaopen_lvgl(lua_State *L) { - luaL_rometatable(L, LVGL_METATABLE, LROT_TABLEREF(lvgllib)); + luaL_rometatable(L, LVGL_SIMPLEMETATABLE, LROT_TABLEREF(lvgl_base_mt)); + luaL_rometatable(L, LVGL_METATABLE, LROT_TABLEREF(lvgl_mt)); return 0; } } diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index b75fe8532..61034e792 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -83,13 +83,6 @@ struct our_longjmp * global_lj = nullptr; uint32_t luaExtraMemoryUsage = 0; #endif -#if defined(USE_HATS_AS_KEYS) -static bool _is_standalone_script() -{ - return scriptInternalData[0].reference == SCRIPT_STANDALONE; -} -#endif - #if defined(LUA_ALLOCATOR_TRACER) LuaMemTracer lsScriptsTrace; @@ -257,10 +250,6 @@ void luaDisable() { POPUP_WARNING("Lua disabled!"); luaState = INTERPRETER_PANIC; - -#if defined(USE_HATS_AS_KEYS) - if (_is_standalone_script()) setTransposeHatsForLUA(false); -#endif } void luaClose(lua_State ** L) @@ -893,9 +882,6 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) ScriptInternalData & sid = scriptInternalData[luaScriptsCount++]; sid.reference = SCRIPT_STANDALONE; if (luaLoad(filename, sid)) { -#if defined(COLORLCD) - StandaloneLuaWindow::setup(true); -#endif luaError(lsScripts, sid.state); break; } @@ -933,14 +919,6 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) sid.run = luaRegisterFunction("run"); sid.background = luaRegisterFunction("background"); initFunction = luaRegisterFunction("init"); -#if defined(COLORLCD) - if (sid.reference == SCRIPT_STANDALONE) { - lua_getfield(lsScripts, -1, "useLvgl"); - sid.useLvgl = lua_toboolean(lsScripts, -1); - lua_pop(lsScripts, 1); - StandaloneLuaWindow::setup(sid.useLvgl); - } -#endif if (sid.run == LUA_NOREF) { snprintf(lua_warning_info, LUA_WARNING_INFO_LEN, "luaLoadScripts(%.*s): No run function\n", LEN_SCRIPT_FILENAME, getScriptName(idx)); sid.state = SCRIPT_SYNTAX_ERROR; @@ -962,10 +940,6 @@ static void luaLoadScripts(bool init, const char * filename = nullptr) else { snprintf(lua_warning_info, LUA_WARNING_INFO_LEN, "luaLoadScripts(%.*s): The script did not return a table\n", LEN_SCRIPT_FILENAME, getScriptName(idx)); sid.state = SCRIPT_SYNTAX_ERROR; -#if defined(COLORLCD) - if (sid.reference == SCRIPT_STANDALONE) - StandaloneLuaWindow::setup(true); -#endif initFunction = LUA_NOREF; } @@ -1187,8 +1161,7 @@ static bool resumeLua(bool init, bool allowLcdUsage) luaState = INTERPRETER_RELOAD_PERMANENT_SCRIPTS; } else if (luaDisplayStatistics) { - #if defined(COLORLCD) - #else + #if !defined(COLORLCD) lcdDrawSolidHorizontalLine(0, 7*FH-1, lcdLastRightPos+6, ERASE); lcdDrawText(0, 7*FH, "GV Use: "); lcdDrawNumber(lcdLastRightPos, 7*FH, luaGetMemUsed(lsScripts), LEFT); @@ -1264,10 +1237,6 @@ bool luaTask(bool allowLcdUsage) case INTERPRETER_RELOAD_PERMANENT_SCRIPTS: init = true; luaState = INTERPRETER_LOADING; - -#if defined(USE_HATS_AS_KEYS) - if (_is_standalone_script()) setTransposeHatsForLUA(false); -#endif case INTERPRETER_LOADING: PROTECT_LUA() { @@ -1280,10 +1249,6 @@ bool luaTask(bool allowLcdUsage) case INTERPRETER_START_RUNNING: init = true; luaState = INTERPRETER_RUNNING; - -#if defined(USE_HATS_AS_KEYS) - if (_is_standalone_script()) setTransposeHatsForLUA(true); -#endif case INTERPRETER_RUNNING: PROTECT_LUA() { @@ -1291,6 +1256,13 @@ bool luaTask(bool allowLcdUsage) } else luaDisable(); UNPROTECT_LUA(); + break; + +#if defined(COLORLCD) + case INTERPRETER_PAUSED: + // stand alone script running + break; +#endif } return scriptWasRun; } diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index bdb650a50..322dc060e 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -169,6 +169,9 @@ enum InterpreterState { INTERPRETER_LOADING, INTERPRETER_START_RUNNING, INTERPRETER_RUNNING, +#if defined(COLORLCD) + INTERPRETER_PAUSED, +#endif INTERPRETER_PANIC = 255 }; diff --git a/radio/src/lua/lua_lvgl_widget.cpp b/radio/src/lua/lua_lvgl_widget.cpp index bc4c27d9f..39852f825 100644 --- a/radio/src/lua/lua_lvgl_widget.cpp +++ b/radio/src/lua/lua_lvgl_widget.cpp @@ -20,14 +20,97 @@ */ #include "lua_widget.h" +#include "color_picker.h" #include "edgetx.h" +#include "lua_event.h" #include "page.h" #include "slider.h" +#include "sourcechoice.h" +#include "switchchoice.h" #include "toggleswitch.h" -#include "lua_event.h" //----------------------------------------------------------------------------- +static int pcallcont (lua_State *L) { + int status = lua_getctx(L, NULL); + return status == LUA_OK; +} + +static bool pcallFunc(lua_State *L, int funcRef, int nretval) +{ + if (funcRef != LUA_REFNIL) { + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + return lua_pcallk(L, 0, nretval, 0, 0, pcallcont) == LUA_OK; + } + return false; +} + +static bool pcallFuncWithInt(lua_State *L, int funcRef, int nretval, int val) +{ + if (funcRef != LUA_REFNIL) { + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + lua_pushinteger(L, val); + return lua_pcallk(L, 1, nretval, 0, 0, pcallcont) == LUA_OK; + } + return false; +} + +static bool pcallFuncWithString(lua_State *L, int funcRef, int nretval, const char* val) +{ + if (funcRef != LUA_REFNIL) { + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + lua_pushstring(L, val); + return lua_pcallk(L, 1, nretval, 0, 0, pcallcont) == LUA_OK; + } + return false; +} + +//----------------------------------------------------------------------------- + +LvglWidgetObjectBase *LvglWidgetObjectBase::checkLvgl(lua_State *L, int index) +{ + LvglWidgetObjectBase **p; + + p = (LvglWidgetObjectBase **)luaL_testudata(L, index, LVGL_METATABLE); + if (p) return *p; + + p = (LvglWidgetObjectBase **)luaL_testudata(L, index, LVGL_SIMPLEMETATABLE); + if (p) return *p; + + return nullptr; +} + +//----------------------------------------------------------------------------- + +LvglWidgetObjectBase::LvglWidgetObjectBase(const char* meta) : + metatable(meta), + lvglManager(luaLvglManager) +{ +} + +int LvglWidgetObjectBase::getRef(lua_State *L) +{ + LvglWidgetObjectBase **p = + (LvglWidgetObjectBase **)lua_newuserdata(L, sizeof(LvglWidgetObjectBase *)); + *p = this; + luaL_getmetatable(L, metatable); + lua_setmetatable(L, -2); + + // Save reference + luaRef = luaL_ref(L, LUA_REGISTRYINDEX); + lvglManager->saveLvglObjectRef(luaRef); + + return luaRef; +} + +void LvglWidgetObjectBase::push(lua_State *L) +{ + // Save reference + auto ref = getRef(L); + // Push userdata back into Lua stack (return object) + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); +} + void LvglWidgetObjectBase::getParams(lua_State *L, int index) { luaL_checktype(L, index, LUA_TTABLE); @@ -39,15 +122,9 @@ void LvglWidgetObjectBase::getParams(lua_State *L, int index) } } -bool LvglWidgetObjectBase::pcallFunc(lua_State *L, int getFuncRef, int nretval) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, getFuncRef); - return lua_pcall(L, 0, nretval, 0) == 0; -} - void LvglWidgetObjectBase::pcallSimpleFunc(lua_State *L, int funcRef) { - if (funcRef) { + if (funcRef != LUA_REFNIL) { PROTECT_LUA() { if (!pcallFunc(L, funcRef, 0)) { @@ -61,7 +138,7 @@ void LvglWidgetObjectBase::pcallSimpleFunc(lua_State *L, int funcRef) bool LvglWidgetObjectBase::pcallUpdateBool(lua_State *L, int getFuncRef, std::function update) { - if (getFuncRef) { + if (getFuncRef != LUA_REFNIL) { int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 1)) { bool val = lua_toboolean(L, -1); @@ -77,7 +154,7 @@ bool LvglWidgetObjectBase::pcallUpdateBool(lua_State *L, int getFuncRef, bool LvglWidgetObjectBase::pcallUpdate1Int(lua_State *L, int getFuncRef, std::function update) { - if (getFuncRef) { + if (getFuncRef != LUA_REFNIL) { int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 1)) { int val = luaL_checkunsigned(L, -1); @@ -93,7 +170,7 @@ bool LvglWidgetObjectBase::pcallUpdate1Int(lua_State *L, int getFuncRef, bool LvglWidgetObjectBase::pcallUpdate2Int(lua_State *L, int getFuncRef, std::function update) { - if (getFuncRef) { + if (getFuncRef != LUA_REFNIL) { int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 2)) { int v1 = luaL_checkunsigned(L, -2); @@ -110,7 +187,7 @@ bool LvglWidgetObjectBase::pcallUpdate2Int(lua_State *L, int getFuncRef, int LvglWidgetObjectBase::pcallGetIntVal(lua_State *L, int getFuncRef) { int val = 0; - if (getFuncRef) { + if (getFuncRef != LUA_REFNIL) { int t = lua_gettop(L); PROTECT_LUA() { @@ -132,14 +209,11 @@ int LvglWidgetObjectBase::pcallGetIntVal(lua_State *L, int getFuncRef) void LvglWidgetObjectBase::pcallSetIntVal(lua_State *L, int setFuncRef, int val) { - if (setFuncRef) { + if (setFuncRef != LUA_REFNIL) { int t = lua_gettop(L); PROTECT_LUA() { - lua_rawgeti(L, LUA_REGISTRYINDEX, setFuncRef); - lua_pushinteger(L, val); - bool rv = lua_pcall(L, 1, 0, 0) == 0; - if (!rv) { + if (!pcallFuncWithInt(L, setFuncRef, 0, val)) { lvglManager->luaShowError(); } } @@ -152,40 +226,7 @@ void LvglWidgetObjectBase::pcallSetIntVal(lua_State *L, int setFuncRef, int val) } } -void LvglWidgetObjectBase::clearRef(lua_State *L, int ref) -{ - if (ref) luaL_unref(L, LUA_REGISTRYINDEX, ref); -} - -void LvglWidgetObjectBase::build(lua_State *L) {} - -//----------------------------------------------------------------------------- - -bool LvglWidgetObject::callRefs(lua_State *L) -{ - if (!pcallUpdate1Int(L, getColorFunction, - [=](int color) { setColor(color); })) - return false; - if (!pcallUpdateBool(L, getVisibleFunction, - [=](bool visible) { window->show(visible); })) - return false; - if (!pcallUpdate2Int(L, getSizeFunction, - [=](int w, int h) { setSize(w, h); })) - return false; - if (!pcallUpdate2Int(L, getPosFunction, [=](int x, int y) { setPos(x, y); })) - return false; - return true; -} - -void LvglWidgetObject::clearRefs(lua_State *L) -{ - clearRef(L, getColorFunction); - clearRef(L, getVisibleFunction); - clearRef(L, getSizeFunction); - clearRef(L, getPosFunction); -} - -void LvglWidgetObject::parseParam(lua_State *L, const char *key) +void LvglWidgetObjectBase::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "x")) { x = luaL_checkinteger(L, -1); @@ -212,40 +253,647 @@ void LvglWidgetObject::parseParam(lua_State *L, const char *key) } } -int LvglWidgetObject::getRef(lua_State *L) +bool LvglWidgetObjectBase::callRefs(lua_State *L) { - LvglWidgetObject **p = - (LvglWidgetObject **)lua_newuserdata(L, sizeof(LvglWidgetObject *)); - *p = this; - luaL_getmetatable(L, LVGL_METATABLE); - lua_setmetatable(L, -2); + if (clearRequest) { + clearRequest = false; + if (getWindow()) { + getWindow()->clear(); + clearChildRefs(L); + } + return true; + } - // Save reference - auto ref = luaL_ref(L, LUA_REGISTRYINDEX); - lvglManager->saveLvglObjectRef(ref); + if (!pcallUpdate1Int(L, getColorFunction, + [=](int color) { setColor(color); })) + return false; + if (!pcallUpdateBool(L, getVisibleFunction, + [=](bool visible) { if (visible) show(); else hide(); })) + return false; + if (!pcallUpdate2Int(L, getSizeFunction, + [=](int w, int h) { setSize(w, h); })) + return false; + if (!pcallUpdate2Int(L, getPosFunction, [=](int x, int y) { setPos(x, y); })) + return false; - return ref; + for (size_t i = 0; i < lvglObjectRefs.size(); i += 1) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lvglObjectRefs[i]); + auto p = LvglWidgetObjectBase::checkLvgl(L, -1); + lua_pop(L, 1); + if (p) if (!p->callRefs(L)) return false; + } + + return true; } -void LvglWidgetObject::push(lua_State *L) +void LvglWidgetObjectBase::saveLvglObjectRef(int ref) { - // Save reference - auto ref = getRef(L); - // Push userdata back into Lua stack (return object) - lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + lvglObjectRefs.push_back(ref); } -void LvglWidgetObject::update(lua_State *L) +void LvglWidgetObjectBase::clearRef(lua_State *L, int& ref) +{ + if (ref != LUA_REFNIL) + luaL_unref(L, LUA_REGISTRYINDEX, ref); + ref = LUA_REFNIL; +} + +void LvglWidgetObjectBase::clearChildRefs(lua_State *L) +{ + for (size_t i = 0; i < lvglObjectRefs.size(); i += 1) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lvglObjectRefs[i]); + auto p = LvglWidgetObjectBase::checkLvgl(L, -1); + lua_pop(L, 1); + if (p) p->clearRefs(L); + } + lvglObjectRefs.clear(); +} + +void LvglWidgetObjectBase::clearRefs(lua_State *L) +{ + clearRef(L, luaRef); + clearRef(L, getColorFunction); + clearRef(L, getVisibleFunction); + clearRef(L, getSizeFunction); + clearRef(L, getPosFunction); + + clearChildRefs(L); +} + +void LvglWidgetObjectBase::build(lua_State *L) {} + +void LvglWidgetObjectBase::refresh() +{ + setPos(x, y); + setSize(w, h); + setColor(color); +} + +void LvglWidgetObjectBase::update(lua_State *L) { getParams(L, 2); refresh(); } -void LvglWidgetObject::refresh() +bool LvglWidgetObjectBase::colorChanged(LcdFlags newColor) { + color = newColor; + LcdFlags c = color; + if (!(c & RGB_FLAG)) + c = COLOR(COLOR_VAL(c)) | RGB_FLAG; // Convert index to RGB + if (currentColor != c) { + currentColor = c; + return true; + } + return false; +} +//----------------------------------------------------------------------------- + +void LvglSimpleWidgetObject::setPos(coord_t x, coord_t y) +{ + this->x = x; + this->y = y; + if (lvobj) lv_obj_set_pos(lvobj, x, y); +} + +void LvglSimpleWidgetObject::setSize(coord_t w, coord_t h) +{ + this->w = w; + this->h = h; + if (lvobj) lv_obj_set_size(lvobj, w, h); +} + +void LvglSimpleWidgetObject::show() +{ + if (lvobj) lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_HIDDEN); +} + +void LvglSimpleWidgetObject::hide() +{ + if (lvobj) lv_obj_add_flag(lvobj, LV_OBJ_FLAG_HIDDEN); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetLabel::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "text")) { + if (lua_isfunction(L, -1)) + getTextFunction = luaL_ref(L, LUA_REGISTRYINDEX); + else + txt = luaL_checkstring(L, -1); + } else if (!strcmp(key, "font")) { + if (lua_isfunction(L, -1)) + getFontFunction = luaL_ref(L, LUA_REGISTRYINDEX); + else + font = luaL_checkunsigned(L, -1); + } else { + LvglSimpleWidgetObject::parseParam(L, key); + } +} + +bool LvglWidgetLabel::callRefs(lua_State *L) +{ + int t = lua_gettop(L); + if (getTextFunction != LUA_REFNIL) { + if (pcallFunc(L, getTextFunction, 1)) { + const char *s = luaL_checkstring(L, -1); + setText(s); + lua_settop(L, t); + } else { + return false; + } + } + if (!pcallUpdate1Int(L, getFontFunction, [=](int val) { setFont(val); })) + return false; + return LvglSimpleWidgetObject::callRefs(L); +} + +void LvglWidgetLabel::clearRefs(lua_State *L) +{ + clearRef(L, getTextFunction); + clearRef(L, getFontFunction); + LvglSimpleWidgetObject::clearRefs(L); +} + +void LvglWidgetLabel::setText(const char *s) +{ + uint32_t h = hash(s, strlen(s)); + if (h != textHash) { + txt = s; + textHash = h; + if (lvobj) lv_label_set_text(lvobj, s); + } +} + +void LvglWidgetLabel::setColor(LcdFlags newColor) +{ + if (lvobj && colorChanged(newColor)) { + if (color & RGB_FLAG) { + etx_remove_txt_color(lvobj); + lv_obj_set_style_text_color(lvobj, makeLvColor(color), LV_PART_MAIN); + } else { + lv_obj_remove_local_style_prop(lvobj, LV_STYLE_TEXT_COLOR, LV_PART_MAIN); + etx_txt_color(lvobj, (LcdColorIndex)COLOR_VAL(color)); + } + } +} + +void LvglWidgetLabel::setFont(LcdFlags font) +{ + if (lvobj) { + this->font = font; + if (font & VCENTERED) { + lv_obj_align(lvobj, LV_ALIGN_LEFT_MID, 0, 0); + } + lv_obj_set_style_text_align(lvobj, + (font & RIGHT) ? LV_TEXT_ALIGN_RIGHT + : (font & CENTERED) ? LV_TEXT_ALIGN_CENTER + : LV_TEXT_ALIGN_LEFT, + LV_PART_MAIN); + lv_obj_set_style_text_font(lvobj, getFont(font), LV_PART_MAIN); + } +} + +void LvglWidgetLabel::build(lua_State *L) +{ + lvobj = lv_label_create(lvglManager->getCurrentParent()->getLvObj()); setPos(x, y); setSize(w, h); + setText(txt); setColor(color); + setFont(font); + callRefs(L); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetLineBase::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "rounded")) { + rounded = lua_toboolean(L, -1); + } else { + LvglSimpleWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetLineBase::setColor(LcdFlags newColor) +{ + if (lvobj && colorChanged(newColor)) { + if (color & RGB_FLAG) { + etx_remove_line_color(lvobj); + lv_obj_set_style_line_color(lvobj, makeLvColor(color), LV_PART_MAIN); + } else { + lv_obj_remove_local_style_prop(lvobj, LV_STYLE_LINE_COLOR, LV_PART_MAIN); + etx_line_color(lvobj, (LcdColorIndex)COLOR_VAL(color)); + } + } +} + +void LvglWidgetLineBase::setPos(coord_t x, coord_t y) +{ + this->x = x; + this->y = y; + setLine(); +} + +void LvglWidgetLineBase::setSize(coord_t w, coord_t h) +{ + this->w = w; + this->h = h; + setLine(); +} + +void LvglWidgetLineBase::build(lua_State* L) +{ + lvobj = lv_line_create(lvglManager->getCurrentParent()->getLvObj()); + lv_obj_set_style_line_opa(lvobj, LV_OPA_COVER, LV_PART_MAIN); + refresh(); +} + +void LvglWidgetLineBase::refresh() +{ + setColor(color); + setLine(); + lv_obj_set_style_line_rounded(lvobj, rounded, LV_PART_MAIN); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetHLine::setLine() +{ + if (lvobj) { + pts[0].x = x; + pts[1].x = x + w; + pts[0].y = y; + pts[1].y = y; + lv_line_set_points(lvobj, pts, 2); + lv_obj_set_style_line_width(lvobj, abs(h), LV_PART_MAIN); + } +} + +//----------------------------------------------------------------------------- + +void LvglWidgetVLine::setLine() +{ + if (lvobj) { + pts[0].x = x; + pts[1].x = x; + pts[0].y = y; + pts[1].y = y + h; + lv_line_set_points(lvobj, pts, 2); + lv_obj_set_style_line_width(lvobj, abs(w), LV_PART_MAIN); + } +} + +//----------------------------------------------------------------------------- + +void LvglWidgetLine::getPt(lua_State* L, int n) +{ + lua_rawgeti(L, -1, n + 1); + luaL_checktype(L, -1, LUA_TTABLE); + lua_rawgeti(L, -1, 1); + pts[n].x = luaL_checkunsigned(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + pts[n].y = luaL_checkunsigned(L, -1); + lua_pop(L, 2); +} + +void LvglWidgetLine::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "thickness")) { + thickness = luaL_checkunsigned(L, -1); + } else if (!strcmp(key, "rounded")) { + rounded = lua_toboolean(L, -1); + } else if (!strcmp(key, "pts")) { + luaL_checktype(L, -1, LUA_TTABLE); + ptCnt = lua_rawlen(L, -1); + if (pts) delete pts; + if (ptCnt > 1) { + pts = new lv_point_t[ptCnt]; + for (size_t i = 0; i < ptCnt; i += 1) + getPt(L, i); + } + } else { + LvglSimpleWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetLine::setColor(LcdFlags newColor) +{ + if (lvobj && colorChanged(newColor)) { + if (color & RGB_FLAG) { + etx_remove_line_color(lvobj); + lv_obj_set_style_line_color(lvobj, makeLvColor(color), LV_PART_MAIN); + } else { + lv_obj_remove_local_style_prop(lvobj, LV_STYLE_LINE_COLOR, LV_PART_MAIN); + etx_line_color(lvobj, (LcdColorIndex)COLOR_VAL(color)); + } + } +} + +void LvglWidgetLine::setPos(coord_t x, coord_t y) +{ + if (pts) { + coord_t dx = this->x - x; + coord_t dy = this->y - y; + for (size_t i = 0; i < ptCnt; i += 1) { + pts[i].x += dx; + pts[i].y += dy; + } + setLine(); + } +} + +void LvglWidgetLine::setSize(coord_t w, coord_t h) +{ +} + +void LvglWidgetLine::setLine() +{ + if (lvobj && pts) { + x = pts[0].x; + y = pts[0].y; + for (size_t i = 1; i < ptCnt; i += 1) { + if (pts[i].x < x) x = pts[i].x; + if (pts[i].y < y) y = pts[i].y; + } + + lv_line_set_points(lvobj, pts, ptCnt); + lv_obj_set_style_line_width(lvobj, thickness, LV_PART_MAIN); + lv_obj_set_style_line_rounded(lvobj, rounded, LV_PART_MAIN); + } +} + +void LvglWidgetLine::build(lua_State *L) +{ + lvobj = lv_line_create(lvglManager->getCurrentParent()->getLvObj()); + lv_obj_set_style_line_opa(lvobj, LV_OPA_COVER, LV_PART_MAIN); + refresh(); + callRefs(L); +} + +void LvglWidgetLine::refresh() +{ + setColor(color); + setLine(); +} + +//----------------------------------------------------------------------------- + +LvglWidgetTriangle::~LvglWidgetTriangle() +{ + if (mask) + free(mask); + mask = nullptr; +} + +void LvglWidgetTriangle::getPt(lua_State* L, int n) +{ + lua_rawgeti(L, -1, n + 1); + luaL_checktype(L, -1, LUA_TTABLE); + lua_rawgeti(L, -1, 1); + px[n] = luaL_checkunsigned(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + py[n] = luaL_checkunsigned(L, -1); + lua_pop(L, 2); +} + +void LvglWidgetTriangle::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "pts")) { + luaL_checktype(L, -1, LUA_TTABLE); + getPt(L, 0); + getPt(L, 1); + getPt(L, 2); + } else { + LvglSimpleWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetTriangle::setColor(LcdFlags newColor) +{ + if (lvobj && colorChanged(newColor)) { + if (color & RGB_FLAG) { + etx_remove_img_color(lvobj); + lv_obj_set_style_img_recolor(lvobj, makeLvColor(color), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(lvobj, LV_OPA_COVER, LV_PART_MAIN); + } else { + lv_obj_remove_local_style_prop(lvobj, LV_STYLE_IMG_RECOLOR, LV_PART_MAIN); + etx_img_color(lvobj, (LcdColorIndex)COLOR_VAL(color)); + } + } +} + +void LvglWidgetTriangle::setSize(coord_t w, coord_t h) +{ + // TODO: Scale triangle? +} + +void LvglWidgetTriangle::fillLine(coord_t x1, coord_t x2, coord_t y) +{ + memset(&mask->data[y * w + x1], 255, x2 - x1 + 1); +} + +// Swap two bytes +#define SWAP(x,y) (x)=(x)^(y); (y)=(x)^(y); (x)=(x)^(y); + +void LvglWidgetTriangle::fillTriangle() +{ + if (!mask) return; + + coord_t x1 = px[0], y1 = py[0], x2 = px[1], y2 = py[1], x3 = px[2], y3 = py[2]; + + coord_t t1x, t2x, y, minx, maxx, t1xp, t2xp; + bool changed1 = false; + bool changed2 = false; + coord_t signx1, signx2, dx1, dy1, dx2, dy2; + coord_t e1, e2; + + // Sort vertices + if (y1 > y2) { SWAP(y1, y2); SWAP(x1, x2); } + if (y1 > y3) { SWAP(y1, y3); SWAP(x1, x3); } + if (y2 > y3) { SWAP(y2, y3); SWAP(x2, x3); } + + t1x = t2x = x1; y = y1; // Starting points + + dx1 = (coord_t)(x2 - x1); if(dx1 < 0) { dx1 = -dx1; signx1 = -1; } else signx1 = 1; + dy1 = (coord_t)(y2 - y1); + + dx2 = (coord_t)(x3 - x1); if(dx2 < 0) { dx2 = -dx2; signx2 = -1; } else signx2 = 1; + dy2 = (coord_t)(y3 - y1); + + if (dy1 > dx1) { // swap values + SWAP(dx1,dy1); + changed1 = true; + } + if (dy2 > dx2) { // swap values + SWAP(dy2,dx2); + changed2 = true; + } + + e2 = (coord_t)(dx2 >> 1); + // Flat top, just process the second half + if (y1 == y2) goto next; + e1 = (coord_t)(dx1 >> 1); + + for (coord_t i = 0; i < dx1;) { + t1xp = 0; t2xp = 0; + if (t1x < t2x) { minx = t1x; maxx = t2x; } + else { minx = t2x; maxx = t1x; } + // process first line until y value is about to change + while (i < dx1) { + i++; + e1 += dy1; + while (e1 >= dx1) { + e1 -= dx1; + if (changed1) t1xp = signx1; + else goto next1; + } + if (changed1) break; + else t1x += signx1; + } + // Move line +next1: + // process second line until y value is about to change + while (1) { + e2 += dy2; + while (e2 >= dx2) { + e2 -= dx2; + if (changed2) t2xp = signx2; + else goto next2; + } + if (changed2) break; + else t2x += signx2; + } +next2: + if (minx > t1x) minx = t1x; + if (minx > t2x) minx = t2x; + if (maxx < t1x) maxx = t1x; + if (maxx < t2x) maxx = t2x; + fillLine(minx, maxx, y); // Draw line from min to max points found on the y + // Now increase y + if (!changed1) t1x += signx1; + t1x += t1xp; + if (!changed2) t2x += signx2; + t2x += t2xp; + y += 1; + if (y == y2) break; + } +next: + // Second half + dx1 = (int8_t)(x3 - x2); if(dx1<0) { dx1 = -dx1; signx1 = -1; } else signx1 = 1; + dy1 = (int8_t)(y3 - y2); + t1x = x2; + + if (dy1 > dx1) { // swap values + SWAP(dy1,dx1); + changed1 = true; + } else changed1 = false; + + e1 = (coord_t)(dx1 >> 1); + + for (coord_t i = 0; i <= dx1; i++) { + t1xp = 0; t2xp = 0; + if (t1x < t2x) { minx = t1x; maxx = t2x; } + else { minx = t2x; maxx = t1x; } + // process first line until y value is about to change + while (i < dx1) { + e1 += dy1; + while (e1 >= dx1) { + e1 -= dx1; + if (changed1) { t1xp = signx1; break; } + else goto next3; + } + if (changed1) break; + else t1x += signx1; + if (i < dx1) i++; + } +next3: + // process second line until y value is about to change + while (t2x != x3) { + e2 += dy2; + while (e2 >= dx2) { + e2 -= dx2; + if(changed2) t2xp = signx2; + else goto next4; + } + if (changed2) break; + else t2x += signx2; + } +next4: + if (minx > t1x) minx = t1x; + if (minx > t2x) minx = t2x; + if (maxx < t1x) maxx = t1x; + if (maxx < t2x) maxx = t2x; + fillLine(minx, maxx, y); // Draw line from min to max points found on the y + // Now increase y + if (!changed1) t1x += signx1; + t1x += t1xp; + if (!changed2) t2x += signx2; + t2x += t2xp; + y += 1; + if (y > y3) return; + } +} + +void LvglWidgetTriangle::build(lua_State *L) +{ + // Bounds + x = min(min(px[0], px[1]), px[2]); + y = min(min(py[0], py[1]), py[2]); + w = max(max(px[0], px[1]), px[2]) - x + 1; + h = max(max(py[0], py[1]), py[2]) - y + 1; + + // Convert to relative coords + px[0] -= x; px[1] -= x; px[2] -= x; + py[0] -= y; py[1] -= y; py[2] -= y; + + // Allocate mask + size_t size = w * h; + mask = (MaskBitmap*)malloc(size + 4); + if (mask) { + mask->width = w; + mask->height = h; + memset(mask->data, 0, size); + + // Draw triangle + fillTriangle(); + + // Create canvas from mask buffer + if (lvobj == nullptr) + lvobj = lv_canvas_create(lvglManager->getCurrentParent()->getLvObj()); + lv_canvas_set_buffer(lvobj, (void*)mask->data, mask->width, mask->height, + LV_IMG_CF_ALPHA_8BIT); + + // Set position, size and color + setPos(x, y); + LvglSimpleWidgetObject::setSize(w,h); + setColor(color); + } + if (L) callRefs(L); +} + +void LvglWidgetTriangle::refresh() +{ + if (mask) free(mask); + mask = nullptr; + build(nullptr); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetObject::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "flexFlow")) { + flexFlow = luaL_checkinteger(L, -1); + } else if (!strcmp(key, "flexPad")) { + flexPad = luaL_checkinteger(L, -1); + } else { + LvglWidgetObjectBase::parseParam(L, key); + } } void LvglWidgetObject::setPos(coord_t x, coord_t y) @@ -262,6 +910,50 @@ void LvglWidgetObject::setSize(coord_t w, coord_t h) if (window) window->setSize(w, h); } +bool LvglWidgetObject::setFlex() +{ + if (flexFlow >= 0) { + window->padAll(PAD_TINY); + window->setFlexLayout((lv_flex_flow_t)flexFlow, flexPad, w, h); + return true; + } else { + window->padAll(PAD_ZERO); + return false; + } +} + +//----------------------------------------------------------------------------- + +void LvglWidgetBox::build(lua_State* L) +{ + window = + new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); + if (setFlex()) + lv_obj_set_flex_align(window->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetSetting::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "title")) { + txt = luaL_checkstring(L, -1); + } else { + LvglWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetSetting::build(lua_State* L) +{ + window = + new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); + window->padAll(PAD_TINY); + auto lbl = lv_label_create(window->getLvObj()); + lv_obj_align(lbl, LV_ALIGN_LEFT_MID, 0, 0); + etx_txt_color(lbl, COLOR_THEME_PRIMARY1_INDEX); + lv_label_set_text(lbl, txt); +} + //----------------------------------------------------------------------------- void LvglWidgetBorderedObject::parseParam(lua_State *L, const char *key) @@ -275,18 +967,18 @@ void LvglWidgetBorderedObject::parseParam(lua_State *L, const char *key) } } -void LvglWidgetBorderedObject::setColor(LcdFlags color) +void LvglWidgetBorderedObject::setColor(LcdFlags newColor) { - if (color != currentColor) { - currentColor = color; + if (colorChanged(newColor)) { if (filled) { etx_bg_color_from_flags(window->getLvObj(), color); } else { if (color & RGB_FLAG) { etx_remove_border_color(window->getLvObj()); lv_obj_set_style_border_color(window->getLvObj(), - makeLvColor(colorToRGB(color)), LV_PART_MAIN); + makeLvColor(color), LV_PART_MAIN); } else { + lv_obj_remove_local_style_prop(window->getLvObj(), LV_STYLE_BORDER_COLOR, LV_PART_MAIN); etx_border_color(window->getLvObj(), (LcdColorIndex)COLOR_VAL(color)); } } @@ -358,93 +1050,6 @@ void LvglWidgetRoundObject::setRadius(coord_t r) //----------------------------------------------------------------------------- -void LvglWidgetLabel::parseParam(lua_State *L, const char *key) -{ - if (!strcmp(key, "text")) { - if (lua_isfunction(L, -1)) - getTextFunction = luaL_ref(L, LUA_REGISTRYINDEX); - else - txt = luaL_checkstring(L, -1); - } else if (!strcmp(key, "font")) { - if (lua_isfunction(L, -1)) - getFontFunction = luaL_ref(L, LUA_REGISTRYINDEX); - else - font = luaL_checkunsigned(L, -1); - } else { - LvglWidgetObject::parseParam(L, key); - } -} - -bool LvglWidgetLabel::callRefs(lua_State *L) -{ - int t = lua_gettop(L); - if (getTextFunction) { - if (pcallFunc(L, getTextFunction, 1)) { - const char *s = luaL_checkstring(L, -1); - setText(s); - lua_settop(L, t); - } else { - return false; - } - } - if (!pcallUpdate1Int(L, getFontFunction, [=](int val) { setFont(val); })) - return false; - return LvglWidgetObject::callRefs(L); -} - -void LvglWidgetLabel::clearRefs(lua_State *L) -{ - clearRef(L, getTextFunction); - clearRef(L, getFontFunction); - LvglWidgetObject::clearRefs(L); -} - -void LvglWidgetLabel::setText(const char *s) -{ - uint32_t h = hash(s, strlen(s)); - if (h != textHash) { - txt = s; - textHash = h; - lv_label_set_text(window->getLvObj(), s); - } -} - -void LvglWidgetLabel::setColor(LcdFlags color) -{ - if (color != currentColor) { - currentColor = color; - if (color & RGB_FLAG) { - etx_remove_txt_color(window->getLvObj()); - lv_obj_set_style_text_color(window->getLvObj(), - makeLvColor(colorToRGB(color)), LV_PART_MAIN); - } else { - etx_txt_color(window->getLvObj(), (LcdColorIndex)COLOR_VAL(color)); - } - } -} - -void LvglWidgetLabel::setFont(LcdFlags font) -{ - this->font = font; - lv_obj_set_style_text_align(window->getLvObj(), - (font & RIGHT) ? LV_TEXT_ALIGN_RIGHT - : (font & CENTERED) ? LV_TEXT_ALIGN_CENTER - : LV_TEXT_ALIGN_LEFT, - LV_PART_MAIN); - lv_obj_set_style_text_font(window->getLvObj(), getFont(font), LV_PART_MAIN); -} - -void LvglWidgetLabel::build(lua_State *L) -{ - window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, - lv_label_create); - setText(txt); - setColor(color); - setFont(font); -} - -//----------------------------------------------------------------------------- - void LvglWidgetRectangle::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "rounded")) { @@ -462,6 +1067,7 @@ void LvglWidgetRectangle::build(lua_State *L) (rounded >= thickness) ? rounded : thickness, LV_PART_MAIN); } + callRefs(L); } //----------------------------------------------------------------------------- @@ -474,6 +1080,7 @@ void LvglWidgetCircle::build(lua_State *L) setRadius(radius); LvglWidgetBorderedObject::build(L); lv_obj_set_style_radius(window->getLvObj(), LV_RADIUS_CIRCLE, LV_PART_MAIN); + callRefs(L); } //----------------------------------------------------------------------------- @@ -495,15 +1102,14 @@ void LvglWidgetArc::parseParam(lua_State *L, const char *key) } } -void LvglWidgetArc::setColor(LcdFlags color) +void LvglWidgetArc::setColor(LcdFlags newColor) { - if (color != currentColor) { - currentColor = color; + if (colorChanged(newColor)) { if (color & RGB_FLAG) { etx_remove_arc_color(window->getLvObj()); - lv_obj_set_style_arc_color(window->getLvObj(), makeLvColor(colorToRGB(color)), - LV_PART_INDICATOR); + lv_obj_set_style_arc_color(window->getLvObj(), makeLvColor(color), LV_PART_INDICATOR); } else { + lv_obj_remove_local_style_prop(window->getLvObj(), LV_STYLE_ARC_COLOR, LV_PART_MAIN); etx_arc_color(window->getLvObj(), (LcdColorIndex)COLOR_VAL(color), LV_PART_INDICATOR); } } @@ -558,6 +1164,7 @@ void LvglWidgetArc::build(lua_State *L) lv_obj_set_style_arc_width(window->getLvObj(), thickness, LV_PART_MAIN); lv_obj_set_style_arc_opa(window->getLvObj(), LV_OPA_COVER, LV_PART_INDICATOR); lv_obj_set_style_arc_width(window->getLvObj(), thickness, LV_PART_INDICATOR); + callRefs(L); } //----------------------------------------------------------------------------- @@ -599,365 +1206,6 @@ void LvglWidgetQRCode::build(lua_State *L) //----------------------------------------------------------------------------- -// class LvglWidgetScaleIndicator : public LvglWidgetObjectBase -// { -// public: -// LvglWidgetScaleIndicator() {} - -// virtual void buildIndicator(lv_obj_t *parent, lv_meter_scale_t *scale) = 0; - -// protected: -// lv_obj_t *meter = nullptr; -// lv_meter_indicator_t *indic = nullptr; -// }; - -// class LvglWidgetScaleArc : public LvglWidgetScaleIndicator -// { -// public: -// LvglWidgetScaleArc(lua_State *L) { getParams(L, -1); } - -// bool callRefs(lua_State *L) override -// { -// if (!pcallUpdate1Int(L, getStartPosFunction, [=](int val) { -// lv_meter_set_indicator_start_value(meter, indic, val); -// })) -// return false; -// if (!pcallUpdate1Int(L, getEndPosFunction, [=](int val) { -// lv_meter_set_indicator_end_value(meter, indic, val); -// })) -// return false; -// return true; -// } - -// void clearRefs(lua_State *L) override -// { -// clearRef(L, getStartPosFunction); -// clearRef(L, getEndPosFunction); -// } - -// void buildIndicator(lv_obj_t *parent, lv_meter_scale_t *scale) override -// { -// meter = parent; - -// indic = -// lv_meter_add_arc(meter, scale, w, makeLvColor(colorToRGB(color)), rmod); -// if (hasStart) lv_meter_set_indicator_start_value(meter, indic, startPos); -// if (hasEnd) lv_meter_set_indicator_end_value(meter, indic, endPos); -// } - -// protected: -// uint8_t w = 0; -// int8_t rmod = 0; -// LcdFlags color = COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX); -// bool hasStart = false, hasEnd = false; -// int16_t startPos = 0, endPos = 0; - -// int getStartPosFunction = 0; -// int getEndPosFunction = 0; - -// void parseParam(lua_State *L, const char *key) override -// { -// if (!strcmp(key, "w")) { -// w = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "rmod")) { -// rmod = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "startPos")) { -// if (lua_isfunction(L, -1)) -// getStartPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); -// else { -// hasStart = true; -// startPos = luaL_checkinteger(L, -1); -// } -// } else if (!strcmp(key, "endPos")) { -// if (lua_isfunction(L, -1)) -// getEndPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); -// else { -// hasEnd = true; -// endPos = luaL_checkinteger(L, -1); -// } -// } else if (!strcmp(key, "color")) { -// color = luaL_checkunsigned(L, -1); -// } -// } -// }; - -// class LvglWidgetScaleLines : public LvglWidgetScaleIndicator -// { -// public: -// LvglWidgetScaleLines(lua_State *L) { getParams(L, -1); } - -// bool callRefs(lua_State *L) override -// { -// if (!pcallUpdate1Int(L, getStartPosFunction, [=](int val) { -// lv_meter_set_indicator_start_value(meter, indic, val); -// })) -// return false; -// if (!pcallUpdate1Int(L, getEndPosFunction, [=](int val) { -// lv_meter_set_indicator_end_value(meter, indic, val); -// })) -// return false; -// return true; -// } - -// void clearRefs(lua_State *L) override -// { -// clearRef(L, getStartPosFunction); -// clearRef(L, getEndPosFunction); -// } - -// void buildIndicator(lv_obj_t *parent, lv_meter_scale_t *scale) override -// { -// meter = parent; - -// indic = lv_meter_add_scale_lines( -// meter, scale, makeLvColor(colorToRGB(startColor)), -// makeLvColor(colorToRGB(endColor)), localFade, wmod); -// if (hasStart) lv_meter_set_indicator_start_value(meter, indic, startPos); -// if (hasEnd) lv_meter_set_indicator_end_value(meter, indic, endPos); -// } - -// protected: -// int8_t wmod = 0; -// LcdFlags startColor = COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX); -// LcdFlags endColor = COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX); -// bool hasStart = false, hasEnd = false; -// int16_t startPos = 0, endPos = 0; -// bool localFade = false; - -// int getStartPosFunction = 0; -// int getEndPosFunction = 0; - -// void parseParam(lua_State *L, const char *key) override -// { -// if (!strcmp(key, "wmod")) { -// wmod = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "startPos")) { -// if (lua_isfunction(L, -1)) { -// getStartPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); -// } else { -// hasStart = true; -// startPos = luaL_checkinteger(L, -1); -// } -// } else if (!strcmp(key, "endPos")) { -// if (lua_isfunction(L, -1)) { -// getEndPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); -// } else { -// hasEnd = true; -// endPos = luaL_checkinteger(L, -1); -// } -// } else if (!strcmp(key, "startColor")) { -// startColor = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "endColor")) { -// endColor = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "localFade")) { -// localFade = lua_toboolean(L, -1); -// } -// } -// }; - -// class LvglWidgetScaleNeedle : public LvglWidgetScaleIndicator -// { -// public: -// LvglWidgetScaleNeedle(lua_State *L) { getParams(L, -1); } - -// bool callRefs(lua_State *L) override -// { -// if (!pcallUpdate1Int(L, getPosFunction, [=](int val) { -// lv_meter_set_indicator_value(meter, indic, val); -// })) -// return false; -// return true; -// } - -// void clearRefs(lua_State *L) override { clearRef(L, getPosFunction); } - -// void buildIndicator(lv_obj_t *parent, lv_meter_scale_t *scale) override -// { -// meter = parent; - -// indic = lv_meter_add_needle_line(meter, scale, w, -// makeLvColor(colorToRGB(color)), rmod); -// } - -// protected: -// uint8_t w = 0; -// int8_t rmod = 0; -// LcdFlags color = COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX); - -// int getPosFunction = 0; - -// void parseParam(lua_State *L, const char *key) override -// { -// if (!strcmp(key, "w")) { -// w = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "rmod")) { -// rmod = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "color")) { -// color = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "pos")) { -// getPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); -// } -// } -// }; - -// class LvglWidgetMeterScale : public LvglWidgetObjectBase -// { -// public: -// LvglWidgetMeterScale(lua_State *L) { getParams(L, -1); } - -// bool callRefs(lua_State *L) override -// { -// for (auto it = indicators.cbegin(); it != indicators.cend(); ++it) { -// if (!(*it)->callRefs(L)) return false; -// } -// return true; -// } - -// void clearRefs(lua_State *L) override -// { -// for (auto it = indicators.cbegin(); it != indicators.cend(); ++it) { -// (*it)->clearRefs(L); -// } -// } - -// void build(lv_obj_t *parent) -// { -// meter = parent; -// lv_meter_scale_t *scale = lv_meter_add_scale(meter); - -// if (ticks) { -// lv_meter_set_scale_ticks(meter, scale, ticks, tickWidth, tickLen, -// makeLvColor(colorToRGB(tickColor))); -// if (majorNth) -// lv_meter_set_scale_major_ticks( -// meter, scale, majorNth, majorWidth, majorLen, -// makeLvColor(colorToRGB(majorColor)), labelGap); -// } - -// lv_meter_set_scale_range(meter, scale, scaleMin, scaleMax, scaleAngle, -// scaleRotate); - -// if (centerDotSize > 0) { -// lv_obj_set_style_size(meter, centerDotSize, LV_PART_INDICATOR); -// etx_bg_color_from_flags(meter, centerDotColor, LV_PART_INDICATOR); -// lv_obj_set_style_bg_opa(meter, LV_OPA_COVER, LV_PART_INDICATOR); -// lv_obj_set_style_radius(meter, LV_RADIUS_CIRCLE, LV_PART_INDICATOR); -// } - -// for (auto it = indicators.cbegin(); it != indicators.cend(); ++it) { -// (*it)->buildIndicator(meter, scale); -// } -// } - -// protected: -// lv_obj_t *meter; -// std::vector indicators; - -// int16_t scaleMin = 0, scaleMax = 100, scaleAngle = 360, scaleRotate = 0; -// uint8_t ticks = 0, majorNth = 0; -// uint8_t tickWidth = 0, majorWidth = 0; -// uint8_t tickLen = 0, majorLen = 0; -// LcdFlags tickColor = COLOR2FLAGS(COLOR_THEME_PRIMARY1); -// LcdFlags majorColor = COLOR2FLAGS(COLOR_THEME_PRIMARY1); -// uint8_t labelGap = 0, centerDotSize = 0; -// LcdFlags centerDotColor = COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX); - -// void parseParam(lua_State *L, const char *key) override -// { -// if (!strcmp(key, "ticks")) { -// ticks = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "tickWidth")) { -// tickWidth = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "tickLen")) { -// tickLen = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "tickColor")) { -// tickColor = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "majorNth")) { -// majorNth = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "majorWidth")) { -// majorWidth = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "majorLen")) { -// majorLen = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "majorColor")) { -// majorColor = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "labelGap")) { -// labelGap = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "min")) { -// scaleMin = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "max")) { -// scaleMax = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "angle")) { -// scaleAngle = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "rotate")) { -// scaleRotate = luaL_checkinteger(L, -1); -// } else if (!strcmp(key, "dotSize")) { -// centerDotSize = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "dotColor")) { -// centerDotColor = luaL_checkunsigned(L, -1); -// } else if (!strcmp(key, "indicators")) { -// luaL_checktype(L, -1, LUA_TTABLE); -// for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { -// lua_getfield(L, -1, "type"); -// const char *key = lua_tostring(L, -1); -// lua_pop(L, 1); -// if (!strcmp(key, "arc")) { -// indicators.push_back(new LvglWidgetScaleArc(L)); -// } else if (!strcmp(key, "lines")) { -// indicators.push_back(new LvglWidgetScaleLines(L)); -// } else if (!strcmp(key, "needle")) { -// indicators.push_back(new LvglWidgetScaleNeedle(L)); -// } -// } -// } -// } -// }; - -// void LvglWidgetMeter::parseParam(lua_State *L, const char *key) -// { -// if (!strcmp(key, "scales")) { -// luaL_checktype(L, -1, LUA_TTABLE); -// for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { -// scales.push_back(new LvglWidgetMeterScale(L)); -// } -// } else { -// LvglWidgetRoundObject::parseParam(L, key); -// } -// } - -// bool LvglWidgetMeter::callRefs(lua_State *L) -// { -// for (auto it = scales.cbegin(); it != scales.cend(); ++it) { -// if (!(*it)->callRefs(L)) return false; -// } -// return LvglWidgetRoundObject::callRefs(L); -// } - -// void LvglWidgetMeter::clearRefs(lua_State *L) -// { -// for (auto it = scales.cbegin(); it != scales.cend(); ++it) { -// (*it)->clearRefs(L); -// } -// LvglWidgetRoundObject::clearRefs(L); -// } - -// void LvglWidgetMeter::build(lua_State *L) -// { -// window = new Window(lvglManager->getCurrentParent(), -// {x - radius, y - radius, radius * 2, radius * 2}, -// lv_meter_create); - -// lv_obj_t *meter = window->getLvObj(); - -// lv_obj_add_flag(meter, LV_OBJ_FLAG_EVENT_BUBBLE); -// lv_obj_clear_flag(meter, LV_OBJ_FLAG_CLICKABLE); - -// for (auto it = scales.cbegin(); it != scales.cend(); ++it) { -// (*it)->build(meter); -// } -// } - -//----------------------------------------------------------------------------- - void LvglWidgetTextButton::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "text")) { @@ -1025,38 +1273,6 @@ void LvglWidgetToggleSwitch::build(lua_State *L) //----------------------------------------------------------------------------- -void LvglWidgetConfirmDialog::parseParam(lua_State *L, const char *key) -{ - if (!strcmp(key, "title")) { - title = luaL_checkstring(L, -1); - } else if (!strcmp(key, "message")) { - message = luaL_checkstring(L, -1); - } else if (!strcmp(key, "confirm")) { - confirmFunction = luaL_ref(L, LUA_REGISTRYINDEX); - } else if (!strcmp(key, "cancel")) { - cancelFunction = luaL_ref(L, LUA_REGISTRYINDEX); - } else { - LvglWidgetObject::parseParam(L, key); - } -} - -void LvglWidgetConfirmDialog::clearRefs(lua_State *L) -{ - clearRef(L, confirmFunction); - clearRef(L, cancelFunction); - LvglWidgetObject::clearRefs(L); -} - -void LvglWidgetConfirmDialog::build(lua_State *L) -{ - window = new ConfirmDialog( - MainWindow::instance(), title, message, - [=]() { pcallSimpleFunc(L, confirmFunction); }, - [=]() { pcallSimpleFunc(L, cancelFunction); }); -} - -//----------------------------------------------------------------------------- - void LvglWidgetTextEdit::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "value")) { @@ -1084,14 +1300,11 @@ void LvglWidgetTextEdit::build(lua_State *L) if (h == LV_SIZE_CONTENT) h = 0; window = new TextEdit(lvglManager->getCurrentParent(), {x, y, w, h}, value, maxLen, [=]() { - if (setFunction) { + if (setFunction != LUA_REFNIL) { int t = lua_gettop(L); PROTECT_LUA() { - lua_rawgeti(L, LUA_REGISTRYINDEX, setFunction); - lua_pushstring(L, value); - bool rv = lua_pcall(L, 1, 0, 0) == 0; - if (!rv) { + if (!pcallFuncWithString(L, setFunction, 0, value)) { lvglManager->luaShowError(); } } @@ -1139,16 +1352,13 @@ void LvglWidgetNumberEdit::build(lua_State *L) lvglManager->getCurrentParent(), {x, y, w, h}, min, max, [=]() { return pcallGetIntVal(L, getFunction); }, [=](int val) { pcallSetIntVal(L, setFunction, val); }); - if (dispFunction) { + if (dispFunction != LUA_REFNIL) { ((NumberEdit *)window)->setDisplayHandler([=](int val) { const char *s = "???"; int t = lua_gettop(L); PROTECT_LUA() { - lua_rawgeti(L, LUA_REGISTRYINDEX, dispFunction); - lua_pushinteger(L, val); - bool rv = lua_pcall(L, 1, 1, 0) == 0; - if (rv) { + if (pcallFuncWithInt(L, dispFunction, 1, val)) { s = luaL_checkstring(L, -1); } else { lvglManager->luaShowError(); @@ -1167,42 +1377,6 @@ void LvglWidgetNumberEdit::build(lua_State *L) //----------------------------------------------------------------------------- -void LvglWidgetChoice::parseParam(lua_State *L, const char *key) -{ - if (!strcmp(key, "title")) { - title = luaL_checkstring(L, -1); - } else if (!strcmp(key, "values")) { - luaL_checktype(L, -1, LUA_TTABLE); - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - values.push_back(lua_tostring(L, -1)); - } - } else if (!strcmp(key, "get")) { - getFunction = luaL_ref(L, LUA_REGISTRYINDEX); - } else if (!strcmp(key, "set")) { - setFunction = luaL_ref(L, LUA_REGISTRYINDEX); - } else { - LvglWidgetObject::parseParam(L, key); - } -} - -void LvglWidgetChoice::clearRefs(lua_State *L) -{ - clearRef(L, getFunction); - clearRef(L, setFunction); - LvglWidgetObject::clearRefs(L); -} - -void LvglWidgetChoice::build(lua_State *L) -{ - if (h == LV_SIZE_CONTENT) h = 0; - window = new Choice( - lvglManager->getCurrentParent(), {x, y, w, h}, values, 0, - values.size() - 1, [=]() { return pcallGetIntVal(L, getFunction) - 1; }, - [=](int val) { pcallSetIntVal(L, setFunction, val + 1); }, title.c_str()); -} - -//----------------------------------------------------------------------------- - void LvglWidgetSlider::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "get")) { @@ -1260,8 +1434,6 @@ class WidgetPage : public NavWindow, public LuaEventHandler LV_PART_MAIN); etx_scrollbar(body->getLvObj()); - body->padAll(PAD_ZERO); - #if defined(HARDWARE_TOUCH) addBackButton(); #endif @@ -1314,7 +1486,249 @@ void LvglWidgetPage::build(lua_State *L) auto page = new WidgetPage( lvglManager->getCurrentParent(), [=]() { pcallSimpleFunc(L, backActionFunction); }, title, subtitle, iconFile); + window = page->getBody(); + if (setFlex()) + lv_obj_set_flex_align(window->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_SPACE_AROUND); +} + +//----------------------------------------------------------------------------- + +class LvglDialog : public BaseDialog +{ + public: + LvglDialog(const char* title, coord_t w, coord_t h, std::function onClose) : + BaseDialog(title, true, w, h, false), + onClose(std::move(onClose)) + { + form->setHeight(h - EdgeTxStyles::UI_ELEMENT_HEIGHT); + } + + Window *getBody() { return form; } + + protected: + std::function onClose; + + void onCancel() override + { + onClose(); + BaseDialog::onCancel(); + } +}; + +void LvglWidgetDialog::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "close")) { + closeFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else if (!strcmp(key, "title")) { + title = luaL_checkstring(L, -1); + } else { + LvglWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetDialog::clearRefs(lua_State *L) +{ + clearRef(L, closeFunction); + LvglWidgetObject::clearRefs(L); +} + +void LvglWidgetDialog::build(lua_State *L) +{ + if (w == LV_SIZE_CONTENT) w = DIALOG_DEFAULT_WIDTH; + if (h == LV_SIZE_CONTENT) h = DIALOG_DEFAULT_HEIGHT; + auto dlg = new LvglDialog(title, w, h, + [=]() { pcallSimpleFunc(L, closeFunction); }); + window = dlg->getBody(); + window->setWidth(w); + setFlex(); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetConfirmDialog::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "title")) { + title = luaL_checkstring(L, -1); + } else if (!strcmp(key, "message")) { + message = luaL_checkstring(L, -1); + } else if (!strcmp(key, "confirm")) { + confirmFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else if (!strcmp(key, "cancel")) { + cancelFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + LvglWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetConfirmDialog::clearRefs(lua_State *L) +{ + clearRef(L, confirmFunction); + clearRef(L, cancelFunction); + LvglWidgetObject::clearRefs(L); +} + +void LvglWidgetConfirmDialog::build(lua_State *L) +{ + window = new ConfirmDialog(title, message, + [=]() { pcallSimpleFunc(L, confirmFunction); }, + [=]() { pcallSimpleFunc(L, cancelFunction); }); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetMessageDialog::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "title")) { + title = luaL_checkstring(L, -1); + } else if (!strcmp(key, "message")) { + message = luaL_checkstring(L, -1); + } else if (!strcmp(key, "details")) { + details = luaL_checkstring(L, -1); + } else { + LvglWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetMessageDialog::build(lua_State *L) +{ + window = new MessageDialog(title, message, details); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetPicker::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "get")) { + getFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else if (!strcmp(key, "set")) { + setFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + LvglWidgetObject::parseParam(L, key); + } +} + +void LvglWidgetPicker::clearRefs(lua_State *L) +{ + clearRef(L, getFunction); + clearRef(L, setFunction); + LvglWidgetObject::clearRefs(L); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetChoice::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "title")) { + title = luaL_checkstring(L, -1); + } else if (!strcmp(key, "values")) { + luaL_checktype(L, -1, LUA_TTABLE); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + values.push_back(lua_tostring(L, -1)); + } + } else { + LvglWidgetPicker::parseParam(L, key); + } +} + +void LvglWidgetChoice::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + window = new Choice( + lvglManager->getCurrentParent(), {x, y, w, h}, values, 0, values.size() - 1, + [=]() { return pcallGetIntVal(L, getFunction) - 1; }, + [=](int val) { pcallSetIntVal(L, setFunction, val + 1); }, title.c_str()); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetFontPicker::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + window = new Choice( + lvglManager->getCurrentParent(), {x, y, w, h}, STR_FONT_SIZES, 0, FONTS_COUNT - 1, + [=]() { return FONT_INDEX(pcallGetIntVal(L, getFunction)); }, + [=](int val) { pcallSetIntVal(L, setFunction, val << 8u); }); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetAlignPicker::build(lua_State *L) +{ + static LcdFlags alignments[3] = { LEFT, CENTERED, RIGHT }; + + if (h == LV_SIZE_CONTENT) h = 0; + window = new Choice( + lvglManager->getCurrentParent(), {x, y, w, h}, STR_ALIGN_OPTS, 0, ALIGN_COUNT - 1, + [=]() -> int { + auto v = (LcdFlags)pcallGetIntVal(L, getFunction); + for (size_t i = 0; i < DIM(alignments); i += 1) + if (alignments[i] == v) return i; + return 0; + }, + [=](int val) { pcallSetIntVal(L, setFunction, alignments[val]); }); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetColorPicker::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + window = new ColorPicker( + lvglManager->getCurrentParent(), {x, y, w, h}, + [=]() { return pcallGetIntVal(L, getFunction); }, + [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetTimerPicker::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + auto tmChoice = new Choice( + lvglManager->getCurrentParent(), {x, y, w, h}, 0, TIMERS - 1, + [=]() { return pcallGetIntVal(L, getFunction); }, + [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }); + tmChoice->setTextHandler([](int value) { + return std::string(STR_TIMER) + std::to_string(value + 1); + }); + window = tmChoice; +} + +//----------------------------------------------------------------------------- + +void LvglWidgetSwitchPicker::parseParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "min")) { + vmin = luaL_checkinteger(L, -1); + } else if (!strcmp(key, "max")) { + vmax = luaL_checkinteger(L, -1); + } else { + LvglWidgetPicker::parseParam(L, key); + } +} + +void LvglWidgetSwitchPicker::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + window = new SwitchChoice( + lvglManager->getCurrentParent(), {x, y, w, h}, + vmin, vmax, + [=]() { return pcallGetIntVal(L, getFunction); }, + [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }); +} + +//----------------------------------------------------------------------------- + +void LvglWidgetSourcePicker::build(lua_State *L) +{ + if (h == LV_SIZE_CONTENT) h = 0; + window = new SourceChoice( + lvglManager->getCurrentParent(), {x, y, w, h}, + 0, MIXSRC_LAST_TELEM, + [=]() { return pcallGetIntVal(L, getFunction); }, + [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }, + true); } //----------------------------------------------------------------------------- diff --git a/radio/src/lua/lua_lvgl_widget.h b/radio/src/lua/lua_lvgl_widget.h index e4df51046..e4b90a5d1 100644 --- a/radio/src/lua/lua_lvgl_widget.h +++ b/radio/src/lua/lua_lvgl_widget.h @@ -22,29 +22,64 @@ #pragma once #define LVGL_METATABLE "LVGL*" +#define LVGL_SIMPLEMETATABLE "LVGLSIMPLE*" //----------------------------------------------------------------------------- class LvglWidgetObjectBase { public: - LvglWidgetObjectBase() { lvglManager = luaLvglManager; } + LvglWidgetObjectBase(const char* meta); virtual ~LvglWidgetObjectBase() {} + int getRef(lua_State *L); + void push(lua_State *L); + void saveLvglObjectRef(int ref); + void getParams(lua_State *L, int index); virtual void build(lua_State *L); - virtual bool callRefs(lua_State *L) = 0; - virtual void clearRefs(lua_State *L) = 0; + virtual bool callRefs(lua_State *L); + virtual void clearRefs(lua_State *L); + void clearChildRefs(lua_State *L); + + virtual void show() = 0; + virtual void hide() = 0; + + virtual void setColor(LcdFlags newColor) {} + virtual void setPos(coord_t x, coord_t y) {} + virtual void setSize(coord_t w, coord_t h) {} + + void update(lua_State *L); + + virtual Window *getWindow() const = 0; + + static LvglWidgetObjectBase *checkLvgl(lua_State *L, int index); + + void clear() { clearRequest = true; } protected: + int luaRef = LUA_REFNIL; + std::vector lvglObjectRefs; + const char* metatable = nullptr; + bool clearRequest = false; LuaLvglManager *lvglManager = nullptr; + coord_t x = 0, y = 0, w = LV_SIZE_CONTENT, h = LV_SIZE_CONTENT; + LcdFlags color = COLOR2FLAGS(COLOR_THEME_SECONDARY1_INDEX); + LcdFlags currentColor = -1; + int getColorFunction = LUA_REFNIL; + int getVisibleFunction = LUA_REFNIL; + int getSizeFunction = LUA_REFNIL; + int getPosFunction = LUA_REFNIL; - virtual void parseParam(lua_State *L, const char *key) = 0; + virtual void refresh(); - void clearRef(lua_State *L, int ref); + virtual void parseParam(lua_State *L, const char *key); + + bool colorChanged(LcdFlags newColor); + + void clearRef(lua_State *L, int& ref); - bool pcallFunc(lua_State *L, int getFuncRef, int nret); void pcallSimpleFunc(lua_State *L, int funcRef); bool pcallUpdateBool(lua_State *L, int getFuncRef, std::function update); @@ -58,54 +93,32 @@ class LvglWidgetObjectBase //----------------------------------------------------------------------------- -class LvglWidgetObject : public LvglWidgetObjectBase +class LvglSimpleWidgetObject : public LvglWidgetObjectBase { public: - LvglWidgetObject() : LvglWidgetObjectBase() {} + LvglSimpleWidgetObject() : LvglWidgetObjectBase(LVGL_SIMPLEMETATABLE) {} - int getRef(lua_State *L); - void push(lua_State *L); + void show() override; + void hide() override; - void update(lua_State *L); + void setPos(coord_t x, coord_t y) override; + void setSize(coord_t w, coord_t h) override; - virtual void setColor(LcdFlags color) {} - virtual void setPos(coord_t x, coord_t y); - void setSize(coord_t w, coord_t h); - - void show() { window->show(); } - void hide() { window->hide(); } - - Window *getWindow() const { return window; } - - static LvglWidgetObject *checkLvgl(lua_State *L, int index); - - bool callRefs(lua_State *L) override; - void clearRefs(lua_State *L) override; + Window *getWindow() const override { return nullptr; } protected: - Window *window = nullptr; - LcdFlags currentColor = -1; - - coord_t x = 0, y = 0, w = LV_SIZE_CONTENT, h = LV_SIZE_CONTENT; - LcdFlags color = COLOR2FLAGS(COLOR_THEME_SECONDARY1_INDEX); - int getColorFunction = 0; - int getVisibleFunction = 0; - int getSizeFunction = 0; - int getPosFunction = 0; - - void parseParam(lua_State *L, const char *key) override; - virtual void refresh(); + lv_obj_t* lvobj = nullptr; }; //----------------------------------------------------------------------------- -class LvglWidgetLabel : public LvglWidgetObject +class LvglWidgetLabel : public LvglSimpleWidgetObject { public: - LvglWidgetLabel() : LvglWidgetObject() {} + LvglWidgetLabel() : LvglSimpleWidgetObject() {} void setText(const char *s); - void setColor(LcdFlags color) override; + void setColor(LcdFlags newColor) override; void setFont(LcdFlags font); void build(lua_State *L) override; @@ -117,26 +130,174 @@ class LvglWidgetLabel : public LvglWidgetObject const char *txt = ""; LcdFlags font = FONT(STD); - int getTextFunction = 0; - int getFontFunction = 0; + int getTextFunction = LUA_REFNIL; + int getFontFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; void refresh() override { setText(txt); setFont(font); - LvglWidgetObject::refresh(); + LvglSimpleWidgetObject::refresh(); } }; //----------------------------------------------------------------------------- +class LvglWidgetLineBase : public LvglSimpleWidgetObject +{ + public: + LvglWidgetLineBase() : LvglSimpleWidgetObject() {} + + void setColor(LcdFlags newColor) override; + void setPos(coord_t x, coord_t y) override; + void setSize(coord_t w, coord_t h) override; + + void build(lua_State *L) override; + + protected: + bool rounded = false; + lv_point_t pts[2]; + + virtual void setLine() = 0; + + void parseParam(lua_State *L, const char *key) override; + void refresh() override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetHLine : public LvglWidgetLineBase +{ + public: + LvglWidgetHLine() : LvglWidgetLineBase() {} + + protected: + void setLine() override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetVLine : public LvglWidgetLineBase +{ + public: + LvglWidgetVLine() : LvglWidgetLineBase() {} + + protected: + void setLine() override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetLine : public LvglSimpleWidgetObject +{ + public: + LvglWidgetLine() : LvglSimpleWidgetObject() {} + + void setColor(LcdFlags newColor) override; + void setPos(coord_t x, coord_t y) override; + void setSize(coord_t w, coord_t h) override; + + void build(lua_State *L) override; + + protected: + coord_t thickness = 1; + bool rounded = false; + size_t ptCnt = 0; + lv_point_t* pts = nullptr; + + void setLine(); + + void getPt(lua_State* L, int n); + void parseParam(lua_State *L, const char *key) override; + void refresh() override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetTriangle : public LvglSimpleWidgetObject +{ + public: + LvglWidgetTriangle() : LvglSimpleWidgetObject() {} + ~LvglWidgetTriangle(); + + void setColor(LcdFlags newColor) override; + void setSize(coord_t w, coord_t h) override; + + void build(lua_State *L) override; + + protected: + coord_t px[3] = {0}, py[3] = {0}; + MaskBitmap* mask = nullptr; + + void fillTriangle(); + void fillLine(coord_t x1, coord_t x2, coord_t y); + + void getPt(lua_State* L, int n); + void parseParam(lua_State *L, const char *key) override; + void refresh() override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetObject : public LvglWidgetObjectBase +{ + public: + LvglWidgetObject(const char* meta = LVGL_METATABLE) : LvglWidgetObjectBase(meta) {} + + void show() override { window->show(); } + void hide() override { window->hide(); } + + void setPos(coord_t x, coord_t y) override; + void setSize(coord_t w, coord_t h) override; + + Window *getWindow() const override { return window; } + + protected: + Window *window = nullptr; + int8_t flexFlow = -1; + int8_t flexPad = PAD_TINY; + + void parseParam(lua_State *L, const char *key) override; + + bool setFlex(); +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetBox : public LvglWidgetObject +{ + public: + LvglWidgetBox() : LvglWidgetObject() {} + + void build(lua_State *L) override; + + protected: +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetSetting : public LvglWidgetObject +{ + public: + LvglWidgetSetting() : LvglWidgetObject() {} + + void build(lua_State *L) override; + + protected: + const char *txt = ""; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + class LvglWidgetBorderedObject : public LvglWidgetObject { public: LvglWidgetBorderedObject() : LvglWidgetObject() {} - void setColor(LcdFlags color) override; + void setColor(LcdFlags newColor) override; void build(lua_State *L) override; @@ -163,7 +324,7 @@ class LvglWidgetRoundObject : public LvglWidgetBorderedObject protected: coord_t radius = 0; - int getRadiusFunction = 0; + int getRadiusFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; void refresh() override @@ -207,7 +368,7 @@ class LvglWidgetArc : public LvglWidgetRoundObject public: LvglWidgetArc() : LvglWidgetRoundObject() {} - void setColor(LcdFlags color) override; + void setColor(LcdFlags newColor) override; void setStartAngle(coord_t angle); void setEndAngle(coord_t angle); @@ -218,8 +379,8 @@ class LvglWidgetArc : public LvglWidgetRoundObject protected: coord_t startAngle = 0, endAngle = 0; - int getStartAngleFunction = 0; - int getEndAngleFunction = 0; + int getStartAngleFunction = LUA_REFNIL; + int getEndAngleFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; void refresh() override @@ -235,7 +396,7 @@ class LvglWidgetArc : public LvglWidgetRoundObject class LvglWidgetImage : public LvglWidgetObject { public: - LvglWidgetImage() : LvglWidgetObject() {} + LvglWidgetImage() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; @@ -251,7 +412,7 @@ class LvglWidgetImage : public LvglWidgetObject class LvglWidgetQRCode : public LvglWidgetObject { public: - LvglWidgetQRCode() : LvglWidgetObject() {} + LvglWidgetQRCode() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; @@ -264,29 +425,10 @@ class LvglWidgetQRCode : public LvglWidgetObject //----------------------------------------------------------------------------- -// class LvglWidgetMeterScale; - -// class LvglWidgetMeter : public LvglWidgetRoundObject -// { -// public: -// LvglWidgetMeter() : LvglWidgetRoundObject() {} - -// void build(lua_State *L) override; -// bool callRefs(lua_State *L) override; -// void clearRefs(lua_State *L) override; - -// protected: -// std::vector scales; - -// void parseParam(lua_State *L, const char *key) override; -// }; - -//----------------------------------------------------------------------------- - class LvglWidgetTextButton : public LvglWidgetObject { public: - LvglWidgetTextButton() : LvglWidgetObject() {} + LvglWidgetTextButton() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void setText(const char *s); @@ -297,7 +439,7 @@ class LvglWidgetTextButton : public LvglWidgetObject uint32_t textHash = -1; const char *txt = ""; - int pressFunction = 0; + int pressFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; }; @@ -307,34 +449,14 @@ class LvglWidgetTextButton : public LvglWidgetObject class LvglWidgetToggleSwitch : public LvglWidgetObject { public: - LvglWidgetToggleSwitch() : LvglWidgetObject() {} + LvglWidgetToggleSwitch() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; void clearRefs(lua_State *L) override; protected: - int getStateFunction = 0; - int setStateFunction = 0; - - void parseParam(lua_State *L, const char *key) override; -}; - -//----------------------------------------------------------------------------- - -class LvglWidgetConfirmDialog : public LvglWidgetObject -{ - public: - LvglWidgetConfirmDialog() : LvglWidgetObject() {} - - void build(lua_State *L) override; - void clearRefs(lua_State *L) override; - - protected: - const char *title = nullptr; - const char *message = nullptr; - - int confirmFunction = 0; - int cancelFunction = 0; + int getStateFunction = LUA_REFNIL; + int setStateFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; }; @@ -344,7 +466,7 @@ class LvglWidgetConfirmDialog : public LvglWidgetObject class LvglWidgetTextEdit : public LvglWidgetObject { public: - LvglWidgetTextEdit() : LvglWidgetObject() {} + LvglWidgetTextEdit() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; void clearRefs(lua_State *L) override; @@ -354,7 +476,7 @@ class LvglWidgetTextEdit : public LvglWidgetObject char value[129]; int maxLen = 32; - int setFunction = 0; + int setFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; }; @@ -364,7 +486,7 @@ class LvglWidgetTextEdit : public LvglWidgetObject class LvglWidgetNumberEdit : public LvglWidgetObject { public: - LvglWidgetNumberEdit() : LvglWidgetObject() {} + LvglWidgetNumberEdit() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; void clearRefs(lua_State *L) override; @@ -372,29 +494,9 @@ class LvglWidgetNumberEdit : public LvglWidgetObject protected: int min = -1024, max = 1024; - int getFunction = 0; - int setFunction = 0; - int dispFunction = 0; - - void parseParam(lua_State *L, const char *key) override; -}; - -//----------------------------------------------------------------------------- - -class LvglWidgetChoice : public LvglWidgetObject -{ - public: - LvglWidgetChoice() : LvglWidgetObject() {} - - void build(lua_State *L) override; - void clearRefs(lua_State *L) override; - - protected: - std::string title; - std::vector values; - - int getFunction = 0; - int setFunction = 0; + int getFunction = LUA_REFNIL; + int setFunction = LUA_REFNIL; + int dispFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; }; @@ -404,7 +506,7 @@ class LvglWidgetChoice : public LvglWidgetObject class LvglWidgetSlider : public LvglWidgetObject { public: - LvglWidgetSlider() : LvglWidgetObject() {} + LvglWidgetSlider() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} void build(lua_State *L) override; void clearRefs(lua_State *L) override; @@ -412,8 +514,8 @@ class LvglWidgetSlider : public LvglWidgetObject protected: int32_t vmin = 0; int32_t vmax = 100; - int getValueFunction = 0; - int setValueFunction = 0; + int getValueFunction = LUA_REFNIL; + int setValueFunction = LUA_REFNIL; void parseParam(lua_State *L, const char *key) override; }; @@ -429,12 +531,166 @@ class LvglWidgetPage : public LvglWidgetObject void clearRefs(lua_State *L) override; protected: - int backActionFunction = 0; std::string title; std::string subtitle; std::string iconFile; + int backActionFunction = LUA_REFNIL; + void parseParam(lua_State *L, const char *key) override; }; //----------------------------------------------------------------------------- + +class LvglWidgetDialog : public LvglWidgetObject +{ + public: + LvglWidgetDialog() : LvglWidgetObject() {} + + void build(lua_State *L) override; + void clearRefs(lua_State *L) override; + + protected: + const char *title = nullptr; + + int closeFunction = LUA_REFNIL; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetConfirmDialog : public LvglWidgetObject +{ + public: + LvglWidgetConfirmDialog() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} + + void build(lua_State *L) override; + void clearRefs(lua_State *L) override; + + protected: + const char *title = nullptr; + const char *message = nullptr; + + int confirmFunction = LUA_REFNIL; + int cancelFunction = LUA_REFNIL; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetMessageDialog : public LvglWidgetObject +{ + public: + LvglWidgetMessageDialog() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} + + void build(lua_State *L) override; + + protected: + const char *title = nullptr; + const char *message = nullptr; + const char *details = nullptr; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetPicker : public LvglWidgetObject +{ + public: + LvglWidgetPicker() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} + + void clearRefs(lua_State *L) override; + + protected: + int getFunction = LUA_REFNIL; + int setFunction = LUA_REFNIL; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetChoice : public LvglWidgetPicker +{ + public: + LvglWidgetChoice() : LvglWidgetPicker() {} + + void build(lua_State *L) override; + + protected: + std::string title; + std::vector values; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetFontPicker : public LvglWidgetPicker +{ + public: + LvglWidgetFontPicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetAlignPicker : public LvglWidgetPicker +{ + public: + LvglWidgetAlignPicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetColorPicker : public LvglWidgetPicker +{ + public: + LvglWidgetColorPicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetTimerPicker : public LvglWidgetPicker +{ + public: + LvglWidgetTimerPicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetSwitchPicker : public LvglWidgetPicker +{ + public: + LvglWidgetSwitchPicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; + + protected: + int16_t vmin = SWSRC_FIRST; + int16_t vmax = SWSRC_LAST; + + void parseParam(lua_State *L, const char *key) override; +}; + +//----------------------------------------------------------------------------- + +class LvglWidgetSourcePicker : public LvglWidgetPicker +{ + public: + LvglWidgetSourcePicker() : LvglWidgetPicker() {} + + void build(lua_State *L) override; +}; + +//----------------------------------------------------------------------------- diff --git a/radio/src/lua/lua_widget.cpp b/radio/src/lua/lua_widget.cpp index 95f4c864a..35a696e47 100644 --- a/radio/src/lua/lua_widget.cpp +++ b/radio/src/lua/lua_widget.cpp @@ -221,12 +221,15 @@ LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, WidgetPersistentData* persistentData, int luaWidgetDataRef, int zoneRectDataRef, int optionsDataRef) : Widget(factory, parent, rect, persistentData), - zoneRectDataRef(zoneRectDataRef), optionsDataRef(optionsDataRef), + zoneRectDataRef(zoneRectDataRef), optionsDataRef(optionsDataRef), luaWidgetDataRef(luaWidgetDataRef), errorMessage(nullptr) { - this->luaWidgetDataRef = luaWidgetDataRef; - lv_obj_add_event_cb(lvobj, LuaWidget::redraw_cb, LV_EVENT_DRAW_MAIN, - nullptr); + if (useLvglLayout()) { + update(); + } else { + lv_obj_add_event_cb(lvobj, LuaWidget::redraw_cb, LV_EVENT_DRAW_MAIN, + nullptr); + } } LuaWidget::~LuaWidget() @@ -265,12 +268,6 @@ void LuaWidget::checkEvents() setFullscreen(false); } - // Call update once after widget first created - if (!created) { - created = true; - update(); - } - // refresh() has not been called if (!refreshed) background(); @@ -348,6 +345,24 @@ void LuaWidget::update() if (lua_pcall(lsWidgets, 2, 0, 0) != 0) setErrorMessage("update()"); + if (useLvglLayout()) { + if (!lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN)) { + lv_area_t a; + lv_obj_get_coords(lvobj, &a); + // Check widget is at least partially visible + if (a.x2 >= 0 && a.x1 < LCD_W) { + PROTECT_LUA() { + if (!callRefs(lsWidgets)) { + setErrorMessage("callRefs()"); + } + } else { + // TODO: error handling + } + UNPROTECT_LUA(); + } + } + } + luaLvglManager = nullptr; } @@ -495,9 +510,8 @@ void LuaWidget::background() { if (lsWidgets == 0 || errorMessage) return; - // TRACE("LuaWidget::background()"); - luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS); if (luaFactory()->backgroundFunction) { + luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS); lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->backgroundFunction); lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef); runningFS = this; @@ -524,25 +538,37 @@ void LuaWidget::clear() bool LuaWidget::useLvglLayout() const { return luaFactory()->useLvglLayout(); } +bool LuaWidget::isAppMode() const +{ + return fullscreen && ViewMain::instance()->isAppMode(); +} + +void LuaLvglManager::saveLvglObjectRef(int ref) +{ + if (tempParent) + tempParent->saveLvglObjectRef(ref); + else + lvglObjectRefs.push_back(ref); +} + void LuaLvglManager::clearRefs(lua_State *L) { for (size_t i = 0; i < lvglObjectRefs.size(); i += 1) { lua_rawgeti(L, LUA_REGISTRYINDEX, lvglObjectRefs[i]); - auto lvobj = LvglWidgetObject::checkLvgl(L, -1); - lvobj->clearRefs(L); - - luaL_unref(L, LUA_REGISTRYINDEX, lvglObjectRefs[i]); + auto p = LvglWidgetObjectBase::checkLvgl(L, -1); + lua_pop(L, 1); + if (p) p->clearRefs(L); } lvglObjectRefs.clear(); } bool LuaLvglManager::callRefs(lua_State *L) { - bool rv = true; for (size_t i = 0; i < lvglObjectRefs.size(); i += 1) { lua_rawgeti(L, LUA_REGISTRYINDEX, lvglObjectRefs[i]); - auto lvobj = LvglWidgetObject::checkLvgl(L, -1); - lvobj->callRefs(L); + auto p = LvglWidgetObjectBase::checkLvgl(L, -1); + lua_pop(L, 1); + if (p) if (!p->callRefs(L)) return false; } - return rv; + return true; } diff --git a/radio/src/lua/lua_widget.h b/radio/src/lua/lua_widget.h index 20210be80..2e0e74582 100644 --- a/radio/src/lua/lua_widget.h +++ b/radio/src/lua/lua_widget.h @@ -40,16 +40,17 @@ class LuaLvglManager LuaLvglManager() = default; std::vector getLvglObjectRefs() const { return lvglObjectRefs; } - void saveLvglObjectRef(int ref) { lvglObjectRefs.push_back(ref); } + void saveLvglObjectRef(int ref); void clearRefs(lua_State *L); bool callRefs(lua_State *L); - void setTempParent(Window *p) { tempParent = p; } - Window* getTempParent() const { return tempParent; } + void setTempParent(LvglWidgetObjectBase *p) { tempParent = p; } + LvglWidgetObjectBase* getTempParent() const { return tempParent; } virtual Window* getCurrentParent() const = 0; virtual void clear() = 0; virtual bool useLvglLayout() const = 0; + virtual bool isAppMode() const = 0; virtual void luaShowError() = 0; @@ -59,7 +60,7 @@ class LuaLvglManager protected: std::vector lvglObjectRefs; - Window* tempParent = nullptr; + LvglWidgetObjectBase* tempParent = nullptr; }; class LuaEventHandler @@ -92,25 +93,6 @@ class LuaWidget : public Widget, public LuaEventHandler, public LuaLvglManager { friend class LuaWidgetFactory; - int zoneRectDataRef; - int optionsDataRef; - char* errorMessage; - bool refreshed = false; - - // Window interface - void onClicked() override; - void onCancel() override; - void checkEvents() override; - - // Widget interface - void onFullscreen(bool enable) override; - - void setErrorMessage(const char* funcName); - - // Update 'zone' data - void updateZoneRect(rect_t rect, bool updateUI = true) override; - bool updateTable(const char* idx, int val); - public: LuaWidget(const WidgetFactory* factory, Window* parent, const rect_t& rect, WidgetPersistentData* persistentData, int luaWidgetDataRef, int zoneRectDataRef, @@ -121,6 +103,8 @@ class LuaWidget : public Widget, public LuaEventHandler, public LuaLvglManager std::string getName() const override { return "LuaWidget"; } #endif + void setErrorMessage(const char* funcName); + // Widget interface const char* getErrorMessage() const override; void update() override; @@ -130,24 +114,41 @@ class LuaWidget : public Widget, public LuaEventHandler, public LuaLvglManager LuaWidgetFactory* luaFactory() const { return (LuaWidgetFactory*)factory; } - Window* getCurrentParent() const override { return tempParent ? tempParent : (Window*)this; } + Window* getCurrentParent() const override { return (tempParent && tempParent->getWindow()) ? tempParent->getWindow() : (Window*)this; } bool useLvglLayout() const override; + bool isAppMode() const override; void luaShowError() override {} - bool isWidget() override { return true; } + void pushOptionsTable(); + + bool isWidget() override { return !inSettings; } protected: - bool created = false; + bool inSettings = false; lv_obj_t* errorLabel = nullptr; + int zoneRectDataRef; + int optionsDataRef; int luaWidgetDataRef = 0; + char* errorMessage; + bool refreshed = false; + + // Window interface + void onClicked() override; + void onCancel() override; + void checkEvents() override; + void onEvent(event_t event) override; + + // Widget interface + void onFullscreen(bool enable) override; + + // Update 'zone' data + void updateZoneRect(rect_t rect, bool updateUI = true) override; + bool updateTable(const char* idx, int val); // Calls LUA widget 'refresh' method void refresh(BitmapBuffer* dc); - // Window interface - void onEvent(event_t event) override; - static void redraw_cb(lv_event_t *e); }; diff --git a/radio/src/lua/lua_widget_factory.h b/radio/src/lua/lua_widget_factory.h index f4e763269..1b4a3b11f 100644 --- a/radio/src/lua/lua_widget_factory.h +++ b/radio/src/lua/lua_widget_factory.h @@ -49,5 +49,6 @@ class LuaWidgetFactory : public WidgetFactory int refreshFunction; int backgroundFunction; int translateFunction; + int settingsFunction; bool lvglLayout; }; diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index b16c04cf5..3ed67d93c 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -217,7 +217,7 @@ void luaLoadWidgetCallback() TRACE("luaLoadWidgetCallback()"); const char * name=NULL; - int widgetOptions = 0, createFunction = 0, updateFunction = 0, + int widgetOptions = 0, createFunction = 0, updateFunction = 0, settingsFunction = 0, refreshFunction = 0, backgroundFunction = 0, translateFunction = 0; bool lvglLayout = false; @@ -252,7 +252,11 @@ void luaLoadWidgetCallback() translateFunction = luaL_ref(lsWidgets, LUA_REGISTRYINDEX); lua_pushnil(lsWidgets); } - else if (!strcasecmp(key, "uselvgl")) { + else if (!strcmp(key, "settings")) { + settingsFunction = luaL_ref(lsWidgets, LUA_REGISTRYINDEX); + lua_pushnil(lsWidgets); + } + else if (!strcasecmp(key, "useLvgl")) { lvglLayout = lua_toboolean(lsWidgets, -1); } } @@ -263,10 +267,11 @@ void luaLoadWidgetCallback() LuaWidgetFactory * factory = new LuaWidgetFactory(name, options, createFunction); factory->updateFunction = updateFunction; factory->refreshFunction = refreshFunction; - factory->backgroundFunction = backgroundFunction; // NOSONAR - factory->lvglLayout = lvglLayout; + factory->backgroundFunction = backgroundFunction; factory->translateFunction = translateFunction; + factory->settingsFunction = settingsFunction; factory->translateOptions(options); + factory->lvglLayout = lvglLayout; TRACE("Loaded Lua widget %s", name); } } diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 1e2a2627b..4d3a76f28 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -68,7 +68,7 @@ void openUsbMenu() { if (_usbMenu || _usbDisabled) return; - _usbMenu = new Menu(MainWindow::instance()); + _usbMenu = new Menu(); _usbMenu->setCloseHandler([]() { _usbMenu = nullptr; });