Skip to content

Slider

A slider component for selecting numeric values within a specified range. Supports both single value and range selection modes, horizontal and vertical orientations, custom styling, and step intervals.

Import

rust
use gpui_component::slider::{Slider, SliderState, SliderEvent, SliderValue};

Usage

Basic Slider

rust
let slider_state = cx.new(|_| {
    SliderState::new()
        .min(0.0)
        .max(100.0)
        .default_value(50.0)
        .step(1.0)
});

Slider::new(&slider_state)

Slider with Event Handling

rust
struct MyView {
    slider_state: Entity<SliderState>,
    current_value: f32,
}

impl MyView {
    fn new(cx: &mut Context<Self>) -> Self {
        let slider_state = cx.new(|_| {
            SliderState::new()
                .min(0.0)
                .max(100.0)
                .default_value(25.0)
                .step(5.0)
        });

        let subscription = cx.subscribe(&slider_state, |this, _, event: &SliderEvent, cx| {
            match event {
                SliderEvent::Change(value) => {
                    this.current_value = value.start();
                    cx.notify();
                }
            }
        });

        Self {
            slider_state,
            current_value: 25.0,
        }
    }
}

impl Render for MyView {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        v_flex()
            .gap_2()
            .child(Slider::new(&self.slider_state))
            .child(format!("Value: {}", self.current_value))
    }
}

Range Slider

rust
let range_slider = cx.new(|_| {
    SliderState::new()
        .min(0.0)
        .max(100.0)
        .default_value(20.0..80.0)  // Range from 20 to 80
        .step(1.0)
});

Slider::new(&range_slider)

Vertical Slider

rust
Slider::new(&slider_state)
    .vertical()
    .h(px(200.))

Custom Step Intervals

rust
// Integer steps
let integer_slider = cx.new(|_| {
    SliderState::new()
        .min(0.0)
        .max(10.0)
        .step(1.0)
        .default_value(5.0)
});

// Decimal steps
let decimal_slider = cx.new(|_| {
    SliderState::new()
        .min(0.0)
        .max(1.0)
        .step(0.01)
        .default_value(0.5)
});

Min/Max Configuration

rust
// Temperature slider
let temp_slider = cx.new(|_| {
    SliderState::new()
        .min(-10.0)
        .max(40.0)
        .default_value(20.0)
        .step(0.5)
});

// Percentage slider
let percent_slider = cx.new(|_| {
    SliderState::new()
        .min(0.0)
        .max(100.0)
        .default_value(75.0)
        .step(5.0)
});

Disabled State

rust
Slider::new(&slider_state)
    .disabled(true)

Custom Styling

rust
Slider::new(&slider_state)
    .bg(cx.theme().success)
    .text_color(cx.theme().success_foreground)
    .rounded(px(4.))

API Reference

SliderState

MethodDescription
new()Create a new slider state with default values
min(f32)Set minimum value (default: 0.0)
max(f32)Set maximum value (default: 100.0)
step(f32)Set step interval (default: 1.0)
default_value(impl Into<SliderValue>)Set initial value
set_value(impl Into<SliderValue>)Update slider value
value()Get current slider value

Slider

MethodDescription
new(&Entity<SliderState>)Create a new slider bound to state
horizontal()Set horizontal orientation (default)
vertical()Set vertical orientation
disabled(bool)Set disabled state

SliderValue

The slider supports two types of values:

VariantDescription
Single(f32)A single numeric value
Range(f32, f32)A range with start and end values

Methods

MethodDescription
start()Get the start value (for both single and range)
end()Get the end value (for both single and range)
is_single()Check if value is single
is_range()Check if value is range
clamp(min, max)Clamp value to range

Conversions

rust
// From f32
let single_value: SliderValue = 42.0.into();

// From tuple
let range_value: SliderValue = (10.0, 90.0).into();

// From Range
let range_value: SliderValue = (10.0..90.0).into();

SliderEvent

EventDescription
Change(SliderValue)Emitted when slider value changes

Styling

The slider component implements Styled trait and supports:

  • Background color for track and thumb
  • Text color for thumb
  • Border radius
  • Size customization

Examples

Color Picker

rust
struct ColorPicker {
    hue_slider: Entity<SliderState>,
    saturation_slider: Entity<SliderState>,
    lightness_slider: Entity<SliderState>,
    alpha_slider: Entity<SliderState>,
    current_color: Hsla,
}

impl ColorPicker {
    fn new(cx: &mut Context<Self>) -> Self {
        let hue_slider = cx.new(|_| {
            SliderState::new()
                .min(0.0)
                .max(1.0)
                .step(0.01)
                .default_value(0.5)
        });

        let saturation_slider = cx.new(|_| {
            SliderState::new()
                .min(0.0)
                .max(1.0)
                .step(0.01)
                .default_value(1.0)
        });

        // Subscribe to all sliders to update color
        let subscriptions = [&hue_slider, &saturation_slider /* ... */]
            .iter()
            .map(|slider| {
                cx.subscribe(slider, |this, _, event: &SliderEvent, cx| {
                    match event {
                        SliderEvent::Change(_) => {
                            this.update_color(cx);
                        }
                    }
                })
            })
            .collect::<Vec<_>>();

        Self {
            hue_slider,
            saturation_slider,
            // ... other fields
        }
    }

