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:
parent
9ca9f63046
commit
cc028cca78
5 changed files with 99 additions and 35 deletions
|
@ -686,26 +686,9 @@ fn diagnostic_header_renderer(entry: DiagnosticEntry<Point>, path: ProjectPath)
|
||||||
})
|
})
|
||||||
.with_tooltip(
|
.with_tooltip(
|
||||||
entry.diagnostic.group_id,
|
entry.diagnostic.group_id,
|
||||||
Flex::row()
|
"Jump to diagnostic".to_string(),
|
||||||
.with_child(
|
Some(Box::new(editor::OpenExcerpts)),
|
||||||
Label::new(
|
tooltip_style,
|
||||||
"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(),
|
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.aligned()
|
.aligned()
|
||||||
|
|
|
@ -31,7 +31,7 @@ use crate::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext,
|
json, Action, DebugContext, Event, EventContext, LayoutContext, PaintContext, RenderContext,
|
||||||
SizeConstraint, View,
|
SizeConstraint, View,
|
||||||
};
|
};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
|
@ -160,13 +160,15 @@ pub trait Element {
|
||||||
fn with_tooltip<T: View>(
|
fn with_tooltip<T: View>(
|
||||||
self,
|
self,
|
||||||
id: usize,
|
id: usize,
|
||||||
tooltip: ElementBox,
|
text: String,
|
||||||
|
action: Option<Box<dyn Action>>,
|
||||||
|
style: TooltipStyle,
|
||||||
cx: &mut RenderContext<T>,
|
cx: &mut RenderContext<T>,
|
||||||
) -> Tooltip
|
) -> Tooltip
|
||||||
where
|
where
|
||||||
Self: 'static + Sized,
|
Self: 'static + Sized,
|
||||||
{
|
{
|
||||||
Tooltip::new(id, self.boxed(), tooltip, cx)
|
Tooltip::new(id, text, action, style, self.boxed(), cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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::{
|
use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::Duration,
|
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);
|
const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
pub struct Tooltip {
|
pub struct Tooltip {
|
||||||
|
@ -26,17 +31,53 @@ struct TooltipState {
|
||||||
debounce: RefCell<Option<Task<()>>>,
|
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 {
|
impl Tooltip {
|
||||||
pub fn new<T: View>(
|
pub fn new<T: View>(
|
||||||
id: usize,
|
id: usize,
|
||||||
|
text: String,
|
||||||
|
action: Option<Box<dyn Action>>,
|
||||||
|
style: TooltipStyle,
|
||||||
child: ElementBox,
|
child: ElementBox,
|
||||||
tooltip: ElementBox,
|
|
||||||
cx: &mut RenderContext<T>,
|
cx: &mut RenderContext<T>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let state_handle = cx.element_state::<TooltipState, Rc<TooltipState>>(id);
|
let state_handle = cx.element_state::<TooltipState, Rc<TooltipState>>(id);
|
||||||
let state = state_handle.read(cx).clone();
|
let state = state_handle.read(cx).clone();
|
||||||
let tooltip = if state.visible.get() {
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -73,6 +114,36 @@ impl Tooltip {
|
||||||
state: state_handle,
|
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 {
|
impl Element for Tooltip {
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod theme_registry;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{ContainerStyle, ImageStyle, LabelStyle},
|
elements::{ContainerStyle, ImageStyle, LabelStyle, TooltipStyle},
|
||||||
fonts::{HighlightStyle, TextStyle},
|
fonts::{HighlightStyle, TextStyle},
|
||||||
Border, MouseState,
|
Border, MouseState,
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ pub struct Theme {
|
||||||
pub project_diagnostics: ProjectDiagnostics,
|
pub project_diagnostics: ProjectDiagnostics,
|
||||||
pub breadcrumbs: ContainedText,
|
pub breadcrumbs: ContainedText,
|
||||||
pub contact_notification: ContactNotification,
|
pub contact_notification: ContactNotification,
|
||||||
pub tooltip: ContainedText,
|
pub tooltip: TooltipStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default)]
|
||||||
|
|
|
@ -9,6 +9,14 @@ export default function tooltip(theme: Theme) {
|
||||||
margin: { top: 6, left: 6 },
|
margin: { top: 6, left: 6 },
|
||||||
shadow: shadow(theme),
|
shadow: shadow(theme),
|
||||||
cornerRadius: 6,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue