Skip to content

Docking

Docking arranges panels in zones around a window's content — a file tree on the left, an inspector on the right, a terminal at the bottom — and lets the user drag a panel from one zone to another. Tesserae Engine docks panels, drags them, and reports what's happening; what a drag looks like — the handle you grab and the highlight over the target zone — is yours to draw.

Zones and panels

A zone is an ordinary node you build and register for a side. Docking a panel attaches it under that node and makes it the zone's shown panel.

left_zone = window.create("box", fill=(0xF5, 0xF5, 0xF5, 0xFF), width=200, height=400)
window.root.add_child(left_zone)
window.add_dock_zone("left", left_zone, size=200.0)

right_zone = window.create("box", fill=(0xF5, 0xF5, 0xF5, 0xFF), width=200, height=400)
window.root.add_child(right_zone)
window.add_dock_zone("right", right_zone, size=200.0)

panel = window.create("box", fill=(0xFF, 0xFF, 0xFF, 0xFF), width=180, height=380)
window.dock_panel("left", panel)

Accepted side values: "left", "right", "top", "bottom", "center" — anything else raises ValueError.

A zone holds any number of panels and shows one at a time. set_active_panel(side, index) picks which, counting in docking order.

Method Purpose
add_dock_zone(side, container, size) Registers container as side's dock zone, seeding its extent
dock_panel(side, panel) Docks panel into side's zone and shows it — moving it, if it's docked in another zone
set_active_panel(side, index) Shows the zone's indexth panel
start_panel_drag(panel) Starts dragging panel, which must be docked
undock_panel(panel) Takes panel out of its zone and off the tree; it can be docked again

Moving a panel without a drag

dock_panel on a panel that's docked in another zone moves it, exactly as a drop would: the old zone stops listing it and shows another of its panels. That's how a "Move to" menu moves a panel for someone who can't drag:

window.dock_panel("right", panel)   # from wherever it's docked now

Closing a panel

undock_panel(panel) takes a panel out of docking for good: out of its zone's list, whose later panels move down one index, and off the tree. If it was the one shown, the zone shows the next panel, or else the previous, as closing a tab does — or nothing, if it was the last. A drag of it in progress is cancelled.

window.undock_panel(panel)          # a closed tab, or a panel a hot-reloaded
                                    # layout no longer names
window.dock_panel("left", panel)    # it can come back while you hold it

Use it rather than panel.remove(), which takes the panel off the tree but leaves it in its zone's list, where set_active_panel would show it again. A panel that is destroyed, or freed after remove(), leaves its zone on the next docking call.

Dragging a panel

Start a drag from your own handle's pointer_down. While the pointer moves, the dock_target window event reports the zone under it (event.side, or None over no zone) each time that changes. Releasing the primary button moves the panel into the zone there — nowhere, if it's over no zone — and reports dock_drop (event.panel, event.side).

handle.on("pointer_down", lambda: window.start_panel_drag(panel))

highlight = window.create("box", fill=(0, 0x80, 0xFF, 0x60),
                          position="absolute", width="100%", height="100%")
zones = {"left": left_zone, "right": right_zone}

def show_target(event):
    highlight.remove()
    if event.side is not None:
        zones[event.side].add_child(highlight)

window.on("dock_target", show_target)
window.on("dock_drop", lambda: highlight.remove())

The drag runs in the same input pipeline as every other pointer event, so a headless test drives it with window.simulate:

window.start_panel_drag(panel)
window.simulate("pointer_move", node=right_zone)
window.simulate("pointer_up", node=right_zone)
assert right_zone.children() == [panel]