Skip to content

Commit

Permalink
drawer: Render notification at the side of drawer when drawer (top, r…
Browse files Browse the repository at this point in the history
…ight) 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)
  • Loading branch information
huacnlee authored Dec 24, 2024
1 parent d051ac6 commit ea0954b
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 22 deletions.
12 changes: 9 additions & 3 deletions crates/story/src/modal_story.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
15 changes: 2 additions & 13 deletions crates/ui/src/drawer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>,
title: Option<AnyElement>,
Expand Down Expand Up @@ -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;
Expand Down
70 changes: 64 additions & 6 deletions crates/ui/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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<F>(&mut self, build: F)
where
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.
fn has_active_drawer(&self) -> bool;

Expand Down Expand Up @@ -49,6 +56,13 @@ pub trait ContextModal: Sized {

impl ContextModal for WindowContext<'_> {
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
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static,
{
Expand All @@ -62,6 +76,7 @@ impl ContextModal for WindowContext<'_> {

root.active_drawer = Some(ActiveDrawer {
focus_handle,
placement,
builder: Rc::new(build),
});
cx.notify();
Expand Down Expand Up @@ -156,6 +171,13 @@ impl<V> ContextModal for ViewContext<'_, V> {
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 {
self.deref().has_active_modal()
}
Expand Down Expand Up @@ -208,12 +230,14 @@ pub struct Root {
active_drawer: Option<ActiveDrawer>,
active_modals: Vec<ActiveModal>,
pub notification: View<NotificationList>,
drawer_size: Option<DefiniteLength>,
view: AnyView,
}

#[derive(Clone)]
struct ActiveDrawer {
focus_handle: FocusHandle,
placement: Placement,
builder: Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>,
}

Expand All @@ -230,6 +254,7 @@ impl Root {
active_drawer: None,
active_modals: Vec::new(),
notification: cx.new_view(NotificationList::new),
drawer_size: None,
view,
}
}
Expand Down Expand Up @@ -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.
pub fn render_notification_layer(cx: &mut WindowContext) -> Option<impl IntoElement> {
let root = cx
Expand All @@ -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.
Expand All @@ -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
Expand Down

0 comments on commit ea0954b

Please sign in to comment.