Simplify usage of tooltip

Now you simply specify a text, an action and a style and GPUI will
take of rendering it properly. This is simpler compared to always
providing a custom element and should make tooltip more consistent
across the UI.
This commit is contained in:
Antonio Scandurra 2022-06-02 09:12:50 +02:00
parent 9ca9f63046
commit cc028cca78
5 changed files with 99 additions and 35 deletions

View file

@ -686,26 +686,9 @@ fn diagnostic_header_renderer(entry: DiagnosticEntry<Point>, 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()

View file

@ -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<T: View>(
self,
id: usize,
tooltip: ElementBox,
text: String,
action: Option<Box<dyn Action>>,
style: TooltipStyle,
cx: &mut RenderContext<T>,
) -> Tooltip
where
Self: 'static + Sized,
{
Tooltip::new(id, self.boxed(), tooltip, cx)
Tooltip::new(id, text, action, style, self.boxed(), cx)
}
}

View file

@ -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<Option<Task<()>>>,
}
#[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<T: View>(
id: usize,
text: String,
action: Option<Box<dyn Action>>,
style: TooltipStyle,
child: ElementBox,
tooltip: ElementBox,
cx: &mut RenderContext<T>,
) -> Self {
let state_handle = cx.element_state::<TooltipState, Rc<TooltipState>>(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<Box<dyn Action>>,
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 {

View file

@ -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)]

View file

@ -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,
}
}