[lvgl] Allow linear positioning of grid cells (#9196)

This commit is contained in:
Clyde Stubbs 2025-06-26 08:45:14 +10:00 committed by GitHub
parent f35be6b5cc
commit 6d0c6329ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 8 deletions

View File

@ -466,7 +466,7 @@ LVGL_SCHEMA = cv.All(
): lvalid.lv_color, ): lvalid.lv_color,
cv.Optional(df.CONF_THEME): cv.Schema( cv.Optional(df.CONF_THEME): cv.Schema(
{ {
cv.Optional(name): obj_schema(w) cv.Optional(name): obj_schema(w).extend(FULL_STYLE_SCHEMA)
for name, w in WIDGET_TYPES.items() for name, w in WIDGET_TYPES.items()
} }
), ),

View File

@ -21,7 +21,7 @@ from esphome.core.config import StartupTrigger
from esphome.schema_extractors import SCHEMA_EXTRACT from esphome.schema_extractors import SCHEMA_EXTRACT
from . import defines as df, lv_validation as lvalid from . import defines as df, lv_validation as lvalid
from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR, TYPE_GRID
from .helpers import add_lv_use, requires_component, validate_printf from .helpers import add_lv_use, requires_component, validate_printf
from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity
from .lvcode import LvglComponent, lv_event_t_ptr from .lvcode import LvglComponent, lv_event_t_ptr
@ -349,7 +349,60 @@ def obj_schema(widget_type: WidgetType):
) )
def _validate_grid_layout(config):
layout = config[df.CONF_LAYOUT]
rows = len(layout[df.CONF_GRID_ROWS])
columns = len(layout[df.CONF_GRID_COLUMNS])
used_cells = [[None] * columns for _ in range(rows)]
for index, widget in enumerate(config[df.CONF_WIDGETS]):
_, w = next(iter(widget.items()))
if (df.CONF_GRID_CELL_COLUMN_POS in w) != (df.CONF_GRID_CELL_ROW_POS in w):
# pylint: disable=raise-missing-from
raise cv.Invalid(
"Both row and column positions must be specified, or both omitted",
[df.CONF_WIDGETS, index],
)
if df.CONF_GRID_CELL_ROW_POS in w:
row = w[df.CONF_GRID_CELL_ROW_POS]
column = w[df.CONF_GRID_CELL_COLUMN_POS]
else:
try:
row, column = next(
(r_idx, c_idx)
for r_idx, row in enumerate(used_cells)
for c_idx, value in enumerate(row)
if value is None
)
except StopIteration:
# pylint: disable=raise-missing-from
raise cv.Invalid(
"No free cells available in grid layout", [df.CONF_WIDGETS, index]
)
w[df.CONF_GRID_CELL_ROW_POS] = row
w[df.CONF_GRID_CELL_COLUMN_POS] = column
for i in range(w[df.CONF_GRID_CELL_ROW_SPAN]):
for j in range(w[df.CONF_GRID_CELL_COLUMN_SPAN]):
if row + i >= rows or column + j >= columns:
# pylint: disable=raise-missing-from
raise cv.Invalid(
f"Cell at {row}/{column} span {w[df.CONF_GRID_CELL_ROW_SPAN]}x{w[df.CONF_GRID_CELL_COLUMN_SPAN]} "
f"exceeds grid size {rows}x{columns}",
[df.CONF_WIDGETS, index],
)
if used_cells[row + i][column + j] is not None:
# pylint: disable=raise-missing-from
raise cv.Invalid(
f"Cell span {row + i}/{column + j} already occupied by widget at index {used_cells[row + i][column + j]}",
[df.CONF_WIDGETS, index],
)
used_cells[row + i][column + j] = index
return config
LAYOUT_SCHEMAS = {} LAYOUT_SCHEMAS = {}
LAYOUT_VALIDATORS = {TYPE_GRID: _validate_grid_layout}
ALIGN_TO_SCHEMA = { ALIGN_TO_SCHEMA = {
cv.Optional(df.CONF_ALIGN_TO): cv.Schema( cv.Optional(df.CONF_ALIGN_TO): cv.Schema(
@ -402,8 +455,8 @@ LAYOUT_SCHEMA = {
} }
GRID_CELL_SCHEMA = { GRID_CELL_SCHEMA = {
cv.Required(df.CONF_GRID_CELL_ROW_POS): cv.positive_int, cv.Optional(df.CONF_GRID_CELL_ROW_POS): cv.positive_int,
cv.Required(df.CONF_GRID_CELL_COLUMN_POS): cv.positive_int, cv.Optional(df.CONF_GRID_CELL_COLUMN_POS): cv.positive_int,
cv.Optional(df.CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int, cv.Optional(df.CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int,
cv.Optional(df.CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int, cv.Optional(df.CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int,
cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments,
@ -474,7 +527,10 @@ def container_validator(schema, widget_type: WidgetType):
result = result.extend( result = result.extend(
LAYOUT_SCHEMAS.get(ltype.lower(), LAYOUT_SCHEMAS[df.TYPE_NONE]) LAYOUT_SCHEMAS.get(ltype.lower(), LAYOUT_SCHEMAS[df.TYPE_NONE])
) )
return result(value) value = result(value)
if layout_validator := LAYOUT_VALIDATORS.get(ltype):
value = layout_validator(value)
return value
return validator return validator

View File

@ -839,9 +839,7 @@ lvgl:
styles: bdr_style styles: bdr_style
grid_cell_x_align: center grid_cell_x_align: center
grid_cell_y_align: stretch grid_cell_y_align: stretch
grid_cell_row_pos: 0 grid_cell_column_span: 2
grid_cell_column_pos: 1
grid_cell_column_span: 1
text: "Grid cell 0/1" text: "Grid cell 0/1"
- label: - label:
grid_cell_x_align: end grid_cell_x_align: end