diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index f5b97dd3a6c10e0d8b6f5c75d6da6611733b6bb0..fcdc3540e9cdf1837572caa33b27c2d2b31d03df 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -28,11 +28,12 @@ use project::{Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktr use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings, ShowScrollbar}; use serde::{Deserialize, Serialize}; use std::{ - cell::OnceCell, + cell::{Cell, OnceCell}, collections::HashSet, ffi::OsStr, ops::Range, path::{Path, PathBuf}, + rc::Rc, sync::Arc, time::Duration, }; @@ -71,6 +72,7 @@ pub struct ProjectPanel { width: Option, pending_serialization: Task>, show_scrollbar: bool, + is_dragging_scrollbar: Rc>, hide_scrollbar_task: Option>, } @@ -287,6 +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(), }; this.update_visible_entries(None, cx); @@ -2228,7 +2231,7 @@ impl ProjectPanel { let height = scroll_handle .last_item_height - .filter(|_| self.show_scrollbar)?; + .filter(|_| self.show_scrollbar || self.is_dragging_scrollbar.get())?; 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; @@ -2264,6 +2267,19 @@ impl ProjectPanel { .on_any_mouse_down(|_, cx| { cx.stop_propagation(); }) + .on_mouse_up( + MouseButton::Left, + cx.listener(|this, _, cx| { + if !this.is_dragging_scrollbar.get() + && !this.focus_handle.contains_focused(cx) + { + this.hide_scrollbar(cx); + cx.notify(); + } + + cx.stop_propagation(); + }), + ) .on_scroll_wheel(cx.listener(|_, _, cx| { cx.notify(); })) @@ -2277,6 +2293,8 @@ impl ProjectPanel { .child(ProjectPanelScrollbar::new( percentage as f32..end_offset as f32, self.scroll_handle.clone(), + self.is_dragging_scrollbar.clone(), + cx.view().clone().into(), items_count, )), ) @@ -2312,8 +2330,8 @@ impl ProjectPanel { .timer(SCROLLBAR_SHOW_INTERVAL) .await; panel - .update(&mut cx, |editor, cx| { - editor.show_scrollbar = false; + .update(&mut cx, |panel, cx| { + panel.show_scrollbar = false; cx.notify(); }) .log_err(); diff --git a/crates/project_panel/src/scrollbar.rs b/crates/project_panel/src/scrollbar.rs index 8670e54325b2d7416954e2c943b0c60e180e1cdf..0de7d3c4bc6328314911eac7093f6934b90a5601 100644 --- a/crates/project_panel/src/scrollbar.rs +++ b/crates/project_panel/src/scrollbar.rs @@ -1,27 +1,33 @@ -use std::ops::Range; +use std::{cell::Cell, ops::Range, rc::Rc}; use gpui::{ - point, Bounds, ContentMask, Hitbox, MouseDownEvent, MouseMoveEvent, ScrollWheelEvent, Style, - UniformListScrollHandle, + point, AnyView, Bounds, ContentMask, Hitbox, MouseDownEvent, MouseMoveEvent, MouseUpEvent, + ScrollWheelEvent, Style, UniformListScrollHandle, }; use ui::{prelude::*, px, relative, IntoElement}; pub(crate) struct ProjectPanelScrollbar { thumb: Range, scroll: UniformListScrollHandle, + is_dragging_scrollbar: Rc>, item_count: usize, + view: AnyView, } impl ProjectPanelScrollbar { pub(crate) fn new( thumb: Range, scroll: UniformListScrollHandle, + is_dragging_scrollbar: Rc>, + view: AnyView, item_count: usize, ) -> Self { Self { thumb, scroll, + is_dragging_scrollbar, item_count, + view, } } } @@ -68,7 +74,6 @@ impl gpui::Element for ProjectPanelScrollbar { _prepaint: &mut Self::PrepaintState, cx: &mut ui::WindowContext, ) { - let hitbox_id = _prepaint.id; cx.with_content_mask(Some(ContentMask { bounds }), |cx| { let colors = cx.theme().colors(); let scrollbar_background = colors.scrollbar_track_border; @@ -77,32 +82,38 @@ impl gpui::Element for ProjectPanelScrollbar { let thumb_offset = self.thumb.start * bounds.size.height; let thumb_end = self.thumb.end * bounds.size.height; - let thumb_upper_left = point(bounds.origin.x, bounds.origin.y + thumb_offset); - let thumb_lower_right = point( - bounds.origin.x + bounds.size.width, - bounds.origin.y + thumb_end, - ); + let thumb_percentage_size = self.thumb.end - self.thumb.start; - cx.paint_quad(gpui::fill( - Bounds::from_corners(thumb_upper_left, thumb_lower_right), - thumb_background, - )); + let thumb_bounds = { + let thumb_upper_left = point(bounds.origin.x, bounds.origin.y + thumb_offset); + let thumb_lower_right = point( + bounds.origin.x + bounds.size.width, + bounds.origin.y + thumb_end, + ); + Bounds::from_corners(thumb_upper_left, thumb_lower_right) + }; + cx.paint_quad(gpui::fill(thumb_bounds, thumb_background)); let scroll = self.scroll.clone(); let item_count = self.item_count; cx.on_mouse_event({ let scroll = self.scroll.clone(); + let is_dragging = self.is_dragging_scrollbar.clone(); move |event: &MouseDownEvent, phase, _cx| { if phase.bubble() && bounds.contains(&event.position) { - 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; + if !thumb_bounds.contains(&event.position) { + 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 = percentage.min(1. - thumb_percentage_size); - scroll - .base_handle - .set_offset(point(px(0.), -max_offset * percentage)); + let percentage = percentage.min(1. - thumb_percentage_size); + scroll + .base_handle + .set_offset(point(px(0.), -max_offset * percentage)); + } + } else { + is_dragging.set(true); } } } @@ -119,24 +130,30 @@ impl gpui::Element for ProjectPanelScrollbar { } } }); + let is_dragging = self.is_dragging_scrollbar.clone(); + let view_id = self.view.entity_id(); + cx.on_mouse_event(move |event: &MouseMoveEvent, _, cx| { + if event.dragging() && is_dragging.get() { + 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; - cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| { - if phase.bubble() && bounds.contains(&event.position) && hitbox_id.is_hovered(cx) { - if 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 = percentage.min(1. - thumb_percentage_size); - scroll - .base_handle - .set_offset(point(px(0.), -max_offset * percentage)); - } - } else { - cx.stop_propagation(); + let percentage = percentage.min(1. - thumb_percentage_size); + scroll + .base_handle + .set_offset(point(px(0.), -max_offset * percentage)); + cx.notify(view_id); } + } else { + is_dragging.set(false); + } + }); + let is_dragging = self.is_dragging_scrollbar.clone(); + cx.on_mouse_event(move |_event: &MouseUpEvent, phase, cx| { + if phase.bubble() { + is_dragging.set(false); + cx.notify(view_id); } }); })