Layers¶
One mechanism for everything that floats over a window's
content — dialogs, menus, tooltips, snackbars, side sheets, drawers, context
menus. You build the content from ordinary nodes; Tesserae Engine stacks it, places it,
routes input around it, and tells you when it's dismissed. Tesserae Engine draws no scrim:
a modal's scrim is a window-sized box inside the layer.
menu = window.create("box", width=200, flex_direction="vertical", role="menu")
# ... add item nodes to menu ...
menu.on("dismiss", lambda: window.hide_layer(menu))
window.show_layer(menu, anchor=button, placement="below")
show_layer¶
show_layer(node, anchor=None, placement="below", modal=False,
dismissible=True)
Shows node over the window's content, above every layer already open —
layers stack in the order they're shown. A node attached elsewhere moves.
- Placement. With
anchor, the layer sits against that node on theplacementside:"below","above","start"(left), or"end"(right), aligned with the anchor's start. When that side lacks the room and the opposite side has more, it flips; then it shifts to stay inside the window. This happens at every layout, so it follows its anchor.node.get("layer_placement")reports the side it's on. Without an anchor, a layer sits at its ownx/y(0, 0by default) — size a scrim withwidth="100%", height="100%". - Modal. Input beneath the layer is blocked: presses, hovers, and wheel events outside it reach nothing. Focus moves to the layer's first focusable node, and Tab stays inside it.
- Dismissal. With
dismissible=True, a press outside the layer — and outside its anchor — deliversdismissto the layer's node, and the press goes no further. Escape deliversdismissto the topmost dismissible layer. A press inside a layer never dismisses the layers beneath it, so a submenu keeps its parent menu open. Closing is your call: callhide_layer, or not.
Every open layer is its own focus scope: Tab moves among the nodes of the layer holding focus. Events inside a layer bubble up to the layer's node and stop there, never reaching the content beneath.
A layer isn't part of the content: window.root.children() doesn't include it,
and its parent() is None. Content added while a layer is open still goes
beneath every layer.
hide_layer¶
hide_layer(node)
Hides the layer. The node is detached — alive while you hold it, ready to show
again — and focus inside it returns to the node that held focus when the layer
opened. Raises ValueError if node isn't a shown layer.
Building one¶
A modal dialog:
scrim = window.create("box", width="100%", height="100%", fill=(0, 0, 0, 82),
align_items="center", justify_content="center")
panel = window.create("box", width=312, padding=24, fill=(255, 251, 254, 255),
corner_radius=28, role="dialog", label="Discard draft?")
scrim.add_child(panel)
scrim.on("dismiss", lambda: window.hide_layer(scrim))
window.show_layer(scrim, modal=True)
A tooltip that nothing dismisses but your own timer:
window.show_layer(tip, anchor=icon, placement="above", dismissible=False)