From cbd4515900ad0f1472172990484986a05d89d0dc Mon Sep 17 00:00:00 2001 From: Michael Davidson Date: Sun, 31 Dec 2023 09:54:07 +1100 Subject: [PATCH] Add ability for FixedDimensionPanel to operate in one of three modes; display dimension, child dimension, or fixed. - Display dimensions use the width/height of the display (minus margin, border, and padding) for the dimension - Child dimensions fix the dimension to that of the child - Fixed dimension uses a specified pixel value (or lambda returning pixel value) --- .../fixed_dimension_panel.cpp | 45 ++++++++++++++----- .../graphical_layout/fixed_dimension_panel.h | 13 +++++- .../graphical_layout/fixed_dimension_panel.py | 42 ++++++++++++----- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.cpp b/esphome/components/graphical_layout/fixed_dimension_panel.cpp index c768da06ef..ef564d2790 100644 --- a/esphome/components/graphical_layout/fixed_dimension_panel.cpp +++ b/esphome/components/graphical_layout/fixed_dimension_panel.cpp @@ -11,28 +11,53 @@ static const char *const TAG = "fixeddimensionpanel"; void FixedDimensionPanel::dump_config(int indent_depth, int additional_level_depth) { this->dump_config_base_properties(TAG, indent_depth); - 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)); + if (this->width_.value() < 0) { + ESP_LOGCONFIG(TAG, "%*sWidth: UNSET (Will use %s's width)", indent_depth, "", + this->unset_width_uses_display_width_ ? "DISPLAY" : "CHILD"); + } else { + ESP_LOGCONFIG(TAG, "%*sWidth: %i", indent_depth, "", this->width_.value()); + } + +if (this->height_.value() < 0) { + ESP_LOGCONFIG(TAG, "%*sHeight: UNSET (Will use %s's height)", indent_depth, "", + this->unset_height_uses_display_height_ ? "DISPLAY" : "CHILD"); + } else { + ESP_LOGCONFIG(TAG, "%*sHeight: %i", indent_depth, "", this->height_.value()); + } + this->child_->dump_config(indent_depth + additional_level_depth, additional_level_depth); } display::Rect FixedDimensionPanel::measure_item_internal(display::Display *display) { + // Call measure_child so they can do any measurements + display::Rect child_size = this->child_->measure_item(display); display::Rect rect(0, 0, this->width_.value(), this->height_.value()); - if (rect.w < 1) { - rect.w = display->get_width(); + + if (rect.w < 0) { + if (this->unset_width_uses_display_width_) { + rect.w = display->get_width(); + // We need to account for our own padding + margin + border + rect.w -= (this->margin_ + this->border_ + this->padding_) * 2; + } else { + rect.w = child_size.w; + } } - if (rect.h < 1) { - rect.h = display->get_height(); + + if (rect.h < 0) { + if (this->unset_height_uses_display_height_) { + rect.h = display->get_height(); + // We need to account for our own padding + margin + border + rect.h -= (this->margin_ + this->border_ + this->padding_) * 2; + } else { + rect.h = child_size.h; + } } - // Call measure_child just so they can do any measurements - this->child_->measure_item(display); return rect; } void FixedDimensionPanel::render_internal(display::Display *display, display::Rect bounds) { - this->child_->render_internal(display, bounds); + this->child_->render(display, bounds); } } // namespace graphical_layout diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.h b/esphome/components/graphical_layout/fixed_dimension_panel.h index e199de3599..e7221a58be 100644 --- a/esphome/components/graphical_layout/fixed_dimension_panel.h +++ b/esphome/components/graphical_layout/fixed_dimension_panel.h @@ -19,10 +19,19 @@ class FixedDimensionPanel : public LayoutItem { template void set_width(V width) { this->width_ = width; }; template void set_height(V height) { this->height_ = height; } + void set_unset_width_uses_display_width(bool use_display) { + this->unset_width_uses_display_width_ = use_display; + } + void set_unset_height_uses_display_height(bool use_display) { + this->unset_height_uses_display_height_ = use_display; + } + protected: LayoutItem *child_{nullptr}; - TemplatableValue width_{0}; - TemplatableValue height_{0}; + TemplatableValue width_{-1}; + TemplatableValue height_{-1}; + bool unset_width_uses_display_width_{false}; + bool unset_height_uses_display_height_{false}; }; } // namespace graphical_layout diff --git a/esphome/components/graphical_layout/fixed_dimension_panel.py b/esphome/components/graphical_layout/fixed_dimension_panel.py index 62568167fa..94b6652e43 100644 --- a/esphome/components/graphical_layout/fixed_dimension_panel.py +++ b/esphome/components/graphical_layout/fixed_dimension_panel.py @@ -5,19 +5,25 @@ 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" +UNSET_DIMENSION_MODE = ["CHILD", "DISPLAY"] + 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)), + cv.Optional(CONF_WIDTH, default=-1): cv.Any( + cv.one_of(*UNSET_DIMENSION_MODE, upper=True), + cv.templatable(cv.int_range(min=-1)), + ), + cv.Optional(CONF_HEIGHT, default=-1): cv.Any( + cv.one_of(*UNSET_DIMENSION_MODE, upper=True), + cv.templatable(cv.int_range(min=-1)), + ), } ) @@ -25,13 +31,29 @@ def get_config_schema(base_item_schema, item_type_schema): 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)) + if width_config := item_config.get(CONF_WIDTH): + if width_config in UNSET_DIMENSION_MODE: + cg.add( + var.set_unset_width_uses_display_width( + width_config.upper() == "DISPLAY" + ) + ) + else: + width = await cg.templatable(width_config, 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)) + if height_config := item_config.get(CONF_HEIGHT): + if height_config in UNSET_DIMENSION_MODE: + cg.add( + var.set_unset_height_uses_display_height( + height_config.upper() == "DISPLAY" + ) + ) + else: + 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]