Splitter

N-pane split container with draggable, collapsible dividers.
Public types
| Kind | Name |
|---|---|
struct | Splitter — An N-pane resizable split container driven by a SplitterModel |
struct | PaneDescriptor — Per-pane configuration passed to SplitterModel::from_panes / SplitterModel::insert_pane |
struct | PaneSnapshot — Immutable per-pane view handed to the pure distribute sizing function (the internal splitter::distribute engine) |
struct | PaneState — Persistable per-pane layout state |
struct | SplitterState — Full serializable snapshot of a SplitterModel's sizes + collapsed flags |
struct | SplitterModel — A shared, cloneable handle to a splitter's layout state |
Public functions
Splitter
PaneDescriptor
| Returns | Function |
|---|---|
| Constructors | |
Self | new() |
| Builder methods | |
Self | size(size: f32) |
Self | min_size(min: f32) |
Self | max_size(max: f32) |
Self | stretch(stretch: f32) |
Self | collapsible(collapsible: bool) |
Self | collapsed(collapsed: bool) |
Self | collapsed_size(px: f32) |
Self | visible(visible: bool) |
SplitterModel
Detailed description
Splitter arranges N ≥ 2 panes along one axis (per Orientation)
with N − 1 grabbable handles between them — the Qt QSplitter
model. All layout state (per-pane size / min / max / stretch /
collapsed) lives in a shared, cloneable SplitterModel; the app
holds a clone to read, mutate, persist, and import/export, while the
widget renders it and reacts to the model's version signal.
Strengths carried over from the old two-pane SplitView: anti-jump
drag, keyboard resize, Role::Splitter accessibility, per-pane content
clipping, RTL-correct horizontal layout. New: N panes, per-pane
stretch (container-resize policy), animated collapse with four triggers
(programmatic / double-click / drag-past-min snap / keyboard), a Tier-3
SplitterStyle, and serializable import/export. It is the building
block DockingLayout is built from.
Collapsing and hiding a pane each have an animated door
(SplitterModel::set_collapsed, SplitterModel::set_pane_visible) and
an instant one (SplitterModel::set_collapsed_immediate,
SplitterModel::set_pane_visible_immediate) for restoring a layout.
let model = SplitterModel::from_panes(vec![
PaneDescriptor::new().size(220.0).min_size(160.0).stretch(0.0).collapsible(true),
PaneDescriptor::new().stretch(1.0).min_size(320.0),
PaneDescriptor::new().size(280.0).stretch(0.0).collapsible(true),
], Orientation::Horizontal);
Splitter::new(model.clone())
.pane(sidebar).pane(editor).pane(inspector)
.pane_label(0, tr!(sidebar()));
Density
The picture above is the widget at TargetDensity::Compact, the mouse-and-keyboard ladder. Below is the same subject on the same canvas with only the ladder changed, so what moves is the density and nothing else — where the subject no longer fits, that is what the denser targets cost it at that size. See docs/density-and-targets.md.
Touch

