mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 15:30:21 +00:00
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
from esphome import codegen as cg, config_validation as cv
|
|
from esphome.const import CONF_BUTTON, CONF_ID, CONF_TEXT
|
|
from esphome.core import ID
|
|
from esphome.cpp_generator import MockObjClass
|
|
from esphome.cpp_types import nullptr
|
|
|
|
from ..defines import (
|
|
CONF_BODY,
|
|
CONF_BUTTON_STYLE,
|
|
CONF_BUTTONS,
|
|
CONF_CLOSE_BUTTON,
|
|
CONF_HEADER_BUTTONS,
|
|
CONF_MAIN,
|
|
CONF_MSGBOXES,
|
|
CONF_SRC,
|
|
CONF_TITLE,
|
|
LV_OBJ_FLAG,
|
|
TYPE_FLEX,
|
|
add_warning,
|
|
literal,
|
|
)
|
|
from ..helpers import add_lv_use
|
|
from ..lv_validation import lv_bool, lv_image, lv_text, pixels_or_percent
|
|
from ..lvcode import EVENT_ARG, LambdaContext, LocalVariable, lv, lv_expr, lv_obj
|
|
from ..schemas import (
|
|
STYLE_SCHEMA,
|
|
STYLED_TEXT_SCHEMA,
|
|
TEXT_SCHEMA,
|
|
container_schema,
|
|
part_schema,
|
|
)
|
|
from ..styles import LVStyle
|
|
from ..types import LV_EVENT, lv_obj_t
|
|
from . import Widget, WidgetType, add_widgets, set_obj_properties, widget_to_code
|
|
from .button import button_spec, lv_button_t
|
|
from .img import CONF_IMAGE
|
|
from .label import CONF_LABEL
|
|
from .obj import obj_spec
|
|
|
|
CONF_MSGBOX = "msgbox"
|
|
|
|
OUTER_STYLE = LVStyle(
|
|
"msgbox_outer",
|
|
{
|
|
"bg_opa": 0.5,
|
|
"bg_color": "black",
|
|
"border_width": 0,
|
|
"pad_all": 0,
|
|
"radius": 0,
|
|
},
|
|
)
|
|
|
|
|
|
class FooterButtonType(WidgetType):
|
|
def __init__(self):
|
|
super().__init__(
|
|
CONF_BUTTON, lv_button_t, (CONF_MAIN,), TEXT_SCHEMA, is_mock=True
|
|
)
|
|
|
|
async def obj_creator(self, parent: MockObjClass, config: dict):
|
|
return lv_expr.msgbox_add_footer_button(parent, config[CONF_TEXT])
|
|
|
|
|
|
footer_button_spec = FooterButtonType()
|
|
|
|
|
|
class HeaderButtonType(WidgetType):
|
|
def __init__(self):
|
|
super().__init__(
|
|
CONF_BUTTON,
|
|
lv_button_t,
|
|
(CONF_MAIN,),
|
|
cv.Schema(
|
|
{
|
|
cv.Required(CONF_SRC): lv_image,
|
|
}
|
|
),
|
|
is_mock=True,
|
|
)
|
|
|
|
async def obj_creator(self, parent: MockObjClass, config: dict):
|
|
return lv_expr.msgbox_add_header_button(
|
|
parent, await lv_image.process(config[CONF_SRC])
|
|
)
|
|
|
|
|
|
header_button_spec = HeaderButtonType()
|
|
|
|
MSGBOX_SCHEMA = container_schema(
|
|
obj_spec,
|
|
STYLE_SCHEMA.extend(
|
|
{
|
|
cv.GenerateID(CONF_ID): cv.declare_id(lv_obj_t),
|
|
cv.Required(CONF_TITLE): STYLED_TEXT_SCHEMA,
|
|
cv.Optional(CONF_BODY, default=""): STYLED_TEXT_SCHEMA,
|
|
cv.Optional(CONF_BUTTONS): cv.ensure_list(
|
|
container_schema(footer_button_spec)
|
|
),
|
|
cv.Optional(CONF_HEADER_BUTTONS): cv.ensure_list(
|
|
container_schema(header_button_spec)
|
|
),
|
|
cv.Optional(CONF_CLOSE_BUTTON, default=True): lv_bool,
|
|
cv.Optional(CONF_BUTTON_STYLE): part_schema(button_spec.parts),
|
|
}
|
|
),
|
|
)
|
|
|
|
|
|
async def msgbox_to_code(top_layer, conf):
|
|
"""
|
|
Construct a message box. This consists of a full-screen translucent background enclosing a centered container
|
|
with an optional title, body, close button and a set of footer buttons.
|
|
Header buttons can be added - they can be image buttons only.
|
|
The body of the message box may have any widgets the user wants to add.
|
|
:param conf: The config data
|
|
:return: code to add to the init lambda
|
|
"""
|
|
add_lv_use(
|
|
TYPE_FLEX,
|
|
CONF_BUTTON,
|
|
CONF_LABEL,
|
|
CONF_MSGBOX,
|
|
CONF_IMAGE,
|
|
*button_spec.get_uses(),
|
|
)
|
|
if CONF_BUTTON_STYLE in conf:
|
|
add_warning(
|
|
"'button_style' for msgbox is deprecated - style the buttons directly."
|
|
)
|
|
messagebox_id = conf[CONF_ID]
|
|
outer_id = ID(f"{messagebox_id.id}_outer", type=lv_obj_t)
|
|
outer = cg.Pvariable(outer_id, lv_expr.obj_create(top_layer))
|
|
outer_widget = Widget.create(outer_id.id, outer, obj_spec, conf)
|
|
msgbox = cg.Pvariable(messagebox_id, lv_expr.msgbox_create(outer))
|
|
outer_widget.move_to_foreground = True
|
|
msgbox_widget = Widget.create(messagebox_id, msgbox, obj_spec, conf)
|
|
msgbox_widget.outer = outer_widget
|
|
text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, ""))
|
|
title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, ""))
|
|
close_button = conf[CONF_CLOSE_BUTTON]
|
|
percent100 = await pixels_or_percent.process(1.0)
|
|
lv_obj.set_size(outer, percent100, percent100)
|
|
outer_widget.add_style(await OUTER_STYLE.get_var())
|
|
outer_widget.add_flag(LV_OBJ_FLAG.HIDDEN)
|
|
lv.msgbox_add_title(msgbox, title)
|
|
lv.msgbox_add_text(msgbox, text)
|
|
lv_obj.set_style_align(msgbox, literal("LV_ALIGN_CENTER"), 0)
|
|
await set_obj_properties(msgbox_widget, conf)
|
|
await add_widgets(msgbox_widget, conf)
|
|
for button in conf.get(CONF_BUTTONS, ()):
|
|
await widget_to_code(button, footer_button_spec, msgbox)
|
|
for button in conf.get(CONF_HEADER_BUTTONS, ()):
|
|
await widget_to_code(button, header_button_spec, msgbox)
|
|
|
|
async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action:
|
|
outer_widget.add_flag(LV_OBJ_FLAG.HIDDEN)
|
|
if close_button:
|
|
with LocalVariable(
|
|
"close_btn_", lv_obj_t, lv_expr.msgbox_add_close_button(msgbox)
|
|
) as close_btn:
|
|
lv_obj.remove_event(close_btn, 0)
|
|
lv_obj.add_event_cb(
|
|
close_btn,
|
|
await close_action.get_lambda(),
|
|
LV_EVENT.CLICKED,
|
|
nullptr,
|
|
)
|
|
else:
|
|
lv_obj.add_event_cb(
|
|
outer, await close_action.get_lambda(), LV_EVENT.CLICKED, nullptr
|
|
)
|
|
|
|
|
|
async def msgboxes_to_code(lv_component, config):
|
|
top_layer = lv_expr.disp_get_layer_top(lv_component.get_disp())
|
|
for conf in config.get(CONF_MSGBOXES, ()):
|
|
await msgbox_to_code(top_layer, conf)
|