diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index f079939787..972350ce5d 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -7,7 +7,7 @@ use crate::{ }; use gpui::{ div, px, AnyElement, AsyncWindowContext, FontWeight, Hsla, InteractiveElement, IntoElement, - MouseButton, ParentElement, Pixels, ScrollHandle, Size, StatefulInteractiveElement, + MouseButton, ParentElement, Pixels, ScrollHandle, Size, Stateful, StatefulInteractiveElement, StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext, }; use itertools::Itertools; @@ -21,7 +21,7 @@ use std::rc::Rc; use std::{borrow::Cow, cell::RefCell}; use std::{ops::Range, sync::Arc, time::Duration}; use theme::ThemeSettings; -use ui::{prelude::*, window_is_transparent}; +use ui::{prelude::*, window_is_transparent, Scrollbar, ScrollbarState}; use util::TryFutureExt; pub const HOVER_DELAY_MILLIS: u64 = 350; pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200; @@ -144,10 +144,12 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie let blocks = vec![inlay_hover.tooltip]; let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await; + let scroll_handle = ScrollHandle::new(); let hover_popover = InfoPopover { symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()), parsed_content, - scroll_handle: ScrollHandle::new(), + scrollbar_state: ScrollbarState::new(scroll_handle.clone()), + scroll_handle, keyboard_grace: Rc::new(RefCell::new(false)), anchor: None, }; @@ -435,12 +437,14 @@ fn show_hover( let language = hover_result.language; let parsed_content = parse_blocks(&blocks, &language_registry, language, &mut cx).await; + let scroll_handle = ScrollHandle::new(); info_popover_tasks.push(( range.clone(), InfoPopover { symbol_range: RangeInEditor::Text(range), parsed_content, - scroll_handle: ScrollHandle::new(), + scrollbar_state: ScrollbarState::new(scroll_handle.clone()), + scroll_handle, keyboard_grace: Rc::new(RefCell::new(ignore_timeout)), anchor: Some(anchor), }, @@ -680,13 +684,13 @@ impl HoverState { } #[derive(Debug, Clone)] - -pub struct InfoPopover { - pub symbol_range: RangeInEditor, - pub parsed_content: Option>, - pub scroll_handle: ScrollHandle, - pub keyboard_grace: Rc>, - pub anchor: Option, +pub(crate) struct InfoPopover { + pub(crate) symbol_range: RangeInEditor, + pub(crate) parsed_content: Option>, + pub(crate) scroll_handle: ScrollHandle, + pub(crate) scrollbar_state: ScrollbarState, + pub(crate) keyboard_grace: Rc>, + pub(crate) anchor: Option, } impl InfoPopover { @@ -695,10 +699,6 @@ impl InfoPopover { let mut d = div() .id("info_popover") .elevation_2(cx) - .overflow_y_scroll() - .track_scroll(&self.scroll_handle) - .max_w(max_size.width) - .max_h(max_size.height) // Prevent a mouse down/move on the popover from being propagated to the editor, // because that would dismiss the popover. .on_mouse_move(|_, cx| cx.stop_propagation()) @@ -706,11 +706,21 @@ impl InfoPopover { let mut keyboard_grace = keyboard_grace.borrow_mut(); *keyboard_grace = false; cx.stop_propagation(); - }) - .p_2(); + }); if let Some(markdown) = &self.parsed_content { - d = d.child(markdown.clone()); + d = d + .child( + div() + .id("info-md-container") + .overflow_y_scroll() + .max_w(max_size.width) + .max_h(max_size.height) + .p_2() + .track_scroll(&self.scroll_handle) + .child(markdown.clone()), + ) + .child(self.render_vertical_scrollbar(cx)); } d.into_any_element() } @@ -724,6 +734,38 @@ impl InfoPopover { cx.notify(); self.scroll_handle.set_offset(current); } + fn render_vertical_scrollbar(&self, cx: &mut ViewContext) -> Stateful
{ + div() + .occlude() + .id("info-popover-vertical-scroll") + .on_mouse_move(cx.listener(|_, _, cx| { + cx.notify(); + cx.stop_propagation() + })) + .on_hover(|_, cx| { + cx.stop_propagation(); + }) + .on_any_mouse_down(|_, cx| { + cx.stop_propagation(); + }) + .on_mouse_up( + MouseButton::Left, + cx.listener(|_, _, cx| { + cx.stop_propagation(); + }), + ) + .on_scroll_wheel(cx.listener(|_, _, cx| { + cx.notify(); + })) + .h_full() + .absolute() + .right_1() + .top_1() + .bottom_0() + .w(px(12.)) + .cursor_default() + .children(Scrollbar::vertical(self.scrollbar_state.clone())) + } } #[derive(Debug, Clone)] diff --git a/crates/ui/src/components/scrollbar.rs b/crates/ui/src/components/scrollbar.rs index 5f25547b43..b11e9118c1 100644 --- a/crates/ui/src/components/scrollbar.rs +++ b/crates/ui/src/components/scrollbar.rs @@ -16,7 +16,7 @@ pub struct Scrollbar { } /// Wrapper around scroll handles. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum ScrollableHandle { Uniform(UniformListScrollHandle), NonUniform(ScrollHandle), @@ -91,7 +91,7 @@ impl From for ScrollableHandle { } /// A scrollbar state that should be persisted across frames. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ScrollbarState { // If Some(), there's an active drag, offset by percentage from the origin of a thumb. drag: Rc>>, @@ -142,7 +142,6 @@ impl ScrollbarState { } let mut percentage = current_offset / main_dimension_size; let viewport_size = self.scroll_handle.viewport().size; - let end_offset = (current_offset + viewport_size.along(axis).0) / main_dimension_size; // Scroll handle might briefly report an offset greater than the length of a list; // in such case we'll adjust the starting offset as well to keep the scrollbar thumb length stable.