Simple Tooltip usage.

This commit is contained in:
Jason Lee 2024-07-02 00:13:07 +08:00
parent f2703122b8
commit 88dcca590d
4 changed files with 20 additions and 223 deletions

View file

@ -38,18 +38,7 @@ impl Render for TooltipStory {
.style(ButtonStyle::Primary),
)
.id("tooltip-1")
.tooltip(|cx| Tooltip::text("This is a Button", cx)),
)
.child(
div()
.cursor(CursorStyle::PointingHand)
.child(
Button::new("button-meta")
.label("With meta, Hover me")
.style(ButtonStyle::Primary),
)
.id("tooltip-2")
.tooltip(|cx| Tooltip::with_meta("This is a Button", "Click if you want", cx)),
.tooltip(|cx| Tooltip::new("This is a Button", cx)),
)
.child(
h_flex()
@ -57,7 +46,7 @@ impl Render for TooltipStory {
.cursor(CursorStyle::PointingHand)
.child(Label::new("Hover me"))
.id("tooltip-3")
.tooltip(|cx| Tooltip::text("This is a Label", cx)),
.tooltip(|cx| Tooltip::new("This is a Label", cx)),
)
.child(
div()
@ -68,7 +57,7 @@ impl Render for TooltipStory {
.checked(Selection::Selected),
)
.id("tooltip-4")
.tooltip(|cx| Tooltip::text("Checked!", cx)),
.tooltip(|cx| Tooltip::new("Checked!", cx)),
)
}
}

View file

@ -1,157 +0,0 @@
use std::any::TypeId;
use std::borrow::Cow;
use std::sync::Arc;
use gpui::{
AnyView, DismissEvent, ElementId, Entity, EntityId, EventEmitter, IntoElement, ParentElement,
Render, SharedString, Styled, View, ViewContext, WindowContext,
};
use crate::theme::ActiveTheme;
use crate::{label::Label, v_flex, StyledExt};
#[derive(Debug, PartialEq, Clone)]
pub struct NotificationId {
/// A [`TypeId`] used to uniquely identify this notification.
type_id: TypeId,
/// A supplementary ID used to distinguish between multiple
/// notifications that have the same [`type_id`](Self::type_id);
id: Option<ElementId>,
}
impl NotificationId {
/// Returns a unique [`NotificationId`] for the given type.
pub fn unique<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
id: None,
}
}
/// Returns a [`NotificationId`] for the given type that is also identified
/// by the provided ID.
pub fn identified<T: 'static>(id: impl Into<ElementId>) -> Self {
Self {
type_id: TypeId::of::<T>(),
id: Some(id.into()),
}
}
}
// impl Workspace {
// pub fn dismiss_notification(&mut self, id: &NotificationId, cx: &mut ViewContext<Self>) {
// self.dismiss_notification_internal(id, cx)
// }
// pub fn show_toast(&mut self, toast: MessageNotification, cx: &mut ViewContext<Self>) {
// self.dismiss_notification(&toast.id, cx);
// self.show_notification(toast.id, cx, |cx| {
// cx.new_view(|_cx| match toast.on_click.as_ref() {
// Some((click_msg, on_click)) => {
// let on_click = on_click.clone();
// simple_message_notification::MessageNotification::new(toast.msg.clone())
// .with_click_message(click_msg.clone())
// .on_click(move |cx| on_click(cx))
// }
// None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
// })
// })
// }
// }
pub struct Toast {
id: NotificationId,
msg: Cow<'static, str>,
on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
}
impl Toast {
pub fn new<I: Into<Cow<'static, str>>>(id: NotificationId, msg: I) -> Self {
Toast {
id,
msg: msg.into(),
on_click: None,
}
}
pub fn on_click<F, M>(mut self, message: M, on_click: F) -> Self
where
M: Into<Cow<'static, str>>,
F: Fn(&mut WindowContext) + 'static,
{
self.on_click = Some((message.into(), Arc::new(on_click)));
self
}
}
impl PartialEq for Toast {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.msg == other.msg
&& self.on_click.is_some() == other.on_click.is_some()
}
}
impl Clone for Toast {
fn clone(&self) -> Self {
Toast {
id: self.id.clone(),
msg: self.msg.clone(),
on_click: self.on_click.clone(),
}
}
}
pub trait Notification: EventEmitter<DismissEvent> + Render {}
impl<V: EventEmitter<DismissEvent> + Render> Notification for V {}
pub trait NotificationHandle: Send {
fn id(&self) -> EntityId;
fn to_any(&self) -> AnyView;
}
impl<T: Notification> NotificationHandle for View<T> {
fn id(&self) -> EntityId {
self.entity_id()
}
fn to_any(&self) -> AnyView {
self.clone().into()
}
}
impl From<&dyn NotificationHandle> for AnyView {
fn from(val: &dyn NotificationHandle) -> Self {
val.to_any()
}
}
pub struct MessageNotification {
title: SharedString,
description: SharedString,
}
impl MessageNotification {
pub fn new(title: impl Into<SharedString>, description: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
description: description.into(),
}
}
}
impl Render for MessageNotification {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_flex()
.elevation_3(cx)
.p_4()
.max_w_80()
.bg(cx.theme().background)
.child(
v_flex()
.child(Label::new(self.title.clone()))
.child(Label::new(self.description.clone())),
)
}
}

