feat(lua): support for using LVGL objects on colorlcd (#4887)

This commit is contained in:
philmoz
2024-06-13 13:16:07 +10:00
committed by GitHub
parent 22128df1cd
commit b4e33fc439
30 changed files with 2257 additions and 123 deletions
+1
View File
@@ -134,6 +134,7 @@ if(LUA)
lua/lua_widget.cpp
lua/lua_widget_factory.cpp
lua/widgets.cpp
lua/lua_lvgl_widget.cpp
)
endif()
@@ -68,9 +68,6 @@ void Layout::adjustLayout()
// Save settings
decorationSettings = checkSettings;
// Set visible decoration
show();
}
void Layout::show(bool visible)
+1 -2
View File
@@ -142,7 +142,7 @@ class ModelButton : public Button
if (modelLayouts[layout].hasImage) {
if (modelCell->modelBitmap[0]) {
GET_FILENAME(filename, BITMAPS_PATH, modelCell->modelBitmap, "");
auto bitmap = new StaticImage(this, {PAD_TINY, PAD_TINY, w, h}, filename, true);
auto bitmap = new StaticImage(this, {PAD_TINY, PAD_TINY, w, h}, filename, false, true);
lv_obj_move_background(bitmap->getLvObj());
bitmap->show(bitmap->hasImage());
if (bitmap->hasImage()) {
@@ -626,7 +626,6 @@ void ModelLabelsWindow::newModel()
snprintf(path + len, LEN_BUFFER - len, "/%s%s", name.c_str(), SCRIPT_EXT);
if (f_stat(path, 0) == FR_OK) {
luaExec(path);
StandaloneLuaWindow::instance()->attach();
}
#endif
}
@@ -417,7 +417,6 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name,
else if (isExtensionMatching(ext, SCRIPTS_EXT)) {
menu->addLine(STR_EXECUTE_FILE, [=]() {
luaExec(fullpath);
StandaloneLuaWindow::instance()->attach();
});
}
#endif
-2
View File
@@ -102,8 +102,6 @@ static void run_lua_tool(Window* parent, const std::string& path)
f_chdir(toolPath);
luaExec(path.c_str());
auto lua_win = StandaloneLuaWindow::instance();
lua_win->attach();
}
// LUA scripts in TOOLS
+97 -20
View File
@@ -85,8 +85,9 @@ void StandaloneLuaWindow::redraw_cb(lv_event_t* e)
// singleton instance
StandaloneLuaWindow* StandaloneLuaWindow::_instance;
StandaloneLuaWindow::StandaloneLuaWindow() :
Window(nullptr, {0, 0, LCD_W, LCD_H}),
StandaloneLuaWindow::StandaloneLuaWindow(bool useLvgl) :
Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}),
useLvgl(useLvgl),
lcdBuffer(BMP_RGB565, LCD_W, LCD_H),
popup({50, 73, LCD_W - 100, LCD_H - 146})
{
@@ -94,24 +95,42 @@ StandaloneLuaWindow::StandaloneLuaWindow() :
etx_solid_bg(lvobj);
lcdBuffer.clear();
lcdBuffer.drawText(LCD_W / 2, LCD_H / 2 - 20, STR_LOADING,
FONT(L) | COLOR_THEME_PRIMARY2 | CENTERED);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
if (useLvglLayout()) {
padAll(PAD_ZERO);
etx_scrollbar(lvobj);
lv_obj_t* lbl = lv_label_create(lvobj);
lv_obj_set_pos(lbl, 0, 0);
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);
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);
luaLvglManager = this;
} else {
lcdBuffer.clear();
lcdBuffer.drawText(LCD_W / 2, LCD_H / 2 - 20, STR_LOADING,
FONT(L) | COLOR_THEME_PRIMARY2 | CENTERED);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_event_cb(lvobj, StandaloneLuaWindow::redraw_cb, LV_EVENT_DRAW_MAIN, nullptr);
}
// setup LUA event handler
setupHandler(this);
lv_obj_add_event_cb(lvobj, StandaloneLuaWindow::redraw_cb, LV_EVENT_DRAW_MAIN, nullptr);
attach();
}
void StandaloneLuaWindow::setup(bool useLvgl)
{
_instance = new StandaloneLuaWindow(useLvgl);
}
StandaloneLuaWindow* StandaloneLuaWindow::instance()
{
if (!_instance) {
_instance = new StandaloneLuaWindow();
}
return _instance;
}
@@ -121,13 +140,13 @@ void StandaloneLuaWindow::attach()
// backup previous screen
prevScreen = lv_scr_act();
// and load new one
lv_scr_load(lvobj);
Layer::back()->hide();
Layer::push(this);
lv_group_add_obj(lv_group_get_default(), lvobj);
lv_group_set_editing(lv_group_get_default(), true);
if (!useLvglLayout()) {
lv_group_add_obj(lv_group_get_default(), lvobj);
lv_group_set_editing(lv_group_get_default(), true);
}
}
}
@@ -135,10 +154,12 @@ void StandaloneLuaWindow::deleteLater(bool detach, bool trash)
{
if (_deleted) return;
luaLvglManager = nullptr;
Layer::pop(this);
Layer::back()->show();
if (prevScreen) {
lv_scr_load(prevScreen);
prevScreen = nullptr;
}
@@ -159,8 +180,20 @@ void StandaloneLuaWindow::checkEvents()
if (luaState != INTERPRETER_RELOAD_PERMANENT_SCRIPTS) {
// if LUA finished a full cycle,
// invalidate to display the screen buffer
bool useLvgl = useLvglLayout();
if (luaTask(true)) {
invalidate();
if (useLvgl && !hasError) {
PROTECT_LUA() {
if (!callRefs(lsScripts)) {
luaShowError();
}
} else {
luaShowError();
}
UNPROTECT_LUA();
} else {
invalidate();
}
}
}
@@ -174,7 +207,7 @@ void StandaloneLuaWindow::checkEvents()
luaLcdBuffer = nullptr;
}
void StandaloneLuaWindow::onClicked() { LuaEventHandler::onClicked(); }
void StandaloneLuaWindow::onClicked() { Keyboard::hide(false); LuaEventHandler::onClicked(); }
void StandaloneLuaWindow::onCancel() { LuaEventHandler::onCancel(); }
@@ -210,3 +243,47 @@ bool StandaloneLuaWindow::displayPopup(event_t event, uint8_t type,
return false;
}
void StandaloneLuaWindow::clear()
{
clearRefs(lsScripts);
Window::clear();
}
void StandaloneLuaWindow::luaShowError()
{
hasError = true;
extern void luaError(lua_State *L, uint8_t error);
luaError(lsScripts, SCRIPT_SYNTAX_ERROR);
}
void StandaloneLuaWindow::showError(bool firstCall, const char* title, const char* msg)
{
hasError = true;
if (!errorModal) {
lv_obj_set_scroll_dir(lvobj, LV_DIR_NONE);
errorModal = lv_obj_create(lvobj);
lv_obj_set_pos(errorModal, lv_obj_get_scroll_x(lvobj), lv_obj_get_scroll_y(lvobj));
lv_obj_set_size(errorModal, LCD_W, LCD_H);
etx_bg_color(errorModal, COLOR_BLACK_INDEX);
etx_obj_add_style(errorModal, styles->bg_opacity_75, LV_PART_MAIN);
errorTitle = lv_label_create(errorModal);
lv_obj_set_pos(errorTitle, 50, 30);
lv_obj_set_size(errorTitle, LCD_W - 100, 32);
etx_txt_color(errorTitle, COLOR_THEME_PRIMARY2_INDEX);
etx_solid_bg(errorTitle, COLOR_THEME_SECONDARY1_INDEX);
etx_font(errorTitle, FONT_L_INDEX);
etx_obj_add_style(errorTitle, styles->text_align_center, LV_PART_MAIN);
errorMsg = lv_label_create(errorModal);
lv_obj_set_pos(errorMsg, 50, 62);
lv_obj_set_size(errorMsg, LCD_W - 100, LCD_H - 92);
lv_obj_set_style_pad_all(errorMsg, 4, LV_PART_MAIN);
etx_txt_color(errorMsg, COLOR_THEME_PRIMARY1_INDEX);
etx_solid_bg(errorMsg, COLOR_THEME_SECONDARY3_INDEX);
etx_font(errorMsg, FONT_STD_INDEX);
etx_obj_add_style(errorMsg, styles->text_align_center, LV_PART_MAIN);
}
lv_label_set_text(errorTitle, title);
lv_label_set_text(errorMsg, msg);
}
+19 -2
View File
@@ -33,14 +33,15 @@ struct LuaPopup
void paint(BitmapBuffer* dc, uint8_t type, const char* text, const char* info);
};
class StandaloneLuaWindow : public Window, public LuaEventHandler
class StandaloneLuaWindow : public Window, public LuaEventHandler, public LuaLvglManager
{
static StandaloneLuaWindow* _instance;
explicit StandaloneLuaWindow();
explicit StandaloneLuaWindow(bool useLvgl);
public:
static StandaloneLuaWindow* instance();
static void setup(bool useLvgl);
void attach();
void deleteLater(bool detach = true, bool trash = true) override;
@@ -52,8 +53,24 @@ 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; }
void clear() override;
bool useLvglLayout() const override { return useLvgl; }
void luaShowError() override;
void showError(bool firstCall, const char* title, const char* msg);
bool isWidget() override { return false; }
protected:
lv_obj_t* prevScreen = nullptr;
lv_obj_t* errorModal = nullptr;
lv_obj_t* errorTitle = nullptr;
lv_obj_t* errorMsg = nullptr;
bool hasError = false;
bool useLvgl = false;
// GFX
BitmapBuffer lcdBuffer;
+1
View File
@@ -75,6 +75,7 @@ class ViewMain : public NavWindow
bool onLongPress() override;
void show(bool visible = true) override;
bool viewIsVisible() const { return isVisible; }
void showTopBarEdgeTxButton();
void hideTopBarEdgeTxButton();
+3 -2
View File
@@ -115,16 +115,17 @@ void Widget::setFullscreen(bool enable)
}
// Enter Fullscreen Mode
else {
ViewMain::instance()->enableWidgetSelect(false);
// ViewMain hidden - re-show this widget
show();
// Set window opaque (inhibits redraw from windows below)
setWindowFlag(OPAQUE);
updateZoneRect(parent->getRect());
updateZoneRect(parent->getRect(), false);
setRect(parent->getRect());
ViewMain::instance()->enableWidgetSelect(false);
bringToTop();
if (!lv_obj_get_group(lvobj)) {
+1 -1
View File
@@ -83,7 +83,7 @@ class Widget : public ButtonBase
virtual void background() {}
// Update widget 'zone' data (for Lua widgets)
virtual void updateZoneRect(rect_t rect) {}
virtual void updateZoneRect(rect_t rect, bool updateUI = true) {}
void enableFocus(bool enable);
+1 -1
View File
@@ -121,7 +121,7 @@ class TimerWidget : public Widget
int val = lastValue;
if (lastStartValue && timerData.showElapsed &&
lastStartValue != lastValue)
(int)lastStartValue != lastValue)
val = (int)lastStartValue - lastValue;
if (isLarge) {
+1 -1
View File
@@ -43,7 +43,7 @@ add_custom_target(lua_mixsrc DEPENDS ${HW_DESC_JSON} lua_mixsrc.inc)
add_custom_target(lua_keys DEPENDS ${HW_DESC_JSON} lua_keys.inc)
if(GUI_DIR STREQUAL colorlcd)
set(SRC ${SRC} lua/api_colorlcd.cpp lua/widgets.cpp)
set(SRC ${SRC} lua/api_colorlcd.cpp lua/api_colorlcd_lvgl.cpp lua/widgets.cpp)
else()
set(SRC ${SRC} lua/api_stdlcd.cpp)
endif()
+6 -5
View File
@@ -30,17 +30,18 @@
#include "theme.h"
#include "lua_api.h"
#include "lua_widget.h"
#include "api_colorlcd.h"
#define BITMAP_METATABLE "BITMAP*"
constexpr coord_t INVERT_BOX_MARGIN = 2;
constexpr int8_t text_horizontal_offset[7] {-2,-1,-2,-2,-2,-2,-2};
constexpr int8_t text_vertical_offset[7] {0,0,0,0,0,-1,7};
constexpr int8_t text_horizontal_offset[7] = {-2,-1,-2,-2,-2,-2,-2};
constexpr int8_t text_vertical_offset[7] = {0,0,0,0,0,-1,7};
BitmapBuffer* luaLcdBuffer = nullptr;
Widget* runningFS = nullptr;
LuaWidget *runningFS = nullptr;
static int8_t getTextHorizontalOffset(LcdFlags flags)
{
// no need to adjust if not right aligned
@@ -1413,7 +1414,7 @@ Exit full screen widget mode.
static int luaLcdExitFullScreen(lua_State *L)
{
if (runningFS) {
Widget* rfs = runningFS;
auto rfs = runningFS;
runningFS = nullptr;
rfs->setFullscreen(false);
}
+1
View File
@@ -22,3 +22,4 @@
#include "definitions.h"
EXTERN_C(LUALIB_API int luaopen_bitmap(lua_State * L));
EXTERN_C(LUALIB_API int luaopen_lvgl(lua_State * L));
+301
View File
@@ -0,0 +1,301 @@
/*
* 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.
*/
#define LUA_LIB
#include "libopenui.h"
#include "lua_api.h"
#include "lua_widget.h"
#include "opentx.h"
#include "api_colorlcd.h"
LuaLvglManager *luaLvglManager = nullptr;
static int luaLvglObj(lua_State *L, std::function<LvglWidgetObject*()> create, bool standalone = false)
{
if (luaLvglManager && (!standalone || !luaLvglManager->isWidget())) {
auto obj = create();
obj->getParams(L, 1);
obj->build(L);
obj->push(L);
} else {
lua_pushnil(L);
}
return 1;
}
static int luaLvglLabel(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetLabel(); });
}
static int luaLvglRectangle(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetRectangle(); });
}
static int luaLvglCircle(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetCircle(); });
}
static int luaLvglArc(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetArc(); });
}
static int luaLvglImage(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetImage(); });
}
static int luaLvglMeter(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetMeter(); });
}
static int luaLvglButton(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetTextButton(); }, true);
}
static int luaLvglToggle(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetToggleSwitch(); }, true);
}
static int luaLvglTextEdit(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetTextEdit(); }, true);
}
static int luaLvglNumberEdit(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetNumberEdit(); }, true);
}
static int luaLvglChoice(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetChoice(); }, true);
}
static int luaLvglSlider(lua_State *L)
{
return luaLvglObj(L, [=]() { return new LvglWidgetSlider(); }, true);
}
static int luaLvglConfirm(lua_State *L)
{
auto obj = new LvglWidgetConfirmDialog();
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 LvglWidgetObject *optLvgl(lua_State *L, int index)
{
LvglWidgetObject **p =
(LvglWidgetObject **)luaL_testudata(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;
return 0;
}
static int luaLvglSet(lua_State *L)
{
auto p = LvglWidgetObject::checkLvgl(L, 1);
if (p) {
p->update(L);
}
return 0;
}
static int luaLvglClear(lua_State *L)
{
if (luaLvglManager) luaLvglManager->clear();
return 0;
}
static int luaLvglSetParent(lua_State *L)
{
if (luaLvglManager) {
auto p = optLvgl(L, 1);
luaLvglManager->setTempParent(p ? p->getWindow() : nullptr);
}
return 0;
}
static int luaLvglShow(lua_State *L)
{
auto p = LvglWidgetObject::checkLvgl(L, 1);
if (p) {
p->show();
}
return 0;
}
static int luaLvglHide(lua_State *L)
{
auto p = LvglWidgetObject::checkLvgl(L, 1);
if (p) {
p->hide();
}
return 0;
}
class LvglWidgetParams
{
public:
LvglWidgetParams(lua_State *L, int index = 1)
{
luaL_checktype(L, index, LUA_TTABLE);
for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
const char *key = lua_tostring(L, -2);
if (!strcmp(key, "type")) {
type = luaL_checkstring(L, -1);
} else if (!strcmp(key, "name")) {
name = luaL_checkstring(L, -1);
} else if (!strcmp(key, "children")) {
hasChildren = true;
}
}
}
const char *type = nullptr;
const char *name = nullptr;
bool hasChildren = false;
};
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;
if (strcasecmp(p.type, "label") == 0)
lvobj = new LvglWidgetLabel();
else if (strcasecmp(p.type, "rectangle") == 0)
lvobj = new LvglWidgetRectangle();
else if (strcasecmp(p.type, "circle") == 0)
lvobj = new LvglWidgetCircle();
else if (strcasecmp(p.type, "arc") == 0)
lvobj = new LvglWidgetArc();
else if (strcasecmp(p.type, "image") == 0)
lvobj = new LvglWidgetImage();
else if (strcasecmp(p.type, "meter") == 0)
lvobj = new LvglWidgetMeter();
else if (!luaLvglManager->isWidget()) {
if (strcasecmp(p.type, "button") == 0)
lvobj = new LvglWidgetTextButton();
else if (strcasecmp(p.type, "toggle") == 0)
lvobj = new LvglWidgetToggleSwitch();
else if (strcasecmp(p.type, "textEdit") == 0)
lvobj = new LvglWidgetTextEdit();
else if (strcasecmp(p.type, "numberEdit") == 0)
lvobj = new LvglWidgetNumberEdit();
else if (strcasecmp(p.type, "choice") == 0)
lvobj = new LvglWidgetChoice();
else if (strcasecmp(p.type, "slider") == 0)
lvobj = new LvglWidgetSlider();
}
if (lvobj) {
lvobj->getParams(L, -1);
lvobj->build(L);
auto ref = lvobj->getRef(L);
if (p.name) {
lua_pushstring(L, p.name);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_settable(L, refIndex - 4);
}
if (p.hasChildren) {
lua_getfield(L, -1, "children");
auto prevParent = luaLvglManager->getTempParent();
luaLvglManager->setTempParent(lvobj->getWindow());
buildLvgl(L, -1, refIndex - 3);
lua_pop(L, 1);
luaLvglManager->setTempParent(prevParent);
}
}
}
}
static int luaLvglBuild(lua_State *L)
{
if (luaLvglManager) {
// Return array of lvgl object references
lua_newtable(L);
buildLvgl(L, -2, -1);
} else {
lua_pushnil(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)
LROT_FUNCENTRY(setParent, luaLvglSetParent)
// Objects
LROT_FUNCENTRY(label, luaLvglLabel)
LROT_FUNCENTRY(rectangle, luaLvglRectangle)
LROT_FUNCENTRY(circle, luaLvglCircle)
LROT_FUNCENTRY(arc, luaLvglArc)
LROT_FUNCENTRY(image, luaLvglImage)
LROT_FUNCENTRY(meter, luaLvglMeter)
LROT_FUNCENTRY(button, luaLvglButton)
LROT_FUNCENTRY(toggle, luaLvglToggle)
LROT_FUNCENTRY(confirm, luaLvglConfirm)
LROT_FUNCENTRY(textEdit, luaLvglTextEdit)
LROT_FUNCENTRY(numberEdit, luaLvglNumberEdit)
LROT_FUNCENTRY(choice, luaLvglChoice)
LROT_FUNCENTRY(slider, luaLvglSlider)
// Manipulation functions
LROT_FUNCENTRY(set, luaLvglSet)
LROT_FUNCENTRY(show, luaLvglShow)
LROT_FUNCENTRY(hide, luaLvglHide)
LROT_END(lvgllib, NULL, LROT_MASK_GC_INDEX)
extern "C" {
LUALIB_API int luaopen_lvgl(lua_State *L)
{
luaL_rometatable(L, LVGL_METATABLE, LROT_TABLEREF(lvgllib));
return 0;
}
}
+8
View File
@@ -2165,7 +2165,15 @@ Get percent of already used Lua instructions in current script execution cycle.
*/
static int luaGetUsage(lua_State * L)
{
#if defined(COLORLCD)
if (luaLvglManager && luaLvglManager->useLvglLayout()) {
lua_pushinteger(L, luaLvglManager->refreshInstructionsPercent);
} else {
lua_pushinteger(L, instructionsPercent);
}
#else
lua_pushinteger(L, instructionsPercent);
#endif
return 1;
}
+19 -34
View File
@@ -35,6 +35,10 @@
#include "api_filesystem.h"
#include "switches.h"
#if defined(COLORLCD)
#include "standalone_lua.h"
#endif
#if defined(LIBOPENUI)
#include "libopenui.h"
#else
@@ -725,42 +729,11 @@ void displayLuaError(bool firstCall = false)
}
#if defined(COLORLCD)
static bool drewBackground = false;
if (firstCall)
drewBackground = false;
if (!luaLcdAllowed || luaLcdBuffer == nullptr)
return;
coord_t w = 0.75 * LCD_W;
coord_t left = (LCD_W - w) / 2;
#if (!PORTRAIT_LCD)
coord_t hh = getFontHeight(FONT(XL)) + 4;
#else
coord_t hh = getFontHeight(FONT(L)) + 4;
#endif
coord_t h = 0.75 * LCD_H - hh;
coord_t top = (LCD_H - h + hh / 2) / 2;
if (!drewBackground) {
drewBackground = true;
luaLcdBuffer->drawFilledRect(0, 0, LCD_W, LCD_H, SOLID, COLOR_BLACK, OPACITY(6));
}
luaLcdBuffer->drawSolidFilledRect(left, top - hh, w, hh, COLOR_THEME_SECONDARY1);
luaLcdBuffer->drawSolidFilledRect(left, top, w, h, COLOR_THEME_SECONDARY3);
#if (!PORTRAIT_LCD)
luaLcdBuffer->drawText(left + 10, top - hh + 2, title, FONT(XL) | COLOR_THEME_PRIMARY2);
luaLcdBuffer->drawTextLines(left + 10, top + 5, w - 20, h - 10, lua_warning_info, FONT(L) | COLOR_THEME_PRIMARY1);
#else
luaLcdBuffer->drawText(left + 10, top - hh + 2, title, FONT(L) | COLOR_THEME_PRIMARY2);
luaLcdBuffer->drawTextLines(left + 10, top + 5, w - 20, h - 10, lua_warning_info, FONT(STD) | COLOR_THEME_PRIMARY1);
#endif
StandaloneLuaWindow::instance()->showError(firstCall, title, lua_warning_info);
#else
if (!luaLcdAllowed)
return;
drawMessageBox(title);
coord_t y = WARNING_LINE_Y + FH + 4;
@@ -955,6 +928,13 @@ 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);
}
#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;
@@ -985,7 +965,12 @@ static void luaLoadScripts(bool init, const char * filename = nullptr)
// If init(), push it on the stack
if (initFunction != LUA_NOREF) {
lua_rawgeti(lsScripts, LUA_REGISTRYINDEX, initFunction);
if (ref == SCRIPT_STANDALONE) luaLcdAllowed = true;
if (ref == SCRIPT_STANDALONE) {
luaLcdAllowed = true;
#if defined(COLORLCD)
StandaloneLuaWindow::setup(sid.useLvgl);
#endif
}
}
}
}
+9 -6
View File
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
#ifndef _LUA_API_H_
#define _LUA_API_H_
#pragma once
#if defined(LUA)
@@ -66,8 +65,11 @@ extern bool luaLcdAllowed;
class BitmapBuffer;
extern BitmapBuffer* luaLcdBuffer;
class Widget;
extern Widget* runningFS;
class LuaWidget;
extern LuaWidget* runningFS;
class LuaLvglManager;
extern LuaLvglManager* luaLvglManager;
LcdFlags flagsRGB(LcdFlags flags);
@@ -149,6 +151,9 @@ struct ScriptInternalData {
int run;
int background;
uint8_t instructions;
#if defined(COLORLCD)
bool useLvgl;
#endif
};
struct ScriptInputsOutputs {
@@ -252,5 +257,3 @@ void * tracer_alloc(void * ud, void * ptr, size_t osize, size_t nsize);
#define LUA_LOAD_MODEL_SCRIPTS()
#endif // defined(LUA)
#endif // _LUA_API_H_
File diff suppressed because it is too large Load Diff
+405
View File
@@ -0,0 +1,405 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#define LVGL_METATABLE "LVGL*"
//-----------------------------------------------------------------------------
class LvglWidgetObjectBase
{
public:
LvglWidgetObjectBase() { lvglManager = luaLvglManager; }
virtual ~LvglWidgetObjectBase() {}
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;
protected:
LuaLvglManager *lvglManager = nullptr;
virtual void parseParam(lua_State *L, const char *key) = 0;
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<void(bool)> update);
bool pcallUpdate1Int(lua_State *L, int getFuncRef,
std::function<void(int)> update);
bool pcallUpdate2Int(lua_State *L, int getFuncRef,
std::function<void(int, int)> update);
int pcallGetIntVal(lua_State *L, int getFuncRef);
void pcallSetIntVal(lua_State *L, int setFuncRef, int val);
};
//-----------------------------------------------------------------------------
class LvglWidgetObject : public LvglWidgetObjectBase
{
public:
LvglWidgetObject() : LvglWidgetObjectBase() {}
int getRef(lua_State *L);
void push(lua_State *L);
void update(lua_State *L);
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;
protected:
Window *window = nullptr;
LcdFlags currentColor = -1;
coord_t x = 0, y = 0, w = LV_SIZE_CONTENT, h = LV_SIZE_CONTENT;
LcdFlags color = COLOR_THEME_SECONDARY1;
int getColorFunction = 0;
int getVisibleFunction = 0;
int getSizeFunction = 0;
int getPosFunction = 0;
void parseParam(lua_State *L, const char *key) override;
virtual void refresh();
};
//-----------------------------------------------------------------------------
class LvglWidgetLabel : public LvglWidgetObject
{
public:
LvglWidgetLabel() : LvglWidgetObject() {}
void setText(const char *s);
void setColor(LcdFlags color) override;
void setFont(LcdFlags font);
void build(lua_State *L) override;
bool callRefs(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
uint32_t textHash = -1;
const char *txt = "";
LcdFlags font = FONT(STD);
int getTextFunction = 0;
int getFontFunction = 0;
void parseParam(lua_State *L, const char *key) override;
void refresh() override
{
setText(txt);
setFont(font);
LvglWidgetObject::refresh();
}
};
//-----------------------------------------------------------------------------
class LvglWidgetBorderedObject : public LvglWidgetObject
{
public:
LvglWidgetBorderedObject() : LvglWidgetObject() {}
void setColor(LcdFlags color) override;
void build(lua_State *L) override;
protected:
coord_t thickness = 1;
bool filled = false;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetRoundObject : public LvglWidgetBorderedObject
{
public:
LvglWidgetRoundObject() : LvglWidgetBorderedObject() {}
void setPos(coord_t x, coord_t y) override;
void setRadius(coord_t r);
bool callRefs(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
coord_t radius = 0;
int getRadiusFunction = 0;
void parseParam(lua_State *L, const char *key) override;
void refresh() override
{
setRadius(radius);
LvglWidgetObject::refresh();
}
};
//-----------------------------------------------------------------------------
class LvglWidgetRectangle : public LvglWidgetBorderedObject
{
public:
LvglWidgetRectangle() : LvglWidgetBorderedObject() {}
void build(lua_State *L) override;
protected:
coord_t rounded = 0;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetCircle : public LvglWidgetRoundObject
{
public:
LvglWidgetCircle() : LvglWidgetRoundObject() {}
void build(lua_State *L) override;
protected:
};
//-----------------------------------------------------------------------------
class LvglWidgetArc : public LvglWidgetRoundObject
{
public:
LvglWidgetArc() : LvglWidgetRoundObject() {}
void setColor(LcdFlags color) override;
void setStartAngle(coord_t angle);
void setEndAngle(coord_t angle);
void build(lua_State *L) override;
bool callRefs(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
coord_t startAngle = 0, endAngle = 0;
int getStartAngleFunction = 0;
int getEndAngleFunction = 0;
void parseParam(lua_State *L, const char *key) override;
void refresh() override
{
setStartAngle(startAngle);
setEndAngle(endAngle);
LvglWidgetRoundObject::refresh();
}
};
//-----------------------------------------------------------------------------
class LvglWidgetImage : public LvglWidgetObject
{
public:
LvglWidgetImage() : LvglWidgetObject() {}
void build(lua_State *L) override;
protected:
std::string filename;
bool fillFrame = false;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
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<LvglWidgetMeterScale*> scales;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetTextButton : public LvglWidgetObject
{
public:
LvglWidgetTextButton() : LvglWidgetObject() {}
void setText(const char *s);
void build(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
uint32_t textHash = -1;
const char *txt = "";
int pressFunction = 0;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetToggleSwitch : public LvglWidgetObject
{
public:
LvglWidgetToggleSwitch() : LvglWidgetObject() {}
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;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetTextEdit : public LvglWidgetObject
{
public:
LvglWidgetTextEdit() : LvglWidgetObject() {}
void build(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
const char *txt = "";
char value[129];
int maxLen = 32;
int setFunction = 0;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetNumberEdit : public LvglWidgetObject
{
public:
LvglWidgetNumberEdit() : LvglWidgetObject() {}
void build(lua_State *L) override;
void clearRefs(lua_State *L) override;
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<std::string> values;
int getFunction = 0;
int setFunction = 0;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
class LvglWidgetSlider : public LvglWidgetObject
{
public:
LvglWidgetSlider() : LvglWidgetObject() {}
void build(lua_State *L) override;
void clearRefs(lua_State *L) override;
protected:
int32_t vmin = 0;
int32_t vmax = 100;
int getValueFunction = 0;
int setValueFunction = 0;
void parseParam(lua_State *L, const char *key) override;
};
//-----------------------------------------------------------------------------
+111 -34
View File
@@ -25,6 +25,7 @@
#include "lua_event.h"
#include "lua_widget_factory.h"
#include "touch.h"
#include "view_main.h"
#define MAX_INSTRUCTIONS (20000 / 100)
@@ -162,7 +163,9 @@ void LuaEventHandler::onClicked()
luaPushEvent(EVT_KEY_BREAK(KEY_ENTER));
}
void LuaEventHandler::onCancel() { luaPushEvent(EVT_KEY_BREAK(KEY_EXIT)); }
void LuaEventHandler::onCancel() {
luaPushEvent(EVT_KEY_BREAK(KEY_EXIT));
}
void LuaEventHandler::onEvent(event_t event)
{
@@ -189,28 +192,35 @@ void LuaWidget::redraw_cb(lv_event_t* e)
LuaWidget* widget = (LuaWidget*)lv_obj_get_user_data(target);
if (widget) {
lv_draw_ctx_t* draw_ctx = lv_event_get_draw_ctx(e);
if (widget->useLvglLayout()) {
if (!widget->isCreated()) {
widget->update();
widget->setCreated();
}
} else {
lv_draw_ctx_t* draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t a, clipping, obj_coords;
lv_area_copy(&a, draw_ctx->buf_area);
lv_area_copy(&clipping, draw_ctx->clip_area);
lv_obj_get_coords(target, &obj_coords);
lv_area_t a, clipping, obj_coords;
lv_area_copy(&a, draw_ctx->buf_area);
lv_area_copy(&clipping, draw_ctx->clip_area);
lv_obj_get_coords(target, &obj_coords);
auto w = a.x2 - a.x1 + 1;
auto h = a.y2 - a.y1 + 1;
auto w = a.x2 - a.x1 + 1;
auto h = a.y2 - a.y1 + 1;
TRACE_WINDOWS("Draw %s", widget->getWindowDebugString().c_str());
TRACE_WINDOWS("Draw %s", widget->getWindowDebugString().c_str());
BitmapBuffer buf = {BMP_RGB565, (uint16_t)w, (uint16_t)h,
(uint16_t*)draw_ctx->buf};
BitmapBuffer buf = {BMP_RGB565, (uint16_t)w, (uint16_t)h,
(uint16_t*)draw_ctx->buf};
buf.setDrawCtx(draw_ctx);
buf.setDrawCtx(draw_ctx);
buf.setOffset(obj_coords.x1 - a.x1, obj_coords.y1 - a.y1);
buf.setClippingRect(clipping.x1 - a.x1, clipping.x2 + 1 - a.x1,
clipping.y1 - a.y1, clipping.y2 + 1 - a.y1);
buf.setOffset(obj_coords.x1 - a.x1, obj_coords.y1 - a.y1);
buf.setClippingRect(clipping.x1 - a.x1, clipping.x2 + 1 - a.x1,
clipping.y1 - a.y1, clipping.y2 + 1 - a.y1);
widget->refresh(&buf);
widget->refresh(&buf);
}
}
}
@@ -218,11 +228,12 @@ LuaWidget::LuaWidget(const WidgetFactory* factory, Window* parent,
const rect_t& rect, WidgetPersistentData* persistentData,
int luaWidgetDataRef, int zoneRectDataRef) :
Widget(factory, parent, rect, persistentData),
luaWidgetDataRef(luaWidgetDataRef),
zoneRectDataRef(zoneRectDataRef),
errorMessage(nullptr)
{
lv_obj_add_event_cb(lvobj, LuaWidget::redraw_cb, LV_EVENT_DRAW_MAIN, nullptr);
this->luaWidgetDataRef = luaWidgetDataRef;
lv_obj_add_event_cb(lvobj, LuaWidget::redraw_cb, LV_EVENT_DRAW_MAIN,
nullptr);
}
LuaWidget::~LuaWidget()
@@ -258,12 +269,32 @@ void LuaWidget::checkEvents()
// paint has not been called
if (!refreshed) {
background();
if (isCreated())
background();
refreshed = true;
}
refreshed = false;
invalidate();
if (useLvglLayout()) {
if (!lv_obj_has_flag(lvobj, LV_OBJ_FLAG_HIDDEN) && isCreated()) {
PROTECT_LUA() {
luaLvglManager = this;
refresh(nullptr);
if (!errorMessage) {
if (!callRefs(lsWidgets)) {
setErrorMessage("callRefs()");
}
}
refreshInstructionsPercent = instructionsPercent;
} else {
// TODO: error handling
}
luaLvglManager = nullptr;
UNPROTECT_LUA();
}
} else {
invalidate();
}
#if defined(DEBUG_WINDOWS)
TRACE_WINDOWS("# refresh: %s", getWindowDebugString().c_str());
@@ -282,10 +313,9 @@ void LuaWidget::update()
Widget::update();
if (lsWidgets == 0 || errorMessage) return;
LuaWidgetFactory* lua_factory = (LuaWidgetFactory*)factory;
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, lua_factory->updateFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->updateFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
lua_newtable(lsWidgets);
@@ -308,9 +338,12 @@ void LuaWidget::update()
}
}
if (lua_pcall(lsWidgets, 2, 0, 0) != 0) {
if (useLvglLayout()) luaLvglManager = this;
if (lua_pcall(lsWidgets, 2, 0, 0) != 0)
setErrorMessage("update()");
}
luaLvglManager = nullptr;
}
// Update table on top of Lua stack - set entry with name 'idx' to value 'val'
@@ -338,7 +371,7 @@ bool LuaWidget::updateTable(const char* idx, int val)
return update;
}
void LuaWidget::updateZoneRect(rect_t rect)
void LuaWidget::updateZoneRect(rect_t rect, bool updateUI)
{
if (lsWidgets) {
// Update widget zone with current size and position
@@ -354,7 +387,8 @@ void LuaWidget::updateZoneRect(rect_t rect)
lua_pop(lsWidgets, 1);
if (changed) update();
if (changed && updateUI)
update();
}
}
@@ -391,15 +425,28 @@ void LuaWidget::refresh(BitmapBuffer* dc)
if (lsWidgets == 0) return;
if (errorMessage) {
dc->drawTextLines(0, 0, fullscreen ? LCD_W : rect.w,
fullscreen ? LCD_H : rect.h, errorMessage,
FONT(XS) | COLOR_THEME_WARNING);
if (dc) {
dc->drawTextLines(0, 0, fullscreen ? LCD_W : rect.w,
fullscreen ? LCD_H : rect.h, errorMessage,
FONT(XS) | COLOR_THEME_WARNING);
} else {
if (errorLabel == nullptr) {
errorLabel = lv_label_create(lvobj);
lv_obj_set_pos(errorLabel, 0, 0);
lv_obj_set_size(errorLabel, width(), height());
lv_label_set_long_mode(errorLabel, LV_LABEL_LONG_WRAP);
etx_txt_color(errorLabel, COLOR_THEME_WARNING_INDEX);
etx_font(errorLabel, FONT_XS_INDEX);
etx_bg_color(errorLabel, COLOR_THEME_SECONDARY3_INDEX);
etx_obj_add_style(errorLabel, styles->bg_opacity_75, LV_PART_MAIN);
}
lv_label_set_text(errorLabel, errorMessage);
}
return;
}
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory* factory = (LuaWidgetFactory*)this->factory;
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->refreshFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->refreshFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
// Pass key event to fullscreen Lua widget
@@ -445,9 +492,8 @@ void LuaWidget::background()
// TRACE("LuaWidget::background()");
luaSetInstructionsLimit(lsWidgets, MAX_INSTRUCTIONS);
LuaWidgetFactory* factory = (LuaWidgetFactory*)this->factory;
if (factory->backgroundFunction) {
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, factory->backgroundFunction);
if (luaFactory()->backgroundFunction) {
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaFactory()->backgroundFunction);
lua_rawgeti(lsWidgets, LUA_REGISTRYINDEX, luaWidgetDataRef);
runningFS = this;
if (lua_pcall(lsWidgets, 1, 0, 0) != 0) {
@@ -464,3 +510,34 @@ void LuaWidget::onEvent(event_t event)
}
Widget::onEvent(event);
}
void LuaWidget::clear()
{
clearRefs(lsWidgets);
Widget::clear();
}
bool LuaWidget::useLvglLayout() const { return luaFactory()->useLvglLayout(); }
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]);
}
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);
}
return rv;
}
+52 -3
View File
@@ -21,14 +21,47 @@
#pragma once
#include <vector>
#include "window.h"
#include "widget.h"
#include "lua_api.h"
#include "lua_lvgl_widget.h"
#include "opentx_types.h"
#define LUA_TAP_TIME 250 // 250 ms
class LuaWidgetFactory;
class LuaLvglManager
{
public:
LuaLvglManager() = default;
std::vector<int> getLvglObjectRefs() const { return lvglObjectRefs; }
void saveLvglObjectRef(int ref) { lvglObjectRefs.push_back(ref); }
void clearRefs(lua_State *L);
bool callRefs(lua_State *L);
void setTempParent(Window *p) { tempParent = p; }
Window* getTempParent() const { return tempParent; }
virtual Window* getCurrentParent() const = 0;
virtual void clear() = 0;
virtual bool useLvglLayout() const = 0;
virtual void luaShowError() = 0;
virtual bool isWidget() = 0;
uint8_t refreshInstructionsPercent;
protected:
std::vector<int> lvglObjectRefs;
Window* tempParent = nullptr;
};
class LuaEventHandler
{
#if defined(HARDWARE_TOUCH)
@@ -55,11 +88,10 @@ public:
void removeHandler(Window* w);
};
class LuaWidget : public Widget, public LuaEventHandler
class LuaWidget : public Widget, public LuaEventHandler, public LuaLvglManager
{
friend class LuaWidgetFactory;
int luaWidgetDataRef;
int zoneRectDataRef;
char* errorMessage;
bool refreshed = false;
@@ -75,7 +107,7 @@ class LuaWidget : public Widget, public LuaEventHandler
void setErrorMessage(const char* funcName);
// Update 'zone' data
void updateZoneRect(rect_t rect) override;
void updateZoneRect(rect_t rect, bool updateUI = true) override;
bool updateTable(const char* idx, int val);
public:
@@ -92,9 +124,26 @@ class LuaWidget : public Widget, public LuaEventHandler
void update() override;
void background() override;
void clear() override;
LuaWidgetFactory* luaFactory() const { return (LuaWidgetFactory*)factory; }
bool isCreated() const { return created; }
void setCreated() { created = true; }
Window* getCurrentParent() const override { return tempParent ? tempParent : (Window*)this; }
bool useLvglLayout() const override;
void luaShowError() override {}
bool isWidget() override { return true; }
protected:
bool created = false;
lv_obj_t* errorLabel = nullptr;
// Calls LUA widget 'refresh' method
void refresh(BitmapBuffer* dc);
int luaWidgetDataRef = 0;
// Window interface
void onEvent(event_t event) override;
+3
View File
@@ -39,6 +39,8 @@ class LuaWidgetFactory : public WidgetFactory
bool isLuaWidgetFactory() const override { return true; }
bool useLvglLayout() const { return lvglLayout; }
protected:
void translateOptions(ZoneOption * options);
@@ -47,4 +49,5 @@ class LuaWidgetFactory : public WidgetFactory
int refreshFunction;
int backgroundFunction;
int translateFunction;
bool lvglLayout;
};
+5
View File
@@ -235,6 +235,7 @@ void luaLoadWidgetCallback()
int widgetOptions = 0, createFunction = 0, updateFunction = 0,
refreshFunction = 0, backgroundFunction = 0, translateFunction = 0;
bool lvglLayout = false;
luaL_checktype(lsWidgets, -1, LUA_TTABLE);
@@ -267,6 +268,9 @@ void luaLoadWidgetCallback()
translateFunction = luaL_ref(lsWidgets, LUA_REGISTRYINDEX);
lua_pushnil(lsWidgets);
}
else if (!strcasecmp(key, "uselvgl")) {
lvglLayout = lua_toboolean(lsWidgets, -1);
}
}
if (name && createFunction) {
@@ -276,6 +280,7 @@ void luaLoadWidgetCallback()
factory->updateFunction = updateFunction;
factory->refreshFunction = refreshFunction;
factory->backgroundFunction = backgroundFunction; // NOSONAR
factory->lvglLayout = lvglLayout;
factory->translateFunction = translateFunction;
factory->translateOptions(options);
TRACE("Loaded Lua widget %s", name);
+1 -1
View File
@@ -601,7 +601,7 @@
#define LV_USE_MENU 0
#define LV_USE_METER 0
#define LV_USE_METER 1
#define LV_USE_MSGBOX 0
+2
View File
@@ -375,6 +375,8 @@ void guiMain(event_t evt)
maxLuaInterval = interval;
}
luaDoGc(lsWidgets, true);
DEBUG_TIMER_START(debugTimerLua);
luaTask(false);
DEBUG_TIMER_STOP(debugTimerLua);
+3
View File
@@ -43,6 +43,7 @@ extern LROT_TABLE(modellib);
extern LROT_TABLE(bitmaplib);
#if defined(COLORLCD)
extern LROT_TABLE(lvgllib);
extern LROT_TABLE(tablib);
#endif
@@ -99,6 +100,7 @@ LROT_BEGIN(rotables, LROT_TABLEREF(rotables_meta), 0)
LROT_TABENTRY( bitmap, bitmaplib )
LROT_TABENTRY( Bitmap, bitmaplib ) /* TODO: obsolete after 2.9 */
#if defined(COLORLCD)
LROT_TABENTRY( lvgl, lvgllib )
LROT_TABENTRY( table, tablib )
#endif
LROT_TABENTRY( ROM, rotables )
@@ -110,6 +112,7 @@ LROT_BEGIN(lua_libs, NULL, 0)
LROT_FUNCENTRY( dir, luaopen_etxdir )
LROT_FUNCENTRY( bitmap_mt, luaopen_bitmap )
#if defined(COLORLCD)
LROT_FUNCENTRY( lvgl_mt, luaopen_lvgl )
LROT_FUNCENTRY( package, luaopen_package )
#endif
#if defined(LUA_ENABLE_STRLIB_MT)
+3 -3
View File
@@ -183,8 +183,8 @@ void StaticIcon::center(coord_t w, coord_t h)
// Display image from file system using LVGL with LVGL scaling
// - LVGL scaling is slow so don't use this if there are many images
StaticImage::StaticImage(Window* parent, const rect_t& rect,
const char* filename, bool dontEnlarge) :
Window(parent, rect), dontEnlarge(dontEnlarge)
const char* filename, bool fillFrame, bool dontEnlarge) :
Window(parent, rect), fillFrame(fillFrame), dontEnlarge(dontEnlarge)
{
setWindowFlag(NO_FOCUS);
@@ -234,7 +234,7 @@ void StaticImage::setZoom()
if (img && img->w && img->h) {
uint16_t zw = (width() * 256) / img->w;
uint16_t zh = (height() * 256) / img->h;
uint16_t z = min(zw, zh);
uint16_t z = fillFrame ? max(zw, zh) : min(zw, zh);
if (dontEnlarge) z = min(z, (uint16_t)256);
lv_img_set_zoom(image, z);
}
+2 -1
View File
@@ -142,7 +142,7 @@ class StaticImage : public Window
{
public:
StaticImage(Window *parent, const rect_t &rect,
const char *filename = nullptr, bool dontEnlarge = false);
const char *filename = nullptr, bool fillFrame = false, bool dontEnlarge = false);
#if defined(DEBUG_WINDOWS)
std::string getName() const override { return "StaticImage"; }
@@ -154,6 +154,7 @@ class StaticImage : public Window
bool hasImage() const;
protected:
bool fillFrame;
bool dontEnlarge = false;
lv_obj_t *image = nullptr;
};
+1 -1
View File
@@ -74,7 +74,7 @@ class Window
typedef std::function<void(bool)> FocusHandler;
void setFocusHandler(FocusHandler h) { focusHandler = std::move(h); }
void clear();
virtual void clear();
virtual void deleteLater(bool detach = true, bool trash = true);
bool hasFocus() const;