From 8bbe4efded8803f0686eb4c71bf9b980a1a67456 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:20:19 +1100 Subject: [PATCH] [lvgl] Revise code generation to allow early widget creation (#7611) --- esphome/components/lvgl/__init__.py | 58 +++++++------ esphome/components/lvgl/automation.py | 32 ++++--- esphome/components/lvgl/defines.py | 16 ++-- esphome/components/lvgl/lvcode.py | 8 +- esphome/components/lvgl/lvgl_esphome.cpp | 96 ++++++++++++--------- esphome/components/lvgl/lvgl_esphome.h | 20 ++--- esphome/components/lvgl/styles.py | 7 +- esphome/components/lvgl/widgets/__init__.py | 35 ++++---- esphome/components/lvgl/widgets/msgbox.py | 11 +-- tests/components/lvgl/lvgl-package.yaml | 4 +- 10 files changed, 155 insertions(+), 132 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 86fdc7d763..beaf279a9a 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -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" diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index cdc7553e81..48472354f8 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -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()) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index c8ece02677..4d48028611 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -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_", diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 3a080d63e9..37d6670b84 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -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): diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 5a6c66c677..413b039af0 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -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 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_register_id()); lv_api_event = static_cast(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_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_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) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 2d326f4ae2..b8c0f5738e 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -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 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 &&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 &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 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 focus_marks_{}; - std::vector> init_lambdas_; CallbackManager idle_callbacks_{}; CallbackManager pause_callbacks_{}; - size_t buffer_frac_{1}; - bool full_refresh_{}; lv_color_t *rotate_buf_{}; }; diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 030db5fd22..6332e0976f 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -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) diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 35ee6c54e8..e946a96000 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -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, {}) diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 1af4ed6e05..be0f2100d7 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -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) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 1f09bc22eb..8d515280c9 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -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"