nano-ui
Safe HaskellNone
LanguageGHC2024

NanoUI.Widgets.SplitPane

Description

Pure pane-grid tree model and geometry, modelled on iced's PaneGrid.

A GridNode is a binary split tree of panes. Each split stores an axis (AxisV = vertical divider splitting width, AxisH = horizontal divider splitting height), a ratio in [0,1] for the first (A) side, and the two child subtrees. Every pane and split has a globally unique Word64 id so pane state can be keyed by pane id regardless of position in the tree.

All functions here are pure; the interactive wrapper in NanoUI.Widgets.PaneGrid persists a GridNode as a Data.Dynamic value in the widget store.

Synopsis

Documentation

data GridAxis Source #

Divider orientation. AxisV draws a vertical divider (panes left/right), AxisH draws a horizontal divider (panes stacked top/bottom).

Constructors

AxisV 
AxisH 

data GridNode Source #

Binary split tree node. Pane and split ids share one monotonic counter. Positional (non-record) so the multi-constructor type keeps total fields.

Constructors

Split 

Fields

Pane !Word64

Pane id.

Instances

Instances details
Eq GridNode Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

Show GridNode Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

data PaneDrop Source #

Result of dropping a dragged pane on a target pane.

Constructors

DropSwap Word64

Drop on the center of the pane: the two panes swap places.

DropSplit Word64 GridAxis Bool

Drop near an edge: the target pane splits along the axis and the dragged pane moves into the new child. True puts the dragged pane on the A (lefttop) side, False on the B (rightbottom) side.

DropTop GridAxis Bool

Drop on the outer edge of the whole grid: the entire tree is wrapped in a new top-level split and the dragged pane takes one side, so the rest of the grid collapses onto the other. True puts the dragged pane on the A (lefttop) side, False on the B (rightbottom) side.

Instances

Instances details
Eq PaneDrop Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

Show PaneDrop Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

treePanes :: GridNode -> [Word64] Source #

Pane ids in the tree (depth-first, A then B).

treeSize :: GridNode -> Int Source #

Number of panes.

paneExist :: GridNode -> Word64 -> Bool Source #

Does a pane with the given id exist?

subtreeMin :: Float -> Float -> GridNode -> (Float, Float) Source #

Minimum (width, height) that must be reserved for a subtree under a minSize per-pane floor and spacing between every split level.

mainMins :: GridAxis -> (Float, Float) -> (Float, Float) -> (Float, Float) Source #

The subtree minima that apply along a split's main axis: widths for AxisV (panes left/right), heights for AxisH (panes stacked).

mainLen :: GridAxis -> Rect -> Float Source #

Extent of a region along a split's main axis.

splitLength :: Float -> Float -> Float -> Float -> Float -> Float Source #

A-side extent for a split along its main axis, honouring the subtree minima. The ratio shares out the extent left after the gutter between the sides, so a 0.5 split gives both sides the same length. Falls back to the raw share when the region is too small to satisfy both minima.

layoutNode :: Float -> Float -> GridNode -> Rect -> (Map Word64 Rect, [DividerInfo]) Source #

Lay out a tree into per-pane regions and divider bands within Rect. Dividers are reported parent-before-child so dragging a divider resizes its immediate subtrees relative to the same region.

data DividerInfo Source #

Per-split divider information: the split's own region (where the ratio applies), the exact spacing band, and the axis ratio id.

Constructors

DividerInfo 

Instances

Instances details
Eq DividerInfo Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

Show DividerInfo Source # 
Instance details

Defined in NanoUI.Widgets.SplitPane

treeSplit :: Word64 -> Word64 -> GridAxis -> Bool -> Word64 -> GridNode -> GridNode Source #

Split the pane (first arg) along the axis with a 0.5 ratio, inserting the new pane. newOnA places the new pane on the A (left/top) side of the new split; False puts it on the B (right/bottom) side. Returns the updated tree (unchanged if the pane does not exist).

treeSetRatio :: Word64 -> Float -> GridNode -> GridNode Source #

Set the raw ratio of a split (clamped to [0,1]).

treeRemovePane :: Word64 -> GridNode -> Maybe GridNode Source #

Remove a pane. The sibling subtree absorbs its space. Nothing if the pane does not exist or removing it would empty the tree.

treeMovePane :: Word64 -> Word64 -> PaneDrop -> GridNode -> Maybe GridNode Source #

Move a pane onto a drop target. Center drops swap the two panes; edge drops split the target pane with the given fresh split id and move the dragged pane into the new child; top-level drops wrap the whole tree in a new root split with the dragged pane on one side.

clampTreeRatio :: GridNode -> Word64 -> Rect -> Float -> Float -> Float -> Float Source #

Clamp a proposed ratio for a split so both subtrees keep at least their minimum size within the given region.

dropPreview :: Float -> Float -> GridNode -> Word64 -> Rect -> PaneDrop -> Maybe (Rect, PaneDrop) Source #

Drop preview for a drop target: the rect to highlight and the PaneDrop the drop performs. The highlight is found by simulating the drop (treeMovePane with a throwaway split id) and laying the resulting tree out (layoutNode) into the grid rect, so it is exactly the region the dragged pane will occupy after the drop, accounting for the restructuring that removing the pane causes (its parent split collapses and sibling subtrees expand) and for spacing and min-size floors. Estimating the rect from the target's pre-drop bounds goes wrong wherever mixed AxisV / AxisH splits make those two layouts diverge. spacing must be the gutter actually laid out between panes: PaneGrid passes pgSpacing + 2 * pgLeeway, not pgSpacing, or the preview regions drift from the on-screen layout. Nothing when the drop cannot be performed (unknown pane ids, DropTop on a single-pane grid).

dropTargetForPane :: Rect -> V2 -> Word64 -> PaneDrop Source #

Classify a drop point on a target pane into the PaneDrop the drop performs: the pane's center swaps the two panes, an edge zone splits the target along that edge's axis with the dragged pane on the near side.

topLevelDropTarget :: Float -> Rect -> V2 -> Maybe PaneDrop Source #

Classify a drop point against the grid's outer boundary. If the pointer sits within band px of a grid edge, return the DropTop target for that edge; otherwise Nothing. Checked before pane-level drops so the outermost edge always restructures the whole grid.