Make margin, borders, and padding individually settable (top vs left vs bottom vs right)

Can still take an int to set all the properties equally
This commit is contained in:
Michael Davidson 2024-01-01 10:23:17 +11:00
parent feef234ddd
commit b18e217313
No known key found for this signature in database
GPG Key ID: B8D1A99712B8B0EB
4 changed files with 143 additions and 39 deletions

View File

@ -24,13 +24,32 @@ CONF_LAYOUT = "layout"
CONF_MARGIN = "margin"
CONF_PADDING = "padding"
CONF_BORDER_COLOR = "border_color"
CONF_LEFT = "left"
CONF_TOP = "top"
CONF_RIGHT = "right"
CONF_BOTTOM = "bottom"
DIMENSION_SCHEMA = cv.Schema(
{
cv.Optional(CONF_LEFT, default=0): cv.int_range(min=0),
cv.Optional(CONF_TOP, default=0): cv.int_range(min=0),
cv.Optional(CONF_RIGHT, default=0): cv.int_range(min=0),
cv.Optional(CONF_BOTTOM, default=0): cv.int_range(min=0),
}
)
BASE_ITEM_SCHEMA = cv.Schema(
{
cv.Optional(CONF_MARGIN, default=0): cv.int_range(min=0),
cv.Optional(CONF_BORDER, default=0): cv.int_range(min=0),
cv.Optional(CONF_MARGIN, default=0): cv.Any(
DIMENSION_SCHEMA, cv.int_range(min=0)
),
cv.Optional(CONF_BORDER, default=0): cv.Any(
DIMENSION_SCHEMA, cv.int_range(min=0)
),
cv.Optional(CONF_BORDER_COLOR): cv.use_id(color.ColorStruct),
cv.Optional(CONF_PADDING, default=0): cv.int_range(min=0),
cv.Optional(CONF_PADDING, default=0): cv.Any(
DIMENSION_SCHEMA, cv.int_range(min=0)
),
}
)
@ -79,22 +98,52 @@ CONFIG_SCHEMA = cv.Schema(
).extend(cv.COMPONENT_SCHEMA)
async def extract_dimension_expression(value_config, individual_set, single_set):
if value_config is not None:
if not isinstance(value_config, int):
# Handle individual dimensions
left = value_config.get(CONF_LEFT)
top = value_config.get(CONF_TOP)
right = value_config.get(CONF_RIGHT)
bottom = value_config.get(CONF_BOTTOM)
individual_set(left, top, right, bottom)
else:
template = await cg.templatable(value_config, args=[], output_type=int)
single_set(template)
async def build_layout_item_pvariable(config):
var = cg.new_Pvariable(config[CONF_ID])
margin = await cg.templatable(config[CONF_MARGIN], args=[], output_type=int)
cg.add(var.set_margin(margin))
await extract_dimension_expression(
config.get(CONF_MARGIN),
lambda left, top, right, bottom: cg.add(
var.set_margin(left, top, right, bottom)
),
lambda margin: cg.add(var.set_margin(margin)),
)
border = await cg.templatable(config[CONF_BORDER], args=[], output_type=int)
cg.add(var.set_border(border))
await extract_dimension_expression(
config.get(CONF_BORDER),
lambda left, top, right, bottom: cg.add(
var.set_border(left, top, right, bottom)
),
lambda border: cg.add(var.set_border(border)),
)
await extract_dimension_expression(
config.get(CONF_PADDING),
lambda left, top, right, bottom: cg.add(
var.set_padding(left, top, right, bottom)
),
lambda padding: cg.add(var.set_padding(padding)),
)
if border_color_config := config.get(CONF_BORDER_COLOR):
border_color = await cg.get_variable(border_color_config)
cg.add(var.set_border_color(border_color))
padding = await cg.templatable(config[CONF_PADDING], args=[], output_type=int)
cg.add(var.set_padding(padding))
return var

View File

