From 00caf53dc4c7d7efc222aec82e48e6a84cc8f6f2 Mon Sep 17 00:00:00 2001 From: Michael Davidson Date: Tue, 19 Dec 2023 22:38:02 +1100 Subject: [PATCH] Add first implementation of a DisplayRenderingPanel Makes use of a standard display_writer_t style Lambda so users can use arbitrary APIs from the Display Rendering Engine within the layout engine --- .../components/graphical_layout/__init__.py | 10 ++++++ .../display_rendering_panel.cpp | 26 +++++++++++++++ .../display_rendering_panel.h | 32 +++++++++++++++++++ .../display_rendering_panel.py | 27 ++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 esphome/components/graphical_layout/display_rendering_panel.cpp create mode 100644 esphome/components/graphical_layout/display_rendering_panel.h create mode 100644 esphome/components/graphical_layout/display_rendering_panel.py diff --git a/esphome/components/graphical_layout/__init__.py b/esphome/components/graphical_layout/__init__.py index f98892714d..8596ab4d03 100644 --- a/esphome/components/graphical_layout/__init__.py +++ b/esphome/components/graphical_layout/__init__.py @@ -5,6 +5,7 @@ from esphome.const import CONF_ID from . import horizontal_stack from . import vertical_stack from . import text_panel +from . import display_rendering_panel graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout") RootLayoutComponent = graphical_layout_ns.class_("RootLayoutComponent", cg.Component) @@ -68,6 +69,14 @@ ITEM_TYPE_SCHEMA = cv.typed_schema( ), } ), + display_rendering_panel.CONF_TYPE: BASE_ITEM_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(display_rendering_panel.DisplayRenderingPanel), + cv.Required(display_rendering_panel.CONF_WIDTH): cv.templatable(cv.int_range(min=1)), + cv.Required(display_rendering_panel.CONF_HEIGHT): cv.templatable(cv.int_range(min=1)), + cv.Required(display_rendering_panel.CONF_LAMBDA): cv.lambda_, + } + ), } ) @@ -75,6 +84,7 @@ CODE_GENERATORS = { text_panel.CONF_TYPE: text_panel.config_to_layout_item, horizontal_stack.CONF_TYPE: horizontal_stack.config_to_layout_item, vertical_stack.CONF_TYPE: vertical_stack.config_to_layout_item, + display_rendering_panel.CONF_TYPE: display_rendering_panel.config_to_layout_item, } CONFIG_SCHEMA = cv.Schema( diff --git a/esphome/components/graphical_layout/display_rendering_panel.cpp b/esphome/components/graphical_layout/display_rendering_panel.cpp new file mode 100644 index 0000000000..f4fe8d8d2e --- /dev/null +++ b/esphome/components/graphical_layout/display_rendering_panel.cpp @@ -0,0 +1,26 @@ +#include "display_rendering_panel.h" + +#include "esphome/components/display/display.h" +#include "esphome/components/display/rect.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace graphical_layout { + +static const char *const TAG = "displayrenderingpanel"; + +void DisplayRenderingPanel::dump_config(int indent_depth, int additional_level_depth) { + ESP_LOGCONFIG(TAG, "%*sDimensions: %ix%i", indent_depth, "", this->width_, this->height_); + ESP_LOGCONFIG(TAG, "%*sHas drawing lambda: %s", indent_depth, "", YESNO(this->lambda_ != nullptr)); +} + +const display::Rect DisplayRenderingPanel::measure_item(display::Display *display) { + return display::Rect(0, 0, this->width_, this->width_); +} + +void DisplayRenderingPanel::render(display::Display *display, display::Rect bounds) { + this->lambda_(*display); +} + +} // namespace graphical_layout +} // namespace esphome diff --git a/esphome/components/graphical_layout/display_rendering_panel.h b/esphome/components/graphical_layout/display_rendering_panel.h new file mode 100644 index 0000000000..d2acd7a3f6 --- /dev/null +++ b/esphome/components/graphical_layout/display_rendering_panel.h @@ -0,0 +1,32 @@ +#pragma once + +#include "esphome/components/graphical_layout/graphical_layout.h" +#include "esphome/components/font/font.h" + +namespace esphome { +namespace graphical_layout { + +/* See display.h for original declaration */ +using display_writer_t = std::function; + +/** The DisplayRenderingPanel is a UI item that renders a custom lambda to the display whilst + * participating in the layout process + */ +class DisplayRenderingPanel : public LayoutItem { + public: + const display::Rect measure_item(display::Display *display); + void render(display::Display *display, display::Rect bounds); + void dump_config(int indent_depth, int additional_level_depth); + + void set_width(int width) { this->width_ = width; }; + void set_height(int height) { this->height_ = height; }; + void set_lambda(display_writer_t lambda) { this->lambda_ = lambda; }; + + protected: + int width_{0}; + int height_{0}; + display_writer_t lambda_{nullptr}; +}; + +} // namespace graphical_layout +} // namespace esphome diff --git a/esphome/components/graphical_layout/display_rendering_panel.py b/esphome/components/graphical_layout/display_rendering_panel.py new file mode 100644 index 0000000000..a25d3d16fb --- /dev/null +++ b/esphome/components/graphical_layout/display_rendering_panel.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +from esphome.const import CONF_ID +from esphome.components.display import DisplayRef + +graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout") +DisplayRenderingPanel = graphical_layout_ns.class_("DisplayRenderingPanel") + +CONF_TYPE = "display_rendering_panel" +CONF_HEIGHT = "height" +CONF_WIDTH = "width" +CONF_LAMBDA = "lambda" + +async def config_to_layout_item(item_config, child_item_builder): + var = cg.new_Pvariable(item_config[CONF_ID]) + + width = await cg.templatable(item_config[CONF_WIDTH], args=[], output_type=int) + cg.add(var.set_width(width)) + + height = await cg.templatable(item_config[CONF_HEIGHT], args=[], output_type=int) + cg.add(var.set_height(height)) + + lambda_ = await cg.process_lambda( + item_config[CONF_LAMBDA], [(DisplayRef, "it")], return_type=cg.void + ) + cg.add(var.set_lambda(lambda_)) + + return var