From ea0954b425955bfbc948d0de1eefe422367e6394 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 24 Dec 2024 09:46:40 +0800 Subject: [PATCH] drawer: Render notification at the side of drawer when drawer (top, right) it opened. (#512) This change to make sure notification will display when WebView render on the drawer. ![Screenshot from 2024-12-24 09-25-10](https://github.com/user-attachments/assets/01d9685c-7790-4fe7-ba0a-217a0fec7d34) --- crates/story/src/modal_story.rs | 12 ++++-- crates/ui/src/drawer.rs | 15 +------ crates/ui/src/root.rs | 70 ++++++++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 22 deletions(-) diff --git a/crates/story/src/modal_story.rs b/crates/story/src/modal_story.rs index 8fb7ffef..4545cd33 100644 --- a/crates/story/src/modal_story.rs +++ b/crates/story/src/modal_story.rs @@ -292,14 +292,20 @@ impl ModalStory { }; let overlay = self.modal_overlay; - cx.open_drawer(move |this, cx| { - this.placement(placement) - .overlay(overlay) + cx.open_drawer_at(placement, move |this, cx| { + this.overlay(overlay) .size(px(400.)) .title("Drawer Title") .gap_4() .child(input.clone()) .child(date_picker.clone()) + .child( + Button::new("send-notification") + .child("Test Notification") + .on_click(|_, cx| { + cx.push_notification("Hello this is message from Drawer.") + }), + ) .child( div() .border_1() diff --git a/crates/ui/src/drawer.rs b/crates/ui/src/drawer.rs index b64623c3..1535a6ed 100644 --- a/crates/ui/src/drawer.rs +++ b/crates/ui/src/drawer.rs @@ -28,8 +28,8 @@ pub fn init(cx: &mut AppContext) { #[derive(IntoElement)] pub struct Drawer { pub(crate) focus_handle: FocusHandle, - placement: Placement, - size: DefiniteLength, + pub(crate) placement: Placement, + pub(crate) size: DefiniteLength, resizable: bool, on_close: Rc, title: Option, @@ -81,17 +81,6 @@ impl Drawer { self } - /// Sets the placement of the drawer, default is `Placement::Right`. - pub fn placement(mut self, placement: Placement) -> Self { - self.placement = placement; - self - } - - /// Sets the placement of the drawer, default is `Placement::Right`. - pub fn set_placement(&mut self, placement: Placement) { - self.placement = placement; - } - /// Sets whether the drawer is resizable, default is `true`. pub fn resizable(mut self, resizable: bool) -> Self { self.resizable = resizable; diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 3a526808..459411c0 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -3,10 +3,12 @@ use crate::{ modal::Modal, notification::{Notification, NotificationList}, theme::ActiveTheme, + Placement, }; use gpui::{ - div, AnyView, FocusHandle, InteractiveElement, IntoElement, ParentElement as _, Render, Styled, - View, ViewContext, VisualContext as _, WindowContext, + canvas, div, prelude::FluentBuilder as _, AnyView, DefiniteLength, FocusHandle, + InteractiveElement, IntoElement, ParentElement as _, Render, Styled, View, ViewContext, + VisualContext as _, WindowContext, }; use std::{ ops::{Deref, DerefMut}, @@ -15,11 +17,16 @@ use std::{ /// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality. pub trait ContextModal: Sized { - /// Opens a Drawer. + /// Opens a Drawer at right placement. fn open_drawer(&mut self, build: F) where F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static; + /// Opens a Drawer at the given placement. + fn open_drawer_at(&mut self, placement: Placement, build: F) + where + F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static; + /// Return true, if there is an active Drawer. fn has_active_drawer(&self) -> bool; @@ -49,6 +56,13 @@ pub trait ContextModal: Sized { impl ContextModal for WindowContext<'_> { fn open_drawer(&mut self, build: F) + where + F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static, + { + self.open_drawer_at(Placement::Right, build) + } + + fn open_drawer_at(&mut self, placement: Placement, build: F) where F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static, { @@ -62,6 +76,7 @@ impl ContextModal for WindowContext<'_> { root.active_drawer = Some(ActiveDrawer { focus_handle, + placement, builder: Rc::new(build), }); cx.notify(); @@ -156,6 +171,13 @@ impl ContextModal for ViewContext<'_, V> { self.deref_mut().open_drawer(build) } + fn open_drawer_at(&mut self, placement: Placement, build: F) + where + F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static, + { + self.deref_mut().open_drawer_at(placement, build) + } + fn has_active_modal(&self) -> bool { self.deref().has_active_modal() } @@ -208,12 +230,14 @@ pub struct Root { active_drawer: Option, active_modals: Vec, pub notification: View, + drawer_size: Option, view: AnyView, } #[derive(Clone)] struct ActiveDrawer { focus_handle: FocusHandle, + placement: Placement, builder: Rc Drawer + 'static>, } @@ -230,6 +254,7 @@ impl Root { active_drawer: None, active_modals: Vec::new(), notification: cx.new_view(NotificationList::new), + drawer_size: None, view, } } @@ -263,6 +288,14 @@ impl Root { } } + fn active_drawer_placement(&self, cx: &WindowContext) -> Option { + if let Some(drawer) = Root::read(cx).active_drawer.as_ref() { + Some(drawer.placement) + } else { + None + } + } + // Render Notification layer. pub fn render_notification_layer(cx: &mut WindowContext) -> Option { let root = cx @@ -271,7 +304,20 @@ impl Root { .and_then(|w| w.root_view(cx).ok()) .expect("The window root view should be of type `ui::Root`."); - Some(div().child(root.read(cx).notification.clone())) + let active_drawer_placement = root.read(cx).active_drawer_placement(cx); + + let (mt, mr) = match active_drawer_placement { + Some(Placement::Right) => (None, root.read(cx).drawer_size), + Some(Placement::Top) => (root.read(cx).drawer_size, None), + _ => (None, None), + }; + + Some( + div() + .when_some(mt, |this, offset| this.mt(offset)) + .when_some(mr, |this, offset| this.mr(offset)) + .child(root.read(cx).notification.clone()), + ) } /// Render the Drawer layer. @@ -286,8 +332,20 @@ impl Root { let mut drawer = Drawer::new(cx); drawer = (active_drawer.builder)(drawer, cx); drawer.focus_handle = active_drawer.focus_handle.clone(); - - return Some(div().child(drawer)); + drawer.placement = active_drawer.placement; + + let drawer_size = drawer.size; + + return Some( + div().relative().child(drawer).child( + canvas( + move |_, cx| root.update(cx, |r, _| r.drawer_size = Some(drawer_size)), + |_, _, _| {}, + ) + .absolute() + .size_full(), + ), + ); } None