From 51df8f2f04edcce2427736f77ac848fd1ba5d502 Mon Sep 17 00:00:00 2001 From: xda <150917089+xda2023@users.noreply.github.com> Date: Tue, 15 Oct 2024 22:47:45 +0900 Subject: [PATCH] drawer: support ESC to close it by on_action and call on_close (#341) Fix #263 ![2024-10-14 21 26 02](https://github.com/user-attachments/assets/b6060f82-117a-4935-91df-d336f905624d) --- crates/ui/src/drawer.rs | 30 ++++++++++++++++++++++-------- crates/ui/src/lib.rs | 1 + crates/ui/src/root.rs | 26 +++++++++++++++++++++----- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/crates/ui/src/drawer.rs b/crates/ui/src/drawer.rs index 13b86d66..04af6257 100644 --- a/crates/ui/src/drawer.rs +++ b/crates/ui/src/drawer.rs @@ -1,10 +1,10 @@ use std::{rc::Rc, time::Duration}; use gpui::{ - anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, - AnyElement, ClickEvent, DefiniteLength, DismissEvent, Div, EventEmitter, FocusHandle, - InteractiveElement as _, IntoElement, MouseButton, ParentElement, Pixels, RenderOnce, Styled, - WindowContext, + actions, anchored, div, point, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, + AnyElement, AppContext, ClickEvent, DefiniteLength, DismissEvent, Div, EventEmitter, + FocusHandle, InteractiveElement as _, IntoElement, KeyBinding, MouseButton, ParentElement, + Pixels, RenderOnce, Styled, WindowContext, }; use crate::{ @@ -17,9 +17,16 @@ use crate::{ v_flex, IconName, Placement, Sizable, StyledExt as _, }; +actions!(drawer, [Escape]); + +const CONTEXT: &str = "Drawer"; +pub fn init(cx: &mut AppContext) { + cx.bind_keys([KeyBinding::new("escape", Escape, Some(CONTEXT))]) +} + #[derive(IntoElement)] pub struct Drawer { - focus_handle: FocusHandle, + pub(crate) focus_handle: FocusHandle, placement: Placement, size: DefiniteLength, resizable: bool, @@ -120,7 +127,6 @@ impl Styled for Drawer { impl RenderOnce for Drawer { fn render(self, cx: &mut WindowContext) -> impl IntoElement { - let focus_handle = self.focus_handle.clone(); let placement = self.placement; let titlebar_height = self.margin_top; let size = cx.viewport_size(); @@ -146,8 +152,16 @@ impl RenderOnce for Drawer { }) .child( v_flex() - .id("") - .track_focus(&focus_handle) + .id("drawer") + .key_context(CONTEXT) + .track_focus(&self.focus_handle) + .on_action({ + let on_close = self.on_close.clone(); + move |_: &Escape, cx| { + on_close(&ClickEvent::default(), cx); + cx.close_drawer(); + } + }) .absolute() .occlude() .bg(cx.theme().background) diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index dae38e92..96e6764a 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -62,6 +62,7 @@ pub fn init(cx: &mut gpui::AppContext) { context_menu::init(cx); date_picker::init(cx); dock::init(cx); + drawer::init(cx); dropdown::init(cx); input::init(cx); list::init(cx); diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 680fdaa9..ded38b75 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -57,7 +57,14 @@ impl<'a> ContextModal for WindowContext<'a> { if root.active_drawer.is_none() { root.previous_focus_handle = cx.focused(); } - root.active_drawer = Some(Rc::new(build)); + + let focus_handle = cx.focus_handle(); + focus_handle.focus(cx); + + root.active_drawer = Some(ActiveDrawer { + focus_handle, + builder: Rc::new(build), + }); cx.notify(); }) } @@ -199,12 +206,18 @@ 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, - active_drawer: Option Drawer + 'static>>, + active_drawer: Option, active_modals: Vec, pub notification: View, child: AnyView, } +#[derive(Clone)] +struct ActiveDrawer { + focus_handle: FocusHandle, + builder: Rc Drawer + 'static>, +} + #[derive(Clone)] struct ActiveModal { focus_handle: FocusHandle, @@ -270,9 +283,12 @@ impl Root { .and_then(|w| w.root_view(cx).ok()) .expect("The window root view should be of type `ui::Root`."); - if let Some(builder) = root.read(cx).active_drawer.clone() { - let drawer = Drawer::new(cx); - return Some(builder(drawer, cx)); + if let Some(active_drawer) = root.read(cx).active_drawer.clone() { + 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)); } None