/* * Copyright (C) EdgeTX * * Based on code named * opentx - https://github.com/opentx/opentx * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "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" //----------------------------------------------------------------------------- static int pcallcont (lua_State *L, int status, lua_KContext extra) { 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); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { int top = lua_gettop(L); parseParam(L, lua_tostring(L, -2)); // Replace value on Lua stack if consumed by parseParam if (top != lua_gettop(L)) lua_pushnil(L); } } void LvglWidgetObjectBase::pcallSimpleFunc(lua_State *L, int funcRef) { if (funcRef != LUA_REFNIL) { PROTECT_LUA() { auto save = luaLvglManager; luaLvglManager = lvglManager; if (!pcallFunc(L, funcRef, 0)) { lvglManager->luaShowError(); } luaLvglManager = save; } UNPROTECT_LUA(); } } bool LvglWidgetObjectBase::pcallUpdateBool(lua_State *L, int getFuncRef, std::function update) { bool res = true; if (getFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 1)) { bool val = false; if (lua_isboolean(L, -1)) val = lua_toboolean(L, -1); else val = luaL_optinteger(L, -1, 0) != 0; update(val); lua_settop(L, t); } else { res = false; } lvglManager = save; } return res; } bool LvglWidgetObjectBase::pcallUpdate1Int(lua_State *L, int getFuncRef, std::function update) { bool res = true; if (getFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 1)) { int val = luaL_checkunsigned(L, -1); update(val); lua_settop(L, t); } else { res = false; } lvglManager = save; } return res; } bool LvglWidgetObjectBase::pcallUpdate2Int(lua_State *L, int getFuncRef, std::function update) { bool res = true; if (getFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); if (pcallFunc(L, getFuncRef, 2)) { int v1 = luaL_checkunsigned(L, -2); int v2 = luaL_checkunsigned(L, -1); update(v1, v2); lua_settop(L, t); } else { res = false; } lvglManager = save; } return res; } int LvglWidgetObjectBase::pcallGetIntVal(lua_State *L, int getFuncRef) { int val = 0; if (getFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); PROTECT_LUA() { if (pcallFunc(L, getFuncRef, 1)) { if (lua_isboolean(L, -1)) val = lua_toboolean(L, -1); else val = luaL_checkinteger(L, -1); } else { lvglManager->luaShowError(); } } else { lvglManager->luaShowError(); } UNPROTECT_LUA(); lua_settop(L, t); lvglManager = save; } return val; } int LvglWidgetObjectBase::pcallGetOptIntVal(lua_State *L, int getFuncRef, int defVal) { int val = 0; if (getFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); PROTECT_LUA() { if (pcallFunc(L, getFuncRef, 1)) { if (lua_isboolean(L, -1)) val = lua_toboolean(L, -1); else val = luaL_optinteger(L, -1, defVal); } else { lvglManager->luaShowError(); } } else { lvglManager->luaShowError(); } UNPROTECT_LUA(); lua_settop(L, t); lvglManager = save; } return val; } void LvglWidgetObjectBase::pcallSetIntVal(lua_State *L, int setFuncRef, int val) { if (setFuncRef != LUA_REFNIL) { auto save = luaLvglManager; luaLvglManager = lvglManager; int t = lua_gettop(L); PROTECT_LUA() { if (!pcallFuncWithInt(L, setFuncRef, 0, val)) { lvglManager->luaShowError(); } } else { lvglManager->luaShowError(); } UNPROTECT_LUA(); lua_settop(L, t); lvglManager = save; } } void LvglWidgetObjectBase::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "x")) { x = luaL_checkinteger(L, -1); } else if (!strcmp(key, "y")) { y = luaL_checkinteger(L, -1); } else if (!strcmp(key, "w")) { w = luaL_checkinteger(L, -1); if (w == 0) w = LV_SIZE_CONTENT; } else if (!strcmp(key, "h")) { h = luaL_checkinteger(L, -1); if (h == 0) h = LV_SIZE_CONTENT; } else if (!strcmp(key, "color")) { if (lua_isfunction(L, -1)) { getColorFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { color = luaL_checkunsigned(L, -1); } } else if (!strcmp(key, "opacity")) { if (lua_isfunction(L, -1)) { getOpacityFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { opacity = luaL_checkunsigned(L, -1); } } else if (!strcmp(key, "visible")) { getVisibleFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "size")) { getSizeFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "pos")) { getPosFunction = luaL_ref(L, LUA_REGISTRYINDEX); } } bool LvglWidgetObjectBase::callRefs(lua_State *L) { if (clearRequest) { clearRequest = false; if (getWindow()) { getWindow()->clear(); clearChildRefs(L); } return true; } if (!pcallUpdate1Int(L, getColorFunction, [=](int color) { setColor(color); })) return false; if (!pcallUpdate1Int(L, getOpacityFunction, [=](int opa) { setOpacity(opa); })) 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; 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 LvglWidgetObjectBase::saveLvglObjectRef(int ref) { lvglObjectRefs.push_back(ref); } 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, getOpacityFunction); 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); setOpacity(opacity); } void LvglWidgetObjectBase::update(lua_State *L) { getParams(L, 2); 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 if (!strcmp(key, "align")) { if (lua_isfunction(L, -1)) getAlignFunction = luaL_ref(L, LUA_REGISTRYINDEX); else align = 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; if (!pcallUpdate1Int(L, getAlignFunction, [=](int val) { setAlign(val); })) return false; return LvglSimpleWidgetObject::callRefs(L); } void LvglWidgetLabel::clearRefs(lua_State *L) { clearRef(L, getTextFunction); clearRef(L, getFontFunction); clearRef(L, getAlignFunction); 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; etx_font(lvobj, FONT_INDEX(font)); } } void LvglWidgetLabel::setAlign(LcdFlags align) { if (lvobj) { this->align = align; if (align & VCENTERED) { lv_obj_align(lvobj, LV_ALIGN_LEFT_MID, 0, 0); } lv_obj_set_style_text_align(lvobj, (align & RIGHT) ? LV_TEXT_ALIGN_RIGHT : (align & CENTERED) ? LV_TEXT_ALIGN_CENTER : LV_TEXT_ALIGN_LEFT, 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); setOpacity(opacity); setFont(font); setAlign(align); callRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetLineBase::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "rounded")) { rounded = lua_toboolean(L, -1); } else if (!strcmp(key, "dashGap")) { dashGap = luaL_checkinteger(L, -1); if (dashGap < 0) dashGap = 0; } else if (!strcmp(key, "dashWidth")) { dashWidth = luaL_checkinteger(L, -1); if (dashWidth < 0) dashWidth = 0; } 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::setOpacity(uint8_t newOpa) { opacity = newOpa; lv_obj_set_style_line_opa(lvobj, opacity, LV_PART_MAIN); } 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()); refresh(); } void LvglWidgetLineBase::refresh() { setColor(color); setOpacity(opacity); setLine(); lv_obj_set_style_line_rounded(lvobj, rounded, LV_PART_MAIN); if (dashGap > 0 && dashWidth > 0) { lv_obj_set_style_line_dash_gap(lvobj, dashGap, LV_PART_MAIN); lv_obj_set_style_line_dash_width(lvobj, dashWidth, 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::setOpacity(uint8_t newOpa) { opacity = newOpa; lv_obj_set_style_line_opa(lvobj, opacity, LV_PART_MAIN); } 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()); refresh(); callRefs(L); } void LvglWidgetLine::refresh() { setColor(color); setOpacity(opacity); 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], opacity, 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 if (!strcmp(key, "active")) { getActiveFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetObjectBase::parseParam(L, key); } } void LvglWidgetObject::setPos(coord_t x, coord_t y) { this->x = x; this->y = y; if (window) window->setPos(x, y); } void LvglWidgetObject::setSize(coord_t w, coord_t h) { this->w = w; this->h = 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; } } bool LvglWidgetObject::callRefs(lua_State *L) { if (!pcallUpdateBool(L, getActiveFunction, [=](bool active) { if (active) enable(); else disable(); })) return false; return LvglWidgetObjectBase::callRefs(L); } void LvglWidgetObject::clearRefs(lua_State *L) { clearRef(L, getActiveFunction); LvglWidgetObjectBase::clearRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetBox::build(lua_State* L) { window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); lv_obj_add_flag(window->getLvObj(), LV_OBJ_FLAG_EVENT_BUBBLE); if (luaLvglManager->isWidget()) { lv_obj_clear_flag(window->getLvObj(), LV_OBJ_FLAG_CLICKABLE); } else { 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); } //----------------------------------------------------------------------------- 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) { if (!strcmp(key, "thickness")) { thickness = luaL_checkunsigned(L, -1); } else if (!strcmp(key, "filled")) { filled = lua_toboolean(L, -1); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetBorderedObject::setColor(LcdFlags newColor) { 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(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)); } } } } void LvglWidgetBorderedObject::setOpacity(uint8_t newOpa) { opacity = newOpa; if (filled) { lv_obj_set_style_bg_opa(window->getLvObj(), opacity, LV_PART_MAIN); } else { lv_obj_set_style_border_opa(window->getLvObj(), opacity, LV_PART_MAIN); lv_obj_set_style_border_width(window->getLvObj(), thickness, LV_PART_MAIN); } } void LvglWidgetBorderedObject::build(lua_State *L) { window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_obj_create); lv_obj_add_flag(window->getLvObj(), LV_OBJ_FLAG_EVENT_BUBBLE); if (luaLvglManager->isWidget()) { lv_obj_clear_flag(window->getLvObj(), LV_OBJ_FLAG_CLICKABLE); } else { etx_scrollbar(window->getLvObj()); } setColor(color); setOpacity(opacity); } //----------------------------------------------------------------------------- void LvglWidgetRoundObject::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "radius")) { if (lua_isfunction(L, -1)) getRadiusFunction = luaL_ref(L, LUA_REGISTRYINDEX); else radius = luaL_checkunsigned(L, -1); } else { LvglWidgetBorderedObject::parseParam(L, key); } } bool LvglWidgetRoundObject::callRefs(lua_State *L) { if (!pcallUpdate1Int(L, getRadiusFunction, [=](int val) { setRadius(val); })) return false; return LvglWidgetObject::callRefs(L); } void LvglWidgetRoundObject::clearRefs(lua_State *L) { clearRef(L, getRadiusFunction); LvglWidgetObject::clearRefs(L); } void LvglWidgetRoundObject::setPos(coord_t x, coord_t y) { LvglWidgetObject::setPos(x - radius, y - radius); } void LvglWidgetRoundObject::setRadius(coord_t r) { // Set position to center x += radius; y += radius; radius = r; w = radius * 2; h = radius * 2; setPos(x, y); setSize(w, h); } //----------------------------------------------------------------------------- void LvglWidgetRectangle::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "rounded")) { rounded = luaL_checkunsigned(L, -1); } else { LvglWidgetBorderedObject::parseParam(L, key); } } void LvglWidgetRectangle::build(lua_State *L) { LvglWidgetBorderedObject::build(L); if (rounded) { lv_obj_set_style_radius(window->getLvObj(), (rounded >= thickness) ? rounded : thickness, LV_PART_MAIN); } callRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetCircle::build(lua_State *L) { // Reset position to corner setPos(x, y); // Set width & height setRadius(radius); LvglWidgetRoundObject::build(L); lv_obj_set_style_radius(window->getLvObj(), LV_RADIUS_CIRCLE, LV_PART_MAIN); callRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetArc::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "startAngle")) { if (lua_isfunction(L, -1)) getStartAngleFunction = luaL_ref(L, LUA_REGISTRYINDEX); else startAngle = luaL_checkunsigned(L, -1); } else if (!strcmp(key, "endAngle")) { if (lua_isfunction(L, -1)) getEndAngleFunction = luaL_ref(L, LUA_REGISTRYINDEX); else endAngle = luaL_checkunsigned(L, -1); } else { LvglWidgetRoundObject::parseParam(L, key); } } void LvglWidgetArc::setColor(LcdFlags newColor) { if (colorChanged(newColor)) { if (color & RGB_FLAG) { etx_remove_arc_color(window->getLvObj()); 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); } } } void LvglWidgetArc::setOpacity(uint8_t newOpa) { opacity = newOpa; lv_obj_set_style_arc_opa(window->getLvObj(), opacity, LV_PART_INDICATOR); } void LvglWidgetArc::setStartAngle(coord_t angle) { lv_arc_set_start_angle(window->getLvObj(), angle); } void LvglWidgetArc::setEndAngle(coord_t angle) { lv_arc_set_end_angle(window->getLvObj(), angle); } bool LvglWidgetArc::callRefs(lua_State *L) { if (!pcallUpdate1Int(L, getStartAngleFunction, [=](int val) { setStartAngle(val); })) return false; if (!pcallUpdate1Int(L, getEndAngleFunction, [=](int val) { setEndAngle(val); })) return false; return LvglWidgetRoundObject::callRefs(L); } void LvglWidgetArc::clearRefs(lua_State *L) { clearRef(L, getStartAngleFunction); clearRef(L, getEndAngleFunction); LvglWidgetRoundObject::clearRefs(L); } void LvglWidgetArc::build(lua_State *L) { // Reset position to corner setPos(x, y); // Set width & height setRadius(radius); window = new Window(lvglManager->getCurrentParent(), {x, y, w, h}, lv_arc_create); lv_obj_add_flag(window->getLvObj(), LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_clear_flag(window->getLvObj(), LV_OBJ_FLAG_CLICKABLE); setColor(color); lv_arc_set_bg_angles(window->getLvObj(), 0, 360); lv_arc_set_range(window->getLvObj(), 0, 360); lv_arc_set_angles(window->getLvObj(), 0, 360); setStartAngle(startAngle); setEndAngle(endAngle); lv_obj_remove_style(window->getLvObj(), NULL, LV_PART_KNOB); lv_obj_set_style_arc_opa(window->getLvObj(), LV_OPA_TRANSP, LV_PART_MAIN); lv_obj_set_style_arc_width(window->getLvObj(), thickness, LV_PART_INDICATOR); setOpacity(opacity); callRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetImage::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "file")) { filename = luaL_checkstring(L, -1); } else if (!strcmp(key, "fill")) { fillFrame = lua_toboolean(L, -1); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetImage::build(lua_State *L) { window = new StaticImage(lvglManager->getCurrentParent(), {x, y, w, h}, filename.c_str(), fillFrame); } //----------------------------------------------------------------------------- void LvglWidgetQRCode::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "data")) { data = luaL_checkstring(L, -1); } else if (!strcmp(key, "bgColor")) { bgColor = luaL_checkunsigned(L, -1); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetQRCode::build(lua_State *L) { window = new QRCode(lvglManager->getCurrentParent(), x, y, w, data, colorToRGB(color), colorToRGB(bgColor)); } //----------------------------------------------------------------------------- void LvglWidgetTextButtonBase::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "text")) { txt = luaL_checkstring(L, -1); } else if (!strcmp(key, "font")) { font = luaL_checkunsigned(L, -1); } else if (!strcmp(key, "cornerRadius")) { rounded = luaL_checkunsigned(L, -1); } else if (!strcmp(key, "textColor")) { textColor = luaL_checkunsigned(L, -1); } else if (!strcmp(key, "press")) { pressFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetTextButtonBase::clearRefs(lua_State *L) { clearRef(L, pressFunction); LvglWidgetObject::clearRefs(L); } void LvglWidgetTextButtonBase::setText(const char *s) { uint32_t h = hash(s, strlen(s)); if (h != textHash) { txt = s; textHash = h; ((TextButton *)window)->setText(s); } } void LvglWidgetTextButtonBase::setSize(coord_t w, coord_t h) { if (w == LV_SIZE_CONTENT || w == 0) w = lv_obj_get_width(window->getLvObj()); if (h == LV_SIZE_CONTENT || h == 0) h = lv_obj_get_height(window->getLvObj()); LvglWidgetObject::setSize(w, h); } void LvglWidgetTextButtonBase::setColor(LcdFlags newColor) { color = newColor; if (color != currentColor) { currentColor = color; etx_bg_color_from_flags(window->getLvObj(), color); } if (textColor != currentTextColor) { currentTextColor = textColor; etx_txt_color_from_flags(window->getLvObj(), textColor); } } void LvglWidgetTextButtonBase::setRounded() { if (rounded >= 0) { lv_obj_remove_style(window->getLvObj(), (lv_style_t*)&styles->rounded, LV_PART_MAIN); if (rounded > 0) lv_obj_set_style_radius(window->getLvObj(), rounded, LV_PART_MAIN); } } //----------------------------------------------------------------------------- void LvglWidgetTextButton::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "checked")) { checked = lua_toboolean(L, -1); } else if (!strcmp(key, "longpress")) { longPressFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetTextButtonBase::parseParam(L, key); } } void LvglWidgetTextButton::clearRefs(lua_State *L) { clearRef(L, longPressFunction); LvglWidgetTextButtonBase::clearRefs(L); } void LvglWidgetTextButton::setFont(LcdFlags font) { if (window) { this->font = font; ((TextButton*)window)->setFont(FONT_INDEX(font)); } } void LvglWidgetTextButton::setChecked(bool checked) { this->checked = checked; ((TextButton*)window)->check(checked); } void LvglWidgetTextButton::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; auto btn = new TextButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt, [=]() { return pcallGetOptIntVal(L, pressFunction, 0); }); if (longPressFunction != LUA_REFNIL) { btn->setLongPressHandler([=]() { return pcallGetOptIntVal(L, longPressFunction, 0); }); } window = btn; setFont(font); setChecked(checked); setColor(color); setRounded(); } //----------------------------------------------------------------------------- void LvglWidgetMomentaryButton::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "release")) { releaseFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetTextButtonBase::parseParam(L, key); } } void LvglWidgetMomentaryButton::setFont(LcdFlags font) { if (window) { this->font = font; ((MomentaryButton*)window)->setFont(FONT_INDEX(font)); } } void LvglWidgetMomentaryButton::clearRefs(lua_State *L) { clearRef(L, releaseFunction); LvglWidgetTextButtonBase::clearRefs(L); } void LvglWidgetMomentaryButton::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; window = new MomentaryButton(lvglManager->getCurrentParent(), {x, y, w, h}, txt, [=]() { pcallSimpleFunc(L, pressFunction); }, [=]() { pcallSimpleFunc(L, releaseFunction); }); setFont(font); setColor(color); setRounded(); } //----------------------------------------------------------------------------- void LvglWidgetToggleSwitch::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "get")) { getStateFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "set")) { setStateFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetToggleSwitch::clearRefs(lua_State *L) { clearRef(L, getStateFunction); clearRef(L, setStateFunction); LvglWidgetObject::clearRefs(L); } void LvglWidgetToggleSwitch::build(lua_State *L) { window = new ToggleSwitch( lvglManager->getCurrentParent(), {x, y, 0, 0}, [=]() { return pcallGetIntVal(L, getStateFunction); }, [=](uint8_t val) { pcallSetIntVal(L, setStateFunction, val); }); } //----------------------------------------------------------------------------- void LvglWidgetTextEdit::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "value")) { txt = luaL_checkstring(L, -1); } else if (!strcmp(key, "length")) { maxLen = luaL_checkinteger(L, -1); if (maxLen > 128) maxLen = 128; if (maxLen <= 0) maxLen = 32; } else if (!strcmp(key, "set")) { setFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetTextEdit::clearRefs(lua_State *L) { clearRef(L, setFunction); LvglWidgetObject::clearRefs(L); } void LvglWidgetTextEdit::build(lua_State *L) { strcpy(value, txt); if (h == LV_SIZE_CONTENT) h = 0; window = new TextEdit(lvglManager->getCurrentParent(), {x, y, w, h}, value, maxLen, [=]() { if (setFunction != LUA_REFNIL) { int t = lua_gettop(L); PROTECT_LUA() { if (!pcallFuncWithString(L, setFunction, 0, value)) { lvglManager->luaShowError(); } } else { lvglManager->luaShowError(); } UNPROTECT_LUA(); lua_settop(L, t); } }); } //----------------------------------------------------------------------------- void LvglWidgetNumberEdit::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "min")) { min = luaL_checkinteger(L, -1); } else if (!strcmp(key, "max")) { max = luaL_checkinteger(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 if (!strcmp(key, "display")) { dispFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetNumberEdit::clearRefs(lua_State *L) { clearRef(L, getFunction); clearRef(L, setFunction); clearRef(L, dispFunction); LvglWidgetObject::clearRefs(L); } void LvglWidgetNumberEdit::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; window = new NumberEdit( lvglManager->getCurrentParent(), {x, y, w, h}, min, max, [=]() { return pcallGetIntVal(L, getFunction); }, [=](int val) { pcallSetIntVal(L, setFunction, val); }); if (dispFunction != LUA_REFNIL) { ((NumberEdit *)window)->setDisplayHandler([=](int val) { const char *s = "???"; int t = lua_gettop(L); PROTECT_LUA() { if (pcallFuncWithInt(L, dispFunction, 1, val)) { s = luaL_checkstring(L, -1); } else { lvglManager->luaShowError(); } } else { lvglManager->luaShowError(); } UNPROTECT_LUA(); lua_settop(L, t); return s; }); } } //----------------------------------------------------------------------------- void LvglWidgetSliderBase::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "get")) { getValueFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "set")) { setValueFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "min")) { vmin = luaL_checkinteger(L, -1); } else if (!strcmp(key, "max")) { vmax = luaL_checkinteger(L, -1); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetSliderBase::setColor(LcdFlags newColor) { color = newColor; if (color != currentColor) { currentColor = color; ((SliderBase*)window)->setColor(color); } } void LvglWidgetSliderBase::clearRefs(lua_State *L) { clearRef(L, getValueFunction); clearRef(L, setValueFunction); LvglWidgetObject::clearRefs(L); } //----------------------------------------------------------------------------- void LvglWidgetSlider::build(lua_State *L) { window = new Slider( lvglManager->getCurrentParent(), w, vmin, vmax, [=]() { return pcallGetIntVal(L, getValueFunction); }, [=](int val) { pcallSetIntVal(L, setValueFunction, val); }); window->setPos(x, y); setColor(color); } //----------------------------------------------------------------------------- void LvglWidgetVerticalSlider::build(lua_State *L) { window = new VerticalSlider( lvglManager->getCurrentParent(), h, vmin, vmax, [=]() { return pcallGetIntVal(L, getValueFunction); }, [=](int val) { pcallSetIntVal(L, setValueFunction, val); }); window->setPos(x, y); setColor(color); } //----------------------------------------------------------------------------- class WidgetPage : public NavWindow, public LuaEventHandler { public: WidgetPage(Window *parent, std::function backAction, std::string title, std::string subtitle, std::string iconFile) : NavWindow(parent, {0, 0, LCD_W, LCD_H}), backAction(std::move(backAction)) { if (iconFile.empty()) header = new PageHeader(this, ICON_EDGETX); else header = new PageHeader(this, iconFile.c_str()); body = new Window( this, {0, EdgeTxStyles::MENU_HEADER_HEIGHT, LCD_W, LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT}); body->setWindowFlag(NO_FOCUS); header->setTitle(title); header->setTitle2(subtitle); etx_solid_bg(lvobj); lv_obj_set_style_max_height(body->getLvObj(), LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT, LV_PART_MAIN); etx_scrollbar(body->getLvObj()); #if defined(HARDWARE_TOUCH) addBackButton(); #endif } Window *getBody() { return body; } protected: std::function backAction; PageHeader *header = nullptr; Window *body = nullptr; bool bubbleEvents() override { return true; } void onClicked() override { Keyboard::hide(false); LuaEventHandler::onClicked(); } void onCancel() override { backAction(); } void onEvent(event_t evt) override { LuaEventHandler::onLuaEvent(evt); parent->onEvent(evt); } protected: }; void LvglWidgetPage::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "back")) { backActionFunction = luaL_ref(L, LUA_REGISTRYINDEX); } else if (!strcmp(key, "title")) { title = luaL_checkstring(L, -1); } else if (!strcmp(key, "subtitle")) { subtitle = luaL_checkstring(L, -1); } else if (!strcmp(key, "icon")) { iconFile = luaL_checkstring(L, -1); } else { LvglWidgetObject::parseParam(L, key); } } void LvglWidgetPage::clearRefs(lua_State *L) { clearRef(L, backActionFunction); LvglWidgetObject::clearRefs(L); } 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 (w == LV_SIZE_CONTENT) w = 0; 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, "filter")) { filter = luaL_checkunsigned(L, -1); } else { LvglWidgetPicker::parseParam(L, key); } } void LvglWidgetSwitchPicker::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; auto c = new SwitchChoice( lvglManager->getCurrentParent(), {x, y, w, h}, SWSRC_FIRST, SWSRC_LAST, [=]() { return pcallGetIntVal(L, getFunction); }, [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }); c->setAvailableHandler([=](int value) { return checkSwitchAvailable(value, filter); }); window = c; } //----------------------------------------------------------------------------- void LvglWidgetSourcePicker::parseParam(lua_State *L, const char *key) { if (!strcmp(key, "filter")) { filter = luaL_checkunsigned(L, -1); } else { LvglWidgetPicker::parseParam(L, key); } } void LvglWidgetSourcePicker::build(lua_State *L) { if (h == LV_SIZE_CONTENT) h = 0; auto c = new SourceChoice( lvglManager->getCurrentParent(), {x, y, w, h}, 0, MIXSRC_LAST_TELEM, [=]() { return pcallGetIntVal(L, getFunction); }, [=](uint32_t val) { pcallSetIntVal(L, setFunction, val); }, filter & SRC_INVERT); c->setAvailableHandler([=](int value) { return checkSourceAvailable(value, filter); }); window = c; } //-----------------------------------------------------------------------------