Skip to main content

PaneTree

Struct PaneTree 

Source
pub struct PaneTree { /* private fields */ }
Expand description

The pane tree owned by App (DESIGN.md §5.9, lives in lattice-ui-tui). v1 supports arbitrary recursive splits; the sole constraint is that the active pane must always exist (closing the last pane is a no-op so the App is never “paneless”).

Leaves are addressed two ways: by index into Self::leaves (what PaneNode::Leaf and most methods use — not stable across Self::close_active) and by PaneId (stable; resolve with Self::index_of).

§Examples

use lattice_core::ui::pane::{PaneRect, PaneState, PaneTree, SplitOrientation};

let mut tree = PaneTree::single(PaneState::default());
let right = tree.split_active(SplitOrientation::Vertical); // `<C-w>v`
assert_eq!(tree.len(), 2);
assert_eq!(tree.active_index(), 0); // focus stays on the original

// An even vertical split of an 80x24 area.
let area = PaneRect { x: 0, y: 0, width: 80, height: 24 };
let rects = tree.compute_rects(area);
assert_eq!(rects[0], (0, PaneRect { x: 0, y: 0, width: 40, height: 24 }));
assert_eq!(rects[1], (right, PaneRect { x: 40, y: 0, width: 40, height: 24 }));

// Zoom (`<C-w>z`) hands the active pane the whole area, non-destructively.
assert!(tree.toggle_zoom());
assert_eq!(tree.compute_rects(area), [(0, area)]);
assert!(tree.toggle_zoom());
assert_eq!(tree.compute_rects(area).len(), 2);

// Closing the last pane is refused.
assert!(tree.close_active());
assert!(!tree.close_active());

Implementations§

Source§

impl PaneTree

Source

pub fn single(state: PaneState) -> PaneTree

Build a single-pane tree pointing at state.

Source

pub fn zoomed(&self) -> Option<PaneId>

The zoomed pane’s id, or None when the full split layout is showing.

Slice: ZP.1.

Source

pub fn is_zoomed(&self) -> bool

Whether a pane is currently zoomed. Read by the modeline’s core.zoom element and the tabline marker.

Slice: ZP.1.

Source

pub fn zoomed_index(&self) -> Option<usize>

The zoomed pane’s leaf index, resolved through Self::index_of. None when nothing is zoomed, and also when the recorded id no longer names a live leaf — a state the enforcement below is meant to prevent, but resolving rather than trusting means a stale id degrades to “not zoomed” instead of to a panic on the render path.

Slice: ZP.1.

Source

pub fn toggle_zoom(&mut self) -> bool

Toggle zoom on the active pane (<C-w>z). Returns true if the zoom state changed.

A single-leaf tree is a no-op: there is nothing to hide, and marking it zoomed would light the indicator for a state the user cannot see.

Slice: ZP.1.

Source

pub fn clear_zoom(&mut self) -> bool

Drop zoom unconditionally. Returns true if it was set. Called by every mutation that would otherwise break the zoomed-is-active invariant.

Slice: ZP.1.

Source

pub fn root(&self) -> &PaneNode

The layout root, ignoring zoom. Renderers that recurse over the tree should use Self::render_root instead.

Source

pub fn leaves(&self) -> &[PaneState]

Every pane, in leaf-index order (the indices PaneNode::Leaf holds). Never empty.

Source

pub fn leaves_mut(&mut self) -> &mut [PaneState]

Mutable access to every pane’s state (cursor, scroll, viewport size…). The slice cannot grow or shrink, so the tree shape stays consistent.

Source

pub fn len(&self) -> usize

Number of panes. Always at least 1.

Source

pub fn is_empty(&self) -> bool

Always false — a tree is never paneless. Present for the len/is_empty convention.

Source

pub fn active_index(&self) -> usize

Leaf index of the focused pane.

Source

pub fn active(&self) -> &PaneState

The focused pane’s state.

Source

pub fn active_mut(&mut self) -> &mut PaneState

The focused pane’s state, mutably.

Source

pub fn set_active(&mut self, idx: usize) -> bool

Set the active pane by index. Out-of-bounds indices are ignored. Returns true if the index changed.

Source

pub fn index_of(&self, id: PaneId) -> Option<usize>

Locate a pane by its PaneId. Returns the index into Self::leaves or None if the id is unknown.

Source

pub fn split_active(&mut self, orientation: SplitOrientation) -> usize

Split the active pane along orientation, inserting a new leaf next to it. The new leaf inherits the active pane’s buffer + cursor + scroll (vim’s <C-w>s / <C-w>v default). Returns the new pane’s index. The active pane stays the original leaf – the new sibling becomes inactive.

Source

pub fn close_active(&mut self) -> bool

Close the active pane. The parent split collapses to the surviving sibling. If the tree has only one pane, the close is a no-op (the App is never paneless). Returns true if a pane was actually removed.

Source

pub fn collapse_to_active(&mut self) -> bool

