diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index a043e4eae3..501ac0fca9 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -686,26 +686,9 @@ fn diagnostic_header_renderer(entry: DiagnosticEntry, path: ProjectPath) }) .with_tooltip( entry.diagnostic.group_id, - Flex::row() - .with_child( - Label::new( - "Jump to diagnostic (".to_string(), - tooltip_style.text.clone(), - ) - .boxed(), - ) - .with_child( - KeystrokeLabel::new( - Box::new(editor::OpenExcerpts), - Default::default(), - tooltip_style.text.clone(), - ) - .boxed(), - ) - .with_child(Label::new(")".to_string(), tooltip_style.text).boxed()) - .contained() - .with_style(tooltip_style.container) - .boxed(), + "Jump to diagnostic".to_string(), + Some(Box::new(editor::OpenExcerpts)), + tooltip_style, cx, ) .aligned() diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 6b0e2a1af6..d2d254d93e 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -31,7 +31,7 @@ use crate::{ rect::RectF, vector::{vec2f, Vector2F}, }, - json, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext, + json, Action, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext, SizeConstraint, View, }; use core::panic; @@ -160,13 +160,15 @@ pub trait Element { fn with_tooltip( self, id: usize, - tooltip: ElementBox, + text: String, + action: Option>, + style: TooltipStyle, cx: &mut RenderContext, ) -> Tooltip where Self: 'static + Sized, { - Tooltip::new(id, self.boxed(), tooltip, cx) + Tooltip::new(id, text, action, style, self.boxed(), cx) } } diff --git a/crates/gpui/src/elements/tooltip.rs b/crates/gpui/src/elements/tooltip.rs index 9fc5894c63..9ed0f5cba6 100644 --- a/crates/gpui/src/elements/tooltip.rs +++ b/crates/gpui/src/elements/tooltip.rs @@ -1,16 +1,21 @@ +use super::{ + ContainerStyle, Element, ElementBox, Flex, KeystrokeLabel, MouseEventHandler, ParentElement, + Text, +}; +use crate::{ + fonts::TextStyle, + geometry::{rect::RectF, vector::Vector2F}, + json::json, + Action, Axis, ElementStateHandle, LayoutContext, PaintContext, RenderContext, SizeConstraint, + Task, View, +}; +use serde::Deserialize; use std::{ cell::{Cell, RefCell}, rc::Rc, time::Duration, }; -use super::{Element, ElementBox, MouseEventHandler}; -use crate::{ - geometry::{rect::RectF, vector::Vector2F}, - json::json, - ElementStateHandle, LayoutContext, PaintContext, RenderContext, SizeConstraint, Task, View, -}; - const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500); pub struct Tooltip { @@ -26,17 +31,53 @@ struct TooltipState { debounce: RefCell>>, } +#[derive(Clone, Deserialize, Default)] +pub struct TooltipStyle { + #[serde(flatten)] + container: ContainerStyle, + text: TextStyle, + keystroke: KeystrokeStyle, + max_text_width: f32, +} + +#[derive(Clone, Deserialize, Default)] +pub struct KeystrokeStyle { + #[serde(flatten)] + container: ContainerStyle, + #[serde(flatten)] + text: TextStyle, +} + impl Tooltip { pub fn new( id: usize, + text: String, + action: Option>, + style: TooltipStyle, child: ElementBox, - tooltip: ElementBox, cx: &mut RenderContext, ) -> Self { let state_handle = cx.element_state::>(id); let state = state_handle.read(cx).clone(); let tooltip = if state.visible.get() { - Some(tooltip) + let mut collapsed_tooltip = Self::render_tooltip( + text.clone(), + style.clone(), + action.as_ref().map(|a| a.boxed_clone()), + true, + ) + .boxed(); + Some( + Self::render_tooltip(text, style, action, false) + .constrained() + .dynamically(move |constraint, cx| { + SizeConstraint::strict_along( + Axis::Vertical, + collapsed_tooltip.layout(constraint, cx).y(), + ) + }) + .boxed(), + ) } else { None }; @@ -73,6 +114,36 @@ impl Tooltip { state: state_handle, } } + + fn render_tooltip( + text: String, + style: TooltipStyle, + action: Option>, + measure: bool, + ) -> impl Element { + Flex::row() + .with_child({ + let text = Text::new(text, style.text) + .constrained() + .with_max_width(style.max_text_width); + if measure { + text.flex(1., false).boxed() + } else { + text.flex(1., false).aligned().boxed() + } + }) + .with_children(action.map(|action| { + let keystroke_label = + KeystrokeLabel::new(action, style.keystroke.container, style.keystroke.text); + if measure { + keystroke_label.boxed() + } else { + keystroke_label.aligned().boxed() + } + })) + .contained() + .with_style(style.container) + } } impl Element for Tooltip { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 9d3bb59bce..3639127633 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -2,7 +2,7 @@ mod theme_registry; use gpui::{ color::Color, - elements::{ContainerStyle, ImageStyle, LabelStyle}, + elements::{ContainerStyle, ImageStyle, LabelStyle, TooltipStyle}, fonts::{HighlightStyle, TextStyle}, Border, MouseState, }; @@ -31,7 +31,7 @@ pub struct Theme { pub project_diagnostics: ProjectDiagnostics, pub breadcrumbs: ContainedText, pub contact_notification: ContactNotification, - pub tooltip: ContainedText, + pub tooltip: TooltipStyle, } #[derive(Deserialize, Default)] diff --git a/styles/src/styleTree/tooltip.ts b/styles/src/styleTree/tooltip.ts index 228d510ddd..bfceae168e 100644 --- a/styles/src/styleTree/tooltip.ts +++ b/styles/src/styleTree/tooltip.ts @@ -9,6 +9,14 @@ export default function tooltip(theme: Theme) { margin: { top: 6, left: 6 }, shadow: shadow(theme), cornerRadius: 6, - ...text(theme, "sans", "secondary", { size: "xs", weight: "bold" }) + text: text(theme, "sans", "secondary", { size: "xs", weight: "bold" }), + keystroke: { + background: backgroundColor(theme, "on500"), + cornerRadius: 4, + margin: { left: 6 }, + padding: { left: 3, right: 3 }, + ...text(theme, "mono", "muted", { size: "xs", weight: "bold" }) + }, + maxTextWidth: 200, } } \ No newline at end of file