Add support for text alignment to TextPanel

This commit is contained in:
Michael Davidson 2023-12-26 14:17:37 +11:00
parent 255d851125
commit fa27e8eafc
No known key found for this signature in database
GPG Key ID: B8D1A99712B8B0EB
3 changed files with 76 additions and 4 deletions

View File

@ -8,6 +8,8 @@ namespace esphome {
namespace graphical_layout {
static const char *const TAG = "textpanel";
static const int TEXT_ALIGN_X_MASK = (int)display::TextAlign::RIGHT | (int)display::TextAlign::CENTER_HORIZONTAL;
static const int TEXT_ALIGN_Y_MASK = (int)display::TextAlign::BOTTOM | (int)display::TextAlign::BASELINE | (int)display::TextAlign::CENTER_VERTICAL;
void TextPanel::dump_config(int indent_depth, int additional_level_depth) {
ESP_LOGCONFIG(TAG, "%*sText: %s", indent_depth, "", this->text_.c_str());
@ -18,15 +20,61 @@ display::Rect TextPanel::measure_item_internal(display::Display *display) {
int y1;
int width;
int height;
display->get_text_bounds(0, 0, this->text_.c_str(), this->font_, display::TextAlign::TOP_LEFT, &x1, &y1, &width,
display->get_text_bounds(0, 0, this->text_.c_str(), this->font_, this->text_align_, &x1, &y1, &width,
&height);
return display::Rect(0, 0, width, height);
}
void TextPanel::render_internal(display::Display *display, display::Rect bounds) {
display->print(0, 0, this->font_, this->foreground_color_, display::TextAlign::TOP_LEFT, this->text_.c_str());
int width, height, x_offset, baseline;
this->font_->measure(this->text_.c_str(), &width, &x_offset, &baseline, &height);
const auto x_align = display::TextAlign(int(this->text_align_) & TEXT_ALIGN_X_MASK);
const auto y_align = display::TextAlign(int(this->text_align_) & TEXT_ALIGN_Y_MASK);
display::Rect text_bounds(0, 0, bounds.w, bounds.h);
switch (x_align) {
case display::TextAlign::RIGHT: {
bounds.x = bounds.w - width;
break;
}
case display::TextAlign::CENTER_HORIZONTAL: {
bounds.x = (bounds.w - width) / 2;
break;
}
case display::TextAlign::LEFT:
default: {
// LEFT
bounds.x = 0;
break;
}
}
switch (y_align) {
case display::TextAlign::BOTTOM: {
bounds.y = bounds.h - height;
break;
}
case display::TextAlign::BASELINE: {
bounds.y = (bounds.h - height) + baseline;
break;
}
case display::TextAlign::CENTER_VERTICAL: {
bounds.y = (bounds.h - height) / 2;
break;
}
case display::TextAlign::TOP:
default: {
bounds.y = 0;
break;
}
}
auto rendered_alignment = display::TextAlign::TOP_LEFT;
display->print(bounds.x, bounds.y, this->font_, this->foreground_color_, rendered_alignment, this->text_.c_str());
}
} // namespace graphical_layout

View File

@ -23,11 +23,13 @@ class TextPanel : public LayoutItem {
void set_font(display::BaseFont *font) { this->font_ = font; };
void set_foreground_color(Color foreground_color) { this->foreground_color_ = foreground_color; };
void set_background_color(Color background_color) { this->background_color_ = background_color; };
void set_text_align(display::TextAlign text_align) { this->text_align_ = text_align; };
protected:
int item_padding_{0};
std::string text_{};
display::BaseFont *font_{nullptr};
display::TextAlign text_align_{display::TextAlign::TOP_LEFT};
Color foreground_color_{COLOR_ON};
Color background_color_{COLOR_OFF};
};

View File

@ -1,9 +1,11 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import font, color
from esphome.components.display import display_ns
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
TextPanel = graphical_layout_ns.class_("TextPanel")
TextAlign = display_ns.enum("TextAlign", is_class=True)
CONF_ITEM_PADDING = "item_padding"
CONF_TEXT_PANEL = "text_panel"
@ -11,8 +13,24 @@ CONF_FONT = "font"
CONF_FOREGROUND_COLOR = "foreground_color"
CONF_BACKGROUND_COLOR = "background_color"
CONF_TEXT = "text"
CONF_TEXT_ALIGN = "text_align"
TEXT_ALIGN = {
"TOP_LEFT": TextAlign.TOP_LEFT,
"TOP_CENTER": TextAlign.TOP_CENTER,
"TOP_RIGHT": TextAlign.TOP_RIGHT,
"CENTER_LEFT": TextAlign.CENTER_LEFT,
"CENTER": TextAlign.CENTER,
"CENTER_RIGHT": TextAlign.CENTER_RIGHT,
"BASELINE_LEFT": TextAlign.BASELINE_LEFT,
"BASELINE_CENTER": TextAlign.BASELINE_CENTER,
"BASELINE_RIGHT": TextAlign.BASELINE_RIGHT,
"BOTTOM_LEFT": TextAlign.BOTTOM_LEFT,
"BOTTOM_CENTER": TextAlign.BOTTOM_CENTER,
"BOTTOM_RIGHT": TextAlign.BOTTOM_RIGHT,
}
def get_config_schema(base_item_schema, item_type_schema):
return base_item_schema.extend(
{
@ -22,6 +40,7 @@ def get_config_schema(base_item_schema, item_type_schema):
cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color.ColorStruct),
cv.Optional(CONF_BACKGROUND_COLOR): cv.use_id(color.ColorStruct),
cv.Required(CONF_TEXT): cv.templatable(cv.string),
cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True)
}
)
@ -46,4 +65,7 @@ async def config_to_layout_item(pvariable_builder, item_config, child_item_build
text = await cg.templatable(item_config[CONF_TEXT], args=[], output_type=str)
cg.add(var.set_text(text))
if text_align := item_config.get(CONF_TEXT_ALIGN):
cg.add(var.set_text_align(text_align))
return var