Update Root API (#150)

This commit is contained in:
Jason Lee 2024-08-15 17:27:34 +08:00 committed by GitHub
parent c9763224c2
commit 981d1391e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 43 deletions

1
assets/icons/bell.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bell"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>

After

Width:  |  Height:  |  Size: 310 B

View file

@ -10,13 +10,14 @@ use workspace::{TitleBar, Workspace};
use std::sync::Arc;
use ui::{
button::{Button, ButtonStyle},
button::Button,
drawer::Drawer,
h_flex,
modal::Modal,
popover::Popover,
popup_menu::PopupMenu,
theme::{ActiveTheme, Theme},
ContextModal as _, IconName, Root, Sizable,
ContextModal, IconName, Root, Sizable,
};
use crate::app_state::AppState;
@ -273,10 +274,11 @@ pub fn open_new(
impl Render for StoryWorkspace {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let active_modal = cx.active_modal();
let active_drawer = cx.active_drawer();
let active_modal = Root::read(cx).active_modal.clone();
let active_drawer = Root::read(cx).active_drawer.clone();
let has_active_modal = active_modal.is_some();
let notification_view = cx.notification_view();
let notification_view = Root::read(cx).notification.clone();
let notifications_count = cx.notifications().len();
div()
.relative()
@ -303,7 +305,6 @@ impl Render for StoryWorkspace {
.items_center()
.justify_end()
.px_2()
.mr_3()
.gap_2()
.child(self.locale_selector.clone())
.child(
@ -330,10 +331,38 @@ impl Render for StoryWorkspace {
Button::new("github", cx)
.icon(IconName::GitHub)
.small()
.style(ButtonStyle::Ghost)
.ghost()
.on_click(|_, cx| {
cx.open_url("https://github.com/huacnlee/gpui-component")
}),
)
.child(
div()
.relative()
.child(
Button::new("bell", cx)
.small()
.ghost()
.compact()
.icon(IconName::Bell),
)
.when(notifications_count > 0, |this| {
this.child(
h_flex()
.absolute()
.rounded_full()
.top(px(-2.))
.right(px(-2.))
.p(px(1.))
.min_w(px(12.))
.bg(ui::red_500())
.text_color(ui::white())
.justify_center()
.text_size(px(10.))
.line_height(relative(1.))
.child(format!("{}", notifications_count.min(99))),
)
}),
),
),
)

View file

@ -11,6 +11,7 @@ pub enum IconName {
ArrowRight,
ArrowUp,
Asterisk,
Bell,
Calendar,
Check,
ChevronDown,
@ -18,6 +19,7 @@ pub enum IconName {
ChevronRight,
ChevronUp,
ChevronsUpDown,
CircleCheck,
CircleX,
Close,
Copy,
@ -50,7 +52,6 @@ pub enum IconName {
ThumbsDown,
ThumbsUp,
TriangleAlert,
CircleCheck,
}
impl IconName {
@ -61,6 +62,7 @@ impl IconName {
IconName::ArrowRight => "icons/arrow-right.svg",
IconName::ArrowUp => "icons/arrow-up.svg",
IconName::Asterisk => "icons/asterisk.svg",
IconName::Bell => "icons/bell.svg",
IconName::Calendar => "icons/calendar.svg",
IconName::Check => "icons/check.svg",
IconName::ChevronDown => "icons/chevron-down.svg",
@ -68,6 +70,7 @@ impl IconName {
IconName::ChevronRight => "icons/chevron-right.svg",
IconName::ChevronUp => "icons/chevron-up.svg",
IconName::ChevronsUpDown => "icons/chevrons-up-down.svg",
IconName::CircleCheck => "icons/circle-check.svg",
IconName::CircleX => "icons/circle-x.svg",
IconName::Close => "icons/close.svg",
IconName::Copy => "icons/copy.svg",
@ -100,7 +103,6 @@ impl IconName {
IconName::ThumbsDown => "icons/thumbs-down.svg",
IconName::ThumbsUp => "icons/thumbs-up.svg",
IconName::TriangleAlert => "icons/triangle-alert.svg",
IconName::CircleCheck => "icons/circle-check.svg",
}
.into()
}

View file

@ -247,7 +247,7 @@ impl Render for Notification {
/// A list of notifications.
pub struct NotificationList {
notifications: Vec<View<Notification>>,
pub(crate) notifications: Vec<View<Notification>>,
}
impl NotificationList {

View file

@ -21,8 +21,8 @@ pub trait ContextModal: Sized {
where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static;
/// Return the active Drawer builder, you must add the Drawer to the view.
fn active_drawer(&self) -> Option<Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>>;
/// Return true, if there is an active Drawer.
fn has_active_drawer(&self) -> bool;
/// Closes the active Drawer.
fn close_drawer(&mut self);
@ -32,8 +32,8 @@ pub trait ContextModal: Sized {
where
F: Fn(Modal, &mut WindowContext) -> Modal + 'static;
/// Return the active Modal builder, you must add the Modal to the view.
fn active_modal(&self) -> Option<Rc<dyn Fn(Modal, &mut WindowContext) -> Modal + 'static>>;
/// Return true, if there is an active Modal.
fn has_active_modal(&self) -> bool;
/// Closes the active Modal.
fn close_modal(&mut self);
@ -41,8 +41,8 @@ pub trait ContextModal: Sized {
/// Pushes a notification to the notification list.
fn push_notification(&mut self, note: impl Into<Notification>);
fn clear_notifications(&mut self);
/// Returns the notification list view.
fn notification_view(&self) -> AnyView;
/// Returns number of notifications.
fn notifications(&self) -> Rc<Vec<View<Notification>>>;
}
impl<'a> ContextModal for WindowContext<'a> {
@ -50,19 +50,19 @@ impl<'a> ContextModal for WindowContext<'a> {
where
F: Fn(Drawer, &mut WindowContext) -> Drawer + 'static,
{
Root::update_root(self, move |root, cx| {
Root::update(self, move |root, cx| {
root.previous_focus_handle = cx.focused();
root.active_drawer = Some(Rc::new(build));
cx.notify();
})
}
fn active_drawer(&self) -> Option<Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>> {
Root::read_root(&self).active_drawer.clone()
fn has_active_drawer(&self) -> bool {
Root::read(&self).active_drawer.is_some()
}
fn close_drawer(&mut self) {
Root::update_root(self, |root, cx| {
Root::update(self, |root, cx| {
root.active_drawer = None;
root.focus_back(cx);
cx.notify();
@ -73,19 +73,19 @@ impl<'a> ContextModal for WindowContext<'a> {
where
F: Fn(Modal, &mut WindowContext) -> Modal + 'static,
{
Root::update_root(self, move |root, cx| {
Root::update(self, move |root, cx| {
root.previous_focus_handle = cx.focused();
root.active_modal = Some(Rc::new(build));
cx.notify();
})
}
fn active_modal(&self) -> Option<Rc<dyn Fn(Modal, &mut WindowContext) -> Modal + 'static>> {
Root::read_root(&self).active_modal.clone()
fn has_active_modal(&self) -> bool {
Root::read(&self).active_modal.is_some()
}
fn close_modal(&mut self) {
Root::update_root(self, |root, cx| {
Root::update(self, |root, cx| {
root.active_modal = None;
root.focus_back(cx);
cx.notify();
@ -94,22 +94,27 @@ impl<'a> ContextModal for WindowContext<'a> {
fn push_notification(&mut self, note: impl Into<Notification>) {
let note = note.into();
Root::update_root(self, move |root, cx| {
root.notification_list
.update(cx, |view, cx| view.push(note, cx));
Root::update(self, move |root, cx| {
root.notification.update(cx, |view, cx| view.push(note, cx));
cx.notify();
})
}
fn clear_notifications(&mut self) {
Root::update_root(self, move |root, cx| {
root.notification_list.update(cx, |view, cx| view.clear(cx));
Root::update(self, move |root, cx| {
root.notification.update(cx, |view, cx| view.clear(cx));
cx.notify();
})
}
fn notification_view(&self) -> AnyView {
Root::read_root(&self).notification_list.clone().into()
fn notifications(&self) -> Rc<Vec<View<Notification>>> {
Rc::new(
Root::read(&self)
.notification
.read(&self)
.notifications
.clone(),
)
}
}
impl<'a, V> ContextModal for ViewContext<'a, V> {
@ -120,8 +125,8 @@ impl<'a, V> ContextModal for ViewContext<'a, V> {
self.deref_mut().open_drawer(build)
}
fn active_modal(&self) -> Option<Rc<dyn Fn(Modal, &mut WindowContext) -> Modal + 'static>> {
self.deref().active_modal()
fn has_active_modal(&self) -> bool {
self.deref().has_active_modal()
}
fn close_drawer(&mut self) {
@ -135,8 +140,8 @@ impl<'a, V> ContextModal for ViewContext<'a, V> {
self.deref_mut().open_modal(build)
}
fn active_drawer(&self) -> Option<Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>> {
self.deref().active_drawer()
fn has_active_drawer(&self) -> bool {
self.deref().has_active_drawer()
}
fn close_modal(&mut self) {
@ -151,8 +156,8 @@ impl<'a, V> ContextModal for ViewContext<'a, V> {
self.deref_mut().clear_notifications()
}
fn notification_view(&self) -> AnyView {
self.deref().notification_view()
fn notifications(&self) -> Rc<Vec<View<Notification>>> {
self.deref().notifications()
}
}
@ -163,9 +168,9 @@ pub struct Root {
/// Used to store the focus handle of the previus revious view.
/// When the Modal, Drawer closes, we will focus back to the previous view.
previous_focus_handle: Option<FocusHandle>,
active_drawer: Option<Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>>,
active_modal: Option<Rc<dyn Fn(Modal, &mut WindowContext) -> Modal + 'static>>,
notification_list: View<NotificationList>,
pub active_drawer: Option<Rc<dyn Fn(Drawer, &mut WindowContext) -> Drawer + 'static>>,
pub active_modal: Option<Rc<dyn Fn(Modal, &mut WindowContext) -> Modal + 'static>>,
pub notification: View<NotificationList>,
child: AnyView,
}
@ -175,12 +180,12 @@ impl Root {
previous_focus_handle: None,
active_drawer: None,
active_modal: None,
notification_list: cx.new_view(NotificationList::new),
notification: cx.new_view(NotificationList::new),
child,
}
}
fn update_root<F>(cx: &mut WindowContext, f: F)
pub fn update<F>(cx: &mut WindowContext, f: F)
where
F: FnOnce(&mut Self, &mut ViewContext<Self>) + 'static,
{
@ -193,7 +198,7 @@ impl Root {
root.update(cx, |root, cx| f(root, cx))
}
fn read_root<'a>(cx: &'a WindowContext) -> &'a Self {
pub fn read<'a>(cx: &'a WindowContext) -> &'a Self {
let root = cx
.window_handle()
.downcast::<Root>()