From b899100d09afa2d3a072b8c7b7364e5e7620f4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Gr=C3=BCndel?= Date: Sun, 9 Nov 2025 04:31:21 +0100 Subject: [PATCH] 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. --- crates/ui/src/slider.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/ui/src/slider.rs b/crates/ui/src/slider.rs index c1f58760..b84ca5bf 100644 --- a/crates/ui/src/slider.rs +++ b/crates/ui/src/slider.rs @@ -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) -> 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, 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() })