<C-w>o / :only / emacs C-x 1 – close every pane except the active one, collapsing the whole tree to a single leaf that keeps the active pane’s state. No-op (returns false) when only one pane is open. Unlike repeated Self::close_active, this keeps the active pane and drops its siblings in one step.

Source

pub fn equalize_ratios(&mut self) -> bool

Walk the tree and reset every split’s ratio to DEFAULT_SPLIT_RATIO (0.5). Vim’s <C-w>=. Returns true if any ratio actually changed, so the renderer can skip the publish when there’s nothing to do.

Slice: Issue #28 (2026-05-22).

Source

pub fn resize_active_split( &mut self, orientation: SplitOrientation, delta: f32, ) -> bool

Adjust the ratio of the nearest split-of-the- requested-orientation containing the active pane. Vim’s <C-w>+ / <C-w>- (HorizontalSplit) / <C-w>> / <C-w>< (VerticalSplit). delta is added to the current ratio (positive = grow active side); clamped to [MIN_SPLIT_RATIO, MAX_SPLIT_RATIO]. Returns true if a ratio was found and changed.

“Active side” semantics: if the active leaf is in the top (or left) child, growing means increasing the ratio (top/left gets bigger). If active is in bottom (or right), growing means DECREASING the ratio.

Slice: Issue #28.

Source

pub fn navigate( &self, direction: PaneDirection, area: PaneRect, ) -> Option<usize>

Navigate cardinally from the active pane. Returns the new active leaf index, or None if there’s no neighbour in that direction. Geometry comes from Self::compute_rects so the navigation matches what the renderer drew. A candidate must also OVERLAP the source on the perpendicular axis, and among those that do, the source’s cursor decides. Both halves are load-bearing, and their absence was one bug:

In a 2×2 grid every pane below the top row starts at the same y, so ranking by travel distance alone left every candidate tied — and the winner fell out of leaf iteration order, which is tree order, not screen order. <C-w>j from the top-RIGHT pane landed in the bottom- LEFT one, and so did <C-w>j from the top-left, which is how the bug reads to a user: the direction keys ignore where you are.

Overlap alone is not enough either. One wide pane above two narrow ones overlaps both, so vim breaks that tie with the cursor’s screen position — you go down into the pane under your cursor — and that is the behaviour muscle memory expects.

Source

pub fn next_pane(&self) -> usize

Cycle to the next pane (<C-w>w). Wraps around.

Source

pub fn prev_pane(&self) -> usize

Cycle to the previous pane (<C-w>W). Wraps around.

Source

pub fn compute_rects(&self, area: PaneRect) -> Vec<(usize, PaneRect)>

Compute the rectangle each leaf occupies inside area. The renderer + navigation use this to lay out / find spatial neighbours. Each split divides its area by its ratio (rounded to whole cells). Under zoom, only the zoomed pane is returned, with the whole area. Entries are (leaf index, rect) in depth-first, top-to-bottom / left-to-right order.

Source

pub fn render_root(&self) -> Cow<'_, PaneNode>

The node a renderer should paint — the zoomed leaf when zoomed, otherwise the real root.

For renderers that recurse over PaneNode themselves rather than calling Self::compute_rects (the GPUI peer’s collect_pane_geometries and paint_pane_tree). Expressing zoom as “the tree is one leaf” means those walks stay exactly as they were — no zoom branch inside the recursion, where it would have to be re-checked at every level.

Allocation-free in both arms: borrowed for the real root, and the owned arm is a bare Leaf(usize) with no boxed children.

Slice: ZP.3.

Source

pub fn compute_rects_layout(&self, area: PaneRect) -> Vec<(usize, PaneRect)>

The always-unzoomed peer of Self::compute_rects — the full split layout, whatever the zoom state.

One caller: Self::navigate. Cardinal navigation has to ask where a pane sits in the REAL layout, because the answer decides where focus lands after the zoom drops. Reading the zoom-aware view instead would hand it a one-entry list, no neighbour would be found in any direction, and <C-w>j while zoomed would silently do nothing.

Slice: ZP.1.

Trait Implementations§

Source§

impl Clone for PaneTree

Source§

fn clone(&self) -> PaneTree

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PaneTree

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for PaneTree

Default builds a single-pane tree with a placeholder PaneState. Used by Editor::default() for headless / test scaffolding; production paths construct via PaneTree::single with a real pane.

Source§

fn default() -> PaneTree

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> IntoMaybeUndefined<T> for T

§

fn into_maybe_undefined(self) -> MaybeUndefined<T>

Converts this value into a three-state builder argument.
§

impl<T> IntoOption<T> for T

§

fn into_option(self) -> Option<T>

Converts this value into an optional builder argument.
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> Pointee for T

§

type Pointer = u32

§

fn debug( pointer: <T as Pointee>::Pointer, f: &mut Formatter<'_>, ) -> Result<(), Error>

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more