@ -37,7 +37,7 @@ display::Rect FixedDimensionPanel::measure_item_internal(display::Display *displ
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;
rect.w -= this->margin_.horizontal() + this->border_.horizontal() + this->padding_.horizontal();
} else {
rect.w = child_size.w;
}
@ -47,7 +47,7 @@ display::Rect FixedDimensionPanel::measure_item_internal(display::Display *displ
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;
rect.h -= this->margin_.vertical() + this->border_.vertical() + this->padding_.vertical();
} else {
rect.h = child_size.h;
}

View File

@ -13,38 +13,43 @@ const Color COLOR_ON(255, 255, 255, 255);
display::Rect LayoutItem::measure_item(display::Display *display) {
display::Rect inner_size = this->measure_item_internal(display);
int margin_border_padding = this->margin_ + this->border_ + this->padding_;
return display::Rect(0, 0, (margin_border_padding * 2) + inner_size.w, (margin_border_padding * 2) + inner_size.h);
return display::Rect(0, 0, this->margin_.horizontal() + this->border_.horizontal() + this->padding_.horizontal() + inner_size.w, this->margin_.vertical() + this->border_.vertical() + this->padding_.vertical() + inner_size.h);
}
void LayoutItem::render(display::Display *display, display::Rect bounds) {
// Margin
display->set_local_coordinates_relative_to_current(this->margin_, this->margin_);
display->set_local_coordinates_relative_to_current(this->margin_.left, this->margin_.top);
// Border
if (this->border_ > 0) {
display::Rect border_bounds(0, 0, bounds.w - (this->margin_ * 2), bounds.h - (this->margin_ * 2));
if (this->border_ == 1) {
if (this->border_.any()) {
display::Rect border_bounds(0, 0, bounds.w - this->margin_.horizontal(), bounds.h - this->margin_.vertical());
if (this->border_.equals(1)) {
// Single pixel border use the native function
display->rectangle(0, 0, border_bounds.w, border_bounds.h, this->border_color_);
} else {
// Thicker border need to do mutiple filled rects
// Top rectangle
display->filled_rectangle(border_bounds.x, border_bounds.y, border_bounds.w, this->border_);
// Bottom rectangle
display->filled_rectangle(border_bounds.x, border_bounds.h - this->border_, border_bounds.w, this->border_);
// Left rectangle
display->filled_rectangle(border_bounds.x, border_bounds.y, this->border_, border_bounds.h);
// Right rectangle
display->filled_rectangle(border_bounds.w - this->border_, border_bounds.y, this->border_, border_bounds.h);
// Top Rectangle
if (this->border_.top > 0) {
display->filled_rectangle(border_bounds.x, border_bounds.y, border_bounds.w, this->border_.top);
}
// Left Rectangle
if (this->border_.left > 0) {
display->filled_rectangle(border_bounds.x, border_bounds.y + this->border_.top, this->border_.left, border_bounds.h - this->border_.bottom - this->border_.top);
}
// Bottom Rectangle
if (this->border_.bottom > 0) {
display->filled_rectangle(border_bounds.x, border_bounds.h - this->border_.bottom, border_bounds.w, this->border_.bottom);
}
// Right Rectangle
if (this->border_.right > 0) {
display->filled_rectangle(border_bounds.w - this->border_.right, border_bounds.y + this->border_.top, this->border_.right, border_bounds.h - this->border_.bottom - this->border_.top);
}
}
}
// Padding
display->set_local_coordinates_relative_to_current(this->border_ + this->padding_, this->border_ + this->padding_);
int margin_border_padding_offset = (this->margin_ + this->border_ + this->padding_) * 2;
display::Rect internal_bounds(0, 0, bounds.w - margin_border_padding_offset, bounds.h - margin_border_padding_offset);
display->set_local_coordinates_relative_to_current(this->border_.left + this->padding_.left, this->border_.top + this->padding_.top);
display::Rect internal_bounds(0, 0, bounds.w - this->margin_.horizontal() - this->border_.horizontal() - this->padding_.horizontal(), bounds.h - this->margin_.vertical() - this->border_.vertical() - this->padding_.vertical());
// Rendering
this->render_internal(display, internal_bounds);
@ -59,11 +64,14 @@ void LayoutItem::render(display::Display *display, display::Rect bounds) {
}
void LayoutItem::dump_config_base_properties(const char *tag, int indent_depth) {
ESP_LOGCONFIG(tag, "%*sMargin: %i", indent_depth, "", this->margin_);
ESP_LOGCONFIG(tag, "%*sBorder: %i", indent_depth, "", this->border_);
ESP_LOGCONFIG(tag, "%*sMargin: : (L: %i, T: %i, R: %i, B: %i)", indent_depth, "", this->margin_.left,
this->margin_.top, this->margin_.right, this->margin_.bottom);
ESP_LOGCONFIG(tag, "%*sBorder: (L: %i, T: %i, R: %i, B: %i)", indent_depth, "", this->border_.left,
this->border_.top, this->border_.right, this->border_.bottom);
ESP_LOGCONFIG(tag, "%*sBorder Color: (R: %i, G: %i, B: %i)", indent_depth, "", this->border_color_.r,
this->border_color_.g, this->border_color_.b);
ESP_LOGCONFIG(tag, "%*sPadding: %i", indent_depth, "", this->padding_);
ESP_LOGCONFIG(tag, "%*sPadding: : (L: %i, T: %i, R: %i, B: %i)", indent_depth, "", this->padding_.left,
this->padding_.top, this->padding_.right, this->padding_.bottom);
}
const LogString *horizontal_child_align_to_string(HorizontalChildAlign align) {

View File

@ -44,6 +44,43 @@ enum class VerticalChildAlign {
STRETCH_TO_FIT_HEIGHT = 0x03
};
struct Dimension {
Dimension() {};
Dimension(int16_t padding) {
this->left = padding;
this->top = padding;
this->right = padding;
this->bottom = padding;
}
Dimension(int16_t left, int16_t top, int16_t right, int16_t bottom) {
this->left = left;
this->top = top;
this->right = right;
this->bottom = bottom;
}
/* Gets the total padding for the horizontal direction (left + right) */
inline int16_t horizontal() const { return this->left + this->right; };
/* Gets the total padding for the vertical direction (top + bottom) */
inline int16_t vertical() const { return this->top + this->bottom; };
/* Returns true if any value is set to a non-zero value*/
inline bool any() const {
return this->left > 0 || this->top > 0 || this->right > 0 || this->bottom > 0;
};
/* Returns true if all dimensions are equal to the value */
inline bool equals(int16_t value) const {
return this->left == value && this->top == value && this->right == value
&& this->bottom == value;
}
int16_t left{0};
int16_t top{0};
int16_t right{0};
int16_t bottom{0};
};
/** LayoutItem is the base from which all items derive from*/
class LayoutItem {
public:
@ -99,15 +136,25 @@ class LayoutItem {
*/
virtual void setup_complete(){};
void set_margin(int margin) { this->margin_ = margin; };
void set_padding(int padding) { this->padding_ = padding; };
void set_border(int border) { this->border_ = border; };
void set_margin(int margin) { this->margin_ = Dimension(margin); };
void set_margin(int left, int top, int right, int bottom) {
this->margin_ = Dimension(left, top, right, bottom);
}
void set_padding(int padding) { this->padding_ = Dimension(padding); };
void set_padding(int left, int top, int right, int bottom) {
this->padding_ = Dimension(left, top, right, bottom);
}
void set_border(int border) { this->border_ = Dimension(border); };
void set_border(int left, int top, int right, int bottom) {
this->border_ = Dimension(left, top, right, bottom);
}
void set_border_color(Color color) { this->border_color_ = color; };
protected:
int margin_{0};
int padding_{0};
int border_{0};
Dimension margin_{};
Dimension padding_{};
Dimension border_{};
Color border_color_{COLOR_ON};
};