diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index fcdc3540e9..0751b926cf 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -72,7 +72,7 @@ pub struct ProjectPanel { width: Option, pending_serialization: Task>, show_scrollbar: bool, - is_dragging_scrollbar: Rc>, + scrollbar_drag_thumb_offset: Rc>>, hide_scrollbar_task: Option>, } @@ -289,7 +289,7 @@ impl ProjectPanel { pending_serialization: Task::ready(None), show_scrollbar: !Self::should_autohide_scrollbar(cx), hide_scrollbar_task: None, - is_dragging_scrollbar: Default::default(), + scrollbar_drag_thumb_offset: Default::default(), }; this.update_visible_entries(None, cx); @@ -2231,7 +2231,7 @@ impl ProjectPanel { let height = scroll_handle .last_item_height - .filter(|_| self.show_scrollbar || self.is_dragging_scrollbar.get())?; + .filter(|_| self.show_scrollbar || self.scrollbar_drag_thumb_offset.get().is_some())?; let total_list_length = height.0 as f64 * items_count as f64; let current_offset = scroll_handle.base_handle.offset().y.0.min(0.).abs() as f64; @@ -2270,7 +2270,7 @@ impl ProjectPanel { .on_mouse_up( MouseButton::Left, cx.listener(|this, _, cx| { - if !this.is_dragging_scrollbar.get() + if this.scrollbar_drag_thumb_offset.get().is_none() && !this.focus_handle.contains_focused(cx) { this.hide_scrollbar(cx); @@ -2293,7 +2293,7 @@ impl ProjectPanel { .child(ProjectPanelScrollbar::new( percentage as f32..end_offset as f32, self.scroll_handle.clone(), - self.is_dragging_scrollbar.clone(), + self.scrollbar_drag_thumb_offset.clone(), cx.view().clone().into(), items_count, )), diff --git a/crates/project_panel/src/scrollbar.rs b/crates/project_panel/src/scrollbar.rs index 0de7d3c4bc..8696b1eed2 100644 --- a/crates/project_panel/src/scrollbar.rs +++ b/crates/project_panel/src/scrollbar.rs @@ -9,7 +9,8 @@ use ui::{prelude::*, px, relative, IntoElement}; pub(crate) struct ProjectPanelScrollbar { thumb: Range, scroll: UniformListScrollHandle, - is_dragging_scrollbar: Rc>, + // If Some(), there's an active drag, offset by percentage from the top of thumb. + scrollbar_drag_state: Rc>>, item_count: usize, view: AnyView, } @@ -18,14 +19,14 @@ impl ProjectPanelScrollbar { pub(crate) fn new( thumb: Range, scroll: UniformListScrollHandle, - is_dragging_scrollbar: Rc>, + scrollbar_drag_state: Rc>>, view: AnyView, item_count: usize, ) -> Self { Self { thumb, scroll, - is_dragging_scrollbar, + scrollbar_drag_state, item_count, view, } @@ -97,7 +98,7 @@ impl gpui::Element for ProjectPanelScrollbar { let item_count = self.item_count; cx.on_mouse_event({ let scroll = self.scroll.clone(); - let is_dragging = self.is_dragging_scrollbar.clone(); + let is_dragging = self.scrollbar_drag_state.clone(); move |event: &MouseDownEvent, phase, _cx| { if phase.bubble() && bounds.contains(&event.position) { if !thumb_bounds.contains(&event.position) { @@ -113,7 +114,9 @@ impl gpui::Element for ProjectPanelScrollbar { .set_offset(point(px(0.), -max_offset * percentage)); } } else { - is_dragging.set(true); + let thumb_top_offset = + (event.position.y - thumb_bounds.origin.y) / bounds.size.height; + is_dragging.set(Some(thumb_top_offset)); } } } @@ -130,14 +133,15 @@ impl gpui::Element for ProjectPanelScrollbar { } } }); - let is_dragging = self.is_dragging_scrollbar.clone(); + let drag_state = self.scrollbar_drag_state.clone(); let view_id = self.view.entity_id(); cx.on_mouse_event(move |event: &MouseMoveEvent, _, cx| { - if event.dragging() && is_dragging.get() { + if let Some(drag_state) = drag_state.get().filter(|_| event.dragging()) { let scroll = scroll.0.borrow(); if let Some(last_height) = scroll.last_item_height { let max_offset = item_count as f32 * last_height; - let percentage = (event.position.y - bounds.origin.y) / bounds.size.height; + let percentage = + (event.position.y - bounds.origin.y) / bounds.size.height - drag_state; let percentage = percentage.min(1. - thumb_percentage_size); scroll @@ -146,13 +150,13 @@ impl gpui::Element for ProjectPanelScrollbar { cx.notify(view_id); } } else { - is_dragging.set(false); + drag_state.set(None); } }); - let is_dragging = self.is_dragging_scrollbar.clone(); + let is_dragging = self.scrollbar_drag_state.clone(); cx.on_mouse_event(move |_event: &MouseUpEvent, phase, cx| { if phase.bubble() { - is_dragging.set(false); + is_dragging.set(None); cx.notify(view_id); } });