Widgets widgets/split
Split
A container that divides its rectangle among focusable panes.
A container that divides its rectangle among focusable panes.
Package widgets/split. API reference: pkg.go.dev/widgets/split.
Split arranges panes along one axis.
Rendered output
cmd/capture through
widgettest.Capture: colour at 3 widths
(40, 80, 120 columns), and the exact plain-text cell grid at
the widest of them.
MinSize() returns 3 × 1 cells.
That is the whole widget including its own chrome, and it is the
widget's assertion about itself — below it, Draw clips rather
than blanks, and what to do about that is the application's decision.
40 columns × 11 rows
╭ modules ────────────╮ ╭ packages ────╮ │ module size │ │ input │ │ buffer 8.2 … │ │ layout │ │ geometry 3.1 … │ │> render █│ │› headless 11.4… │ │ term █│ │ layout 6.7 … │ │ virtual █│ │ render 14.9… │ │ widgets/b…█│ │ virtual 5.0 … │ │ widgets/b…█│ │ │ │ widgets/d… │ │ │ │ widgets/f… │ ╰─────────────────────╯ ╰──────────────╯
80 columns × 11 rows
╭ modules ────────────────────────────────────╮ ╭ packages ────────────────────╮ │ module size state │ │ input │ │ buffer 8.2 kB stable │ │ layout │ │ geometry 3.1 kB stable │ │> render █│ │› headless 11.4 kB beta │ │ term █│ │ layout 6.7 kB stable │ │ virtual █│ │ render 14.9 kB beta │ │ widgets/basic █│ │ virtual 5.0 kB alpha │ │ widgets/block █│ │ │ │ widgets/data │ │ │ │ widgets/form │ ╰─────────────────────────────────────────────╯ ╰──────────────────────────────╯
120 columns × 11 rows
╭ modules ────────────────────────────────────────────────────────────╮ ╭ packages ────────────────────────────────────╮ │ module size state │ │ input │ │ buffer 8.2 kB stable │ │ layout │ │ geometry 3.1 kB stable │ │> render █│ │› headless 11.4 kB beta │ │ term █│ │ layout 6.7 kB stable │ │ virtual █│ │ render 14.9 kB beta │ │ widgets/basic █│ │ virtual 5.0 kB alpha │ │ widgets/block █│ │ │ │ widgets/data │ │ │ │ widgets/form │ ╰─────────────────────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────╯
Plain text — the exact cell grid, trimmed per line. This is the honest form: Braille and block-element glyphs can take a different advance width in a web font than a terminal gives them, which breaks the alignment they depend on.
# Split — A container that divides its rectangle among focusable panes. # package: widgets/split # constructor: split.New(d layout.Direction, panes ...termmosaic.Widget) *split.Split # 120 columns x 11 rows, rendered through widgettest.Capture ╭ modules ────────────────────────────────────────────────────────────╮ ╭ packages ────────────────────────────────────╮ │ module size state │ │ input │ │ buffer 8.2 kB stable │ │ layout │ │ geometry 3.1 kB stable │ │> render █│ │› headless 11.4 kB beta │ │ term █│ │ layout 6.7 kB stable │ │ virtual █│ │ render 14.9 kB beta │ │ widgets/basic █│ │ virtual 5.0 kB alpha │ │ widgets/block █│ │ │ │ widgets/data │ │ │ │ widgets/form │ ╰─────────────────────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────╯
What you are looking at. This is the cell grid the renderer produced, in a web page. It is not a screenshot of a terminal, and it cannot show interaction, resize or animation — a capture is one settled frame after N frames of settling. Braille and Block Elements can take a different advance width in your browser's font than in a terminal, so if a gauge or a sparkline above looks wrong, the plain-text form below it is the exact output and the colour one is the decoration. The colour frames are at 40, 80, 120 columns; the plain-text frame is at 120. More on this.
About this capture. Focus moves between panes with Tab and the focused pane is the one that takes keys. A still frame cannot show that; the marker in the second pane’s title is where the capture has to stop.
Package context
Package split provides Split, the pane composer: it solves one layout and hands each pane a rectangle.
It exists because a terminal application that wants two panes needs three things and none of them is interesting: solve a constraint list, hand the results out as rectangles, and let the user move the divider. The first is layout’s job and Split calls it rather than reimplementing it — ADR 0004 chose a closed, predictable constraint set precisely so that exactly one solver exists, and a second arithmetic pass inside a widget is how two solvers start to disagree about overflow.
A widget receives its rectangle through Bounds, and nothing in the framework hands it one: there is no Resize method to push down the tree (ADR 0007 rejects it), so a container must be able to SET bounds. Hence Bounded, which a pane implements by having a SetBounds method — block.Block, basic.Text and basic.Paragraph all do. A pane that does not implement it is still drawn, with whatever rectangle the application gave it; Split never invents one.
Constructing it
split.New(d layout.Direction, panes ...termmosaic.Widget) *split.Split
Split solves one constraint list and hands each pane a rectangle. It is the only widget that introduces an interface of its own — Bounded — for a pane that wants to say what it needs.
Direction—layout.Direction—DirectionRoworDirectionColumn. One axis only; nesting aSplitinside aSplitis how you get a grid.Spacing—int, viaSetSpacing(n)— cells between panes, removed from the pane rects rather than overlapped. It was an exported field until v0.5.0; assigning it left the cached solve stale and the panes kept their old sizes until the next resize. Read it back withSpacing().Background—buffer.Style— the gap colour. Visible whereverSpacingis non-zero.
When not to use it
Do not use it for a grid. Split arranges panes along one axis. Two or three panes is the shape it solves; a grid is a Split inside a Split, and if you find yourself writing that, the constraint list is doing more work than the pane model is.
Do not use it as a border. It draws no frame. Each pane that needs one composes a Block; Split will not do it for you, and it deliberately does not know about Block.
Do not use it to get equal panes for free. Split calls the layout solver, and layout.Fill is order-insensitive — a deliberate divergence from tmux, pinned by a test. If you want fixed ratios, say so with layout.Ratio or layout.Percentage; do not rely on declaration order to break a tie.
Do not use it when one pane should be able to vanish. Panes are given at construction as a variadic. Hiding a pane means rebuilding the Split, which loses the pane’s state unless the application holds it — which is a fine design, but it is the application’s job, not the framework’s.
Do not expect it to solve a focus problem you have not described. Tab moves between focusable panes and the focused pane is the one that takes keys. That is the whole focus behaviour; a Split does not know which of your panes is a form and will not validate it.
Instead: layout.Solve directly for a fixed arrangement, Composition for the patterns that come up.
Related
- Layout and constraints — the solver
Splitdelegates to, including whyFillis order-insensitive. Block— the border each pane composes for itself.- Widgets and focus — what
Focusablemeans and who maintains focus order. - TermMosaic limitations that apply to every widget
- Source:
widgets/spliton GitHub