slider: Improve Slider interaction to click on bar to drag. (#1544)

This pull request adds a drag event to the slider bar itself, analogous
to the drag handles, in case the slider is not a range slider. This
allows for a more intuitive interaction with the slider, where one can
click and drag on any part of the slider bar container.

https://github.com/user-attachments/assets/4d41ba10-2992-4a42-beb6-0bc376185c5e

This doesn't change the behavior for range sliders, as it's ambiguous
which slider should be dragged in case the event starts between the
sliders. Though if desired, it wouldn't be difficult to implement the
same behavior for clicks below the start or above the end slider.
This commit is contained in:
Nico Gründel 2025-11-09 04:31:21 +01:00 committed by GitHub
parent a2c55dda46
commit b899100d09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,6 +17,15 @@ impl Render for DragThumb {
}
}
#[derive(Clone)]
struct DragSlider(EntityId);
impl Render for DragSlider {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
Empty
}
}
/// Events emitted by the [`SliderState`].
pub enum SliderEvent {
Change(SliderValue),
@ -387,6 +396,7 @@ impl Styled for Slider {
impl RenderOnce for Slider {
fn render(self, window: &mut Window, cx: &mut gpui::App) -> impl IntoElement {
let axis = self.axis;
let entity_id = self.state.entity_id();
let state = self.state.read(cx);
let is_range = state.value().is_range();
let bar_size = state.bounds.size.along(axis);
@ -440,6 +450,7 @@ impl RenderOnce for Slider {
.text_color(cx.theme().foreground)
.child(
h_flex()
.id("slider-bar-container")
.when(!self.disabled, |this| {
this.on_mouse_down(
MouseButton::Left,
@ -464,6 +475,31 @@ impl RenderOnce for Slider {
),
)
})
.when(!self.disabled && !is_range, |this| {
this.on_drag(DragSlider(entity_id), |drag, _, _, cx| {
cx.stop_propagation();
cx.new(|_| drag.clone())
})
.on_drag_move(window.listener_for(
&self.state,
move |view, e: &DragMoveEvent<DragSlider>, window, cx| match e.drag(cx)
{
DragSlider(id) => {
if *id != entity_id {
return;
}
view.update_value_by_position(
axis,
e.event.position,
false,
window,
cx,
)
}
},
))
})
.when(axis.is_horizontal(), |this| {
this.items_center().h_6().w_full()
})