tooltip.rs

  1use gpui::{overlay, Action, AnyView, IntoElement, Render, VisualContext};
  2use settings::Settings;
  3use theme::ThemeSettings;
  4
  5use crate::prelude::*;
  6use crate::{h_flex, v_flex, 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        tooltip_container(cx, |el, _| {
 73            el.child(
 74                h_flex()
 75                    .gap_4()
 76                    .child(self.title.clone())
 77                    .when_some(self.key_binding.clone(), |this, key_binding| {
 78                        this.justify_between().child(key_binding)
 79                    }),
 80            )
 81            .when_some(self.meta.clone(), |this, meta| {
 82                this.child(Label::new(meta).size(LabelSize::Small).color(Color::Muted))
 83            })
 84        })
 85    }
 86}
 87
 88fn tooltip_container<V>(
 89    cx: &mut ViewContext<V>,
 90    f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
 91) -> impl IntoElement {
 92    let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
 93    overlay().child(
 94        // padding to avoid mouse cursor
 95        div().pl_2().pt_2p5().child(
 96            v_flex()
 97                .elevation_2(cx)
 98                .font(ui_font)
 99                .text_ui()
100                .text_color(cx.theme().colors().text)
101                .py_1()
102                .px_2()
103                .map(|el| f(el, cx)),
104        ),
105    )
106}
107
108pub struct LinkPreview {
109    link: SharedString,
110}
111
112impl LinkPreview {
113    pub fn new(url: &str, cx: &mut WindowContext) -> AnyView {
114        let mut wrapped_url = String::new();
115        for (i, ch) in url.chars().enumerate() {
116            if i == 500 {
117                wrapped_url.push('…');
118                break;
119            }
120            if i % 100 == 0 && i != 0 {
121                wrapped_url.push('\n');
122            }
123            wrapped_url.push(ch);
124        }
125        cx.new_view(|_cx| LinkPreview {
126            link: wrapped_url.into(),
127        })
128        .into()
129    }
130}
131
132impl Render for LinkPreview {
133    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
134        tooltip_container(cx, |el, _| {
135            el.child(
136                Label::new(self.link.clone())
137                    .size(LabelSize::XSmall)
138                    .color(Color::Muted),
139            )
140        })
141    }
142}