    fn update_color(&mut self, cx: &mut Context<Self>) {
        let h = self.hue_slider.read(cx).value().start();
        let s = self.saturation_slider.read(cx).value().start();
        // ... calculate color
        self.current_color = hsla(h, s, l, a);
        cx.notify();
    }
}

impl Render for ColorPicker {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        h_flex()
            .gap_4()
            .child(
                v_flex()
                    .gap_2()
                    .child("Hue")
                    .child(Slider::new(&self.hue_slider).vertical().h(px(120.)))
            )
            .child(
                v_flex()
                    .gap_2()
                    .child("Saturation")
                    .child(Slider::new(&self.saturation_slider).vertical().h(px(120.)))
            )
            // ... other sliders
    }
}

Volume Control

rust
struct VolumeControl {
    volume_slider: Entity<SliderState>,
    volume: f32,
}

impl VolumeControl {
    fn new(cx: &mut Context<Self>) -> Self {
        let volume_slider = cx.new(|_| {
            SliderState::new()
                .min(0.0)
                .max(100.0)
                .step(1.0)
                .default_value(50.0)
        });

        let subscription = cx.subscribe(&volume_slider, |this, _, event: &SliderEvent, cx| {
            match event {
                SliderEvent::Change(value) => {
                    this.volume = value.start();
                    this.apply_volume_change();
                    cx.notify();
                }
            }
        });

        Self {
            volume_slider,
            volume: 50.0,
        }
    }

    fn apply_volume_change(&self) {
        // Apply volume change to audio system
        println!("Volume changed to: {}%", self.volume);
    }
}

impl Render for VolumeControl {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        h_flex()
            .items_center()
            .gap_3()
            .child("🔊")
            .child(Slider::new(&self.volume_slider).flex_1())
            .child(format!("{}%", self.volume as i32))
    }
}

Price Range Filter

rust
struct PriceFilter {
    price_range: Entity<SliderState>,
    min_price: f32,
    max_price: f32,
}

impl PriceFilter {
    fn new(cx: &mut Context<Self>) -> Self {
        let price_range = cx.new(|_| {
            SliderState::new()
                .min(0.0)
                .max(1000.0)
                .step(10.0)
                .default_value(100.0..500.0)  // Range slider
        });

        let subscription = cx.subscribe(&price_range, |this, _, event: &SliderEvent, cx| {
            match event {
                SliderEvent::Change(value) => {
                    this.min_price = value.start();
                    this.max_price = value.end();
                    this.filter_products();
                    cx.notify();
                }
            }
        });

        Self {
            price_range,
            min_price: 100.0,
            max_price: 500.0,
        }
    }

    fn filter_products(&self) {
        println!("Filtering products: ${} - ${}", self.min_price, self.max_price);
    }
}

impl Render for PriceFilter {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        v_flex()
            .gap_2()
            .child("Price Range")
            .child(Slider::new(&self.price_range))
            .child(format!("${} - ${}", self.min_price as i32, self.max_price as i32))
    }
}

Temperature Slider with Custom Styling

rust
struct TemperatureControl {
    temp_slider: Entity<SliderState>,
    temperature: f32,
}

impl Render for TemperatureControl {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let temp_color = if self.temperature < 10.0 {
            cx.theme().info  // Cold - blue
        } else if self.temperature > 25.0 {
            cx.theme().destructive  // Hot - red
        } else {
            cx.theme().success  // Comfortable - green
        };

        v_flex()
            .gap_3()
            .child("Temperature Control")
            .child(
                Slider::new(&self.temp_slider)
                    .bg(temp_color)
                    .text_color(cx.theme().background)
                    .rounded(px(8.))
            )
            .child(format!("{}°C", self.temperature as i32))
    }
}

Accessibility

  • Keyboard Navigation: Sliders are focusable and support keyboard interaction
  • Arrow Keys: Use left/right (horizontal) or up/down (vertical) arrows to adjust values
  • Page Up/Down: Make larger adjustments
  • Home/End: Jump to minimum/maximum values
  • Tab Navigation: Properly integrated into tab order
  • Screen Readers: Value changes are announced
  • Tooltips: Display current value on hover
  • Focus Indicators: Clear visual focus state
  • Disabled State: Disabled sliders cannot be focused or interacted with

Keyboard Shortcuts

KeyAction
/ Decrease value by step
/ Increase value by step
Page DownDecrease by larger amount
Page UpIncrease by larger amount
HomeSet to minimum value
EndSet to maximum value
TabMove focus to next element
Shift + TabMove focus to previous element