tooltip.rs

 1use gpui::{overlay, Action, AnyView, IntoElement, Render, VisualContext};
 2use settings::Settings;
 3use theme::ThemeSettings;
 4
 5use crate::prelude::*;
 6use crate::{h_stack, v_stack, Color, KeyBinding, Label, LabelSize, StyledExt};
 7
 8pub struct Tooltip {
 9    title: SharedString,
10    meta: Option<SharedString>,
11    key_binding: Option<KeyBinding>,
12}
13
14impl Tooltip {
15    pub fn text(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
16        cx.new_view(|_cx| Self {
17            title: title.into(),
18            meta: None,
19            key_binding: None,
20        })
21        .into()
22    }
23
24    pub fn for_action(
25        title: impl Into<SharedString>,
26        action: &dyn Action,
27        cx: &mut WindowContext,
28    ) -> AnyView {
29        cx.new_view(|cx| Self {
30            title: title.into(),
31            meta: None,
32            key_binding: KeyBinding::for_action(action, cx),
33        })
34        .into()
35    }
36
37    pub fn with_meta(
38        title: impl Into<SharedString>,
39        action: Option<&dyn Action>,
40        meta: impl Into<SharedString>,
41        cx: &mut WindowContext,
42    ) -> AnyView {
43        cx.new_view(|cx| Self {
44            title: title.into(),
45            meta: Some(meta.into()),
46            key_binding: action.and_then(|action| KeyBinding::for_action(action, cx)),
47        })
48        .into()
49    }
50
51    pub fn new(title: impl Into<SharedString>) -> Self {
52        Self {
53            title: title.into(),
54            meta: None,
55            key_binding: None,
56        }
57    }
58
59    pub fn meta(mut self, meta: impl Into<SharedString>) -> Self {
60        self.meta = Some(meta.into());
61        self
62    }
63
64    pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
65        self.key_binding = key_binding.into();
66        self
67    }
68}
69
70impl Render for Tooltip {
71    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
72        let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
73        overlay().child(
74            // padding to avoid mouse cursor
75            div().pl_2().pt_2p5().child(
76                v_stack()
77                    .elevation_2(cx)
78                    .font(ui_font)
79                    .text_ui()
80                    .text_color(cx.theme().colors().text)
81                    .py_1()
82                    .px_2()
83                    .child(
84                        h_stack()
85                            .gap_4()
86                            .child(self.title.clone())
87                            .when_some(self.key_binding.clone(), |this, key_binding| {
88                                this.justify_between().child(key_binding)
89                            }),
90                    )
91                    .when_some(self.meta.clone(), |this, meta| {
92                        this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted))
93                    }),
94            ),
95        )
96    }
97}