API reference
📖 Full rustdoc API for this module
pub struct Splitter
Import: use teksilo::widgets::Splitter;
An N-pane resizable split container driven by a SplitterModel.
See the module-level documentation for a usage overview and
constructor patterns.
#![allow(unused)] fn main() { pub struct Splitter { /* fields */ } }
Methods
pub fn new(model: SplitterModel) -> Self
Create a Splitter bound to the given model. Panes must be appended
with pane in model order.
pub fn pane(mut self, widget: impl teksilo_core::IntoTeksiChild) -> Self
Append a content pane (model order). Call once per pane; the count
must match model.pane_count().
pub fn panes(self, iter: impl IntoIterator<Item = impl teksilo_core::IntoTeksiChild>) -> Self
Append several content panes from an iterator, in model order.
The loop form of pane. The total pane count still has to
match model.pane_count().
pub fn child(self, widget: impl teksilo_core::IntoTeksiChild) -> Self
teksu! ergonomic alias for pane: a bare child in a
Splitter { ... } block lowers to .child(...).
pub fn children( self, iter: impl IntoIterator<Item = impl teksilo_core::IntoTeksiChild>, ) -> Self
teksu! ergonomic alias for panes: a for loop in a
Splitter { ... } block lowers to .children(...).
pub fn child_opt(self, widget: Option<impl teksilo_core::IntoTeksiChild>) -> Self
Attach widget when it is Some, and do nothing when it is None.
The conditional-child form. teksu!'s if without an else lowers to
this, and it is what cond.then(|| w) is for in a builder chain. None
adds no arena node, so nothing is laid out, painted, or published to the
accessibility tree, and a stack applies no spacing around it.
pub fn pane_label(mut self, index: usize, label: impl Into<Prop<String>>) -> Self
Set an accessible region name for pane index (locale-reactive).
Labeled panes become a named Role::Group; unlabeled panes stay
AT-transparent (their content represents itself).
pub fn style(mut self, style: impl SplitterStyle) -> Self
Override the active SplitterStyle for this instance only.
pub fn enabled(mut self, enabled: impl Into<Prop<bool>>) -> Self (default: true)
Enable or disable handle dragging. When false, divider handles are
rendered inert — the pane layout is still valid but the user cannot
resize panes. Read once at build time: a bound Signal is
snapshotted, not tracked.
pub struct PaneDescriptor
Import: none — PaneDescriptor is public in a private module of teksilo-widgets and nothing re-exports it.
Per-pane configuration passed to SplitterModel::from_panes /
SplitterModel::insert_pane. Public fields + Default so it can
be built with struct-literal ..Default::default() syntax, or via the
fluent setters.
#![allow(unused)] fn main() { pub struct PaneDescriptor { /* fields */ } }
Fields
initial_size:Option<f32>— Initial main-axis size in px.None⇒ take an equal share (the first layout equalizes via the stretch path).min_size:f32— Hard compression floor in px.max_size:Option<f32>— Optional growth ceiling in px.stretch:f32— Container-resize slack weight (QtsetStretchFactor).0.0⇒ rigid (keeps its size on resize);>0⇒ absorbs slack ∝ weight.collapsible:bool— Whether the user may collapse this pane (drag-snap / double-click / keyboard). Programmaticset_collapsedignores this flag (it governs interactive collapse only, like Qt'schildrenCollapsible).collapsed:bool— Initial collapsed state.collapsed_size:f32— The main-axis size a collapsed pane folds down to (default0⇒ fully gone). Set this to keep a sliver visible while collapsed — e.g. an accordion's header height, so the pane shrinks to just its header and can be re-expanded from there. The pane restores to its prior size on expand regardless.visible:bool— Whether the pane is present at all. Unlikecollapsed(which folds the pane but keeps its grabbable gutter), a hidden pane removes both the pane and an adjacent gutter from the layout — it reads as absent. Toggled reactively viaset_pane_visible.
Methods
pub fn new() -> Self
pub fn size(mut self, size: f32) -> Self (default: None)
pub fn min_size(mut self, min: f32) -> Self (default: SPLITTER_MIN_PANE_SIZE)
pub fn max_size(mut self, max: f32) -> Self (default: None)
pub fn stretch(mut self, stretch: f32) -> Self (default: 1.0)
pub fn collapsible(mut self, collapsible: bool) -> Self (default: false)
pub fn collapsed(mut self, collapsed: bool) -> Self (default: false)
pub fn collapsed_size(mut self, px: f32) -> Self
Size a collapsed pane folds down to (default 0). See
collapsed_size.
pub fn visible(mut self, visible: bool) -> Self (default: true)
pub struct PaneSnapshot
Import: none — PaneSnapshot is public in a private module of teksilo-widgets and nothing re-exports it.
Immutable per-pane view handed to the pure distribute sizing
function (the internal splitter::distribute engine).
#![allow(unused)] fn main() { pub struct PaneSnapshot { /* fields */ } }
Fields
stored_size:f32min_size:f32max_size:Option<f32>stretch:f32collapsed:boolcollapsed_size:f32visible:bool
pub struct PaneState
Import: none — PaneState is public in a private module of teksilo-widgets and nothing re-exports it.
Persistable per-pane layout state. Captures the user-controllable
values (size + collapsed); structural config (min/max/stretch/
collapsible) is app-declared and not serialized — Qt saveState
parity.
#![allow(unused)] fn main() { pub struct PaneState { /* fields */ } }
Fields
stored_size:f32collapsed:bool
pub struct SplitterState
Import: none — SplitterState is public in a private module of teksilo-widgets and nothing re-exports it.
Full serializable snapshot of a SplitterModel's sizes + collapsed
flags. Round-trips through SplitterModel::export_state /
import_state and implements
Versioned so apps persist it through
SettingsFile<SplitterState> + Migrator (TOML).
#![allow(unused)] fn main() { pub struct SplitterState { /* fields */ } }
Fields
version:u32panes:Vec<PaneState>
pub struct SplitterModel
Import: none — SplitterModel is public in a private module of teksilo-widgets and nothing re-exports it.
A shared, cloneable handle to a splitter's layout state. Clone =
share-by-handle (cheap Rc bump).
#![allow(unused)] fn main() { pub struct SplitterModel(Rc<RefCell<SplitterModelInner>>); }
Methods
pub fn new(n: usize, orientation: Orientation) -> Self
n equal-share panes (each stretch = 1, min = SPLITTER_MIN_PANE_SIZE).
pub fn from_panes(panes: Vec<PaneDescriptor>, orientation: Orientation) -> Self
Build from explicit per-pane descriptors.
pub fn handle_count(&self) -> usize
Number of distinct handles to this model (1 = unshared).
pub fn set_stored_size(&self, index: usize, size: f32)
pub fn set_stored_size_silent(&self, index: usize, size: f32)
Like set_stored_size but without a version
bump — for writes made from inside a layout/effect pass that is already
relaying out (e.g. capturing the displayed size as the collapse
reference), where a bump would re-enter the effect.
pub fn set_pair_sizes(&self, index: usize, size_a: f32, size_b: f32)
Set both sides of handle index (panes index and index+1) in
one mutation — a single version bump, so a drag produces exactly
one relayout per move.
pub fn set_min_size(&self, index: usize, min: f32)
pub fn set_max_size(&self, index: usize, max: Option<f32>)
pub fn set_stretch(&self, index: usize, stretch: f32)
pub fn set_collapsible(&self, index: usize, collapsible: bool)
pub fn set_collapsed(&self, index: usize, collapsed: bool)
Programmatically collapse/expand pane index, animated. Ignores
the collapsible flag (that flag only gates interactive triggers).
pub fn set_collapsed_immediate(&self, index: usize, collapsed: bool)
Collapse/expand pane index instantly (no tween). Used by the
drag handlers — the pointer is already the motion.
pub fn toggle_collapsed(&self, index: usize)
Toggle pane index's collapsed state, animated.
pub fn set_collapsed_size(&self, index: usize, px: f32)
Set the size pane index folds down to when collapsed (default 0).
See PaneDescriptor::collapsed_size. No version bump on its own — it
only affects the next collapse.
pub fn set_pane_visible(&self, index: usize, visible: bool)
Show or hide pane index (animated). A hidden pane removes both the
pane and an adjacent gutter from the layout — it reads as absent,
unlike a collapsed pane (which keeps its grabbable gutter). The pane
must be pre-mounted in the Splitter; this is the reactive "add /
remove a pane from a fixed set" trick (no rebuild).
pub fn set_pane_visible_immediate(&self, index: usize, visible: bool)
Show or hide pane index instantly (no tween): the next layout
already has the pane removed from, or restored to, the split. The
counterpart of Self::set_collapsed_immediate, for a caller that is
restoring a layout or reacting to something that is already the motion.
pub fn is_pane_visible(&self, index: usize) -> bool
pub fn consume_animate_flag(&self) -> bool
Read-and-reset the "animate the next collapse change?" latch. The
widget's collapse effect calls this once per version bump; it
resets to true so the default (programmatic) path animates.
pub fn insert_pane(&self, index: usize, desc: PaneDescriptor)
Insert a pane at index (clamped to [0, len]). A None
initial_size takes the average of the existing panes' sizes; the
next layout rebalances. The app must rebuild the Splitter widget
to supply the new pane's content (retained-mode: changing a
container's child set is a rebuild; the model keeps the
persistent size/collapse state across it).
pub fn remove_pane(&self, index: usize)
Remove the pane at index (no-op if out of range). The app must
rebuild the Splitter widget to drop the corresponding content.
pub fn replace_pane_desc(&self, index: usize, desc: PaneDescriptor)
Replace the metadata of pane index (keeps its current size unless
the descriptor specifies one).
pub fn set_gutter_thickness(&self, thickness: f32)
pub fn set_snap_offset(&self, offset: f32)
pub fn set_keyboard_step_px(&self, step: f32)
pub fn set_orientation(&self, orientation: Orientation)
pub fn pane_count(&self) -> usize
pub fn stored_size(&self, index: usize) -> f32
pub fn min_size(&self, index: usize) -> f32
pub fn max_size(&self, index: usize) -> Option<f32>
pub fn stretch(&self, index: usize) -> f32
pub fn is_collapsible(&self, index: usize) -> bool
pub fn collapsed_size(&self, index: usize) -> f32
The size pane index folds to when collapsed (default 0). See
PaneDescriptor::collapsed_size.
pub fn is_collapsed(&self, index: usize) -> bool
pub fn orientation(&self) -> Orientation
pub fn gutter_thickness(&self) -> f32
pub fn snap_offset(&self) -> f32
pub fn keyboard_step_px(&self) -> f32
pub fn version(&self) -> Signal<u64>
The reactive version signal. The Splitter widget binds this at
BindingLevel::Relayout.
pub fn pane_snapshots(&self) -> Vec<PaneSnapshot>
Immutable per-pane snapshot for the pure sizing engine.
pub fn export_state(&self) -> SplitterState
Snapshot the per-pane sizes + collapsed flags into a serializable
SplitterState.
pub fn import_state(&self, state: &SplitterState) -> bool
Restore sizes + collapsed flags from a SplitterState. Returns
false (and changes nothing) if the pane count doesn't match — the
structural config must be reconstructed first. Restoration is
instant (collapsed panes don't animate open on load).