DockManager
A dockable-panel workbench — the "free docking" familiar from IDEs like Visual Studio, VS Code and Lazarus. Panels split, tab, float into their own OS windows and redock at any depth, dragging by their title bars, with their live content preserved across every rearrangement. Layouts save and restore.
const dock = app.DockManager();
dock.pack.configure({ fill: 'both', expand: true });
const files = dock.panel({ title: 'Files', region: 'left' });
const editor = dock.panel({ title: 'Editor', region: 'center' });
const props = dock.panel({ title: 'Properties', region: 'right' });
const console = dock.panel({ title: 'Console', region: 'bottom' });
// Fill each panel's `body` with your content:
files.body.Treeview({ columns: ['name'] }).pack.configure({ fill: 'both', expand: true });
editor.body.Notepad().pack.configure({ fill: 'both', expand: true });
That builds the familiar cross — a left/right/top/bottom frame around a center fill —
and the user can then drag the panels apart, tab them together, or float them out. A
full runnable showcase lives in apps/docking/main.js; the
concepts are walked through in the Docking guide.
The model
A DockManager is a recursive space-partitioning tree. Every node is a split
(a paned window, horizontal or vertical), a tabs group (a notebook), or a panel
leaf. The named regions — left, right, top, bottom, center — are sugar that
builds the canonical cross; from there, split / tab / float / dock operate on
the tree directly. Panels are never reparented — they are shown wherever the tree puts
them, so relocating a panel never disturbs its content (selection, scroll, focus,
in-flight input all survive).
Panels
dock.panel(options) creates a DockPanel and places it. Fill panel.body (a
Frame) with your widgets.
| Option | Description |
|---|---|
title |
The panel title (title bar + tab label). |
key |
A stable id for save/restore (defaults to title). |
region |
'left' | 'right' | 'top' | 'bottom' | 'center'. |
size |
Initial extent (px, or { chars } / { em }) of an edge region. |
minSize |
Minimum extent (px); the panel can't be sashed/loaded smaller. |
closable |
false shows a lock marker instead of the ✕ and drops Close from the menu. |
floatable |
false pins the panel (no floating). |
icon |
A Feather icon name shown in the title bar / tab. |
DockPanel methods
| Method | Description |
|---|---|
body |
The content Frame — put your widgets here. |
dock(region?) |
Dock the panel into region (from floating/closed/elsewhere). |
float(geometry?) |
Float the panel out into its own OS window ('360x260+120+80'). |
close() |
Hide the panel (stays alive; re-openable via dock() / float()). |
tabWith(other, where?) |
Tab other beside this panel. where: 'before' | 'after' | 'first' | 'last' (default). |
maximize() / restore() / toggleMaximize() |
Temporarily give the panel the whole dock, then put it back. |
moveUp() / moveDown() |
Reorder the panel among its siblings (split panes or tabs). |
key / title / state |
state is 'docked' | 'floating' | 'closed'. |
minSize / maximized |
The min extent (or null); whether currently maximized. |
DockManager methods
| Method | Description |
|---|---|
panel(options) |
Create + place a panel (above). |
panelsIn(region) |
The panels currently in a named region. |
regionSize(region, size?) |
Set or read an edge region's extent — px, or { chars: N } / { em: N } (font-relative, so it scales with DPI). Getter with no size. |
region(name) |
The live widget hosting a region (for measuring). |
splitPanel(target, newPanel, side) |
Split target's cell in two, newPanel on side ('left'/'right'/'top'/'bottom'). |
saveLayout() / loadLayout(snapshot) |
Snapshot / restore the whole arrangement (structure, sizes, floats, closed) — panels matched by key. loadLayout is suppressed from onLayoutChange. |
saveTree() / loadTree(tree) |
The structural tree alone (split / tabs / panel nodes with sizes). |
onLayoutChange |
Assign a callback fired (coalesced) after any interactive or programmatic change. |
Tree operations
The four primitives the gestures are built from — use them for custom docking UIs:
| Method | Description |
|---|---|
newLeaf(options) |
Create a panel not yet placed (state 'closed'). |
glue(panel, side) |
Dock panel as a full-span band on the whole tree's side edge. |
treeSplit(target, panel, side) |
Split target's cell, panel on side (the tree twin of splitPanel). |
treeTab(target, panel, where?) |
Tab panel beside target (where as tabWith). |
treeRemove(panel) |
Remove a panel; the tree simplifies (a 1-child split collapses, a 1-tab group demotes). |
Precedence is glue-order: the latest edge glue is the outermost band and bounds
the earlier ones (so a full-height side column can sit outside a full-width bottom bar).
Persisting a layout
dock.onLayoutChange = () => localStore.set('layout', dock.saveLayout());
// …on next launch:
const saved = localStore.get('layout');
if (saved) dock.loadLayout(saved);
See also
- Docking guide — the gestures, the tree, sizing, persistence
- The
apps/docking/main.jsshowcase - Drag & drop · Layout · Notebook · PanedWindow