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:
Greyson Parrelli
2026-08-11 11:25:44 -04:00
parent 5b00c98e03
commit 918ccec6e1
6 changed files with 656 additions and 212 deletions
+22 -6
View File
@@ -68,6 +68,11 @@
padding: 6px 6px 6px 0;
}
/* Paned itself draws nothing; the panes inside it carry the styling. */
.vtabs-view paned {
background: none;
}
/* Each terminal sits in its own rounded frame so multiple panes in a view
read as distinct surfaces. */
.vtabs-pane {
@@ -115,9 +120,20 @@
opacity: 1;
}
/* Drop indicator: an inset bar on the edge the pane would land against, so
the preview also tells you which way the view is about to be arranged. */
.vtabs-pane.drop-left { box-shadow: inset 4px 0 0 0 #b29df5; }
.vtabs-pane.drop-right { box-shadow: inset -4px 0 0 0 #b29df5; }
.vtabs-pane.drop-top { box-shadow: inset 0 4px 0 0 #b29df5; }
.vtabs-pane.drop-bottom { box-shadow: inset 0 -4px 0 0 #b29df5; }
/* The pane being dragged. There is no separate drop indicator: the layout
rearranges live during the drag, so the view itself is the preview. */
.vtabs-pane.dragging {
opacity: 0.65;
border-color: #b29df5;
}
/* Divider between panes. Wide enough to grab without hunting for it. */
.vtabs-view paned > separator {
background-color: #0f0d14;
min-width: 6px;
min-height: 6px;
}
.vtabs-view paned > separator:hover {
background-color: #4a3f6b;
}