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), .style(ButtonStyle::Primary),
) )
.id("tooltip-1") .id("tooltip-1")
.tooltip(|cx| Tooltip::text("This is a Button", cx)), .tooltip(|cx| Tooltip::new("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)),
) )
.child( .child(
h_flex() h_flex()
@ -57,7 +46,7 @@ impl Render for TooltipStory {
.cursor(CursorStyle::PointingHand) .cursor(CursorStyle::PointingHand)
.child(Label::new("Hover me")) .child(Label::new("Hover me"))
.id("tooltip-3") .id("tooltip-3")
.tooltip(|cx| Tooltip::text("This is a Label", cx)), .tooltip(|cx| Tooltip::new("This is a Label", cx)),
) )
.child( .child(
div() div()
@ -68,7 +57,7 @@ impl Render for TooltipStory {
.checked(Selection::Selected), .checked(Selection::Selected),
) )
.id("tooltip-4") .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,64 +1,28 @@
use gpui::{ use gpui::{
div, prelude::FluentBuilder, px, AnyView, Div, IntoElement, ParentElement, Render, div, px, AnyView, IntoElement, ParentElement, Render, SharedString, Styled, ViewContext,
SharedString, Styled, ViewContext, VisualContext, WindowContext, VisualContext, WindowContext,
}; };
use crate::{h_flex, theme::ActiveTheme, v_flex, StyledExt}; use crate::{h_flex, theme::ActiveTheme, v_flex, StyledExt};
pub struct Tooltip { pub struct Tooltip {
title: SharedString, title: SharedString,
meta: Option<SharedString>,
} }
impl Tooltip { 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 { cx.new_view(|_cx| Self {
title: title.into(), title: title.into(),
meta: None,
}) })
.into() .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 { impl Render for Tooltip {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
tooltip_container(cx, |el, _| { div().child(
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))
})
})
}
}
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() v_flex()
.m_3()
.bg(cx.theme().popover) .bg(cx.theme().popover)
.rounded(px(8.)) .rounded(px(8.))
.border_1() .border_1()
@ -67,6 +31,7 @@ pub fn tooltip_container<V>(
.text_color(cx.theme().popover_foreground) .text_color(cx.theme().popover_foreground)
.py_1p5() .py_1p5()
.px_2() .px_2()
.map(|el| f(el, cx)), .child(h_flex().gap_4().child(self.title.clone())),
) )
} }
}

View file

@ -695,7 +695,7 @@ impl Pane {
this.handle_tab_drop(dragged_tab, ix, cx) this.handle_tab_drop(dragged_tab, ix, cx)
})) }))
.when_some(item.tab_tooltip(cx), |tab, text| { .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))
}) })
} }