Add the FixedDimensionPanel

This renders a single child (you'd likely want that child to be a Vertical or Horizontal Stack Panel) with constrained dimensions.

For example to achieve a fixed two column 50/50 display you could have a layout of;
Root
 - Horizontal Stack
   - Fixed Dimension (50% display width)
     - Vertical Stack
       - Text, Display, etc...
   - Fixed Dimension (50% display width)
     - Vertical Stack
       - More elements...
This commit is contained in:
Michael Davidson 2023-12-27 10:12:58 +11:00
parent 1b411a55e4
commit 46285b20f8
No known key found for this signature in database
GPG key ID: B8D1A99712B8B0EB
4 changed files with 115 additions and 1 deletions

View file

@ -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")

View file

@ -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

View file

@ -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<typename V> void set_width(V width) { this->width_ = width; };
template<typename V> void set_height(V height) { this->height_ = height; }
protected:
LayoutItem *child_{nullptr};
TemplatableValue<int> width_{0};
TemplatableValue<int> height_{0};
};
} // namespace graphical_layout
} // namespace esphome

View file

@ -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