limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/Inputs

    Slider

    A value dragged along a track, from 0 to 1: a seek position or a volume.

    Native componentGuide source

    Preview

    CodeSource
    fn view(state: &State, palette: ComponentPalette) -> Element {
        Slider::new("volume", state.volume, palette).width(280.0).render()
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::SliderChanged { id, fraction, committed } = action {
            if id == "volume" {
                state.volume = *fraction;
                if *committed {
                    state.save_volume();
                }
            }
        }
    }

    Examples

    Each one is the library drawing it. Press, type and hover.

    Slider2 / 8
    CodeSource
    Slider::new("input-slider-steps", fraction(cx, "input-slider-steps", 0.5), palette)
        .steps(5)
        .accessibility_label("Quality")
        .width(280.0)
        .render()
    Slider3 / 8
    CodeSource
    Slider::new("input-slider-disabled", 0.6, palette)
        .accessibility_label("Brightness")
        .enabled(false)
        .width(280.0)
        .render()
    Slider4 / 8
    CodeSource
    Slider::new("input-slider-marks", fraction(cx, "input-slider-marks", 0.25), palette)
        .steps(5)
        .marks()
        .accessibility_label("Zoom")
        .width(280.0)
        .render()
    Slider5 / 8
    CodeSource
    Slider::new("input-slider-sizes", fraction(cx, "input-slider-sizes", 0.5), palette)
        .steps(7)
        .mark_labels(["12px", "14px", "15px", "16px", "18px", "20px", "24px"])
        .default_stop(3)
        .ink(cx.state.theme().accent)
        .accessibility_label("Font size")
        .width(280.0)
        .render()
    Slider6 / 8
    CodeSource
    Slider::new("input-slider-sizes-disabled", 0.5, palette)
        .steps(3)
        .mark_labels(["Small", "Medium", "Large"])
        .default_stop(1)
        .accessibility_label("Density")
        .enabled(false)
        .width(280.0)
        .render()
    Slider7 / 8
    CodeSource
    {
        let threshold = fraction(cx, "input-slider-level", 0.4);
        let level = 0.55;
        let theme = cx.state.theme();
        Slider::new("input-slider-level", threshold, palette)
            .ink(theme.accent)
            .level(level, if level >= threshold { Color::rgb(35, 165, 90) } else { Color::rgb(240, 178, 50) })
            .accessibility_label("Input sensitivity")
            .width(280.0)
            .render()
    }
    Slider8 / 8
    CodeSource
    row(EQUALISER
        .iter()
        .map(|(band, gain)| {
            let id = format!("input-slider-eq-{band}");
            Slider::new(&id, fraction(cx, &id, *gain), palette)
                .vertical()
                .origin(0.5)
                .accessibility_label(format!("{band} Hz"))
                .width(120.0)
                .render()
        })
        .collect())

    Slider

    A value dragged along a track, from 0 to 1: a seek position or a volume.

    Import

    use limn::{Component, Slider};

    Usage

    A slider is controlled: pass its fraction each frame and store the one it reports. Values outside 0 to 1 are clamped.

    fn view(state: &State, palette: ComponentPalette) -> Element {
        Slider::new("volume", state.volume, palette).width(280.0).render()
    }
    
    fn update(state: &mut State, action: &UiAction) {
        if let UiAction::SliderChanged { id, fraction, committed } = action {
            if id == "volume" {
                state.volume = *fraction;
                if *committed {
                    state.save_volume();
                }
            }
        }
    }

    committed is false during a drag and true on release, so a host can follow the thumb and write the value once.

    Steps

    Slider::new("quality", state.quality, palette).steps(5).render()

    With steps(n) the slider rests on one of n evenly spaced stops, the ends included. Below two it is continuous, which is the default.

    Marks and labels

    Slider::new("font-size", state.font_size, palette)
        .steps(7)
        .mark_labels(["12px", "14px", "15px", "16px", "18px", "20px", "24px"])
        .default_stop(3)
        .render()

    marks() draws a hairline across the track at each stop, behind the thumb, in the palette’s border color. mark_labels names the stops from the first, in 12 pixel muted text over the track, and draws the marks too. Each label is one line. The labels at the ends line up with the ends of the slider; the others center on their stop. Labels past the last stop are ignored, and an empty one leaves its stop unnamed.

    default_stop(i) shows the label of stop i, counted from 0, in the slider’s ink: the value the setting has until it is changed.

    Without steps of 2 or more there are no stops, so neither marks nor labels. Past 100 stops there are no marks: that close together they would only gray the track. The labels still show.

    Ink

    Slider::new("seek", state.position, palette).ink(accent).render()

    ink colors the filled part, the thumb and the default stop’s label, in place of the palette’s text.

    A level on the track

    let over = state.level >= state.threshold;
    Slider::new("sensitivity", state.threshold, palette)
        .ink(accent)
        .level(state.level, if over { green } else { yellow })
        .accessibility_label("Input sensitivity")
        .render()

    level(level, color) draws a value that moves on its own along the track, from the start to level, in color, in place of the fill: a meter whose threshold is the slider’s value, such as a microphone’s input under the sensitivity the thumb sets. The host passes the new level on every rebuild, and asks for a repaint while it moves. The platform is told only the value. Disabled, the level is muted.

    Vertical and from a middle

    Slider::new("eq-1k", state.gain_1k, palette)
        .vertical()
        .origin(0.5)
        .width(120.0)
        .render()

    vertical() stands the slider up: 0 at the bottom and 1 at the top, so dragging up raises it. It is as tall as width says, or else as its parent stretches it, as a row does, and the whole width it is given takes the pointer, with the track down its middle. It draws no marks and no labels.

    origin(fraction) fills the track from that fraction to the value instead of from the start: 0.5 fills from the middle either way, for a value around a neutral one such as a gain of 0 dB.

    Disabled

    Slider::new("volume", state.volume, palette).enabled(false).render()

    A disabled slider shows its value and takes no pointer or focus. It has no thumb, and its fill and every label are in the muted color.

    Keyboard

    KeyMoves the value
    Left, RightBy a twentieth, or by one stop with steps
    Up, DownThe same, on a vertical slider
    Home, EndTo either end

    Actions

    ActionWhen
    UiAction::SliderChanged { id, fraction, committed }The value changes: during a drag with committed false, on release or from the keyboard with it true.

    Accessibility

    The platform is told a slider and where it stands from 0 to 1, disabled or not. A slider shows no text, so the host names it, or puts it in a Form field, whose label names it.

    Slider::new("volume", volume, palette).accessibility_label("Volume")

    A slider with labels says the label of the stop its value is on, or the nearest one during a drag, as its value in words: “16px”.

    Limits

    Labels do not move apart or wrap when they are too wide for their stops, and the track stays where it is. A label centers on its stop only while it is no wider than twice the way from its stop to the nearer end stop; a wider one starts that way left of its stop and runs on to the right, over its neighbours, and past the slider’s end if it must. Give the slider the width its labels need.

    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/inputs.rs.