2314 lines
59 KiB
C++
2314 lines
59 KiB
C++
/*
|
|
* 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"
|
|
#include "filechoice.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglParamFuncOrValue::parse(lua_State *L, ParamType typ)
|
|
{
|
|
if (lua_isfunction(L, -1)) {
|
|
function = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
if (typ == PTXT)
|
|
txt = luaL_checkstring(L, -1);
|
|
else
|
|
value = luaL_checkunsigned(L, -1);
|
|
}
|
|
}
|
|
|
|
bool LvglParamFuncOrValue::changedColor(LcdFlags newFlags)
|
|
{
|
|
flags = newFlags;
|
|
if (flags != currVal) {
|
|
currVal = flags;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LvglParamFuncOrValue::changedText(const char* s)
|
|
{
|
|
txt = s;
|
|
uint32_t h = hash(txt, strlen(txt));
|
|
if (h != currVal) {
|
|
currVal = h;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LvglParamFuncOrValue::changedValue(uint32_t v)
|
|
{
|
|
value = v;
|
|
if (v != currVal) {
|
|
currVal = v;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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(luaScriptManager)
|
|
{
|
|
}
|
|
|
|
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 = luaScriptManager;
|
|
luaScriptManager = lvglManager;
|
|
if (!pcallFunc(L, funcRef, 0)) {
|
|
lvglManager->luaShowError();
|
|
}
|
|
luaScriptManager = save;
|
|
}
|
|
UNPROTECT_LUA();
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetObjectBase::pcallUpdateBool(lua_State *L, int getFuncRef,
|
|
std::function<void(bool)> update)
|
|
{
|
|
bool res = true;
|
|
if (getFuncRef != LUA_REFNIL) {
|
|
auto save = luaScriptManager;
|
|
luaScriptManager = 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<void(int)> update)
|
|
{
|
|
bool res = true;
|
|
if (getFuncRef != LUA_REFNIL) {
|
|
auto save = luaScriptManager;
|
|
luaScriptManager = 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<void(int, int)> update)
|
|
{
|
|
bool res = true;
|
|
if (getFuncRef != LUA_REFNIL) {
|
|
auto save = luaScriptManager;
|
|
luaScriptManager = 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 = luaScriptManager;
|
|
luaScriptManager = 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 = luaScriptManager;
|
|
luaScriptManager = 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 = luaScriptManager;
|
|
luaScriptManager = 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;
|
|
}
|
|
}
|
|
|
|
const char* LvglWidgetObjectBase::pcallGetStringVal(lua_State *L, int getFuncRef)
|
|
{
|
|
const char* val = "";
|
|
if (getFuncRef != LUA_REFNIL) {
|
|
auto save = luaScriptManager;
|
|
luaScriptManager = lvglManager;
|
|
int t = lua_gettop(L);
|
|
PROTECT_LUA()
|
|
{
|
|
if (pcallFunc(L, getFuncRef, 1)) {
|
|
val = luaL_checkstring(L, -1);
|
|
} else {
|
|
lvglManager->luaShowError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lvglManager->luaShowError();
|
|
}
|
|
UNPROTECT_LUA();
|
|
lua_settop(L, t);
|
|
lvglManager = save;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
void LvglWidgetObjectBase::pcallSetStringVal(lua_State *L, int setFuncRef, const char* val)
|
|
{
|
|
if (setFuncRef != LUA_REFNIL) {
|
|
auto save = luaScriptManager;
|
|
luaScriptManager = lvglManager;
|
|
int t = lua_gettop(L);
|
|
PROTECT_LUA()
|
|
{
|
|
if (!pcallFuncWithString(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")) {
|
|
color.parse(L);
|
|
} else if (!strcmp(key, "opacity")) {
|
|
opacity.parse(L);
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetObjectBase::clear()
|
|
{
|
|
clearRequest = true;
|
|
if (getWindow())
|
|
getWindow()->clear();
|
|
}
|
|
|
|
bool LvglWidgetObjectBase::callRefs(lua_State *L)
|
|
{
|
|
if (clearRequest) {
|
|
clearRequest = false;
|
|
clearChildRefs(L);
|
|
return true;
|
|
}
|
|
|
|
if (!pcallUpdate1Int(L, color.function,
|
|
[=](int color) { setColor(color); }))
|
|
return false;
|
|
if (!pcallUpdate1Int(L, opacity.function,
|
|
[=](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;
|
|
if (clearRequest) {
|
|
clearRequest = false;
|
|
clearChildRefs(L);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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, color.function);
|
|
clearRef(L, opacity.function);
|
|
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.flags);
|
|
setOpacity(opacity.value);
|
|
}
|
|
|
|
void LvglWidgetObjectBase::create(lua_State *L, int index)
|
|
{
|
|
getParams(L, index);
|
|
build(L);
|
|
callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetObjectBase::update(lua_State *L)
|
|
{
|
|
getParams(L, 2);
|
|
refresh();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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")) {
|
|
txt.parse(L, LvglParamFuncOrValue::PTXT);
|
|
} else if (!strcmp(key, "font")) {
|
|
font.parse(L);
|
|
} else if (!strcmp(key, "align")) {
|
|
align.parse(L);
|
|
} else {
|
|
LvglSimpleWidgetObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetLabel::callRefs(lua_State *L)
|
|
{
|
|
int t = lua_gettop(L);
|
|
if (txt.function != LUA_REFNIL) {
|
|
if (pcallFunc(L, txt.function, 1)) {
|
|
const char *s = luaL_checkstring(L, -1);
|
|
setText(s);
|
|
lua_settop(L, t);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
if (!pcallUpdate1Int(L, font.function, [=](int val) { setFont(val); }))
|
|
return false;
|
|
if (!pcallUpdate1Int(L, align.function, [=](int val) { setAlign(val); }))
|
|
return false;
|
|
return LvglSimpleWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetLabel::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, txt.function);
|
|
clearRef(L, font.function);
|
|
clearRef(L, align.function);
|
|
LvglSimpleWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetLabel::setText(const char *s)
|
|
{
|
|
if (lvobj && txt.changedText(s)) {
|
|
lv_label_set_text(lvobj, txt.txt);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetLabel::setColor(LcdFlags newColor)
|
|
{
|
|
if (lvobj && color.changedColor(newColor))
|
|
etx_txt_color_from_flags(lvobj, color.flags);
|
|
}
|
|
|
|
void LvglWidgetLabel::setFont(LcdFlags newFont)
|
|
{
|
|
if (lvobj) {
|
|
font.flags = newFont;
|
|
etx_font(lvobj, FONT_INDEX(font.flags));
|
|
}
|
|
}
|
|
|
|
void LvglWidgetLabel::setAlign(LcdFlags newAlign)
|
|
{
|
|
if (lvobj) {
|
|
align.flags = newAlign;
|
|
if (align.flags & VCENTERED) {
|
|
lv_obj_align(lvobj, LV_ALIGN_LEFT_MID, 0, 0);
|
|
}
|
|
lv_obj_set_style_text_align(lvobj,
|
|
(align.flags & RIGHT) ? LV_TEXT_ALIGN_RIGHT
|
|
: (align.flags & 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.txt);
|
|
setColor(color.flags);
|
|
setOpacity(opacity.value);
|
|
setFont(font.flags);
|
|
setAlign(align.flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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 && color.changedColor(newColor)) {
|
|
etx_line_color_from_flags(lvobj, color.flags);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetLineBase::setOpacity(uint8_t newOpa)
|
|
{
|
|
opacity.value = newOpa;
|
|
lv_obj_set_style_line_opa(lvobj, opacity.value, 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.flags);
|
|
setOpacity(opacity.value);
|
|
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);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LvglWidgetLine::LvglWidgetLine() : LvglSimpleWidgetObject()
|
|
{
|
|
parent = lvglManager->getCurrentParent()->getLvObj();
|
|
}
|
|
|
|
LvglWidgetLine::~LvglWidgetLine()
|
|
{
|
|
if (pts)
|
|
delete pts;
|
|
pts = nullptr;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
uint32_t LvglWidgetLine::getPts(lua_State* L)
|
|
{
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
size_t newPtCnt = lua_rawlen(L, -1);
|
|
if (newPtCnt > 1) {
|
|
ptCnt = newPtCnt;
|
|
if (pts && ptCnt > ptAlloc) {
|
|
delete pts;
|
|
pts = nullptr;
|
|
}
|
|
if (!pts) {
|
|
pts = new lv_point_t[ptCnt];
|
|
ptAlloc = ptCnt;
|
|
}
|
|
for (size_t i = 0; i < ptCnt; i += 1)
|
|
getPt(L, i);
|
|
return hash(pts, sizeof(ptCnt * sizeof(lv_point_t)));
|
|
} else {
|
|
ptCnt = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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")) {
|
|
if (lua_isfunction(L, -1)) {
|
|
getPointsFunction = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
ptsHash = getPts(L);
|
|
}
|
|
} else {
|
|
LvglSimpleWidgetObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetLine::callRefs(lua_State *L)
|
|
{
|
|
int t = lua_gettop(L);
|
|
if (getPointsFunction != LUA_REFNIL) {
|
|
if (pcallFunc(L, getPointsFunction, 1)) {
|
|
uint32_t h = getPts(L);
|
|
if (h != ptsHash) {
|
|
ptsHash = h;
|
|
setLine();
|
|
}
|
|
lua_settop(L, t);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return LvglSimpleWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetLine::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, getPointsFunction);
|
|
LvglSimpleWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetLine::setColor(LcdFlags newColor)
|
|
{
|
|
if (lvobj && color.changedColor(newColor)) {
|
|
etx_line_color_from_flags(lvobj, color.flags);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetLine::setOpacity(uint8_t newOpa)
|
|
{
|
|
opacity.value = newOpa;
|
|
lv_obj_set_style_line_opa(lvobj, opacity.value, 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 (pts) {
|
|
if (!lvobj)
|
|
lvobj = lv_line_create(parent);
|
|
|
|
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)
|
|
{
|
|
if (pts) {
|
|
setColor(color.flags);
|
|
setOpacity(opacity.value);
|
|
setLine();
|
|
}
|
|
}
|
|
|
|
void LvglWidgetLine::refresh()
|
|
{
|
|
color.currVal = -1;
|
|
build(nullptr);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
LvglWidgetTriangle::LvglWidgetTriangle() : LvglSimpleWidgetObject()
|
|
{
|
|
parent = lvglManager->getCurrentParent()->getLvObj();
|
|
}
|
|
|
|
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);
|
|
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 LvglWidgetTriangle::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "pts")) {
|
|
if (lua_isfunction(L, -1)) {
|
|
getPointsFunction = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
getPt(L, 0);
|
|
getPt(L, 1);
|
|
getPt(L, 2);
|
|
}
|
|
} else {
|
|
LvglSimpleWidgetObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetTriangle::callRefs(lua_State *L)
|
|
{
|
|
int t = lua_gettop(L);
|
|
if (getPointsFunction != LUA_REFNIL) {
|
|
if (pcallFunc(L, getPointsFunction, 1)) {
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
getPt(L, 0);
|
|
getPt(L, 1);
|
|
getPt(L, 2);
|
|
lua_settop(L, t);
|
|
uint32_t h = hash(&pts, sizeof(pts));
|
|
if (h != ptsHash) {
|
|
ptsHash = h;
|
|
refresh();
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return LvglSimpleWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetTriangle::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, getPointsFunction);
|
|
LvglSimpleWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetTriangle::setColor(LcdFlags newColor)
|
|
{
|
|
if (lvobj && color.changedColor(newColor)) {
|
|
if (color.flags & RGB_FLAG) {
|
|
etx_remove_img_color(lvobj);
|
|
lv_obj_set_style_img_recolor(lvobj, makeLvColor(color.flags), 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.flags));
|
|
}
|
|
}
|
|
}
|
|
|
|
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.value, 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;
|
|
|
|
// Convert to relative coords
|
|
coord_t x1 = pts[0].x - x, y1 = pts[0].y - y;
|
|
coord_t x2 = pts[1].x - x, y2 = pts[1].y - y;
|
|
coord_t x3 = pts[2].x - x, y3 = pts[2].y - y;
|
|
|
|
coord_t t1x, t2x, ty, 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; ty = 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 ty 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 ty 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, ty); // 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;
|
|
ty += 1;
|
|
if (ty == 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 ty 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 ty 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, ty); // 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;
|
|
ty += 1;
|
|
if (ty > y3) return;
|
|
}
|
|
}
|
|
|
|
void LvglWidgetTriangle::build(lua_State *L)
|
|
{
|
|
// Bounds
|
|
x = min(min(pts[0].x, pts[1].x), pts[2].x);
|
|
y = min(min(pts[0].y, pts[1].y), pts[2].y);
|
|
w = max(max(pts[0].x, pts[1].x), pts[2].x) - x + 1;
|
|
h = max(max(pts[0].y, pts[1].y), pts[2].y) - y + 1;
|
|
|
|
// 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(parent);
|
|
lv_canvas_set_buffer(lvobj, (void*)mask->data, mask->width, mask->height,
|
|
LV_IMG_CF_ALPHA_8BIT);
|
|
|
|
// Set position and, size
|
|
setPos(x, y);
|
|
LvglSimpleWidgetObject::setSize(w,h);
|
|
|
|
// Set color
|
|
setColor(color.flags);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetTriangle::refresh()
|
|
{
|
|
if (mask) {
|
|
free(mask);
|
|
mask = nullptr;
|
|
}
|
|
if (lvobj) {
|
|
// May render incorrectly when trying to reuse previous canvas
|
|
lv_obj_del(lvobj);
|
|
lvobj = nullptr;
|
|
}
|
|
color.forceUpdate();
|
|
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 (luaScriptManager->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_OUTLINE);
|
|
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 (color.changedColor(newColor)) {
|
|
if (filled) {
|
|
etx_bg_color_from_flags(window->getLvObj(), color.flags);
|
|
} else {
|
|
if (color.flags & RGB_FLAG) {
|
|
etx_remove_border_color(window->getLvObj());
|
|
lv_obj_set_style_border_color(window->getLvObj(),
|
|
makeLvColor(color.flags), 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.flags));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LvglWidgetBorderedObject::setOpacity(uint8_t newOpa)
|
|
{
|
|
opacity.value = newOpa;
|
|
if (filled) {
|
|
lv_obj_set_style_bg_opa(window->getLvObj(), opacity.value, LV_PART_MAIN);
|
|
} else {
|
|
lv_obj_set_style_border_opa(window->getLvObj(), opacity.value, 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 (luaScriptManager->isWidget()) {
|
|
lv_obj_clear_flag(window->getLvObj(), LV_OBJ_FLAG_CLICKABLE);
|
|
} else {
|
|
etx_scrollbar(window->getLvObj());
|
|
}
|
|
setColor(color.flags);
|
|
setOpacity(opacity.value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetRoundObject::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "radius")) {
|
|
radius.parse(L);
|
|
} else {
|
|
LvglWidgetBorderedObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetRoundObject::callRefs(lua_State *L)
|
|
{
|
|
if (!pcallUpdate1Int(L, radius.function, [=](int val) { setRadius(val); }))
|
|
return false;
|
|
return LvglWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetRoundObject::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, radius.function);
|
|
LvglWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetRoundObject::setPos(coord_t x, coord_t y)
|
|
{
|
|
LvglWidgetObject::setPos(x - radius.coord, y - radius.coord);
|
|
}
|
|
|
|
void LvglWidgetRoundObject::setRadius(coord_t r)
|
|
{
|
|
// Set position to center
|
|
x += radius.coord;
|
|
y += radius.coord;
|
|
radius.coord = r;
|
|
w = radius.coord * 2;
|
|
h = radius.coord * 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);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetCircle::build(lua_State *L)
|
|
{
|
|
// Reset position to corner
|
|
setPos(x, y);
|
|
// Set width & height
|
|
setRadius(radius.coord);
|
|
LvglWidgetRoundObject::build(L);
|
|
lv_obj_set_style_radius(window->getLvObj(), LV_RADIUS_CIRCLE, LV_PART_MAIN);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetArc::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "rounded")) {
|
|
rounded = lua_toboolean(L, -1);
|
|
} else if (!strcmp(key, "startAngle")) {
|
|
startAngle.parse(L);
|
|
} else if (!strcmp(key, "endAngle")) {
|
|
endAngle.parse(L);
|
|
} else if (!strcmp(key, "bgColor")) {
|
|
bgColor.parse(L);
|
|
} else if (!strcmp(key, "bgOpacity")) {
|
|
bgOpacity.parse(L);
|
|
} else if (!strcmp(key, "bgStartAngle")) {
|
|
bgStartAngle.parse(L);
|
|
} else if (!strcmp(key, "bgEndAngle")) {
|
|
bgEndAngle.parse(L);
|
|
} else {
|
|
LvglWidgetRoundObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setColor(LcdFlags newColor)
|
|
{
|
|
if (color.changedColor(newColor)) {
|
|
etx_arc_color_from_flags(window->getLvObj(), color.flags, LV_PART_INDICATOR);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setBgColor(LcdFlags newColor)
|
|
{
|
|
if (bgColor.changedColor(newColor)) {
|
|
etx_arc_color_from_flags(window->getLvObj(), bgColor.flags, LV_PART_MAIN);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setOpacity(uint8_t newOpa)
|
|
{
|
|
if (opacity.changedValue(newOpa)) {
|
|
lv_obj_set_style_arc_opa(window->getLvObj(), opacity.value, LV_PART_INDICATOR);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setBgOpacity(uint8_t newOpa)
|
|
{
|
|
if (bgOpacity.changedValue(newOpa)) {
|
|
lv_obj_set_style_arc_opa(window->getLvObj(), bgOpacity.value, LV_PART_MAIN);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setStartAngle(coord_t angle)
|
|
{
|
|
if (startAngle.changedValue(angle)) {
|
|
lv_arc_set_start_angle(window->getLvObj(), startAngle.coord);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setBgStartAngle(coord_t angle)
|
|
{
|
|
if (bgStartAngle.changedValue(angle)) {
|
|
lv_arc_set_bg_start_angle(window->getLvObj(), bgStartAngle.coord);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setEndAngle(coord_t angle)
|
|
{
|
|
if (endAngle.changedValue(angle)) {
|
|
lv_arc_set_end_angle(window->getLvObj(), endAngle.coord);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetArc::setBgEndAngle(coord_t angle)
|
|
{
|
|
if (bgEndAngle.changedValue(angle)) {
|
|
lv_arc_set_bg_end_angle(window->getLvObj(), bgEndAngle.coord);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetArc::callRefs(lua_State *L)
|
|
{
|
|
if (!pcallUpdate1Int(L, startAngle.function,
|
|
[=](int val) { setStartAngle(val); }))
|
|
return false;
|
|
if (!pcallUpdate1Int(L, endAngle.function,
|
|
[=](int val) { setEndAngle(val); }))
|
|
return false;
|
|
if (!pcallUpdate1Int(L, bgStartAngle.function,
|
|
[=](int val) { setBgStartAngle(val); }))
|
|
return false;
|
|
if (!pcallUpdate1Int(L, bgEndAngle.function,
|
|
[=](int val) { setBgEndAngle(val); }))
|
|
return false;
|
|
return LvglWidgetRoundObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetArc::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, startAngle.function);
|
|
clearRef(L, endAngle.function);
|
|
clearRef(L, bgColor.function);
|
|
clearRef(L, bgOpacity.function);
|
|
clearRef(L, bgStartAngle.function);
|
|
clearRef(L, bgEndAngle.function);
|
|
LvglWidgetRoundObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetArc::build(lua_State *L)
|
|
{
|
|
// Reset position to corner
|
|
setPos(x, y);
|
|
// Set width & height
|
|
setRadius(radius.coord);
|
|
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);
|
|
lv_arc_set_range(window->getLvObj(), 0, 360);
|
|
lv_obj_remove_style(window->getLvObj(), NULL, LV_PART_KNOB);
|
|
lv_obj_set_style_arc_width(window->getLvObj(), thickness, LV_PART_MAIN);
|
|
lv_obj_set_style_arc_rounded(window->getLvObj(), rounded, LV_PART_MAIN);
|
|
lv_obj_set_style_arc_width(window->getLvObj(), thickness, LV_PART_INDICATOR);
|
|
lv_obj_set_style_arc_rounded(window->getLvObj(), rounded, LV_PART_INDICATOR);
|
|
setBgStartAngle(bgStartAngle.coord);
|
|
setBgEndAngle(bgEndAngle.coord);
|
|
setStartAngle(startAngle.coord);
|
|
setEndAngle(endAngle.coord);
|
|
setColor(color.flags);
|
|
setBgColor(bgColor.flags);
|
|
setOpacity(opacity.value);
|
|
setBgOpacity(bgOpacity.value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetImage::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "file")) {
|
|
filename.parse(L, LvglParamFuncOrValue::PTXT);
|
|
} else if (!strcmp(key, "fill")) {
|
|
fillFrame = lua_toboolean(L, -1);
|
|
} else {
|
|
LvglWidgetObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetImage::callRefs(lua_State *L)
|
|
{
|
|
int t = lua_gettop(L);
|
|
if (filename.function != LUA_REFNIL) {
|
|
if (pcallFunc(L, filename.function, 1)) {
|
|
const char *s = luaL_checkstring(L, -1);
|
|
if (filename.changedText(s)) {
|
|
((StaticImage*)window)->setSource(filename.txt);
|
|
}
|
|
lua_settop(L, t);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return LvglWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetImage::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, filename.function);
|
|
LvglWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetImage::build(lua_State *L)
|
|
{
|
|
window = new StaticImage(lvglManager->getCurrentParent(), {x, y, w, h},
|
|
filename.txt, 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.flags), colorToRGB(bgColor));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetTextButtonBase::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "text")) {
|
|
txt.parse(L, LvglParamFuncOrValue::PTXT);
|
|
} 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.parse(L);
|
|
} else if (!strcmp(key, "press")) {
|
|
pressFunction = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
LvglWidgetObject::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
bool LvglWidgetTextButtonBase::callRefs(lua_State *L)
|
|
{
|
|
int t = lua_gettop(L);
|
|
if (txt.function != LUA_REFNIL) {
|
|
if (pcallFunc(L, txt.function, 1)) {
|
|
const char *s = luaL_checkstring(L, -1);
|
|
setText(s);
|
|
lua_settop(L, t);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
if (!pcallUpdate1Int(L, textColor.function,
|
|
[=](int color) { setTextColor(color); }))
|
|
return false;
|
|
return LvglWidgetObject::callRefs(L);
|
|
}
|
|
|
|
void LvglWidgetTextButtonBase::clearRefs(lua_State *L)
|
|
{
|
|
clearRef(L, txt.function);
|
|
clearRef(L, textColor.function);
|
|
clearRef(L, pressFunction);
|
|
LvglWidgetObject::clearRefs(L);
|
|
}
|
|
|
|
void LvglWidgetTextButtonBase::setText(const char *s)
|
|
{
|
|
if (txt.changedText(s)) {
|
|
((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)
|
|
{
|
|
if (color.changedColor(newColor)) {
|
|
etx_bg_color_from_flags(window->getLvObj(), color.flags);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetTextButtonBase::setTextColor(LcdFlags newColor)
|
|
{
|
|
if (textColor.changedColor(newColor)) {
|
|
etx_txt_color_from_flags(window->getLvObj(), textColor.flags);
|
|
}
|
|
}
|
|
|
|
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.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.flags);
|
|
setTextColor(textColor.flags);
|
|
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.txt,
|
|
[=]() {
|
|
pcallSimpleFunc(L, pressFunction);
|
|
},
|
|
[=]() {
|
|
pcallSimpleFunc(L, releaseFunction);
|
|
});
|
|
setFont(font);
|
|
setColor(color.flags);
|
|
setTextColor(textColor.flags);
|
|
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)
|
|
{
|
|
if (color.changedColor(newColor)) {
|
|
((SliderBase*)window)->setColor(color.flags);
|
|
}
|
|
}
|
|
|
|
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.flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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.flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class WidgetPage : public NavWindow, public LuaEventHandler
|
|
{
|
|
public:
|
|
WidgetPage(Window *parent, std::function<void()> 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<void()> backAction;
|
|
PageHeader *header = nullptr;
|
|
Window *body = nullptr;
|
|
|
|
bool bubbleEvents() override { return true; }
|
|
|
|
void onClicked() override { Keyboard::hide(false); LuaEventHandler::onClickedEvent(); }
|
|
|
|
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<void()> onClose) :
|
|
BaseDialog(title, true, w, h, false),
|
|
onClose(std::move(onClose))
|
|
{
|
|
form->setHeight(h - EdgeTxStyles::UI_ELEMENT_HEIGHT);
|
|
}
|
|
|
|
Window *getBody() { return form; }
|
|
|
|
void onCancel() override
|
|
{
|
|
onClose();
|
|
BaseDialog::onCancel();
|
|
}
|
|
|
|
protected:
|
|
std::function<void()> onClose;
|
|
};
|
|
|
|
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); });
|
|
dialog = dlg;
|
|
window = dlg->getBody();
|
|
window->setWidth(w);
|
|
setFlex();
|
|
}
|
|
|
|
void LvglWidgetDialog::close()
|
|
{
|
|
dialog->onCancel();
|
|
clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void LvglWidgetFilePicker::parseParam(lua_State *L, const char *key)
|
|
{
|
|
if (!strcmp(key, "title")) {
|
|
title = luaL_checkstring(L, -1);
|
|
} else if (!strcmp(key, "folder")) {
|
|
folder = luaL_checkstring(L, -1);
|
|
} else if (!strcmp(key, "extension")) {
|
|
extension = luaL_checkstring(L, -1);
|
|
} else if (!strcmp(key, "maxLen")) {
|
|
maxLen = luaL_checkunsigned(L, -1);
|
|
} else if (!strcmp(key, "hideExtension")) {
|
|
hideExtension = lua_toboolean(L, -1);
|
|
} else {
|
|
LvglWidgetPicker::parseParam(L, key);
|
|
}
|
|
}
|
|
|
|
void LvglWidgetFilePicker::build(lua_State *L)
|
|
{
|
|
if (h == LV_SIZE_CONTENT) h = 0;
|
|
auto c = new FileChoice(
|
|
lvglManager->getCurrentParent(), {x, y, w, h},
|
|
folder, extension, maxLen,
|
|
[=]() { return pcallGetStringVal(L, getFunction); },
|
|
[=](std::string val) { pcallSetStringVal(L, setFunction, val.c_str()); },
|
|
hideExtension, title);
|
|
window = c;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|