gpui2: Notifications
This commit is contained in:
parent
700168467e
commit
039c933d8e
11 changed files with 190 additions and 111 deletions
|
@ -1,6 +1,9 @@
|
|||
use crate::{Toast, Workspace};
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext};
|
||||
use gpui::{
|
||||
AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Render,
|
||||
View, ViewContext, VisualContext,
|
||||
};
|
||||
use std::{any::TypeId, ops::DerefMut};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
|
@ -9,13 +12,9 @@ pub fn init(cx: &mut AppContext) {
|
|||
// simple_message_notification::init(cx);
|
||||
}
|
||||
|
||||
pub enum NotificationEvent {
|
||||
Dismiss,
|
||||
}
|
||||
pub trait Notification: EventEmitter<DismissEvent> + Render {}
|
||||
|
||||
pub trait Notification: EventEmitter<NotificationEvent> + Render {}
|
||||
|
||||
impl<V: EventEmitter<NotificationEvent> + Render> Notification for V {}
|
||||
impl<V: EventEmitter<DismissEvent> + Render> Notification for V {}
|
||||
|
||||
pub trait NotificationHandle: Send {
|
||||
fn id(&self) -> EntityId;
|
||||
|
@ -107,8 +106,8 @@ impl Workspace {
|
|||
let notification = build_notification(cx);
|
||||
cx.subscribe(
|
||||
¬ification,
|
||||
move |this, handle, event: &NotificationEvent, cx| match event {
|
||||
NotificationEvent::Dismiss => {
|
||||
move |this, handle, event: &DismissEvent, cx| match event {
|
||||
DismissEvent::Dismiss => {
|
||||
this.dismiss_notification_internal(type_id, id, cx);
|
||||
}
|
||||
},
|
||||
|
@ -120,6 +119,17 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn show_error<E>(&mut self, err: &E, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
{
|
||||
self.show_notification(0, cx, |cx| {
|
||||
cx.build_view(|_cx| {
|
||||
simple_message_notification::MessageNotification::new(format!("Error: {err:?}"))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn dismiss_notification<V: Notification>(&mut self, id: usize, cx: &mut ViewContext<Self>) {
|
||||
let type_id = TypeId::of::<V>();
|
||||
|
||||
|
@ -166,13 +176,14 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub mod simple_message_notification {
|
||||
use super::NotificationEvent;
|
||||
use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
|
||||
use gpui::{
|
||||
div, AnyElement, AppContext, DismissEvent, Div, EventEmitter, InteractiveElement,
|
||||
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, TextStyle,
|
||||
ViewContext,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
// todo!()
|
||||
// actions!(message_notifications, [CancelMessageNotification]);
|
||||
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
|
||||
|
||||
#[derive(Clone, Default, Deserialize, PartialEq)]
|
||||
pub struct OsOpen(pub Cow<'static, str>);
|
||||
|
@ -197,22 +208,22 @@ pub mod simple_message_notification {
|
|||
// }
|
||||
|
||||
enum NotificationMessage {
|
||||
Text(Cow<'static, str>),
|
||||
Text(SharedString),
|
||||
Element(fn(TextStyle, &AppContext) -> AnyElement),
|
||||
}
|
||||
|
||||
pub struct MessageNotification {
|
||||
message: NotificationMessage,
|
||||
on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>) + Send + Sync>>,
|
||||
click_message: Option<Cow<'static, str>>,
|
||||
click_message: Option<SharedString>,
|
||||
}
|
||||
|
||||
impl EventEmitter<NotificationMessage> for MessageNotification {}
|
||||
impl EventEmitter<DismissEvent> for MessageNotification {}
|
||||
|
||||
impl MessageNotification {
|
||||
pub fn new<S>(message: S) -> MessageNotification
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
S: Into<SharedString>,
|
||||
{
|
||||
Self {
|
||||
message: NotificationMessage::Text(message.into()),
|
||||
|
@ -221,19 +232,20 @@ pub mod simple_message_notification {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_element(
|
||||
message: fn(TextStyle, &AppContext) -> AnyElement,
|
||||
) -> MessageNotification {
|
||||
Self {
|
||||
message: NotificationMessage::Element(message),
|
||||
on_click: None,
|
||||
click_message: None,
|
||||
}
|
||||
}
|
||||
// not needed I think (only for the "new panel" toast, which is outdated now)
|
||||
// pub fn new_element(
|
||||
// message: fn(TextStyle, &AppContext) -> AnyElement,
|
||||
// ) -> MessageNotification {
|
||||
// Self {
|
||||
// message: NotificationMessage::Element(message),
|
||||
// on_click: None,
|
||||
// click_message: None,
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn with_click_message<S>(mut self, message: S) -> Self
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
S: Into<SharedString>,
|
||||
{
|
||||
self.click_message = Some(message.into());
|
||||
self
|
||||
|
@ -247,17 +259,43 @@ pub mod simple_message_notification {
|
|||
self
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext<Self>) {
|
||||
// cx.emit(MessageNotificationEvent::Dismiss);
|
||||
// }
|
||||
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
|
||||
cx.emit(DismissEvent::Dismiss);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for MessageNotification {
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
todo!()
|
||||
v_stack()
|
||||
.elevation_3(cx)
|
||||
.p_4()
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.child(div().max_w_80().child(match &self.message {
|
||||
NotificationMessage::Text(text) => Label::new(text.clone()),
|
||||
NotificationMessage::Element(element) => {
|
||||
todo!()
|
||||
}
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
.id("cancel")
|
||||
.child(IconElement::new(Icon::Close))
|
||||
.cursor_pointer()
|
||||
.on_click(cx.listener(|this, event, cx| this.dismiss(cx))),
|
||||
),
|
||||
)
|
||||
.children(self.click_message.iter().map(|message| {
|
||||
Button::new(message.clone()).on_click(cx.listener(|this, _, cx| {
|
||||
if let Some(on_click) = this.on_click.as_ref() {
|
||||
(on_click)(cx)
|
||||
};
|
||||
this.dismiss(cx)
|
||||
}))
|
||||
}))
|
||||
}
|
||||
}
|
||||
// todo!()
|
||||
|
@ -359,8 +397,6 @@ pub mod simple_message_notification {
|
|||
// .into_any()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl EventEmitter<NotificationEvent> for MessageNotification {}
|
||||
}
|
||||
|
||||
pub trait NotifyResultExt {
|
||||
|
@ -371,6 +407,8 @@ pub trait NotifyResultExt {
|
|||
workspace: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Self::Ok>;
|
||||
|
||||
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<Self::Ok>;
|
||||
}
|
||||
|
||||
impl<T, E> NotifyResultExt for Result<T, E>
|
||||
|
@ -384,14 +422,23 @@ where
|
|||
Ok(value) => Some(value),
|
||||
Err(err) => {
|
||||
log::error!("TODO {err:?}");
|
||||
// todo!()
|
||||
// workspace.show_notification(0, cx, |cx| {
|
||||
// cx.add_view(|_cx| {
|
||||
// simple_message_notification::MessageNotification::new(format!(
|
||||
// "Error: {err:?}",
|
||||
// ))
|
||||
// })
|
||||
// });
|
||||
workspace.show_error(&err, cx);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<T> {
|
||||
match self {
|
||||
Ok(value) => Some(value),
|
||||
Err(err) => {
|
||||
log::error!("TODO {err:?}");
|
||||
cx.update(|view, cx| {
|
||||
if let Ok(workspace) = view.downcast::<Workspace>() {
|
||||
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -683,7 +683,21 @@ impl Workspace {
|
|||
}),
|
||||
];
|
||||
|
||||
cx.defer(|this, cx| this.update_window_title(cx));
|
||||
cx.defer(|this, cx| {
|
||||
this.update_window_title(cx);
|
||||
// todo! @nate - these are useful for testing notifications
|
||||
// this.show_error(
|
||||
// &anyhow::anyhow!("what happens if this message is very very very very very long"),
|
||||
// cx,
|
||||
// );
|
||||
|
||||
// this.show_notification(1, cx, |cx| {
|
||||
// cx.build_view(|_cx| {
|
||||
// simple_message_notification::MessageNotification::new(format!("Error:"))
|
||||
// .with_click_message("click here because!")
|
||||
// })
|
||||
// });
|
||||
});
|
||||
Workspace {
|
||||
window_self: window_handle,
|
||||
weak_self: weak_handle.clone(),
|
||||
|
@ -2566,32 +2580,31 @@ impl Workspace {
|
|||
// }
|
||||
// }
|
||||
|
||||
// fn render_notifications(
|
||||
// &self,
|
||||
// theme: &theme::Workspace,
|
||||
// cx: &AppContext,
|
||||
// ) -> Option<AnyElement<Workspace>> {
|
||||
// if self.notifications.is_empty() {
|
||||
// None
|
||||
// } else {
|
||||
// Some(
|
||||
// Flex::column()
|
||||
// .with_children(self.notifications.iter().map(|(_, _, notification)| {
|
||||
// ChildView::new(notification.as_any(), cx)
|
||||
// .contained()
|
||||
// .with_style(theme.notification)
|
||||
// }))
|
||||
// .constrained()
|
||||
// .with_width(theme.notifications.width)
|
||||
// .contained()
|
||||
// .with_style(theme.notifications.container)
|
||||
// .aligned()
|
||||
// .bottom()
|
||||
// .right()
|
||||
// .into_any(),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
fn render_notifications(&self, cx: &ViewContext<Self>) -> Option<Div> {
|
||||
if self.notifications.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
div()
|
||||
.absolute()
|
||||
.z_index(100)
|
||||
.right_3()
|
||||
.bottom_3()
|
||||
.w_96()
|
||||
.h_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_end()
|
||||
.gap_2()
|
||||
.children(self.notifications.iter().map(|(_, _, notification)| {
|
||||
div()
|
||||
.on_any_mouse_down(|_, cx| cx.stop_propagation())
|
||||
.on_any_mouse_up(|_, cx| cx.stop_propagation())
|
||||
.child(notification.to_any())
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// // RPC handlers
|
||||
|
||||
|
@ -3653,7 +3666,6 @@ impl Render for Workspace {
|
|||
.bg(cx.theme().colors().background)
|
||||
.children(self.titlebar_item.clone())
|
||||
.child(
|
||||
// todo! should this be a component a view?
|
||||
div()
|
||||
.id("workspace")
|
||||
.relative()
|
||||
|
@ -3703,7 +3715,8 @@ impl Render for Workspace {
|
|||
.overflow_hidden()
|
||||
.child(self.right_dock.clone()),
|
||||
),
|
||||
),
|
||||
)
|
||||
.children(self.render_notifications(cx)),
|
||||
)
|
||||
.child(self.status_bar.clone())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue