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 std::sync::Arc;
use ui::{ use ui::{
button::{Button, ButtonStyle}, button::Button,
drawer::Drawer, drawer::Drawer,
h_flex,
modal::Modal, modal::Modal,
popover::Popover, popover::Popover,
popup_menu::PopupMenu, popup_menu::PopupMenu,
theme::{ActiveTheme, Theme}, theme::{ActiveTheme, Theme},
ContextModal as _, IconName, Root, Sizable, ContextModal, IconName, Root, Sizable,
}; };
use crate::app_state::AppState; use crate::app_state::AppState;
@ -273,10 +274,11 @@ pub fn open_new(
impl Render for StoryWorkspace { impl Render for StoryWorkspace {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let active_modal = cx.active_modal(); let active_modal = Root::read(cx).active_modal.clone();
let active_drawer = cx.active_drawer(); let active_drawer = Root::read(cx).active_drawer.clone();
let has_active_modal = active_modal.is_some(); 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() div()
.relative() .relative()
@ -303,7 +305,6 @@ impl Render for StoryWorkspace {
.items_center() .items_center()
.justify_end() .justify_end()
.px_2() .px_2()
.mr_3()
.gap_2() .gap_2()
.child(self.locale_selector.clone()) .child(self.locale_selector.clone())
.child( .child(
@ -330,10 +331,38 @@ impl Render for StoryWorkspace {
Button::new("github", cx) Button::new("github", cx)
.icon(IconName::GitHub) .icon(IconName::GitHub)
.small() .small()
.style(ButtonStyle::Ghost) .ghost()
.on_click(|_, cx| { .on_click(|_, cx| {
cx.open_url("https://github.com/huacnlee/gpui-component") 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, ArrowRight,
ArrowUp, ArrowUp,
Asterisk, Asterisk,
Bell,
Calendar, Calendar,
Check, Check,
ChevronDown, ChevronDown,
@ -18,6 +19,7 @@ pub enum IconName {
ChevronRight, ChevronRight,
ChevronUp, ChevronUp,
ChevronsUpDown, ChevronsUpDown,
CircleCheck,
CircleX, CircleX,
Close, Close,
Copy, Copy,
@ -50,7 +52,6 @@ pub enum IconName {
ThumbsDown, ThumbsDown,
ThumbsUp, ThumbsUp,
TriangleAlert, TriangleAlert,
CircleCheck,
} }
impl IconName { impl IconName {
@ -61,6 +62,7 @@ impl IconName {
IconName::ArrowRight => "icons/arrow-right.svg", IconName::ArrowRight => "icons/arrow-right.svg",
IconName::ArrowUp => "icons/arrow-up.svg", IconName::ArrowUp => "icons/arrow-up.svg",
IconName::Asterisk => "icons/asterisk.svg", IconName::Asterisk => "icons/asterisk.svg",
IconName::Bell => "icons/bell.svg",
IconName::Calendar => "icons/calendar.svg", IconName::Calendar => "icons/calendar.svg",
IconName::Check => "icons/check.svg", IconName::Check => "icons/check.svg",
IconName::ChevronDown => "icons/chevron-down.svg", IconName::ChevronDown => "icons/chevron-down.svg",
@ -68,6 +70,7 @@ impl IconName {
IconName::ChevronRight => "icons/chevron-right.svg", IconName::ChevronRight => "icons/chevron-right.svg",
IconName::ChevronUp => "icons/chevron-up.svg", IconName::ChevronUp => "icons/chevron-up.svg",
IconName::ChevronsUpDown => "icons/chevrons-up-down.svg", IconName::ChevronsUpDown => "icons/chevrons-up-down.svg",
IconName::CircleCheck => "icons/circle-check.svg",
IconName::CircleX => "icons/circle-x.svg", IconName::CircleX => "icons/circle-x.svg",
IconName::Close => "icons/close.svg", IconName::Close => "icons/close.svg",
IconName::Copy => "icons/copy.svg", IconName::Copy => "icons/copy.svg",
@ -100,7 +103,6 @@ impl IconName {
IconName::ThumbsDown => "icons/thumbs-down.svg", IconName::ThumbsDown => "icons/thumbs-down.svg",
IconName::ThumbsUp => "icons/thumbs-up.svg", IconName::ThumbsUp => "icons/thumbs-up.svg",
IconName::TriangleAlert => "icons/triangle-alert.svg", IconName::TriangleAlert => "icons/triangle-alert.svg",
IconName::CircleCheck => "icons/circle-check.svg",
} }
.into() .into()
} }

View file

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

View file

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