editor: Add scrollbar to info popovers (#20184)

Related to https://github.com/zed-industries/zed/issues/5034

Release Notes:

- Added scrollbar to info popovers in editor.
This commit is contained in:
Piotr Osiewicz 2024-11-05 00:08:38 +01:00 committed by GitHub
parent 966b18e142
commit dc02894db4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 21 deletions

View file

@ -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<View<Markdown>>,
pub scroll_handle: ScrollHandle,
pub keyboard_grace: Rc<RefCell<bool>>,
pub anchor: Option<Anchor>,
pub(crate) struct InfoPopover {
pub(crate) symbol_range: RangeInEditor,
pub(crate) parsed_content: Option<View<Markdown>>,
pub(crate) scroll_handle: ScrollHandle,
pub(crate) scrollbar_state: ScrollbarState,
pub(crate) keyboard_grace: Rc<RefCell<bool>>,
pub(crate) anchor: Option<Anchor>,
}
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<Editor>) -> Stateful<Div> {
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)]

View file

@ -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<ScrollHandle> 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<Cell<Option<f32>>>,
@ -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.