limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/Layout

    ResizeHandle

    The strip between two panes that the pointer drags to move their boundary. It reports the drag; the host, which owns the panes' sizes, moves them.

    Native componentGuide source

    Preview

    CodeSource
    fn view(state: &State, palette: ComponentPalette) -> Element {
        Element::row(vec![
            library(state, palette).size(Some(state.library_width), None),
            ResizeHandle::vertical("split", palette).render(),
            content(state, palette).flex(1.0),
        ])
    }
    
    fn update(state: &mut State, action: &UiAction) {
        match action {
            UiAction::DragStarted { id, position } if id == "split" => {
                state.drag_from = Some((position.x, state.library_width));
            }
            UiAction::DragMoved { id, position, .. } if id == "split" => {
                if let Some((x, width)) = state.drag_from {
                    state.library_width = (width + position.x - x).clamp(160.0, 480.0);
                }
            }
            UiAction::Dropped { id, .. } if id == "split" => state.drag_from = None,
            UiAction::DragCancelled { id } if id == "split" => {
                if let Some((_, width)) = state.drag_from.take() {
                    state.library_width = width;
                }
            }
            _ => {}
        }
    }

    ResizeHandle

    The strip between two panes that the pointer drags to move their boundary. It reports the drag; the host, which owns the panes’ sizes, moves them.

    Import

    use limn::{Component, ResizeHandle};

    Usage

    ResizeHandle::vertical(id, palette) stands between two columns and fills the height of the row that holds it. The handle is controlled: it reports DragStarted, DragMoved and Dropped or DragCancelled on its ID, and the host adds how far the pointer has travelled to the width the pane had when the drag began, within its own limits.

    fn view(state: &State, palette: ComponentPalette) -> Element {
        Element::row(vec![
            library(state, palette).size(Some(state.library_width), None),
            ResizeHandle::vertical("split", palette).render(),
            content(state, palette).flex(1.0),
        ])
    }
    
    fn update(state: &mut State, action: &UiAction) {
        match action {
            UiAction::DragStarted { id, position } if id == "split" => {
                state.drag_from = Some((position.x, state.library_width));
            }
            UiAction::DragMoved { id, position, .. } if id == "split" => {
                if let Some((x, width)) = state.drag_from {
                    state.library_width = (width + position.x - x).clamp(160.0, 480.0);
                }
            }
            UiAction::Dropped { id, .. } if id == "split" => state.drag_from = None,
            UiAction::DragCancelled { id } if id == "split" => {
                if let Some((_, width)) = state.drag_from.take() {
                    state.library_width = width;
                }
            }
            _ => {}
        }
    }

    The handle draws nothing at rest, so the gap between two panes can be the handle itself. Under the pointer it fills with the palette’s hover, and while held with its selected. Nothing follows the pointer: the handle stays where the host’s panes put it, so a boundary that has reached its limit shows where it stopped.

    Between two rows

    Element::column(vec![
        editor,
        ResizeHandle::horizontal("terminal-split", palette).render(),
        terminal,
    ])

    ResizeHandle::horizontal lies between two rows, moves up and down and fills the width of the column that holds it. The host reads position.y.

    Thickness

    ResizeHandle::vertical("split", palette).thickness(12.0).render()

    thickness is the room the handle takes across the boundary and its pointer target: 8 unless set and never under 2. A non-finite value is ignored.

    Cursor

    Over the handle and through the drag the pointer takes the column-resize shape (Cursor::ResizeColumn) for a vertical handle and the row-resize shape (Cursor::ResizeRow) for a horizontal one. The tree reports it through UiTree::cursor and the host’s backend applies it; with egui that is limn_egui::set_cursor(ctx, tree.cursor()) once a frame, after the layout.

    Disabled

    ResizeHandle::vertical("split", palette).enabled(state.resizable).render()

    A disabled handle keeps its place and its thickness, so the panes do not move. It cannot be dragged or focused, and the pointer keeps its shape.

    Actions

    ActionWhen
    UiAction::DragStarted { id, position }The pressed pointer leaves the click tolerance. position is where it was pressed.
    UiAction::DragMoved { id, position, over }The pointer moves during the drag, on the handle or away from it.
    UiAction::Dropped { id, position, target }The pointer is released.
    UiAction::DragCancelled { id }Escape or the pointer leaving the window ended the drag.
    UiAction::Clicked { id }A press is released without moving. The host may ignore it.

    The handle has no keyboard resize.

    Accessibility

    The handle is a button known by its ID. It shows no text, so it has no name until the host gives it one; disabled, it is still there as disabled.

    ResizeHandle::vertical("sidebar-split", palette)
        .accessibility_label("Resize sidebar")
        .render()

    API reference

    The guide covers composition, options and actions. Generate the complete Rust type and builder reference from the checkout:

    cargo doc -p limn --open

    Component API overview · State and actions

    The example is drawn by crates/limn-gallery/src/pages/panels.rs.