diff --git a/assets/themes/cave-dark.json b/assets/themes/cave-dark.json index cb1208a1db764bbb540dc73540eb723bfded2d91..3ced0536218e713a2d46ff5b12baa1ad3f29e077 100644 --- a/assets/themes/cave-dark.json +++ b/assets/themes/cave-dark.json @@ -470,6 +470,19 @@ "color": "#efecf4", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/cave-light.json b/assets/themes/cave-light.json index f0b3f5bd438a53178d9f56f88e8fbf1dbd41472d..a0a19149dfddb6f516c9c19895565e6c41353963 100644 --- a/assets/themes/cave-light.json +++ b/assets/themes/cave-light.json @@ -470,6 +470,19 @@ "color": "#19171c", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/dark.json b/assets/themes/dark.json index 9cc3badc8104dbee88d2862ad7feeedba22383e8..cbff53933e5a1077208a9284cbe96522b5eef07b 100644 --- a/assets/themes/dark.json +++ b/assets/themes/dark.json @@ -470,6 +470,19 @@ "color": "#ffffff", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/light.json b/assets/themes/light.json index e2563fadad64d74d0b7add301cbfb2fc0969be6d..c7331a083b416365288861915c27fadd57a75b55 100644 --- a/assets/themes/light.json +++ b/assets/themes/light.json @@ -470,6 +470,19 @@ "color": "#000000", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/solarized-dark.json b/assets/themes/solarized-dark.json index 6e8c405b6c212bf77fc99c99ea3c2a6dcf5a2f07..d3367a3a3f570b3f1c37e70113145e69dd27c6b7 100644 --- a/assets/themes/solarized-dark.json +++ b/assets/themes/solarized-dark.json @@ -470,6 +470,19 @@ "color": "#fdf6e3", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/solarized-light.json b/assets/themes/solarized-light.json index 3f5b26ee56a24a883fc21c45a9de60bec20b9a5d..d0c1f4e6e8e52b2d9e6edc6b9db9714e1152edc7 100644 --- a/assets/themes/solarized-light.json +++ b/assets/themes/solarized-light.json @@ -470,6 +470,19 @@ "color": "#002b36", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/sulphurpool-dark.json b/assets/themes/sulphurpool-dark.json index 0f2a868f24d028be4c960db27b39b36b540a3e5d..05541f91f855d98bf1bfe365f9ff3a799f32606d 100644 --- a/assets/themes/sulphurpool-dark.json +++ b/assets/themes/sulphurpool-dark.json @@ -470,6 +470,19 @@ "color": "#f5f7ff", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/assets/themes/sulphurpool-light.json b/assets/themes/sulphurpool-light.json index b9106c62f3d273a537616f0cbd38090b8c854411..1a9408b17f4cab439dab99ed760a03a902cd0c4a 100644 --- a/assets/themes/sulphurpool-light.json +++ b/assets/themes/sulphurpool-light.json @@ -470,6 +470,19 @@ "color": "#202746", "size": 14, "background": "#000000aa" + }, + "notification": { + "margin": { + "top": 10 + } + }, + "notifications": { + "width": 256, + "margin": { + "right": 10, + "bottom": 10 + }, + "background": "#ff0000" } }, "editor": { diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index bb835c66401d595607546fbca5453d85461c2164..460e01c527bddca2145f5f0fbf284403fdeb952a 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -69,7 +69,7 @@ impl ChatPanel { .with_style(move |cx| { let theme = &cx.global::().theme.chat_panel.channel_select; SelectStyle { - header: theme.header.container.clone(), + header: theme.header.container, menu: theme.menu.clone(), } }) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 72db11c4931436ad6bf90650561d3baa5fe1eef1..58cdc7fc54e596a6af5ac3177ee000f18f61f119 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -45,6 +45,8 @@ pub struct Workspace { pub toolbar: Toolbar, pub disconnected_overlay: ContainedText, pub modal: ContainerStyle, + pub notification: ContainerStyle, + pub notifications: Notifications, } #[derive(Clone, Deserialize, Default)] @@ -109,6 +111,13 @@ pub struct Toolbar { pub item_spacing: f32, } +#[derive(Clone, Deserialize, Default)] +pub struct Notifications { + #[serde(flatten)] + pub container: ContainerStyle, + pub width: f32, +} + #[derive(Clone, Deserialize, Default)] pub struct Search { #[serde(flatten)] diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 688accfcb9dc15f29f020aaad5c4f1ee00db108d..94b0a82f539dab79d8ca4386e436513d702072b2 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -604,6 +604,24 @@ impl WeakItemHandle for WeakViewHandle { } } +pub trait Notification: View {} + +pub trait NotificationHandle { + fn to_any(&self) -> AnyViewHandle; +} + +impl NotificationHandle for ViewHandle { + fn to_any(&self) -> AnyViewHandle { + self.into() + } +} + +impl Into for &dyn NotificationHandle { + fn into(self) -> AnyViewHandle { + self.to_any() + } +} + #[derive(Clone)] pub struct WorkspaceParams { pub project: ModelHandle, @@ -683,6 +701,7 @@ pub struct Workspace { panes: Vec>, active_pane: ViewHandle, status_bar: ViewHandle, + notifications: Vec>, project: ModelHandle, leader_state: LeaderState, follower_states_by_leader: FollowerStatesByLeader, @@ -791,6 +810,7 @@ impl Workspace { panes: vec![pane.clone()], active_pane: pane.clone(), status_bar, + notifications: Default::default(), client: params.client.clone(), remote_entity_subscription: None, user_store: params.user_store.clone(), @@ -971,6 +991,15 @@ impl Workspace { } } + pub fn show_notification( + &mut self, + notification: ViewHandle, + cx: &mut ViewContext, + ) { + self.notifications.push(Box::new(notification)); + cx.notify(); + } + pub fn items<'a>( &'a self, cx: &'a AppContext, @@ -1703,6 +1732,34 @@ impl Workspace { } } + fn render_notifications( + &self, + theme: &theme::Workspace, + cx: &mut RenderContext, + ) -> Option { + if self.notifications.is_empty() { + None + } else { + Some( + Flex::column() + .with_children(self.notifications.iter().map(|notification| { + ChildView::new(notification.as_ref()) + .contained() + .with_style(theme.notification) + .boxed() + })) + .constrained() + .with_width(250.) + .contained() + .with_style(theme.notifications.container) + .aligned() + .bottom() + .right() + .boxed(), + ) + } + } + // RPC handlers async fn handle_follow( @@ -2037,6 +2094,7 @@ impl View for Workspace { .top() .boxed() })) + .with_children(self.render_notifications(&theme.workspace, cx)) .flex(1.0, true) .boxed(), ) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index f74715ac0b928d3162a526e3b3ee18617e7efd37..72547627fabf4e3dd7d0d97e0304e7ab194b7ec1 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -146,5 +146,13 @@ export default function workspace(theme: Theme) { ...text(theme, "sans", "active"), background: "#000000aa", }, + notification: { + margin: { top: 10 }, + }, + notifications: { + width: 256, + margin: { right: 10, bottom: 10 }, + background: "#ff0000", + } }; }