Split tree layouts, resizable dividers, and live drag preview
Replaces the flat pane list with a binary split tree, so a view can hold layouts a single shared orientation could not express. Three panes in a row with the rightmost dragged to the bottom now gives two on top and one spanning the full width beneath them. Each split node owns a GtkPaned, which brings draggable dividers. Positions are stored as ratios and re-applied whenever the available space changes, so proportions survive window resizes while user drags still update them. Drop placement depends on proximity to the view's own border: near an edge the pane is placed against the whole layout and spans it, anywhere else it splits only the pane under the pointer. Drags now rearrange live rather than on release, so the layout under the cursor is always the result. Cancelling restores the original arrangement, which works because only the dragged pane moves: the rest of the tree keeps its shape, so re-inserting beside the original sibling is enough. Fixes a latent ownership bug the tree exposed: panes did not hold a reference to their own widget, so detaching one during a rebuild dropped the last reference and finalized it, producing GTK_IS_WIDGET assertion failures. Panes and split nodes now each own a reference to their widget.
This commit is contained in:
@@ -78,7 +78,8 @@ just the VT core.
|
||||
```
|
||||
main.zig AdwApplication, CSS loading
|
||||
Window.zig sidebar + GtkStack of views, tab management, shortcuts
|
||||
View.zig one tab's content: an ordered list of panes + their layout
|
||||
View.zig one tab's content: its panes, their layout, and drag handling
|
||||
Layout.zig the split tree: nodes, rearranging, GtkPaned materialization
|
||||
Pane.zig a terminal plus its header, drag source, and drop target
|
||||
Terminal.zig GtkDrawingArea: Cairo/Pango renderer + input handling
|
||||
Session.zig libghostty-vt Terminal + parser, fed by the PTY
|
||||
@@ -87,19 +88,32 @@ key.zig GDK keyval -> libghostty-vt key mapping
|
||||
theme.zig colors libghostty-vt has no opinion about
|
||||
```
|
||||
|
||||
A tab is a **view**, and a view holds one or more terminal **panes**. New panes
|
||||
open side by side; moving a pane to a top or bottom edge restacks the view, and
|
||||
moving it to a side edge puts it back in a row.
|
||||
A tab is a **view**, and a view holds one or more terminal **panes** arranged
|
||||
in a **binary split tree**: every interior node is a split with an orientation,
|
||||
every leaf is a pane. That is what allows the layouts a single shared
|
||||
orientation can't express — three panes in a row, drag the rightmost to the
|
||||
bottom, and you get two on top with one spanning the full width beneath them.
|
||||
|
||||
**Layout is a flat list, not a split tree.** A view has one orientation shared
|
||||
by all its panes, so it can express "three side by side" or "three stacked" but
|
||||
not "two beside a stack of three". A tree would handle the general case and is
|
||||
where this goes if mixed layouts turn out to matter; it just brings a lot of
|
||||
structure that a shell-next-to-an-agent view doesn't need yet.
|
||||
Each split node owns a `GtkPaned`, so dividers are draggable and every split
|
||||
remembers its position as a **ratio** rather than a pixel count. The ratio is
|
||||
the source of truth: it is re-applied whenever the available space changes, so
|
||||
proportions survive window resizes, and only user drags update it.
|
||||
|
||||
Panes are held in a `GtkBox`, which can reorder its children in place. Nothing
|
||||
is ever unparented, so rearranging a view never disturbs the running
|
||||
terminals — their scrollback and processes carry straight through the move.
|
||||
Where a drop lands depends on how close it is to the view's own border. Near an
|
||||
edge of the window the pane is placed against the whole layout and spans it;
|
||||
anywhere else it splits just the pane under the pointer. That single rule gives
|
||||
both "put this across the bottom" and "split this one in half".
|
||||
|
||||
Panes and split nodes each hold a strong reference to their own widget, so
|
||||
detaching and reattaching during a rearrangement never finalizes anything.
|
||||
Running terminals keep their scrollback and processes straight through a move.
|
||||
|
||||
**Drags rearrange live.** Each time the drop target changes, the move is
|
||||
applied for real, so the layout under the cursor is always the layout you will
|
||||
get — there is no separate drop indicator because the view itself is the
|
||||
preview. Cancelling a drag puts the pane back: only the dragged pane ever
|
||||
moves, so the rest of the tree is unchanged and re-inserting it beside its
|
||||
original sibling restores the original shape.
|
||||
|
||||
Two design choices worth calling out:
|
||||
|
||||
@@ -127,8 +141,9 @@ in principle, but a terminal grid is small.
|
||||
- Window title (OSC 0/2) becomes the pane header and tab label; the tab shows
|
||||
its pane count once a view holds more than one
|
||||
- Tabs: create, close, switch; closing the last one closes the window
|
||||
- Multiple terminals per tab, rearranged by keyboard or by dragging a pane's
|
||||
header; closing the last pane in a view closes its tab
|
||||
- Multiple terminals per tab in an arbitrary split tree, rearranged by keyboard
|
||||
or by dragging a pane's header, with draggable dividers between them;
|
||||
closing the last pane in a view closes its tab
|
||||
|
||||
### Shortcuts
|
||||
|
||||
@@ -157,10 +172,9 @@ This is a proof of concept, and the following are deliberately absent:
|
||||
- **Ligatures and complex shaping.** Each run is drawn independently at a
|
||||
fixed grid offset, so text that needs shaping across cell boundaries won't
|
||||
look right.
|
||||
- **Resizable splits.** Panes in a view divide the space evenly; there are no
|
||||
draggable dividers. Moving to `GtkPaned` would add them.
|
||||
- **Mixed layouts and moving panes between tabs.** See the flat-list note
|
||||
above; panes can only be rearranged within their own view.
|
||||
- **Moving panes between tabs.** Panes can only be rearranged within their
|
||||
own view.
|
||||
- **Saved layouts.** A view's arrangement lives only as long as the tab.
|
||||
- **Kitty graphics, hyperlinks, tab reordering, config file.**
|
||||
- **Custom terminfo.** `TERM` is reported as `xterm-256color` rather than
|
||||
`ghostty`, since we don't install a terminfo entry.
|
||||
|
||||
Reference in New Issue
Block a user