Components / Selection & input
Slider
crates/ui/src/widgets/controls.rs
use ui::widgets::{self, SliderDrag};
focus::focusable(&theme, &self.slider, widgets::slider(&theme, self.level))
.id("slider")
.on_drag(SliderDrag, |_, _, _, cx| cx.new(|_| gpui::Empty))
.on_drag_move(cx.listener(|view, event: &DragMoveEvent<SliderDrag>, _, cx| {
view.level = widgets::axis_fraction(
event.event.position, event.bounds, Axis::Horizontal, 0.0,
);
cx.notify();
}))The element is the drag source, so the gesture is grab-anywhere-and-slide rather than aim-at-the-knob. axis_fraction turns a pointer position into the value: where the pointer falls along an axis as a fraction of the bounds, clamped to min..=1-min. A slider passes 0.0 because it has no dead zone; a split passes one so neither pane can be squeezed away. On a zero-extent container — the frame before layout has run — it answers min instead of dividing by zero.
SliderDrag is a type of its own so two sliders in one window never answer each other's on_drag_move.
Keyboard is ←/→, which arrive as focus::Decrement and focus::Increment:
.on_action(cx.listener(|view, _: &focus::Decrement, _, cx| view.nudge(-STEP, cx)))
.on_action(cx.listener(|view, _: &focus::Increment, _, cx| view.nudge(STEP, cx)))The actions carry no step. Only the caller knows the range, and a library that picked one would be picking it for a percentage and a font size alike.