diff --git a/esphome/components/graphical_layout/__init__.py b/esphome/components/graphical_layout/__init__.py index ca16f0771b..c6a84e0eed 100644 --- a/esphome/components/graphical_layout/__init__.py +++ b/esphome/components/graphical_layout/__init__.py @@ -6,6 +6,7 @@ from . import horizontal_stack from . import vertical_stack from . import text_panel from . import display_rendering_panel +from . import fixed_dimension_panel graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout") RootLayoutComponent = graphical_layout_ns.class_("RootLayoutComponent", cg.Component) @@ -51,6 +52,9 @@ ITEM_TYPE_SCHEMA = cv.typed_schema( display_rendering_panel.CONF_DISPLAY_RENDERING_PANEL: display_rendering_panel.get_config_schema( BASE_ITEM_SCHEMA, item_type_schema ), + fixed_dimension_panel.CONF_FIXED_DIMENSION_PANEL: fixed_dimension_panel.get_config_schema( + BASE_ITEM_SCHEMA, item_type_schema + ), } ) @@ -59,6 +63,7 @@ CODE_GENERATORS = { horizontal_stack.CONF_HORIZONTAL_STACK: horizontal_stack.config_to_layout_item, vertical_stack.CONF_VERTICAL_STACK: vertical_stack.config_to_layout_item, display_rendering_panel.CONF_DISPLAY_RENDERING_PANEL: display_rendering_panel.config_to_layout_item, + fixed_dimension_panel.CONF_FIXED_DIMENSION_PANEL: fixed_dimension_panel.config_to_layout_item, } CONFIG_SCHEMA = cv.Schema( @@ -100,6 +105,7 @@ async def to_code(config): ) cg.add(var.set_layout_root(layout_var)) else: - raise f"Do not know how to build type {layout_type}" + err = f"Do not know how to build type {layout_type}" + raise RuntimeError(f"Do not know how to build type {layout_type}") cg.add_define("USE_GRAPHICAL_LAYOUT") diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.cpp b/esphome/components/graphical_layout/fixed_dimension_panel.cpp new file mode 100644 index 0000000000..0944d2c72d --- /dev/null +++ b/esphome/components/graphical_layout/fixed_dimension_panel.cpp @@ -0,0 +1,34 @@ +#include "fixed_dimension_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 = "fixeddimensionpanel"; + +void FixedDimensionPanel::dump_config(int indent_depth, int additional_level_depth) { + ESP_LOGCONFIG(TAG, "%*sWidth: %i (Will use display width: %s)", indent_depth, "", this->width_.value(), YESNO(this->width_.value() < 1)); + ESP_LOGCONFIG(TAG, "%*sHeight: %i (Will use display height: %s)", indent_depth, "", this->height_.value(), YESNO(this->height_.value() < 1)); + this->child_->dump_config(indent_depth + additional_level_depth, additional_level_depth); +} + +display::Rect FixedDimensionPanel::measure_item_internal(display::Display *display) { + display::Rect rect(0, 0, this->width_.value(), this->height_.value()); + if (rect.w < 1) { + rect.w = display->get_width(); + } + if (rect.h < 1) { + rect.h = display->get_height(); + } + return rect; +} + +void FixedDimensionPanel::render_internal(display::Display *display, display::Rect bounds) { + this->child_->render_internal(display, bounds); +} + +} // namespace graphical_layout +} // namespace esphome diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.h b/esphome/components/graphical_layout/fixed_dimension_panel.h new file mode 100644 index 0000000000..667427c56b --- /dev/null +++ b/esphome/components/graphical_layout/fixed_dimension_panel.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/components/graphical_layout/graphical_layout.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace graphical_layout { + +/** The FixedDimensionPanel is a UI element which will render a single child with constrained dimensions + */ +class FixedDimensionPanel : public LayoutItem { + public: + display::Rect measure_item_internal(display::Display *display) override; + void render_internal(display::Display *display, display::Rect bounds) override; + void dump_config(int indent_depth, int additional_level_depth) override; + + void set_child(LayoutItem *child) { this->child_ = child; }; + template void set_width(V width) { this->width_ = width; }; + template void set_height(V height) { this->height_ = height; } + + protected: + LayoutItem *child_{nullptr}; + TemplatableValue width_{0}; + TemplatableValue height_{0}; +}; + +} // namespace graphical_layout +} // namespace esphome diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.py b/esphome/components/graphical_layout/fixed_dimension_panel.py new file mode 100644 index 0000000000..62568167fa --- /dev/null +++ b/esphome/components/graphical_layout/fixed_dimension_panel.py @@ -0,0 +1,46 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_TYPE, CONF_WIDTH, CONF_HEIGHT + +graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout") +FixedDimensionPanel = graphical_layout_ns.class_("FixedDimensionPanel") + +CONF_ITEM_PADDING = "item_padding" +CONF_FIXED_DIMENSION_PANEL = "fixed_dimension_panel" +CONF_CHILD = "child" + + +def get_config_schema(base_item_schema, item_type_schema): + return base_item_schema.extend( + { + cv.GenerateID(): cv.declare_id(FixedDimensionPanel), + cv.Optional(CONF_ITEM_PADDING, default=0): cv.int_, + cv.Required(CONF_CHILD): item_type_schema, + cv.Optional(CONF_WIDTH, default=-1): cv.templatable(cv.int_range(min=-1)), + cv.Optional(CONF_HEIGHT, default=-1): cv.templatable(cv.int_range(min=-1)), + } + ) + + +async def config_to_layout_item(pvariable_builder, item_config, child_item_builder): + var = await pvariable_builder(item_config) + + width = await cg.templatable(item_config[CONF_WIDTH], args=[], output_type=cg.int_) + cg.add(var.set_width(width)) + + height = await cg.templatable( + item_config[CONF_HEIGHT], args=[], output_type=cg.int_ + ) + cg.add(var.set_height(height)) + + child_item_config = item_config[CONF_CHILD] + child_item_type = child_item_config[CONF_TYPE] + if child_item_type in child_item_builder: + child_item_var = await child_item_builder[child_item_type]( + pvariable_builder, child_item_config, child_item_builder + ) + cg.add(var.set_child(child_item_var)) + else: + raise f"Do not know how to build type {child_item_type}" + + return var