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:
parent
966b18e142
commit
dc02894db4
2 changed files with 62 additions and 21 deletions
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, AnyElement, AsyncWindowContext, FontWeight, Hsla, InteractiveElement, IntoElement,
|
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,
|
StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -21,7 +21,7 @@ use std::rc::Rc;
|
||||||
use std::{borrow::Cow, cell::RefCell};
|
use std::{borrow::Cow, cell::RefCell};
|
||||||
use std::{ops::Range, sync::Arc, time::Duration};
|
use std::{ops::Range, sync::Arc, time::Duration};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{prelude::*, window_is_transparent};
|
use ui::{prelude::*, window_is_transparent, Scrollbar, ScrollbarState};
|
||||||
use util::TryFutureExt;
|
use util::TryFutureExt;
|
||||||
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
||||||
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
|
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 blocks = vec![inlay_hover.tooltip];
|
||||||
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await;
|
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await;
|
||||||
|
|
||||||
|
let scroll_handle = ScrollHandle::new();
|
||||||
let hover_popover = InfoPopover {
|
let hover_popover = InfoPopover {
|
||||||
symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()),
|
symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()),
|
||||||
parsed_content,
|
parsed_content,
|
||||||
scroll_handle: ScrollHandle::new(),
|
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||||
|
scroll_handle,
|
||||||
keyboard_grace: Rc::new(RefCell::new(false)),
|
keyboard_grace: Rc::new(RefCell::new(false)),
|
||||||
anchor: None,
|
anchor: None,
|
||||||
};
|
};
|
||||||
|
@ -435,12 +437,14 @@ fn show_hover(
|
||||||
let language = hover_result.language;
|
let language = hover_result.language;
|
||||||
let parsed_content =
|
let parsed_content =
|
||||||
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
|
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
|
||||||
|
let scroll_handle = ScrollHandle::new();
|
||||||
info_popover_tasks.push((
|
info_popover_tasks.push((
|
||||||
range.clone(),
|
range.clone(),
|
||||||
InfoPopover {
|
InfoPopover {
|
||||||
symbol_range: RangeInEditor::Text(range),
|
symbol_range: RangeInEditor::Text(range),
|
||||||
parsed_content,
|
parsed_content,
|
||||||
scroll_handle: ScrollHandle::new(),
|
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||||
|
scroll_handle,
|
||||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||||
anchor: Some(anchor),
|
anchor: Some(anchor),
|
||||||
},
|
},
|
||||||
|
@ -680,13 +684,13 @@ impl HoverState {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct InfoPopover {
|
||||||
pub struct InfoPopover {
|
pub(crate) symbol_range: RangeInEditor,
|
||||||
pub symbol_range: RangeInEditor,
|
pub(crate) parsed_content: Option<View<Markdown>>,
|
||||||
pub parsed_content: Option<View<Markdown>>,
|
pub(crate) scroll_handle: ScrollHandle,
|
||||||
pub scroll_handle: ScrollHandle,
|
pub(crate) scrollbar_state: ScrollbarState,
|
||||||
pub keyboard_grace: Rc<RefCell<bool>>,
|
pub(crate) keyboard_grace: Rc<RefCell<bool>>,
|
||||||
pub anchor: Option<Anchor>,
|
pub(crate) anchor: Option<Anchor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InfoPopover {
|
impl InfoPopover {
|
||||||
|
@ -695,10 +699,6 @@ impl InfoPopover {
|
||||||
let mut d = div()
|
let mut d = div()
|
||||||
.id("info_popover")
|
.id("info_popover")
|
||||||
.elevation_2(cx)
|
.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,
|
// Prevent a mouse down/move on the popover from being propagated to the editor,
|
||||||
// because that would dismiss the popover.
|
// because that would dismiss the popover.
|
||||||
.on_mouse_move(|_, cx| cx.stop_propagation())
|
.on_mouse_move(|_, cx| cx.stop_propagation())
|
||||||
|
@ -706,11 +706,21 @@ impl InfoPopover {
|
||||||
let mut keyboard_grace = keyboard_grace.borrow_mut();
|
let mut keyboard_grace = keyboard_grace.borrow_mut();
|
||||||
*keyboard_grace = false;
|
*keyboard_grace = false;
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
})
|
});
|
||||||
.p_2();
|
|
||||||
|
|
||||||
if let Some(markdown) = &self.parsed_content {
|
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()
|
d.into_any_element()
|
||||||
}
|
}
|
||||||
|
@ -724,6 +734,38 @@ impl InfoPopover {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
self.scroll_handle.set_offset(current);
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub struct Scrollbar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around scroll handles.
|
/// Wrapper around scroll handles.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ScrollableHandle {
|
pub enum ScrollableHandle {
|
||||||
Uniform(UniformListScrollHandle),
|
Uniform(UniformListScrollHandle),
|
||||||
NonUniform(ScrollHandle),
|
NonUniform(ScrollHandle),
|
||||||
|
@ -91,7 +91,7 @@ impl From<ScrollHandle> for ScrollableHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A scrollbar state that should be persisted across frames.
|
/// A scrollbar state that should be persisted across frames.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ScrollbarState {
|
pub struct ScrollbarState {
|
||||||
// If Some(), there's an active drag, offset by percentage from the origin of a thumb.
|
// If Some(), there's an active drag, offset by percentage from the origin of a thumb.
|
||||||
drag: Rc<Cell<Option<f32>>>,
|
drag: Rc<Cell<Option<f32>>>,
|
||||||
|
@ -142,7 +142,6 @@ impl ScrollbarState {
|
||||||
}
|
}
|
||||||
let mut percentage = current_offset / main_dimension_size;
|
let mut percentage = current_offset / main_dimension_size;
|
||||||
let viewport_size = self.scroll_handle.viewport().size;
|
let viewport_size = self.scroll_handle.viewport().size;
|
||||||
|
|
||||||
let end_offset = (current_offset + viewport_size.along(axis).0) / main_dimension_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;
|
// 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.
|
// in such case we'll adjust the starting offset as well to keep the scrollbar thumb length stable.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue