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)
This commit is contained in:
Jason Lee 2024-12-24 09:46:40 +08:00 committed by GitHub
parent d051ac6a8c
commit ea0954b425
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 74 additions and 21 deletions

View file

@ -292,14 +292,20 @@ impl ModalStory {
}; };
let overlay = self.modal_overlay; let overlay = self.modal_overlay;
cx.open_drawer(move |this, cx| { cx.open_drawer_at(placement, move |this, cx| {
this.placement(placement) this.overlay(overlay)
.overlay(overlay)
.size(px(400.)) .size(px(400.))
.title("Drawer Title") .title("Drawer Title")
.gap_4() .gap_4()
.child(input.clone()) .child(input.clone())
.child(date_picker.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( .child(
div() div()
.border_1() .border_1()

View file

@ -28,8 +28,8 @@ pub fn init(cx: &mut AppContext) {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Drawer { pub struct Drawer {
pub(crate) focus_handle: FocusHandle, pub(crate) focus_handle: FocusHandle,
placement: Placement, pub(crate) placement: Placement,
size: DefiniteLength, pub(crate) size: DefiniteLength,
resizable: bool, resizable: bool,
on_close: Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>, on_close: Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
title: Option<AnyElement>, title: Option<AnyElement>,
@ -81,17 +81,6 @@ impl Drawer {
self 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`. /// Sets whether the drawer is resizable, default is `true`.
pub fn resizable(mut self, resizable: bool) -> Self { pub fn resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable; self.resizable = resizable;

View file

@ -3,10 +3,12 @@ use crate::{
modal::Modal, modal::Modal,
notification::{Notification, NotificationList}, notification::{Notification, NotificationList},
theme::ActiveTheme, theme::ActiveTheme,
Placement,
}; };
use gpui::{ use gpui::{
div, AnyView, FocusHandle, InteractiveElement, IntoElement, ParentElement as _, Render, Styled, canvas, div, prelude::FluentBuilder as _, AnyView, DefiniteLength, FocusHandle,
View, ViewContext, VisualContext as _, WindowContext, InteractiveElement, IntoElement, ParentElement as _, Render, Styled, View, ViewContext,
VisualContext as _, WindowContext,
}; };
use std::{ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
@ -15,11 +17,16 @@ use std::{
/// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality. /// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality.
pub trait ContextModal: Sized { pub trait ContextModal: Sized {
/// Opens a Drawer. /// Opens a Drawer at right placement.
fn open_drawer<F>(&mut self, build: F) fn open_drawer<F>(&mut self, build: F)
where where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static; F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static;
/// Opens a Drawer at the given placement.
fn open_drawer_at<F>(&mut self, placement: Placement, build: F)
where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static;
/// Return true, if there is an active Drawer. /// Return true, if there is an active Drawer.
fn has_active_drawer(&self) -> bool; fn has_active_drawer(&self) -> bool;
@ -49,6 +56,13 @@ pub trait ContextModal: Sized {
impl ContextModal for WindowContext<'_> { impl ContextModal for WindowContext<'_> {
fn open_drawer<F>(&mut self, build: F) fn open_drawer<F>(&mut self, build: F)
where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static,
{
self.open_drawer_at(Placement::Right, build)
}
fn open_drawer_at<F>(&mut self, placement: Placement, build: F)
where where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static, F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static,
{ {
@ -62,6 +76,7 @@ impl ContextModal for WindowContext<'_> {
root.active_drawer = Some(ActiveDrawer { root.active_drawer = Some(ActiveDrawer {
focus_handle, focus_handle,
placement,
builder: Rc::new(build), builder: Rc::new(build),
}); });
cx.notify(); cx.notify();
@ -156,6 +171,13 @@ impl<V> ContextModal for ViewContext<'_, V> {
self.deref_mut().open_drawer(build) self.deref_mut().open_drawer(build)
} }
fn open_drawer_at<F>(&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 { fn has_active_modal(&self) -> bool {
self.deref().has_active_modal() self.deref().has_active_modal()
} }
@ -208,12 +230,14 @@ pub struct Root {
active_drawer: Option<ActiveDrawer>, active_drawer: Option<ActiveDrawer>,
active_modals: Vec<ActiveModal>, active_modals: Vec<ActiveModal>,
pub notification: View<NotificationList>, pub notification: View<NotificationList>,
drawer_size: Option<DefiniteLength>,
view: AnyView, view: AnyView,
} }
#[derive(Clone)] #[derive(Clone)]
struct ActiveDrawer { struct ActiveDrawer {
focus_handle: FocusHandle, focus_handle: FocusHandle,
placement: Placement,
builder: Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>, builder: Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>,
} }
@ -230,6 +254,7 @@ impl Root {
active_drawer: None, active_drawer: None,
active_modals: Vec::new(), active_modals: Vec::new(),
notification: cx.new_view(NotificationList::new), notification: cx.new_view(NotificationList::new),
drawer_size: None,
view, view,
} }
} }
@ -263,6 +288,14 @@ impl Root {
} }
} }
fn active_drawer_placement(&self, cx: &WindowContext) -> Option<Placement> {
if let Some(drawer) = Root::read(cx).active_drawer.as_ref() {
Some(drawer.placement)
} else {
None
}
}
// Render Notification layer. // Render Notification layer.
pub fn render_notification_layer(cx: &mut WindowContext) -> Option<impl IntoElement> { pub fn render_notification_layer(cx: &mut WindowContext) -> Option<impl IntoElement> {
let root = cx let root = cx
@ -271,7 +304,20 @@ impl Root {
.and_then(|w| w.root_view(cx).ok()) .and_then(|w| w.root_view(cx).ok())
.expect("The window root view should be of type `ui::Root`."); .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. /// Render the Drawer layer.
@ -286,8 +332,20 @@ impl Root {
let mut drawer = Drawer::new(cx); let mut drawer = Drawer::new(cx);
drawer = (active_drawer.builder)(drawer, cx); drawer = (active_drawer.builder)(drawer, cx);
drawer.focus_handle = active_drawer.focus_handle.clone(); drawer.focus_handle = active_drawer.focus_handle.clone();
drawer.placement = active_drawer.placement;
return Some(div().child(drawer)); 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 None