use gpui::{
Action, AnyElement, AnyView, App, AppContext, Context, IntoElement, ParentElement, Render,
SharedString, StyleRefinement, Styled, Window, div, prelude::FluentBuilder,
};
use crate::{ActiveTheme, CardStyle, Kbd, Size, StyleSized, StyledExt, h_flex};
type TooltipElementBuilder = Box<dyn Fn(&mut Window, &mut App) -> AnyElement>;
enum TooltipContent {
Text(SharedString),
Element(TooltipElementBuilder),
}
pub struct Tooltip {
style: StyleRefinement,
content: TooltipContent,
key_binding: Option<Kbd>,
action: Option<(Box<dyn Action>, Option<SharedString>)>,
size: Size,
}
impl Tooltip {
pub fn new(text: impl Into<SharedString>) -> Self {
Self {
style: StyleRefinement::default(),
content: TooltipContent::Text(text.into()),
key_binding: None,
action: None,
size: Size::Medium,
}
}
pub fn element<E, F>(builder: F) -> Self
where
E: IntoElement,
F: Fn(&mut Window, &mut App) -> E + 'static, {
Self {
style: StyleRefinement::default(),
key_binding: None,
action: None,
content: TooltipContent::Element(Box::new(move |window, cx| {
builder(window, cx).into_any_element()
})),
size: Size::Medium,
}
}
pub fn key_binding(mut self, key_binding: Option<Kbd>) -> Self {
self.key_binding = key_binding;
self
}
pub fn action(mut self, action: &dyn Action, context: Option<&str>) -> Self {
self.action = Some((action.boxed_clone(), context.map(SharedString::new)));
self
}
pub fn build(self, _: &mut Window, cx: &mut App) -> AnyView {
cx.new(|_| self).into()
}
}
impl FluentBuilder for Tooltip {}
impl_sizable!(Tooltip);
impl_styled!(Tooltip);
impl Render for Tooltip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let key_binding = if let Some(key_binding) = &self.key_binding {
Some(key_binding.clone())
} else if let Some((action, context)) = &self.action {
Kbd::binding_for_action(
action.as_ref(),
context.as_ref().map(|s| s.as_ref()),
window,
)
} else {
None
};
h_flex()
.m_3()
.tooltip_style(cx.theme())
.justify_between()
.component_padding(self.size)
.component_gap(self.size)
.refine_style(&self.style)
.map(|this| match self.content {
TooltipContent::Text(ref text) => this.child(text.clone()),
TooltipContent::Element(ref builder) => this.child(builder(window, cx)),
})
.when_some(key_binding, |this, kbd| {
this.child(
div()
.text_sm()
.flex_shrink_0()
.text_color(cx.theme().muted_foreground)
.child(kbd.outline()),
)
})
}
}