diff --git a/radio/src/gui/colorlcd/libui/choice.cpp b/radio/src/gui/colorlcd/libui/choice.cpp index f39ba540c..a6a2522fe 100644 --- a/radio/src/gui/colorlcd/libui/choice.cpp +++ b/radio/src/gui/colorlcd/libui/choice.cpp @@ -228,7 +228,7 @@ void Choice::openMenu() { setEditMode(true); // this needs to be done first before menu is created. - auto menu = new Menu(); + auto menu = new Menu(false, popupWidth); if (menuTitle) menu->setTitle(menuTitle); fillMenu(menu); diff --git a/radio/src/gui/colorlcd/libui/choice.h b/radio/src/gui/colorlcd/libui/choice.h index d37e28e60..ea1db334e 100644 --- a/radio/src/gui/colorlcd/libui/choice.h +++ b/radio/src/gui/colorlcd/libui/choice.h @@ -54,10 +54,13 @@ class ChoiceBase : public FormField const char* getTitle() const { return menuTitle; } + void setPopupWidth(coord_t w) { popupWidth = w; } + static LAYOUT_VAL_SCALED(ICON_W, 18) protected: lv_obj_t *label; + coord_t popupWidth = 0; int vmin = 0; int vmax = 0; const char *menuTitle = nullptr; diff --git a/radio/src/gui/colorlcd/libui/menu.cpp b/radio/src/gui/colorlcd/libui/menu.cpp index 0a6c6a84e..80aae068b 100644 --- a/radio/src/gui/colorlcd/libui/menu.cpp +++ b/radio/src/gui/colorlcd/libui/menu.cpp @@ -305,13 +305,15 @@ static lv_obj_t* menu_content_create(lv_obj_t* parent) class MenuWindowContent : public Window { public: - explicit MenuWindowContent(Menu* parent) : + explicit MenuWindowContent(Menu* parent, coord_t popupWidth) : Window(parent, rect_t{}, menu_content_create) { setWindowFlag(OPAQUE); + coord_t w = (popupWidth > MENUS_WIDTH) ? popupWidth : MENUS_WIDTH; + lv_obj_center(lvobj); - setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO, MENUS_WIDTH, LV_SIZE_CONTENT); + setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO, w, LV_SIZE_CONTENT); header = new StaticText(this, {0, 0, LV_PCT(100), 0}, "", COLOR_THEME_PRIMARY2_INDEX); @@ -319,7 +321,7 @@ class MenuWindowContent : public Window header->padAll(PAD_SMALL); header->hide(); - body = new MenuBody(this, rect_t{0, 0, MENUS_WIDTH, LV_SIZE_CONTENT}); + body = new MenuBody(this, rect_t{0, 0, w, LV_SIZE_CONTENT}); lv_obj_set_style_max_height(body->getLvObj(), LCD_H * 0.8, LV_PART_MAIN); } @@ -357,10 +359,10 @@ class MenuWindowContent : public Window //----------------------------------------------------------------------------- -Menu::Menu(bool multiple) : +Menu::Menu(bool multiple, coord_t popupWidth) : ModalWindow(true), multiple(multiple), - content(new MenuWindowContent(this)) + content(new MenuWindowContent(this, popupWidth)) { } diff --git a/radio/src/gui/colorlcd/libui/menu.h b/radio/src/gui/colorlcd/libui/menu.h index 05a674d1c..2ecd36e0a 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(bool multiple = false); + explicit Menu(bool multiple = false, coord_t popupWidth = 0); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "Menu"; } diff --git a/radio/src/gui/colorlcd/libui/window.cpp b/radio/src/gui/colorlcd/libui/window.cpp index 304c0693e..ee77a116b 100644 --- a/radio/src/gui/colorlcd/libui/window.cpp +++ b/radio/src/gui/colorlcd/libui/window.cpp @@ -59,15 +59,20 @@ void Window::eventHandler(lv_event_t *e) switch (code) { case LV_EVENT_SCROLL: { + lv_coord_t scroll_x = lv_obj_get_scroll_x(target); + lv_coord_t scroll_y = lv_obj_get_scroll_y(target); + if (scrollHandler) scrollHandler(scroll_x, scroll_y); + // exclude pointer based scrolling (only focus scrolling) - if (!lv_obj_is_scrolling(target)) { + if (!lv_obj_is_scrolling(target) && !noForcedScroll) { lv_point_t *p = (lv_point_t *)lv_event_get_param(e); lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(target); - lv_coord_t scroll_y = lv_obj_get_scroll_y(target); TRACE("SCROLL[x=%d;y=%d;top=%d;bottom=%d]", p->x, p->y, scroll_y, scroll_bottom); + // Force scroll to top or bottom when near either edge. + // Only applies when using rotary encoder or keys. if (scroll_y <= 45 && p->y > 0) { lv_obj_scroll_by(target, 0, scroll_y, LV_ANIM_OFF); } else if (scroll_bottom <= 16 && p->y < 0) { diff --git a/radio/src/gui/colorlcd/libui/window.h b/radio/src/gui/colorlcd/libui/window.h index 3020ae7ec..39afdd31c 100644 --- a/radio/src/gui/colorlcd/libui/window.h +++ b/radio/src/gui/colorlcd/libui/window.h @@ -75,6 +75,9 @@ class Window typedef std::function FocusHandler; void setFocusHandler(FocusHandler h) { focusHandler = std::move(h); } + typedef std::function ScrollHandler; + void setScrollHandler(ScrollHandler h) { scrollHandler = std::move(h); } + virtual void clear(); virtual void deleteLater(bool detach = true, bool trash = true); @@ -182,6 +185,8 @@ class Window virtual void enable(bool enabled = true); void disable() { enable(false); } + void disableForcedScroll() { noForcedScroll = true; } + protected: static std::list trash; @@ -194,12 +199,14 @@ class Window WindowFlags windowFlags = 0; LcdFlags textFlags = 0; + bool noForcedScroll = false; bool _deleted = false; static bool _longPressed; - std::function closeHandler; - std::function focusHandler; + CloseHandler closeHandler; + FocusHandler focusHandler; + std::function scrollHandler; void deleteChildren(); diff --git a/radio/src/gui/colorlcd/mainview/widget.h b/radio/src/gui/colorlcd/mainview/widget.h index 039c678d5..30f45bb01 100644 --- a/radio/src/gui/colorlcd/mainview/widget.h +++ b/radio/src/gui/colorlcd/mainview/widget.h @@ -143,9 +143,9 @@ class WidgetFactory Widget::PersistentData* persistentData); protected: - const char* name; - const char* displayName; - const ZoneOption* options; + const char* name = nullptr; + const char* displayName = nullptr; + const ZoneOption* options = nullptr; }; template diff --git a/radio/src/lua/api_colorlcd_lvgl.cpp b/radio/src/lua/api_colorlcd_lvgl.cpp index c7147fd1e..920096aa1 100644 --- a/radio/src/lua/api_colorlcd_lvgl.cpp +++ b/radio/src/lua/api_colorlcd_lvgl.cpp @@ -433,7 +433,6 @@ LROT_BEGIN(lvgl_base_mt, NULL, LROT_MASK_GC_INDEX) LROT_FUNCENTRY(set, luaLvglSet) LROT_FUNCENTRY(show, luaLvglShow) LROT_FUNCENTRY(hide, luaLvglHide) - LROT_FUNCENTRY(getScrollPos, luaLvglGetScrollPos) LROT_END(lvgl_base_mt, NULL, LROT_MASK_GC_INDEX) // Metatable for complex objects diff --git a/radio/src/lua/lua_lvgl_widget.cpp b/radio/src/lua/lua_lvgl_widget.cpp index c960c5bf2..d3286cb89 100644 --- a/radio/src/lua/lua_lvgl_widget.cpp +++ b/radio/src/lua/lua_lvgl_widget.cpp @@ -41,15 +41,12 @@ static void clearRef(lua_State *L, int& ref) //----------------------------------------------------------------------------- -void LvglParamFuncOrValue::parse(lua_State *L, ParamType typ) +void LvglParamFuncOrValue::parse(lua_State *L) { if (lua_isfunction(L, -1)) { function = luaL_ref(L, LUA_REGISTRYINDEX); } else { - if (typ == PTXT) - txt = luaL_checkstring(L, -1); - else - value = luaL_checkunsigned(L, -1); + value = luaL_checkunsigned(L, -1); } } @@ -63,17 +60,6 @@ bool LvglParamFuncOrValue::changedColor(LcdFlags newFlags) return false; } -bool LvglParamFuncOrValue::changedText(const char* s) -{ - txt = s; - uint32_t h = hash(txt, strlen(txt)); - if (h != currVal) { - currVal = h; - return true; - } - return false; -} - bool LvglParamFuncOrValue::changedValue(uint32_t v) { value = v; @@ -89,6 +75,31 @@ void LvglParamFuncOrValue::clearRef(lua_State *L) ::clearRef(L, function); } +void LvglParamFuncOrString::parse(lua_State *L) +{ + if (lua_isfunction(L, -1)) { + function = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + txt = luaL_checkstring(L, -1); + } +} + +bool LvglParamFuncOrString::changedText(const char* s) +{ + txt = s; + uint32_t h = hash(chars(), txt.size()); + if (h != txtHash) { + txtHash = h; + return true; + } + return false; +} + +void LvglParamFuncOrString::clearRef(lua_State *L) +{ + ::clearRef(L, function); +} + //----------------------------------------------------------------------------- bool LvglGetSetParams::parseGetSetParam(lua_State *L, const char *key) @@ -113,7 +124,7 @@ void LvglGetSetParams::clearGetSetRefs(lua_State *L) bool LvglTextParams::parseTextParam(lua_State *L, const char *key) { if (!strcmp(key, "text")) { - txt.parse(L, LvglParamFuncOrValue::PTXT); + txt.parse(L); return true; } if (!strcmp(key, "font")) { @@ -189,6 +200,27 @@ bool LvglValuesParam::parseValuesParam(lua_State *L, const char *key) return false; } +bool LvglScrollableParams::parseScrollableParam(lua_State *L, const char *key) +{ + if (!strcmp(key, "scrollBar")) { + showScrollBar = lua_toboolean(L, -1); + return true; + } + if (!strcmp(key, "scrollDir")) { + scrollDir = (lv_dir_t)luaL_checkunsigned(L, -1); + return true; + } + if (!strcmp(key, "scrollTo")) { + scrollToFunction = luaL_ref(L, LUA_REGISTRYINDEX); + return true; + } + if (!strcmp(key, "scrolled")) { + scrolledFunction = luaL_ref(L, LUA_REGISTRYINDEX); + return true; + } + return false; +} + //----------------------------------------------------------------------------- static int pcallcont (lua_State *L, int status, lua_KContext extra) { @@ -214,6 +246,17 @@ static bool pcallFuncWithInt(lua_State *L, int funcRef, int nretval, int val) return false; } +static bool pcallFuncWith2Int(lua_State *L, int funcRef, int nretval, int val1, int val2) +{ + if (funcRef != LUA_REFNIL) { + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + lua_pushinteger(L, val1); + lua_pushinteger(L, val2); + return lua_pcallk(L, 2, 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) { @@ -696,7 +739,7 @@ void LvglWidgetLabel::clearRefs(lua_State *L) void LvglWidgetLabel::setText(const char *s) { if (lvobj && txt.changedText(s)) - lv_label_set_text(lvobj, txt.txt); + lv_label_set_text(lvobj, txt.chars()); } void LvglWidgetLabel::setColor(LcdFlags newColor) @@ -731,7 +774,7 @@ void LvglWidgetLabel::build(lua_State *L) lvobj = lv_label_create(lvglManager->getCurrentParent()->getLvObj()); setPos(x, y); setSize(w, h); - setText(txt.txt); + setText(txt.chars()); setColor(color.flags); setOpacity(opacity.value); setFont(font.flags); @@ -869,7 +912,7 @@ uint32_t LvglWidgetLine::getPts(lua_State* L) } for (size_t i = 0; i < ptCnt; i += 1) getPt(L, i); - return hash(pts, sizeof(ptCnt * sizeof(lv_point_t))); + return hash(pts, ptCnt * sizeof(lv_point_t)); } else { ptCnt = 0; return -1; @@ -1332,11 +1375,8 @@ void LvglWidgetObject::clearRefs(lua_State *L) void LvglWidgetBox::parseParam(lua_State *L, const char *key) { - if (!strcmp(key, "scrollDir")) { - scrollDir = (lv_dir_t)luaL_checkunsigned(L, -1); - } else { - LvglWidgetObject::parseParam(L, key); - } + if (parseScrollableParam(L, key)) return; + LvglWidgetObject::parseParam(L, key); } coord_t LvglWidgetBox::getScrollX() @@ -1349,16 +1389,34 @@ coord_t LvglWidgetBox::getScrollY() return lv_obj_get_scroll_y(window->getLvObj()); } +bool LvglWidgetBox::callRefs(lua_State *L) +{ + if (!pcallUpdate2Int(L, scrollToFunction, + [=](int x, int y) { if (x != getScrollX() || y != getScrollY()) lv_obj_scroll_to(window->getLvObj(), x, y, LV_ANIM_OFF); })) { + return false; + } + return LvglWidgetObject::callRefs(L); +} + +void LvglWidgetBox::clearRefs(lua_State *L) +{ + clearRef(L, scrollToFunction); + LvglWidgetObject::clearRefs(L); +} + void LvglWidgetBox::build(lua_State *L) { window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); + window->disableForcedScroll(); + window->setScrollHandler([=](coord_t x, coord_t y) { pcallFuncWith2Int(L, scrolledFunction, 0, x, y); }); lv_obj_add_flag(window->getLvObj(), LV_OBJ_FLAG_EVENT_BUBBLE); if (luaScriptManager->isWidget()) { lv_obj_clear_flag(window->getLvObj(), LV_OBJ_FLAG_CLICKABLE); } else { lv_obj_set_scroll_dir(window->getLvObj(), scrollDir); - etx_scrollbar(window->getLvObj()); + if (showScrollBar) + etx_scrollbar(window->getLvObj()); } if (setFlex()) lv_obj_set_flex_align(window->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); @@ -1637,7 +1695,7 @@ void LvglWidgetArc::build(lua_State *L) void LvglWidgetImage::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "file")) { - filename.parse(L, LvglParamFuncOrValue::PTXT); + filename.parse(L); } else if (!strcmp(key, "fill")) { fillFrame = lua_toboolean(L, -1); } else { @@ -1652,7 +1710,7 @@ bool LvglWidgetImage::callRefs(lua_State *L) if (pcallFunc(L, filename.function, 1)) { const char *s = luaL_checkstring(L, -1); if (filename.changedText(s)) { - ((StaticImage*)window)->setSource(filename.txt); + ((StaticImage*)window)->setSource(filename.chars()); } lua_settop(L, t); } else { @@ -1671,7 +1729,7 @@ void LvglWidgetImage::clearRefs(lua_State *L) void LvglWidgetImage::build(lua_State *L) { window = new StaticImage(lvglManager->getCurrentParent(), {x, y, w, h}, - filename.txt, fillFrame); + filename.chars(), fillFrame); } //----------------------------------------------------------------------------- @@ -1805,7 +1863,7 @@ void LvglWidgetTextButton::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; auto btn = - new TextButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt.txt, [=]() { + new TextButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt.chars(), [=]() { return pcallGetOptIntVal(L, pressFunction, 0); }); if (longPressFunction != LUA_REFNIL) { @@ -1842,7 +1900,7 @@ void LvglWidgetMomentaryButton::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; window = - new MomentaryButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt.txt, + new MomentaryButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt.chars(), [=]() { pcallSimpleFunc(L, pressFunction); }, @@ -2028,7 +2086,8 @@ class WidgetPage : public NavWindow, public LuaEventHandler { public: WidgetPage(Window *parent, std::function backAction, - std::string title, std::string subtitle, std::string iconFile, lv_dir_t scrollDir) : + std::string title, std::string subtitle, std::string iconFile, + lv_dir_t scrollDir, bool showScrollBar) : NavWindow(parent, {0, 0, LCD_W, LCD_H}), backAction(std::move(backAction)) { if (iconFile.empty()) @@ -2047,7 +2106,8 @@ class WidgetPage : public NavWindow, public LuaEventHandler lv_obj_set_style_max_height(body->getLvObj(), LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT, LV_PART_MAIN); lv_obj_set_scroll_dir(body->getLvObj(), scrollDir); - etx_scrollbar(body->getLvObj()); + if (showScrollBar) + etx_scrollbar(body->getLvObj()); #if defined(HARDWARE_TOUCH) addBackButton(); @@ -2079,14 +2139,13 @@ class WidgetPage : public NavWindow, public LuaEventHandler void LvglWidgetPage::parseParam(lua_State *L, const char *key) { if (parseTitleParam(L, key)) return; + if (parseScrollableParam(L, key)) return; if (!strcmp(key, "back")) { backActionFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "subtitle")) { subtitle = luaL_checkstring(L, -1); } else if (!strcmp(key, "icon")) { iconFile = luaL_checkstring(L, -1); - } else if (!strcmp(key, "scrollDir")) { - scrollDir = (lv_dir_t)luaL_checkunsigned(L, -1); } else { LvglWidgetObject::parseParam(L, key); } @@ -2102,6 +2161,15 @@ coord_t LvglWidgetPage::getScrollY() return lv_obj_get_scroll_y(window->getLvObj()); } +bool LvglWidgetPage::callRefs(lua_State *L) +{ + if (!pcallUpdate2Int(L, scrollToFunction, + [=](int x, int y) { if (x != getScrollX() || y != getScrollY()) lv_obj_scroll_to(window->getLvObj(), x, y, LV_ANIM_OFF); })) { + return false; + } + return LvglWidgetObject::callRefs(L); +} + void LvglWidgetPage::clearRefs(lua_State *L) { clearRef(L, backActionFunction); @@ -2112,9 +2180,11 @@ void LvglWidgetPage::build(lua_State *L) { auto page = new WidgetPage( lvglManager->getCurrentParent(), - [=]() { pcallSimpleFunc(L, backActionFunction); }, title, subtitle, iconFile, scrollDir); + [=]() { pcallSimpleFunc(L, backActionFunction); }, title, subtitle, iconFile, scrollDir, showScrollBar); window = page->getBody(); + window->disableForcedScroll(); + window->setScrollHandler([=](coord_t x, coord_t y) { pcallFuncWith2Int(L, scrolledFunction, 0, x, y); }); if (setFlex()) lv_obj_set_flex_align(window->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_SPACE_AROUND); } @@ -2221,7 +2291,7 @@ void LvglWidgetMessageDialog::parseParam(lua_State *L, const char *key) void LvglWidgetMessageDialog::build(lua_State *L) { - window = new MessageDialog(title.c_str(), message.c_str(), details); + window = new MessageDialog(title.c_str(), message.c_str(), details.c_str()); } //----------------------------------------------------------------------------- @@ -2247,6 +2317,8 @@ void LvglWidgetChoice::parseParam(lua_State *L, const char *key) if (parseValuesParam(L, key)) return; if (!strcmp(key, "filter")) { filterFunction = luaL_ref(L, LUA_REGISTRYINDEX); + } else if (!strcmp(key, "popupWidth")) { + popupWidth = luaL_checkinteger(L, -1); } else { LvglWidgetPicker::parseParam(L, key); } @@ -2266,6 +2338,8 @@ void LvglWidgetChoice::build(lua_State *L) [=]() { return pcallGetIntVal(L, getFunction) - 1; }, [=](int val) { pcallSetIntVal(L, setFunction, val + 1); }, title.c_str()); + c->setPopupWidth(popupWidth); + if (filterFunction != LUA_REFNIL) c->setAvailableHandler([=](int n) { bool rv = true; @@ -2451,7 +2525,7 @@ void LvglWidgetFilePicker::build(lua_State *L) if (h == LV_SIZE_CONTENT) h = 0; auto c = new FileChoice( lvglManager->getCurrentParent(), {x, y, w, h}, - folder, extension, maxLen, + folder.c_str(), extension.c_str(), maxLen, [=]() { return pcallGetStringVal(L, getFunction); }, [=](std::string val) { pcallSetStringVal(L, setFunction, val.c_str()); }, hideExtension, title.c_str()); diff --git a/radio/src/lua/lua_lvgl_widget.h b/radio/src/lua/lua_lvgl_widget.h index 2d35eedeb..5f410a743 100644 --- a/radio/src/lua/lua_lvgl_widget.h +++ b/radio/src/lua/lua_lvgl_widget.h @@ -32,30 +32,39 @@ class LvglDialog; struct LvglParamFuncOrValue { public: - enum ParamType { - PVALUE, - PTXT, - }; - int function; union { LcdFlags flags; coord_t coord; - const char* txt; uint32_t value; }; uint32_t currVal = -1; - void parse(lua_State *L, ParamType typ = PVALUE); + void parse(lua_State *L); void forceUpdate() { currVal = -1; } bool changedColor(LcdFlags newFlags); bool changedFont(LcdFlags newFlags) { return changedColor(newFlags);} - bool changedText(const char* s); bool changedValue(uint32_t v); void clearRef(lua_State *L); }; +struct LvglParamFuncOrString +{ + public: + int function; + std::string txt; + uint32_t txtHash = -1; + + void parse(lua_State *L); + void forceUpdate() { txtHash = -1; } + bool changedText(const char* s); + const char* chars() const { return txt.c_str(); } + void clearRef(lua_State *L); + + private: +}; + //----------------------------------------------------------------------------- class LvglGetSetParams @@ -77,7 +86,7 @@ class LvglTextParams LvglTextParams() {} protected: - LvglParamFuncOrValue txt = { .function = LUA_REFNIL, .txt = ""}; + LvglParamFuncOrString txt = { .function = LUA_REFNIL, .txt = ""}; LvglParamFuncOrValue font = { .function = LUA_REFNIL, .flags = FONT(STD)}; bool parseTextParam(lua_State *L, const char *key); @@ -151,6 +160,20 @@ class LvglValuesParam bool parseValuesParam(lua_State *L, const char *key); }; +class LvglScrollableParams +{ + public: + LvglScrollableParams() {} + + protected: + bool showScrollBar = true; + lv_dir_t scrollDir = LV_DIR_ALL; + int scrollToFunction = LUA_REFNIL; + int scrolledFunction = LUA_REFNIL; + + bool parseScrollableParam(lua_State *L, const char *key); +}; + //----------------------------------------------------------------------------- class LvglWidgetObjectBase @@ -265,7 +288,7 @@ class LvglWidgetLabel : public LvglSimpleWidgetObject, public LvglTextParams void parseParam(lua_State *L, const char *key) override; void refresh() override { - setText(txt.txt); + setText(txt.chars()); setFont(font.flags); setAlign(align.flags); LvglSimpleWidgetObject::refresh(); @@ -414,7 +437,7 @@ class LvglWidgetObject : public LvglWidgetObjectBase //----------------------------------------------------------------------------- -class LvglWidgetBox : public LvglWidgetObject +class LvglWidgetBox : public LvglWidgetObject, public LvglScrollableParams { public: LvglWidgetBox() : LvglWidgetObject() {} @@ -422,9 +445,10 @@ class LvglWidgetBox : public LvglWidgetObject coord_t getScrollX() override; coord_t getScrollY() override; - protected: - lv_dir_t scrollDir = LV_DIR_ALL; + bool callRefs(lua_State *L) override; + void clearRefs(lua_State *L) override; + protected: void build(lua_State *L) override; void parseParam(lua_State *L, const char *key) override; }; @@ -562,7 +586,7 @@ class LvglWidgetImage : public LvglWidgetObject void clearRefs(lua_State *L) override; protected: - LvglParamFuncOrValue filename = { .function = LUA_REFNIL, .txt = ""}; + LvglParamFuncOrString filename = { .function = LUA_REFNIL, .txt = ""}; bool fillFrame = false; void build(lua_State *L) override; @@ -612,7 +636,7 @@ class LvglWidgetTextButtonBase : public LvglWidgetObject, public LvglTextParams void parseParam(lua_State *L, const char *key) override; void refresh() override { - setText(txt.txt); + setText(txt.chars()); setFont(font.flags); setTextColor(textColor.flags); LvglWidgetObject::refresh(); @@ -751,11 +775,12 @@ class LvglWidgetVerticalSlider : public LvglWidgetSliderBase //----------------------------------------------------------------------------- -class LvglWidgetPage : public LvglWidgetObject, public LvglTitleParam +class LvglWidgetPage : public LvglWidgetObject, public LvglTitleParam, public LvglScrollableParams { public: LvglWidgetPage() : LvglWidgetObject() {} + bool callRefs(lua_State *L) override; void clearRefs(lua_State *L) override; coord_t getScrollX() override; @@ -764,7 +789,6 @@ class LvglWidgetPage : public LvglWidgetObject, public LvglTitleParam protected: std::string subtitle; std::string iconFile; - lv_dir_t scrollDir = LV_DIR_ALL; int backActionFunction = LUA_REFNIL; @@ -816,7 +840,7 @@ class LvglWidgetMessageDialog : public LvglWidgetObject, public LvglTitleParam, LvglWidgetMessageDialog() : LvglWidgetObject(LVGL_SIMPLEMETATABLE) {} protected: - const char *details = nullptr; + std::string details; void build(lua_State *L) override; void parseParam(lua_State *L, const char *key) override; @@ -846,6 +870,7 @@ class LvglWidgetChoice : public LvglWidgetPicker, public LvglTitleParam, public protected: int filterFunction = LUA_REFNIL; + coord_t popupWidth = 0; void build(lua_State *L) override; void parseParam(lua_State *L, const char *key) override; @@ -943,8 +968,8 @@ class LvglWidgetFilePicker : public LvglWidgetPicker, public LvglTitleParam LvglWidgetFilePicker() : LvglWidgetPicker() {} protected: - const char* folder = nullptr; - const char* extension = nullptr; + std::string folder; + std::string extension; int maxLen = 255; bool hideExtension = false; diff --git a/radio/src/lua/lua_widget_factory.cpp b/radio/src/lua/lua_widget_factory.cpp index 50d7f4dcc..e4c65ccea 100644 --- a/radio/src/lua/lua_widget_factory.cpp +++ b/radio/src/lua/lua_widget_factory.cpp @@ -49,9 +49,8 @@ LuaWidgetFactory::LuaWidgetFactory(const char* name, ZoneOption* widgetOptions, LuaWidgetFactory::~LuaWidgetFactory() { unregisterWidget(this); - if (displayName) { - delete displayName; - } + if (name) delete name; + if (displayName) delete displayName; auto option = getDefaultOptions(); while (option && option->name != nullptr) { @@ -277,6 +276,7 @@ ZoneOption* LuaWidgetFactory::parseOptionDefinitions(int reference) for (lua_pushnil(lsWidgets); lua_next(lsWidgets, -2); lua_pop(lsWidgets, 1)) { count++; } + lua_pop(lsWidgets, 1); // TRACE("we have %d options", count); if (count > MAX_WIDGET_OPTIONS) { @@ -337,6 +337,7 @@ ZoneOption* LuaWidgetFactory::parseOptionDefinitions(int reference) } option++; } + lua_pop(lsWidgets, 1); option->name = NULL; // sentinel } else diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 6ecac3a88..9cb330210 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -146,7 +146,7 @@ void luaLoadWidgetCallback(const char* filename) if (name && createFunction) { ZoneOption * options = LuaWidgetFactory::parseOptionDefinitions(optionDefinitionsReference); if (options) { - new LuaWidgetFactory(name, options, optionDefinitionsReference, + new LuaWidgetFactory(strdup(name), options, optionDefinitionsReference, createFunction, updateFunction, refreshFunction, backgroundFunction, translateFunction, lvglLayout, filename); TRACE("Loaded Lua widget %s", name); @@ -167,6 +167,7 @@ static void luaLoadFile(const char * filename, std::function callback) if (luaLoadScriptFileToState(lsWidgets, filename, LUA_SCRIPT_LOAD_MODE) == SCRIPT_OK) { if (lua_pcall(lsWidgets, 0, 1, 0) == LUA_OK && lua_istable(lsWidgets, -1)) { callback(); + lua_pop(lsWidgets, 1); } else { TRACE("luaLoadFile(%s): Error parsing script: %s", filename, lua_tostring(lsWidgets, -1));