gpui_kit/overlay/
tooltip.rs1use gpui::{
12 AnyView, App, AppContext as _, Context, IntoElement, ParentElement, Render, RenderOnce,
13 SharedString, Styled, Window, div, px,
14};
15use gpui_kit_semantics::{NodeSpec, Role, Semantic};
16use gpui_kit_theme::{ActiveTheme, Elevation, Radius, Space};
17
18use crate::foundation::{Ident, StyledExt};
19use crate::motion::{Animated, Entrance};
20
21#[derive(Debug, Clone, IntoElement)]
23pub struct Tooltip {
24 ident: Ident,
25 text: SharedString,
26 describes: Option<SharedString>,
27}
28
29impl Tooltip {
30 pub fn new(ident: impl Into<Ident>, text: impl Into<SharedString>) -> Self {
31 Self {
32 ident: ident.into(),
33 text: text.into(),
34 describes: None,
35 }
36 }
37
38 pub fn describes(mut self, control: impl Into<SharedString>) -> Self {
44 self.describes = Some(control.into());
45 self
46 }
47
48 pub fn view(self, cx: &mut App) -> AnyView {
50 cx.new(|_| TooltipView(self)).into()
51 }
52}
53
54impl RenderOnce for Tooltip {
55 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
56 let theme = cx.theme().clone();
57 let mut spec =
58 NodeSpec::new(self.ident.semantic_id(), Role::Tooltip).text(self.text.clone());
59 if let Some(control) = self.describes.clone() {
60 spec = spec.describes(control);
61 }
62
63 let surface = div()
68 .max_w(px(260.0))
69 .px_token(&theme, Space::Sm)
70 .py_token(&theme, Space::Xs)
71 .radius(&theme, Radius::Small)
72 .bg(theme.colors.overlay)
73 .elevation(&theme, Elevation::Overlay)
74 .text_size(px(theme.typography.label.size))
75 .line_height(px(theme.typography.label.line_height))
76 .text_color(theme.colors.text)
77 .child(self.text.clone());
78
79 div()
80 .child(surface.animate_in(self.ident.child("in").element_id(), cx, Entrance::Menu))
81 .semantic_in(cx, spec)
82 }
83}
84
85struct TooltipView(Tooltip);
86
87impl Render for TooltipView {
88 fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
89 self.0.clone()
90 }
91}
92
93pub trait Tooltipped: gpui::StatefulInteractiveElement + Sized {
98 fn tip(self, control: impl Into<Ident>, text: impl Into<SharedString>) -> Self {
101 let control = control.into();
102 let text = text.into();
103 self.tooltip(move |_window, cx| {
104 Tooltip::new(control.child("tooltip"), text.clone())
105 .describes(control.semantic_id())
106 .view(cx)
107 })
108 }
109}
110
111impl<E: gpui::StatefulInteractiveElement + Sized> Tooltipped for E {}