mirror of
https://github.com/esphome/esphome.git
synced 2024-12-12 08:24:55 +01:00
Add text_run_panel
Unlike text_panel (to be renamed) the text_run_panel supports multiple runs of different fonts (and thus sizes, weights, and colours) and will lay them out to the best of its ability - respecting desired alignment - as it can.
This commit is contained in:
parent
92f5f03bfe
commit
93d2e4e8ff
4 changed files with 494 additions and 1 deletions
|
@ -7,6 +7,7 @@ from . import vertical_stack
|
|||
from . import text_panel
|
||||
from . import display_rendering_panel
|
||||
from . import fixed_dimension_panel
|
||||
from . import text_run_panel
|
||||
|
||||
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
|
||||
RootLayoutComponent = graphical_layout_ns.class_("RootLayoutComponent", cg.Component)
|
||||
|
@ -55,6 +56,9 @@ ITEM_TYPE_SCHEMA = cv.typed_schema(
|
|||
fixed_dimension_panel.CONF_FIXED_DIMENSION_PANEL: fixed_dimension_panel.get_config_schema(
|
||||
BASE_ITEM_SCHEMA, item_type_schema
|
||||
),
|
||||
text_run_panel.CONF_TEXT_RUN_PANEL: text_run_panel.get_config_schema(
|
||||
BASE_ITEM_SCHEMA, item_type_schema
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -64,6 +68,7 @@ CODE_GENERATORS = {
|
|||
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,
|
||||
text_run_panel.CONF_TEXT_RUN_PANEL: text_run_panel.config_to_layout_item,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
|
@ -105,7 +110,6 @@ async def to_code(config):
|
|||
)
|
||||
cg.add(var.set_layout_root(layout_var))
|
||||
else:
|
||||
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")
|
||||
|
|
284
esphome/components/graphical_layout/text_run_panel.cpp
Normal file
284
esphome/components/graphical_layout/text_run_panel.cpp
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include "text_run_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 = "textrunpanel";
|
||||
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 TextRunPanel::dump_config(int indent_depth, int additional_level_depth) {
|
||||
ESP_LOGCONFIG(TAG, "%*sMin Width: %i", indent_depth, "", this->min_width_);
|
||||
ESP_LOGCONFIG(TAG, "%*sMax Width: %i", indent_depth, "", this->max_width_);
|
||||
ESP_LOGCONFIG(TAG, "%*sText Align: %s", indent_depth, "", display::text_align_to_string(this->text_align_));
|
||||
ESP_LOGCONFIG(TAG, "%*sText Runs: %i", indent_depth, "", this->text_runs_.size());
|
||||
for (TextRun *run : this->text_runs_) {
|
||||
std::string text = run->text_.value();
|
||||
ESP_LOGCONFIG(TAG, "%*sText: %s", indent_depth + additional_level_depth, "", text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void TextRunPanel::setup_complete() {
|
||||
if (!this->can_wrap_at_character_.has_value()) {
|
||||
ESP_LOGD(TAG, "No custom can_wrap_at_character provided. Will use default implementation");
|
||||
this->can_wrap_at_character_ = [this](CanWrapAtCharacterArguments *args) {
|
||||
return this->default_can_wrap_at_character(args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
display::Rect TextRunPanel::measure_item_internal(display::Display *display) {
|
||||
CalculatedLayout calculated = this->determine_layout(display, display::Rect(0, 0, this->max_width_, display->get_height()), true);
|
||||
return calculated.bounds;
|
||||
}
|
||||
|
||||
void TextRunPanel::render_internal(display::Display *display, display::Rect bounds) {
|
||||
CalculatedLayout layout = this->determine_layout(display, bounds, true);
|
||||
|
||||
for (auto calculated : layout.runs) {
|
||||
if (calculated->run->background_color_ != display::COLOR_OFF) {
|
||||
display->filled_rectangle(calculated->bounds.x, calculated->bounds.y, calculated->bounds.w, calculated->bounds.h, calculated->run->background_color_);
|
||||
}
|
||||
display->print(calculated->bounds.x, calculated->bounds.y, calculated->run->font_, calculated->run->foreground_color_, display::TextAlign::TOP_LEFT, calculated->text_.c_str());
|
||||
}
|
||||
|
||||
if (this->debug_outline_runs_) {
|
||||
ESP_LOGD(TAG, "Outlining character runs");
|
||||
for (auto calculated : layout.runs) {
|
||||
display->rectangle(calculated->bounds.x, calculated->bounds.y, calculated->bounds.w, calculated->bounds.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalculatedLayout TextRunPanel::determine_layout(display::Display *display, display::Rect bounds, bool apply_alignment) {
|
||||
ESP_LOGV(TAG, "Determining layout for (%i, %i)", bounds.w, bounds.h);
|
||||
|
||||
CalculatedLayout calculated_layout;
|
||||
int x_offset = 0;
|
||||
int y_offset = 0;
|
||||
int current_line_max_height = 0;
|
||||
int widest_line = 0;
|
||||
int line_number = 0;
|
||||
|
||||
for (TextRun *run : this->text_runs_) {
|
||||
int x1;
|
||||
int y1;
|
||||
int width;
|
||||
int height;
|
||||
int baseline;
|
||||
std::string text = run->text_.value();
|
||||
|
||||
run->font_->measure(text.c_str(), &width, &x1, &baseline, &height);
|
||||
|
||||
if ((x_offset + width) < bounds.w) {
|
||||
// Item fits on the current line
|
||||
auto calculated = std::make_shared<CalculatedTextRun>(run, text, display::Rect(x_offset, y_offset, width, height), baseline, line_number);
|
||||
calculated_layout.runs.push_back(calculated);
|
||||
|
||||
|
||||
x_offset += width;
|
||||
widest_line = std::max(widest_line, x_offset);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
current_line_max_height = std::max(current_line_max_height, height);
|
||||
|
||||
ESP_LOGVV(TAG, "'%s' will not fit on the line. Finding break characters", text.c_str());
|
||||
|
||||
// Item extends beyond our desired bounds - need to add word by word
|
||||
CanWrapAtCharacterArguments can_wrap_at_args(this, 0, text, ' ');
|
||||
std::string partial_line;
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
can_wrap_at_args.offset = i;
|
||||
can_wrap_at_args.character = text.at(i);
|
||||
|
||||
bool can_wrap = this->can_wrap_at_character_.value(&can_wrap_at_args);
|
||||
if (can_wrap) {
|
||||
ESP_LOGVV(TAG, "Can break at '%c'. String is '%s'", can_wrap_at_args.character, partial_line.c_str());
|
||||
|
||||
run->font_->measure(partial_line.c_str(), &width, &x1, &baseline, &height);
|
||||
if ((x_offset + width) < bounds.w) {
|
||||
ESP_LOGVV(TAG, "... Fits! (%i, %i)", x_offset, y_offset);
|
||||
|
||||
// Item fits on the current line
|
||||
current_line_max_height = std::max(current_line_max_height, height);
|
||||
|
||||
auto calculated = std::make_shared<CalculatedTextRun>(run, partial_line, display::Rect(x_offset, y_offset, width, height), baseline, line_number);
|
||||
calculated_layout.runs.push_back(calculated);
|
||||
|
||||
x_offset += width;
|
||||
widest_line = std::max(widest_line, x_offset);
|
||||
|
||||
partial_line = can_wrap_at_args.character;
|
||||
continue;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "... Doesn't fit - will overflow to next line");
|
||||
|
||||
// Overflows the current line
|
||||
x_offset = 0;
|
||||
y_offset += current_line_max_height;
|
||||
line_number++;
|
||||
current_line_max_height = height;
|
||||
partial_line += can_wrap_at_args.character;
|
||||
continue;
|
||||
}
|
||||
|
||||
partial_line += can_wrap_at_args.character;
|
||||
}
|
||||
|
||||
if (partial_line.length() > 0) {
|
||||
// Remaining text
|
||||
run->font_->measure(partial_line.c_str(), &width, &x1, &baseline, &height);
|
||||
|
||||
current_line_max_height = std::max(height, current_line_max_height);
|
||||
|
||||
ESP_LOGVV(TAG, "'%s' is remaining after character break checks. Rendering to (%i, %i)", partial_line.c_str(), x_offset, y_offset);
|
||||
|
||||
auto calculated = std::make_shared<CalculatedTextRun>(run, partial_line, display::Rect(x_offset, y_offset, width, height), baseline, line_number);
|
||||
calculated_layout.runs.push_back(calculated);
|
||||
|
||||
x_offset += width;
|
||||
widest_line = std::max(widest_line, x_offset);
|
||||
}
|
||||
}
|
||||
|
||||
y_offset += current_line_max_height;
|
||||
|
||||
calculated_layout.bounds = display::Rect(0, 0, widest_line, y_offset);
|
||||
calculated_layout.line_count = line_number + 1;
|
||||
if (calculated_layout.bounds.w < this->min_width_) {
|
||||
calculated_layout.bounds.w = this->min_width_;
|
||||
}
|
||||
|
||||
if (apply_alignment) {
|
||||
this->apply_alignment_to_layout(&calculated_layout);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Measured layout is (%i, %i) (%i lines)", calculated_layout.bounds.w, calculated_layout.bounds.h, calculated_layout.line_count);
|
||||
|
||||
return calculated_layout;
|
||||
}
|
||||
|
||||
void TextRunPanel::apply_alignment_to_layout(CalculatedLayout *calculated_layout) {
|
||||
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);
|
||||
|
||||
ESP_LOGVV(TAG, "We have %i lines to apply alignment to!", calculated_layout->line_count);
|
||||
|
||||
int total_y_offset = 0;
|
||||
|
||||
for (int i = 0; i < calculated_layout->line_count; i++) {
|
||||
std::vector<std::shared_ptr<CalculatedTextRun>> line_runs;
|
||||
|
||||
// Get all the runs for the current line
|
||||
for (auto run : calculated_layout->runs) {
|
||||
if (run->line_number_ == i) {
|
||||
line_runs.push_back(run);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Found %i runs on line %i", line_runs.size(), i);
|
||||
|
||||
int16_t total_line_width = 0;
|
||||
int16_t max_line_height = 0;
|
||||
int16_t max_baseline = 0;
|
||||
for (auto run : line_runs) {
|
||||
total_line_width += run->bounds.w;
|
||||
max_line_height = std::max(run->bounds.h, max_line_height);
|
||||
max_baseline = std::max(run->baseline, max_baseline);
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Line %i totals (%i, %i) pixels of (%i, %i)", i, total_line_width, max_line_height, calculated_layout->bounds.w, calculated_layout->bounds.h);
|
||||
|
||||
int x_adjustment = 0;
|
||||
int y_adjustment = 0;
|
||||
switch (x_align) {
|
||||
case display::TextAlign::RIGHT: {
|
||||
x_adjustment = calculated_layout->bounds.w - total_line_width;
|
||||
ESP_LOGVV(TAG, "Will adjust line %i by %i x-pixels", i, x_adjustment);
|
||||
break;
|
||||
}
|
||||
case display::TextAlign::CENTER_HORIZONTAL: {
|
||||
x_adjustment = (calculated_layout->bounds.w - total_line_width) / 2;
|
||||
ESP_LOGVV(TAG, "Will adjust line %i by %i x-pixels", i, x_adjustment);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int max_line_y_adjustment = 0;
|
||||
for (auto run : line_runs) {
|
||||
ESP_LOGVV(TAG, "Adjusting '%s' from (%i, %i) to (%i, %i)", run->text_.c_str(), run->bounds.x, run->bounds.y, run->bounds.x + x_adjustment, run->bounds.y + y_adjustment);
|
||||
run->bounds.x += x_adjustment;
|
||||
|
||||
switch (y_align) {
|
||||
case display::TextAlign::BOTTOM: {
|
||||
y_adjustment = max_line_height - run->bounds.h;
|
||||
ESP_LOGVV(TAG, "Will adjust line %i by %i y-pixels (%i vs %i)", i, y_adjustment, max_line_height, run->bounds.h);
|
||||
break;
|
||||
}
|
||||
case display::TextAlign::CENTER_VERTICAL: {
|
||||
y_adjustment = (max_line_height - run->bounds.h) / 2;
|
||||
ESP_LOGVV(TAG, "Will adjust line %i by %i y-pixels", i, y_adjustment);
|
||||
break;
|
||||
}
|
||||
case display::TextAlign::BASELINE: {
|
||||
// Adjust this run based on its difference from the maximum baseline in the line
|
||||
y_adjustment = max_baseline - run->baseline;
|
||||
ESP_LOGVV(TAG, "Will adjust '%s' by %i y-pixels (ML: %i, H: %i, BL: %i)", run->text_.c_str(), y_adjustment, max_line_height, run->bounds.h, run->baseline);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
run->bounds.y += y_adjustment + total_y_offset;
|
||||
max_line_y_adjustment = std::max(max_line_y_adjustment, y_adjustment);
|
||||
}
|
||||
|
||||
total_y_offset += max_line_y_adjustment;
|
||||
}
|
||||
|
||||
calculated_layout->bounds.h += total_y_offset;
|
||||
}
|
||||
|
||||
bool TextRunPanel::default_can_wrap_at_character(CanWrapAtCharacterArguments *args) {
|
||||
switch (args->character) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\0':
|
||||
case '=':
|
||||
case '<':
|
||||
case '>':
|
||||
case '/':
|
||||
case '&':
|
||||
case '*':
|
||||
case '+':
|
||||
case '^':
|
||||
case '|':
|
||||
case '\\': {
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace graphical_layout
|
||||
} // namespace esphome
|
106
esphome/components/graphical_layout/text_run_panel.h
Normal file
106
esphome/components/graphical_layout/text_run_panel.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/components/graphical_layout/graphical_layout.h"
|
||||
#include "esphome/components/font/font.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace display {
|
||||
|
||||
extern const Color COLOR_ON;
|
||||
extern const Color COLOR_OFF;
|
||||
|
||||
} // namespace display
|
||||
namespace graphical_layout {
|
||||
|
||||
class TextRunPanel;
|
||||
|
||||
struct CanWrapAtCharacterArguments {
|
||||
CanWrapAtCharacterArguments(const TextRunPanel *panel, int offset, std::string string, char character) {
|
||||
this->panel = panel;
|
||||
this->offset = offset;
|
||||
this->string = string;
|
||||
this->character = character;
|
||||
}
|
||||
|
||||
const TextRunPanel *panel;
|
||||
int offset;
|
||||
std::string string;
|
||||
char character;
|
||||
};
|
||||
|
||||
class TextRun {
|
||||
public:
|
||||
TextRun(TemplatableValue<std::string> text, display::BaseFont *font) {
|
||||
this->text_ = text;
|
||||
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; }
|
||||
|
||||
TemplatableValue<std::string> text_{};
|
||||
display::BaseFont *font_{nullptr};
|
||||
Color foreground_color_{display::COLOR_ON};
|
||||
Color background_color_{display::COLOR_OFF};
|
||||
};
|
||||
|
||||
class CalculatedTextRun {
|
||||
public:
|
||||
CalculatedTextRun(TextRun *run, std::string text, display::Rect bounds, int16_t baseline, int16_t line_number) {
|
||||
this->run = run;
|
||||
this->text_ = text;
|
||||
this->bounds = bounds;
|
||||
this->baseline = baseline;
|
||||
this->line_number_ = line_number;
|
||||
}
|
||||
|
||||
std::string text_;
|
||||
display::Rect bounds;
|
||||
TextRun *run;
|
||||
int16_t line_number_;
|
||||
int16_t baseline;
|
||||
};
|
||||
|
||||
struct CalculatedLayout {
|
||||
std::vector<std::shared_ptr<CalculatedTextRun>> runs;
|
||||
display::Rect bounds;
|
||||
int line_count;
|
||||
};
|
||||
|
||||
/** The TextRunPanel is a UI item that renders a multiple "runs" of text of independent styling to a display */
|
||||
class TextRunPanel : 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 setup_complete() override;
|
||||
|
||||
bool default_can_wrap_at_character(CanWrapAtCharacterArguments *args);
|
||||
|
||||
CalculatedLayout determine_layout(display::Display *display, display::Rect bounds, bool apply_alignment);
|
||||
void apply_alignment_to_layout(CalculatedLayout *layout);
|
||||
|
||||
template<typename V> void set_can_wrap_at(V can_wrap_at_character) {
|
||||
this->can_wrap_at_character_ = can_wrap_at_character;
|
||||
};
|
||||
|
||||
void add_text_run(TextRun *text_run) { this->text_runs_.push_back(text_run); };
|
||||
void set_text_align(display::TextAlign text_align) { this->text_align_ = text_align; };
|
||||
void set_min_width(int min_width) { this->min_width_ = min_width; };
|
||||
void set_max_width(int max_width) { this->max_width_ = max_width; };
|
||||
void set_debug_outline_runs(bool debug_outline_runs) { this->debug_outline_runs_ = debug_outline_runs; };
|
||||
|
||||
protected:
|
||||
std::vector<TextRun *> text_runs_;
|
||||
display::TextAlign text_align_{display::TextAlign::TOP_LEFT};
|
||||
int min_width_{0};
|
||||
int max_width_{0};
|
||||
TemplatableValue<bool, CanWrapAtCharacterArguments *> can_wrap_at_character_{};
|
||||
bool debug_outline_runs_{false};
|
||||
};
|
||||
|
||||
} // namespace graphical_layout
|
||||
} // namespace esphome
|
99
esphome/components/graphical_layout/text_run_panel.py
Normal file
99
esphome/components/graphical_layout/text_run_panel.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import font, color
|
||||
from esphome.components.display import display_ns
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
graphical_layout_ns = cg.esphome_ns.namespace("graphical_layout")
|
||||
TextRunPanel = graphical_layout_ns.class_("TextRunPanel")
|
||||
TextAlign = display_ns.enum("TextAlign", is_class=True)
|
||||
TextRun = graphical_layout_ns.class_("TextRun")
|
||||
|
||||
CONF_TEXT_RUN_PANEL = "text_run_panel"
|
||||
CONF_FONT = "font"
|
||||
CONF_FOREGROUND_COLOR = "foreground_color"
|
||||
CONF_BACKGROUND_COLOR = "background_color"
|
||||
CONF_TEXT = "text"
|
||||
CONF_TEXT_ALIGN = "text_align"
|
||||
CONF_MAX_WIDTH = "max_width"
|
||||
CONF_MIN_WIDTH = "min_width"
|
||||
CONF_RUNS = "runs"
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
RUN_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TextRun),
|
||||
cv.Required(CONF_FONT): cv.use_id(font.Font),
|
||||
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),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def get_config_schema(base_item_schema, item_type_schema):
|
||||
return base_item_schema.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TextRunPanel),
|
||||
cv.Optional(CONF_TEXT_ALIGN): cv.enum(TEXT_ALIGN, upper=True),
|
||||
cv.Required(CONF_MAX_WIDTH): cv.int_range(min=0),
|
||||
cv.Optional(CONF_MIN_WIDTH, default=0): cv.int_range(min=0),
|
||||
cv.Optional(CONF_DEBUG_OUTLINE_RUNS, default=False): cv.boolean,
|
||||
cv.Required(CONF_RUNS): cv.All(
|
||||
cv.ensure_list(RUN_SCHEMA), cv.Length(min=1)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def config_to_layout_item(pvariable_builder, item_config, child_item_builder):
|
||||
var = await pvariable_builder(item_config)
|
||||
|
||||
min_width = item_config[CONF_MIN_WIDTH]
|
||||
cg.add(var.set_min_width(min_width))
|
||||
|
||||
max_width = item_config[CONF_MAX_WIDTH]
|
||||
cg.add(var.set_max_width(max_width))
|
||||
|
||||
if text_align := item_config.get(CONF_TEXT_ALIGN):
|
||||
cg.add(var.set_text_align(text_align))
|
||||
|
||||
debug_outline_runs = item_config[CONF_DEBUG_OUTLINE_RUNS]
|
||||
if debug_outline_runs:
|
||||
cg.add(var.set_debug_outline_runs(debug_outline_runs))
|
||||
|
||||
for run_config in item_config[CONF_RUNS]:
|
||||
run_text = await cg.templatable(
|
||||
run_config[CONF_TEXT], args=[], output_type=cg.std_string
|
||||
)
|
||||
run_font = await cg.get_variable(run_config[CONF_FONT])
|
||||
|
||||
run = cg.new_Pvariable(run_config[CONF_ID], run_text, run_font)
|
||||
|
||||
if run_background_color_config := run_config.get(CONF_BACKGROUND_COLOR):
|
||||
run_background_color = await cg.get_variable(run_background_color_config)
|
||||
cg.add(run.set_background_color(run_background_color))
|
||||
|
||||
if run_foreground_color_config := run_config.get(CONF_FOREGROUND_COLOR):
|
||||
run_foreground_color = await cg.get_variable(run_foreground_color_config)
|
||||
cg.add(run.set_foreground_color(run_foreground_color))
|
||||
|
||||
cg.add(var.add_text_run(run))
|
||||
|
||||
return var
|
Loading…
Reference in a new issue