mirror of
https://github.com/esphome/esphome.git
synced 2024-12-21 16:27:44 +01:00
[lvgl] Revise code generation to allow early widget creation (#7611)
This commit is contained in:
parent
f490585f66
commit
8bbe4efded
@ -22,7 +22,7 @@ from esphome.helpers import write_file_if_changed
|
||||
|
||||
from . import defines as df, helpers, lv_validation as lvalid
|
||||
from .automation import disp_update, focused_widgets, update_to_code
|
||||
from .defines import CONF_WIDGETS, add_define
|
||||
from .defines import add_define
|
||||
from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code
|
||||
from .gradient import GRADIENT_SCHEMA, gradients_to_code
|
||||
from .hello_world import get_hello_world
|
||||
@ -54,7 +54,7 @@ from .types import (
|
||||
lv_style_t,
|
||||
lvgl_ns,
|
||||
)
|
||||
from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used
|
||||
from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used
|
||||
from .widgets.animimg import animimg_spec
|
||||
from .widgets.arc import arc_spec
|
||||
from .widgets.button import button_spec
|
||||
@ -186,7 +186,7 @@ def final_validation(config):
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_library("lvgl/lvgl", "8.4.0")
|
||||
CORE.add_define("USE_LVGL")
|
||||
cg.add_define("USE_LVGL")
|
||||
# suppress default enabling of extra widgets
|
||||
add_define("_LV_KCONFIG_PRESENT")
|
||||
# Always enable - lots of things use it.
|
||||
@ -200,7 +200,13 @@ async def to_code(config):
|
||||
add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc")
|
||||
add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"')
|
||||
|
||||
add_define("LV_LOG_LEVEL", f"LV_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}")
|
||||
add_define(
|
||||
"LV_LOG_LEVEL", f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config[df.CONF_LOG_LEVEL]]}"
|
||||
)
|
||||
cg.add_define(
|
||||
"LVGL_LOG_LEVEL",
|
||||
cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}"),
|
||||
)
|
||||
add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH])
|
||||
for font in helpers.lv_fonts_used:
|
||||
add_define(f"LV_FONT_{font.upper()}")
|
||||
@ -214,15 +220,9 @@ async def to_code(config):
|
||||
"LV_COLOR_CHROMA_KEY",
|
||||
await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]),
|
||||
)
|
||||
CORE.add_build_flag("-Isrc")
|
||||
cg.add_build_flag("-Isrc")
|
||||
|
||||
cg.add_global(lvgl_ns.using)
|
||||
lv_component = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(lv_component, config)
|
||||
Widget.create(config[CONF_ID], lv_component, obj_spec, config)
|
||||
for display in config[df.CONF_DISPLAYS]:
|
||||
cg.add(lv_component.add_display(await cg.get_variable(display)))
|
||||
|
||||
frac = config[CONF_BUFFER_SIZE]
|
||||
if frac >= 0.75:
|
||||
frac = 1
|
||||
@ -232,10 +232,17 @@ async def to_code(config):
|
||||
frac = 4
|
||||
else:
|
||||
frac = 8
|
||||
cg.add(lv_component.set_buffer_frac(int(frac)))
|
||||
cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH]))
|
||||
cg.add(lv_component.set_draw_rounding(config[df.CONF_DRAW_ROUNDING]))
|
||||
cg.add(lv_component.set_resume_on_input(config[df.CONF_RESUME_ON_INPUT]))
|
||||
displays = [await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]]
|
||||
lv_component = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
displays,
|
||||
frac,
|
||||
config[df.CONF_FULL_REFRESH],
|
||||
config[df.CONF_DRAW_ROUNDING],
|
||||
config[df.CONF_RESUME_ON_INPUT],
|
||||
)
|
||||
await cg.register_component(lv_component, config)
|
||||
Widget.create(config[CONF_ID], lv_component, obj_spec, config)
|
||||
|
||||
for font in helpers.esphome_fonts_used:
|
||||
await cg.get_variable(font)
|
||||
@ -257,6 +264,7 @@ async def to_code(config):
|
||||
else:
|
||||
add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font))
|
||||
|
||||
lv_scr_act = get_scr_act(lv_component)
|
||||
async with LvContext(lv_component):
|
||||
await touchscreens_to_code(lv_component, config)
|
||||
await encoders_to_code(lv_component, config)
|
||||
@ -266,11 +274,9 @@ async def to_code(config):
|
||||
await set_obj_properties(lv_scr_act, config)
|
||||
await add_widgets(lv_scr_act, config)
|
||||
await add_pages(lv_component, config)
|
||||
await add_top_layer(config)
|
||||
await msgboxes_to_code(config)
|
||||
await disp_update(f"{lv_component}->get_disp()", config)
|
||||
# At this point only the setup code should be generated
|
||||
assert LvContext.added_lambda_count == 1
|
||||
await add_top_layer(lv_component, config)
|
||||
await msgboxes_to_code(lv_component, config)
|
||||
await disp_update(lv_component.get_disp(), config)
|
||||
# Set this directly since we are limited in how many methods can be added to the Widget class.
|
||||
Widget.widgets_completed = True
|
||||
async with LvContext(lv_component):
|
||||
@ -291,15 +297,15 @@ async def to_code(config):
|
||||
await build_automation(resume_trigger, [], conf)
|
||||
|
||||
for comp in helpers.lvgl_components_required:
|
||||
CORE.add_define(f"USE_LVGL_{comp.upper()}")
|
||||
cg.add_define(f"USE_LVGL_{comp.upper()}")
|
||||
if "transform_angle" in styles_used:
|
||||
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
||||
for use in helpers.lv_uses:
|
||||
add_define(f"LV_USE_{use.upper()}")
|
||||
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
||||
write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
|
||||
CORE.add_build_flag("-DLV_CONF_H=1")
|
||||
CORE.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
|
||||
cg.add_build_flag("-DLV_CONF_H=1")
|
||||
cg.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"')
|
||||
|
||||
|
||||
def display_schema(config):
|
||||
@ -308,9 +314,9 @@ def display_schema(config):
|
||||
|
||||
|
||||
def add_hello_world(config):
|
||||
if CONF_WIDGETS not in config and CONF_PAGES not in config:
|
||||
if df.CONF_WIDGETS not in config and CONF_PAGES not in config:
|
||||
LOGGER.info("No pages or widgets configured, creating default hello_world page")
|
||||
config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
|
||||
config[df.CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world())
|
||||
return config
|
||||
|
||||
|
||||
@ -329,7 +335,7 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
|
||||
cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
|
||||
cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
|
||||
*df.LOG_LEVELS, upper=True
|
||||
*df.LV_LOG_LEVELS, upper=True
|
||||
),
|
||||
cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of(
|
||||
"big_endian", "little_endian"
|
||||
|
@ -5,7 +5,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT
|
||||
from esphome.cpp_generator import RawExpression, get_variable
|
||||
from esphome.cpp_generator import get_variable
|
||||
from esphome.cpp_types import nullptr
|
||||
|
||||
from .defines import (
|
||||
@ -23,7 +23,6 @@ from .lvcode import (
|
||||
UPDATE_EVENT,
|
||||
LambdaContext,
|
||||
LocalVariable,
|
||||
LvConditional,
|
||||
LvglComponent,
|
||||
ReturnStatement,
|
||||
add_line_marks,
|
||||
@ -47,8 +46,8 @@ from .types import (
|
||||
)
|
||||
from .widgets import (
|
||||
Widget,
|
||||
get_scr_act,
|
||||
get_widgets,
|
||||
lv_scr_act,
|
||||
set_obj_properties,
|
||||
wait_for_widgets,
|
||||
)
|
||||
@ -66,8 +65,6 @@ async def action_to_code(
|
||||
):
|
||||
await wait_for_widgets()
|
||||
async with LambdaContext(parameters=args, where=action_id) as context:
|
||||
with LvConditional(lv_expr.is_pre_initialise()):
|
||||
context.add(RawExpression("return"))
|
||||
for widget in widgets:
|
||||
await action(widget)
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
@ -126,7 +123,7 @@ async def lvgl_is_idle(config, condition_id, template_arg, args):
|
||||
async def disp_update(disp, config: dict):
|
||||
if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config:
|
||||
return
|
||||
with LocalVariable("lv_disp_tmp", lv_disp_t, literal(disp)) as disp_temp:
|
||||
with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp:
|
||||
if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None:
|
||||
lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color))
|
||||
if bg_image := config.get(CONF_DISP_BG_IMAGE):
|
||||
@ -136,15 +133,24 @@ async def disp_update(disp, config: dict):
|
||||
@automation.register_action(
|
||||
"lvgl.widget.redraw",
|
||||
ObjUpdateAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ID): cv.use_id(lv_obj_t),
|
||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||
}
|
||||
cv.Any(
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(lv_obj_t),
|
||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||
},
|
||||
key=CONF_ID,
|
||||
),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent),
|
||||
}
|
||||
),
|
||||
),
|
||||
)
|
||||
async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
||||
widgets = await get_widgets(config) or [lv_scr_act]
|
||||
lv_comp = await cg.get_variable(config[CONF_LVGL_ID])
|
||||
widgets = await get_widgets(config) or [get_scr_act(lv_comp)]
|
||||
|
||||
async def do_invalidate(widget: Widget):
|
||||
lv_obj.invalidate(widget.obj)
|
||||
@ -164,7 +170,7 @@ async def obj_invalidate_to_code(config, action_id, template_arg, args):
|
||||
async def lvgl_update_to_code(config, action_id, template_arg, args):
|
||||
widgets = await get_widgets(config)
|
||||
w = widgets[0]
|
||||
disp = f"{w.obj}->get_disp()"
|
||||
disp = literal(f"{w.obj}->get_disp()")
|
||||
async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context:
|
||||
await disp_update(disp, config)
|
||||
var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda())
|
||||
|
@ -189,14 +189,14 @@ LV_ANIM = LvConstant(
|
||||
LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER")
|
||||
LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF")
|
||||
|
||||
LOG_LEVELS = (
|
||||
"TRACE",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"USER",
|
||||
"NONE",
|
||||
)
|
||||
LV_LOG_LEVELS = {
|
||||
"VERBOSE": "TRACE",
|
||||
"DEBUG": "TRACE",
|
||||
"INFO": "INFO",
|
||||
"WARN": "WARN",
|
||||
"ERROR": "ERROR",
|
||||
"NONE": "NONE",
|
||||
}
|
||||
|
||||
LV_LONG_MODES = LvConstant(
|
||||
"LV_LABEL_LONG_",
|
||||
|
@ -183,17 +183,11 @@ class LvContext(LambdaContext):
|
||||
super().__init__(parameters=self.args)
|
||||
self.lv_component = lv_component
|
||||
|
||||
async def add_init_lambda(self):
|
||||
if self.code_list:
|
||||
cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
|
||||
LvContext.added_lambda_count += 1
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
await super().__aexit__(exc_type, exc_val, exc_tb)
|
||||
await self.add_init_lambda()
|
||||
|
||||
def add(self, expression: Union[Expression, Statement]):
|
||||
self.code_list.append(self.indented_statement(expression))
|
||||
cg.add(expression)
|
||||
return expression
|
||||
|
||||
def __call__(self, *args):
|
||||
|
@ -11,12 +11,6 @@ namespace esphome {
|
||||
namespace lvgl {
|
||||
static const char *const TAG = "lvgl";
|
||||
|
||||
#if LV_USE_LOG
|
||||
static void log_cb(const char *buf) {
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
|
||||
}
|
||||
#endif // LV_USE_LOG
|
||||
|
||||
static const char *const EVENT_NAMES[] = {
|
||||
"NONE",
|
||||
"PRESSED",
|
||||
@ -383,26 +377,48 @@ void LvglComponent::write_random_() {
|
||||
}
|
||||
}
|
||||
|
||||
void LvglComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "LVGL Setup starts");
|
||||
#if LV_USE_LOG
|
||||
lv_log_register_print_cb(log_cb);
|
||||
#endif
|
||||
/**
|
||||
* @class LvglComponent
|
||||
* @brief Component for rendering LVGL.
|
||||
*
|
||||
* This component renders LVGL widgets on a display. Some initialisation must be done in the constructor
|
||||
* since LVGL needs to be initialised before any widgets can be created.
|
||||
*
|
||||
* @param displays a list of displays to render onto. All displays must have the same
|
||||
* resolution.
|
||||
* @param buffer_frac the fraction of the display resolution to use for the LVGL
|
||||
* draw buffer. A higher value will make animations smoother but
|
||||
* also increase memory usage.
|
||||
* @param full_refresh if true, the display will be fully refreshed on every frame.
|
||||
* If false, only changed areas will be updated.
|
||||
* @param draw_rounding the rounding to use when drawing. A value of 1 will draw
|
||||
* without any rounding, a value of 2 will round to the nearest
|
||||
* multiple of 2, and so on.
|
||||
* @param resume_on_input if true, this component will resume rendering when the user
|
||||
* presses a key or clicks on the screen.
|
||||
*/
|
||||
LvglComponent::LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh,
|
||||
int draw_rounding, bool resume_on_input)
|
||||
: draw_rounding(draw_rounding),
|
||||
displays_(std::move(displays)),
|
||||
buffer_frac_(buffer_frac),
|
||||
full_refresh_(full_refresh),
|
||||
resume_on_input_(resume_on_input) {
|
||||
lv_init();
|
||||
lv_update_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
||||
lv_api_event = static_cast<lv_event_code_t>(lv_event_register_id());
|
||||
auto *display = this->displays_[0];
|
||||
size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_;
|
||||
auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
|
||||
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
|
||||
if (buf == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
||||
#endif
|
||||
this->mark_failed();
|
||||
this->status_set_error("Memory allocation failure");
|
||||
return;
|
||||
this->rotation = display->get_rotation();
|
||||
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
|
||||
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
|
||||
if (this->rotate_buf_ == nullptr)
|
||||
return;
|
||||
}
|
||||
auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT
|
||||
if (buf == nullptr)
|
||||
return;
|
||||
lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels);
|
||||
lv_disp_drv_init(&this->disp_drv_);
|
||||
this->disp_drv_.draw_buf = &this->draw_buf_;
|
||||
@ -410,18 +426,7 @@ void LvglComponent::setup() {
|
||||
this->disp_drv_.full_refresh = this->full_refresh_;
|
||||
this->disp_drv_.flush_cb = static_flush_cb;
|
||||
this->disp_drv_.rounder_cb = rounder_cb;
|
||||
this->rotation = display->get_rotation();
|
||||
if (this->rotation != display::DISPLAY_ROTATION_0_DEGREES) {
|
||||
this->rotate_buf_ = static_cast<lv_color_t *>(lv_custom_mem_alloc(buf_bytes)); // NOLINT
|
||||
if (this->rotate_buf_ == nullptr) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
|
||||
ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes);
|
||||
#endif
|
||||
this->mark_failed();
|
||||
this->status_set_error("Memory allocation failure");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// reset the display rotation since we will handle all rotations
|
||||
display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES);
|
||||
switch (this->rotation) {
|
||||
default:
|
||||
@ -435,12 +440,30 @@ void LvglComponent::setup() {
|
||||
break;
|
||||
}
|
||||
this->disp_ = lv_disp_drv_register(&this->disp_drv_);
|
||||
for (const auto &v : this->init_lambdas_)
|
||||
v(this);
|
||||
}
|
||||
|
||||
void LvglComponent::setup() {
|
||||
if (this->draw_buf_.buf1 == nullptr) {
|
||||
this->mark_failed();
|
||||
this->status_set_error("Memory allocation failure");
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, "LVGL Setup starts");
|
||||
#if LV_USE_LOG
|
||||
lv_log_register_print_cb([](const char *buf) {
|
||||
auto next = strchr(buf, ')');
|
||||
if (next != nullptr)
|
||||
buf = next + 1;
|
||||
while (isspace(*buf))
|
||||
buf++;
|
||||
esp_log_printf_(LVGL_LOG_LEVEL, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf);
|
||||
});
|
||||
#endif
|
||||
this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0);
|
||||
lv_disp_trig_activity(this->disp_);
|
||||
ESP_LOGCONFIG(TAG, "LVGL Setup complete");
|
||||
}
|
||||
|
||||
void LvglComponent::update() {
|
||||
// update indicators
|
||||
if (this->paused_) {
|
||||
@ -455,13 +478,6 @@ void LvglComponent::loop() {
|
||||
}
|
||||
lv_timer_handler_run_in_period(5);
|
||||
}
|
||||
bool lv_is_pre_initialise() {
|
||||
if (!lv_is_initialized()) {
|
||||
ESP_LOGE(TAG, "LVGL call before component is initialised");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_LVGL_ANIMIMG
|
||||
void lv_animimg_stop(lv_obj_t *obj) {
|
||||
|
@ -39,7 +39,6 @@ namespace lvgl {
|
||||
extern lv_event_code_t lv_api_event; // NOLINT
|
||||
extern lv_event_code_t lv_update_event; // NOLINT
|
||||
extern std::string lv_event_code_name_for(uint8_t event_code);
|
||||
extern bool lv_is_pre_initialise();
|
||||
#if LV_COLOR_DEPTH == 16
|
||||
static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565;
|
||||
#elif LV_COLOR_DEPTH == 32
|
||||
@ -108,6 +107,8 @@ class LvglComponent : public PollingComponent {
|
||||
constexpr static const char *const TAG = "lvgl";
|
||||
|
||||
public:
|
||||
LvglComponent(std::vector<display::Display *> displays, float buffer_frac, bool full_refresh, int draw_rounding,
|
||||
bool resume_on_input);
|
||||
static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||
@ -118,13 +119,10 @@ class LvglComponent : public PollingComponent {
|
||||
this->idle_callbacks_.add(std::move(callback));
|
||||
}
|
||||
void add_on_pause_callback(std::function<void(bool)> &&callback) { this->pause_callbacks_.add(std::move(callback)); }
|
||||
void add_display(display::Display *display) { this->displays_.push_back(display); }
|
||||
void add_init_lambda(const std::function<void(LvglComponent *)> &lamb) { this->init_lambdas_.push_back(lamb); }
|
||||
void dump_config() override;
|
||||
void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; }
|
||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||
void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; }
|
||||
lv_disp_t *get_disp() { return this->disp_; }
|
||||
lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
|
||||
// Pause or resume the display.
|
||||
// @param paused If true, pause the display. If false, resume the display.
|
||||
// @param show_snow If true, show the snow effect when paused.
|
||||
@ -155,17 +153,19 @@ class LvglComponent : public PollingComponent {
|
||||
}
|
||||
// rounding factor to align bounds of update area when drawing
|
||||
size_t draw_rounding{2};
|
||||
void set_draw_rounding(size_t rounding) { this->draw_rounding = rounding; }
|
||||
void set_resume_on_input(bool resume_on_input) { this->resume_on_input_ = resume_on_input; }
|
||||
|
||||
// if set to true, the bounds of the update area will always start at 0,0
|
||||
display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES};
|
||||
|
||||
protected:
|
||||
void write_random_();
|
||||
void draw_buffer_(const lv_area_t *area, lv_color_t *ptr);
|
||||
void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||
|
||||
std::vector<display::Display *> displays_{};
|
||||
size_t buffer_frac_{1};
|
||||
bool full_refresh_{};
|
||||
bool resume_on_input_{};
|
||||
|
||||
lv_disp_draw_buf_t draw_buf_{};
|
||||
lv_disp_drv_t disp_drv_{};
|
||||
lv_disp_t *disp_{};
|
||||
@ -174,14 +174,10 @@ class LvglComponent : public PollingComponent {
|
||||
size_t current_page_{0};
|
||||
bool show_snow_{};
|
||||
bool page_wrap_{true};
|
||||
bool resume_on_input_{};
|
||||
std::map<lv_group_t *, lv_obj_t *> focus_marks_{};
|
||||
|
||||
std::vector<std::function<void(LvglComponent *lv_component)>> init_lambdas_;
|
||||
CallbackManager<void(uint32_t)> idle_callbacks_{};
|
||||
CallbackManager<void(bool)> pause_callbacks_{};
|
||||
size_t buffer_frac_{1};
|
||||
bool full_refresh_{};
|
||||
lv_color_t *rotate_buf_{};
|
||||
};
|
||||
|
||||
|
@ -17,8 +17,6 @@ from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr
|
||||
from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map
|
||||
from .widgets.obj import obj_spec
|
||||
|
||||
TOP_LAYER = literal("lv_disp_get_layer_top(lv_component->get_disp())")
|
||||
|
||||
|
||||
async def styles_to_code(config):
|
||||
"""Convert styles to C__ code."""
|
||||
@ -51,9 +49,10 @@ async def theme_to_code(config):
|
||||
lv_assign(apply, await context.get_lambda())
|
||||
|
||||
|
||||
async def add_top_layer(config):
|
||||
async def add_top_layer(lv_component, config):
|
||||
top_layer = lv.disp_get_layer_top(lv_component.get_disp())
|
||||
if top_conf := config.get(CONF_TOP_LAYER):
|
||||
with LocalVariable("top_layer", lv_obj_t, TOP_LAYER) as top_layer_obj:
|
||||
with LocalVariable("top_layer", lv_obj_t, top_layer) as top_layer_obj:
|
||||
top_w = Widget(top_layer_obj, obj_spec, top_conf)
|
||||
await set_obj_properties(top_w, top_conf)
|
||||
await add_widgets(top_w, top_conf)
|
||||
|
@ -55,18 +55,6 @@ theme_widget_map = {}
|
||||
styles_used = set()
|
||||
|
||||
|
||||
class LvScrActType(WidgetType):
|
||||
"""
|
||||
A "widget" representing the active screen.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("lv_scr_act()", lv_obj_t, ())
|
||||
|
||||
async def to_code(self, w, config: dict):
|
||||
return []
|
||||
|
||||
|
||||
class Widget:
|
||||
"""
|
||||
Represents a Widget.
|
||||
@ -221,6 +209,25 @@ class Widget:
|
||||
widget_map: dict[Any, Widget] = {}
|
||||
|
||||
|
||||
class LvScrActType(WidgetType):
|
||||
"""
|
||||
A "widget" representing the active screen.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("lv_scr_act()", lv_obj_t, ())
|
||||
|
||||
async def to_code(self, w, config: dict):
|
||||
return []
|
||||
|
||||
|
||||
lv_scr_act_spec = LvScrActType()
|
||||
|
||||
|
||||
def get_scr_act(lv_comp: MockObj) -> Widget:
|
||||
return Widget.create(None, lv_comp.get_scr_act(), lv_scr_act_spec, {})
|
||||
|
||||
|
||||
def get_widget_generator(wid):
|
||||
"""
|
||||
Used to wait for a widget during code generation.
|
||||
@ -451,7 +458,3 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent):
|
||||
await set_obj_properties(w, w_cnfig)
|
||||
await add_widgets(w, w_cnfig)
|
||||
await spec.to_code(w, w_cnfig)
|
||||
|
||||
|
||||
lv_scr_act_spec = LvScrActType()
|
||||
lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {})
|
||||
|
@ -20,6 +20,7 @@ from ..lvcode import (
|
||||
EVENT_ARG,
|
||||
LambdaContext,
|
||||
LocalVariable,
|
||||
lv,
|
||||
lv_add,
|
||||
lv_assign,
|
||||
lv_expr,
|
||||
@ -27,7 +28,6 @@ from ..lvcode import (
|
||||
lv_Pvariable,
|
||||
)
|
||||
from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema
|
||||
from ..styles import TOP_LAYER
|
||||
from ..types import LV_EVENT, char_ptr, lv_obj_t
|
||||
from . import Widget, set_obj_properties
|
||||
from .button import button_spec
|
||||
@ -59,7 +59,7 @@ MSGBOX_SCHEMA = container_schema(
|
||||
)
|
||||
|
||||
|
||||
async def msgbox_to_code(conf):
|
||||
async def msgbox_to_code(top_layer, conf):
|
||||
"""
|
||||
Construct a message box. This consists of a full-screen translucent background enclosing a centered container
|
||||
with an optional title, body, close button and a button matrix. And any other widgets the user cares to add
|
||||
@ -101,7 +101,7 @@ async def msgbox_to_code(conf):
|
||||
text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
|
||||
title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
|
||||
close_button = conf[CONF_CLOSE_BUTTON]
|
||||
lv_assign(outer, lv_expr.obj_create(TOP_LAYER))
|
||||
lv_assign(outer, lv_expr.obj_create(top_layer))
|
||||
lv_obj.set_width(outer, lv_pct(100))
|
||||
lv_obj.set_height(outer, lv_pct(100))
|
||||
lv_obj.set_style_bg_opa(outer, 128, 0)
|
||||
@ -141,6 +141,7 @@ async def msgbox_to_code(conf):
|
||||
set_btn_data(buttonmatrix.obj, ctrl_list, width_list)
|
||||
|
||||
|
||||
async def msgboxes_to_code(config):
|
||||
async def msgboxes_to_code(lv_component, config):
|
||||
top_layer = lv.disp_get_layer_top(lv_component.get_disp())
|
||||
for conf in config.get(CONF_MSGBOXES, ()):
|
||||
await msgbox_to_code(conf)
|
||||
await msgbox_to_code(top_layer, conf)
|
||||
|
@ -12,12 +12,12 @@ substitutions:
|
||||
arrow_down: "\U000F004B"
|
||||
|
||||
lvgl:
|
||||
log_level: debug
|
||||
resume_on_input: true
|
||||
on_pause:
|
||||
logger.log: LVGL is Paused
|
||||
on_resume:
|
||||
logger.log: LVGL has resumed
|
||||
log_level: TRACE
|
||||
bg_color: light_blue
|
||||
disp_bg_color: color_id
|
||||
disp_bg_image: cat_image
|
||||
@ -125,6 +125,8 @@ lvgl:
|
||||
on_unload:
|
||||
- logger.log: page unloaded
|
||||
- lvgl.widget.focus: mark
|
||||
- lvgl.widget.redraw: hello_label
|
||||
- lvgl.widget.redraw:
|
||||
on_all_events:
|
||||
logger.log:
|
||||
format: "Event %s"
|
||||
|
Loading…
Reference in New Issue
Block a user