View file

@ -1,72 +1,37 @@
use gpui::{
div, prelude::FluentBuilder, px, AnyView, Div, IntoElement, ParentElement, Render,
SharedString, Styled, ViewContext, VisualContext, WindowContext,
div, px, AnyView, IntoElement, ParentElement, Render, SharedString, Styled, ViewContext,
VisualContext, WindowContext,
};
use crate::{h_flex, theme::ActiveTheme, v_flex, StyledExt};
pub struct Tooltip {
title: SharedString,
meta: Option<SharedString>,
}
impl Tooltip {
pub fn text(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
pub fn new(title: impl Into<SharedString>, cx: &mut WindowContext) -> AnyView {
cx.new_view(|_cx| Self {
title: title.into(),
meta: None,
})
.into()
}
pub fn new(title: impl Into<SharedString>) -> Self {
Self {
title: title.into(),
meta: None,
}
}
pub fn with_meta(
title: impl Into<SharedString>,
meta: impl Into<SharedString>,
cx: &mut WindowContext,
) -> AnyView {
cx.new_view(|_: &mut ViewContext<Tooltip>| Self {
title: title.into(),
meta: Some(meta.into()),
})
.into()
}
// TODO: pub fn for_action
}
impl Render for Tooltip {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
tooltip_container(cx, |el, _| {
el.child(h_flex().gap_4().child(self.title.clone()))
.when_some(self.meta.clone(), |this, meta| {
this.child(div().text_size(px(12.)).child(meta))
})
})
div().child(
v_flex()
.m_3()
.bg(cx.theme().popover)
.rounded(px(8.))
.border_1()
.border_color(cx.theme().border)
.elevation_2(cx)
.text_color(cx.theme().popover_foreground)
.py_1p5()
.px_2()
.child(h_flex().gap_4().child(self.title.clone())),
)
}
}
pub fn tooltip_container<V>(
cx: &mut ViewContext<V>,
f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
) -> impl IntoElement {
// padding to avoid tooltip appearing right below the mouse cursor
div().pl_2().pt_2p5().child(
v_flex()
.bg(cx.theme().popover)
.rounded(px(8.))
.border_1()
.border_color(cx.theme().border)
.elevation_2(cx)
.text_color(cx.theme().popover_foreground)
.py_1p5()
.px_2()
.map(|el| f(el, cx)),
)
}

View file

@ -695,7 +695,7 @@ impl Pane {
this.handle_tab_drop(dragged_tab, ix, cx)
}))
.when_some(item.tab_tooltip(cx), |tab, text| {
tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
tab.tooltip(move |cx| Tooltip::new(text.clone(), cx))
})
}