Studio docking
Studio should dock the way the engine’s level editor does. Any panel can be split, tabbed or floated, and the result is remembered as one line. The approach is one dock brain with two skins. Phase 1 moved the editor’s model and rules into Halcyon as Halcyon.Docking (see Docking), and nothing in that namespace draws. Phase 2 is Studio drawing the same model with Avalonia controls. It starts after the Studio refresh lane lands, because that lane is editing MainWindow now.
What Studio takes from the brain, and what it builds
Section titled “What Studio takes from the brain, and what it builds”Studio adds a project reference to Halcyon. The assembly references nothing and targets net10.0 like Studio, and Studio uses only the Docking namespace. No Halcyon widget, DrawList or UiTree enters Studio.
| Shared (Halcyon) | Studio’s own (Avalonia) |
|---|---|
DockTree edits: tab, reorder, split a pane, split an edge, remove and collapse | The pane host control that arranges views over solved rectangles |
DockLayoutSolver, with per-pane minimum widths | Tab strips, measured by Avalonia’s own text layout |
DockDragging.Resolve, InsertIndex, Tears, TearsFromStrip | The drop indicator and the drag ghost |
DockTree.ResizeDivider, DividerTravel, DividerGrab | The divider strips and their cursors |
DockTree.SizedSplitFraction, the share a docked window arrives at | Floating panes as OS windows |
DockLayoutText, the one-line spelling and its reader | Where the line is stored (StudioSettings) |
DockTabs, for tabs keyed by what they show | Which window ids exist, and what each one shows |
Studio’s panels as windows
Section titled “Studio’s panels as windows”Every panel becomes a window with an id, and the tab group on the right becomes an ordinary tabbed pane.
| Today | Window id | Notes |
|---|---|---|
| Pallet list, the left column | pallets | Its header’s folder and refresh buttons move to the right end of the pane’s strip, so the pane keeps one header row. |
| Asset browser, the center | assets | The required window, the way gameView is the engine’s. Selection starts here, so a stored layout that lost it reads as malformed and falls back to the default. |
| Inspector tab | inspector | Keeps its preview drawer. |
| Visual tab (the patch editor) | visual | Stays in the layout when the asset has no visual editor and shows that as its empty state. Today the tab hides, but a tab that leaves the tree loses the place a person gave it. The auto-switch setting becomes DockTabs.Focus. |
| Raw Edit tab | rawEdit | |
| Diagnostics tab | diagnostics | The status bar’s error link and a tapped toast still open it, now by focusing it wherever it is docked. |
Preview pop-out (PreviewWindow) | preview | Becomes a dockable window. Floating it is the pop-out, so the special window goes away. |
These stay as they are: the toolbar with build and the Edit/View switch, the status bar, toasts, the build progress line, OmniSearch, the modal dialogs (new asset, folder or pallet, move and rename, Blender path, workspace setup), the Settings window and the cache viewer.
The default layout
Section titled “The default layout”The default is today’s three columns in the layout spelling:
h[0.18:pallets,0.58:assets,0.24:inspector+visual+rawEdit+diagnostics]Before anybody drags a tab, Studio looks as it does now. The strips replace the headers the panels already have, the right column’s tabs are the tabs it already shows, and the dividers keep today’s panelSplitter look (an 11-pixel grab area around a 1-pixel line). The preview starts closed, as the pop-out does now.
There is one change in behavior. Today the side columns are fixed pixel widths around a star column. The tree stores shares, so a wider window widens them too. The solver’s per-pane minimum widths (pallets 180, inspector 260, in Avalonia’s device-independent pixels) stop them collapsing. If mltn wants the old pixel-pinned feel back, that is a host choice about which fraction to write after a resize. It is not a change to the brain.
A small layout button in the toolbar opens a list of every window with an open/close check and a Reset layout row. Studio has no menu bar, so this stands in for the editor’s View menu.
Docked panes have no close X, in Studio or in the editor. A tab closes by a middle-click or from its right-click menu’s Close row, both built by the shared DockChrome; Studio’s first window of a mode is the one a mode cannot do without, so its Close row is disabled. Only a floating window carries an X.
The Avalonia skin
Section titled “The Avalonia skin”The pane host. A Panel subclass overrides ArrangeOverride. It solves the tree into its bounds and arranges one pane control per DockPaneRect and one divider per DockDividerRect. Avalonia’s device-independent pixels are already scale-free, which is the same rule as the engine’s “floors in logical units”. The minimum widths pass straight through as DIPs.
Views are cached by window id. Moving a tab reparents the existing view and does not rebuild it, so tree expansion, scroll position and the Raw Edit caret survive a drag. Avalonia refuses a control that still has a parent, so the host detaches before it attaches.
Tab strips are a horizontal ItemsControl of tab headers with a front tab and a close glyph. A drag inside the strip reorders through DockDragging.InsertIndex, fed the right edges of the laid-out tab containers. It tears once TearsFromStrip says so. From then on, every pointer move calls Resolve with a DockDragOptions filled from Studio’s settings.
The drop indicator and ghost draw in an overlay layer above the pane host: a rectangle at the target’s PreviewPosition and PreviewSize, and the tab’s title riding the pointer. Avalonia blends in gamma space, and the engine blends in linear light. The engine’s indicator alpha therefore does not carry over, and Studio keeps its own number for it.
Dividers are thin controls with the resize cursor for their axis. They drive DockTree.ResizeDivider from a DividerGrab anchored at the press, which replaces the GridSplitters.
Floating panes are OS windows. Each one is an owned Avalonia Window (not in the taskbar) that hosts the same pane control. The brain already keeps floating panes outside the tree for exactly this reason. The drag controller works in screen coordinates and converts to the main window’s client space with each window’s own render scaling. It then passes Resolve the floating windows as DockFloatingTargets, so dropping on a floating window’s strip merges into it. Floating geometry is stored in screen DIPs and pulled back onto a visible screen when restored. Some platforms ignore a requested window position (Wayland does). There a restored window opens where the system puts it, and that is accepted rather than worked around.
Tuning. Studio starts from the engine’s numbers: an edge band of 16, a center zone of 0.3, pane and root splits of 0.5 and 0.25, and a tear distance of 12. If both hosts should always agree, the defaults move into the brain as named constants that the engine’s preferences and Studio’s settings both read, rather than being written twice.
Order of work
Section titled “Order of work”- Reference Halcyon from Studio and replace the
Gridwith the pane host. It shows the default layout with no dragging, and a screenshot should match today’s. - Add the dividers.
- Add tab reorder, tear, drop zones and the indicator inside the main window.
- Add floating OS windows and drops between windows.
- Store the layout in
StudioSettings, restore it, and add the layout button with Reset. - Fold the preview pop-out into the
previewwindow.
Each step leaves Studio usable, and none of them changes Halcyon.Docking except where Studio finds a rule the engine never needed. Such a rule lands in the brain with a Halcyon test, so the editor gets it too.