From ffa36c1c5bd734137e61b544731224e03a496aac Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 21 Mar 2025 17:34:32 +0800 Subject: [PATCH] input: Add focused_input, has_focused_input method to Root. #727 Add this to save current focused Input, this used to avoid some special keybinding conflict. For example: `/` as ToggleSearch, if on this action, we can check is there `has_focused_input` to decide to ignore or handle action. - dock: Rename toggle button visible method. --- crates/story/src/lib.rs | 25 +++++++++++++++++++++++-- crates/story/src/main.rs | 4 ++-- crates/story/src/modal_story.rs | 4 ++-- crates/ui/src/dock/mod.rs | 4 ++-- crates/ui/src/input/input.rs | 24 ++++++++++++++---------- crates/ui/src/root.rs | 16 ++++++++++++++++ 6 files changed, 59 insertions(+), 18 deletions(-) diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 9fd18921..dc676517 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -36,7 +36,7 @@ pub use toggle_story::ToggleStory; use gpui::{ actions, div, impl_internal_actions, prelude::FluentBuilder as _, px, size, AnyElement, AnyView, App, AppContext, Bounds, Context, Div, Entity, EventEmitter, Focusable, Global, Hsla, - InteractiveElement, IntoElement, ParentElement, Render, SharedString, + InteractiveElement, IntoElement, KeyBinding, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled as _, Window, WindowBounds, WindowKind, WindowOptions, }; pub use icon_story::IconStory; @@ -86,7 +86,7 @@ impl_internal_actions!( [SelectLocale, SelectFont, SelectRadius, SelectScrollbarShow] ); -actions!(story, [Quit, Open, CloseWindow]); +actions!(story, [Quit, Open, CloseWindow, ToggleSearch]); const PANEL_NAME: &str = "StoryContainer"; @@ -144,6 +144,7 @@ where .open_window(options, |window, cx| { let view = crate_view_fn(window, cx); let root = cx.new(|cx| StoryRoot::new(title.clone(), view, window, cx)); + cx.new(|cx| Root::new(root.into(), window, cx)) }) .expect("failed to open window"); @@ -214,6 +215,8 @@ pub fn init(cx: &mut App) { ); cx.set_http_client(http_client); + cx.bind_keys([KeyBinding::new("/", ToggleSearch, None)]); + register_panel(cx, PANEL_NAME, |_, _, info, window, cx| { let story_state = match info { PanelInfo::Panel(value) => StoryState::from_value(value.clone()), @@ -372,6 +375,23 @@ impl StoryContainer { .id::(); window.push_notification(note, cx); } + + fn on_action_toggle_search( + &mut self, + _: &ToggleSearch, + window: &mut Window, + cx: &mut Context, + ) { + cx.propagate(); + if window.has_focused_input(cx) { + return; + } + + struct Search; + let note = + Notification::new(format!("You have toggled search on: {}", self.name)).id::(); + window.push_notification(note, cx); + } } #[derive(Debug, Serialize, Deserialize)] @@ -527,6 +547,7 @@ impl Render for StoryContainer { .overflow_y_scroll() .track_focus(&self.focus_handle) .on_action(cx.listener(Self::on_action_panel_info)) + .on_action(cx.listener(Self::on_action_toggle_search)) .when(self.description.len() > 0, |this| { this.child( div() diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index fed88806..f2dc41ee 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -489,8 +489,8 @@ impl StoryWorkspace { ) { self.toggle_button_visible = !self.toggle_button_visible; - self.dock_area.update(cx, |dock_area, _| { - dock_area.show_toggle_button(self.toggle_button_visible); + self.dock_area.update(cx, |dock_area, cx| { + dock_area.set_toggle_button_visible(self.toggle_button_visible, cx); }); } } diff --git a/crates/story/src/modal_story.rs b/crates/story/src/modal_story.rs index 9b8bdcd7..e0706ad8 100644 --- a/crates/story/src/modal_story.rs +++ b/crates/story/src/modal_story.rs @@ -576,14 +576,14 @@ impl Render for ModalStory { })), ) .child( - Button::new("show-drawer") + Button::new("show-drawer-right") .label("Right Drawer...") .on_click(cx.listener(|this, _, window, cx| { this.open_drawer_at(Placement::Right, window, cx) })), ) .child( - Button::new("show-drawer") + Button::new("show-drawer-bottom") .label("Bottom Drawer...") .on_click(cx.listener(|this, _, window, cx| { this.open_drawer_at(Placement::Bottom, window, cx) diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index abf95e0a..0c1791e5 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -704,8 +704,8 @@ impl DockArea { } } - /// Show or hide the toggle button. - pub fn show_toggle_button(&mut self, visible: bool) { + /// Set the visibility of the toggle button. + pub fn set_toggle_button_visible(&mut self, visible: bool, _: &mut Context) { self.toggle_button_visible = visible; } diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs index 7e3a5e80..c47db38a 100644 --- a/crates/ui/src/input/input.rs +++ b/crates/ui/src/input/input.rs @@ -31,9 +31,9 @@ use crate::history::History; use crate::indicator::Indicator; use crate::input::clear_button; use crate::scroll::{Scrollbar, ScrollbarAxis, ScrollbarState}; -use crate::ActiveTheme; use crate::Size; use crate::StyledExt; +use crate::{ActiveTheme, Root}; use crate::{Sizable, StyleSized}; actions!( @@ -1371,7 +1371,12 @@ impl TextInput { self.focus_handle.is_focused(window) && self.blink_cursor.read(cx).visible() } - fn on_focus(&mut self, _: &mut Window, cx: &mut Context) { + fn on_focus(&mut self, window: &mut Window, cx: &mut Context) { + let input_entity = cx.entity(); + Root::update(window, cx, |root, _, _| { + root.focused_input = Some(input_entity) + }); + self.blink_cursor.update(cx, |cursor, cx| { cursor.start(cx); }); @@ -1379,6 +1384,10 @@ impl TextInput { } fn on_blur(&mut self, window: &mut Window, cx: &mut Context) { + Root::update(window, cx, |root, _, _| { + root.focused_input = None; + }); + self.unselect(window, cx); self.blink_cursor.update(cx, |cursor, cx| { cursor.stop(cx); @@ -1392,13 +1401,8 @@ impl TextInput { }); } - fn on_key_down_for_blink_cursor( - &mut self, - _: &KeyDownEvent, - _: &mut Window, - cx: &mut Context, - ) { - self.pause_blink_cursor(cx) + fn on_key_down(&mut self, _: &KeyDownEvent, _: &mut Window, cx: &mut Context) { + self.pause_blink_cursor(cx); } pub(super) fn on_drag_move( @@ -1692,7 +1696,7 @@ impl Render for TextInput { .on_action(cx.listener(Self::undo)) .on_action(cx.listener(Self::redo)) .on_action(cx.listener(Self::redo)) - .on_key_down(cx.listener(Self::on_key_down_for_blink_cursor)) + .on_key_down(cx.listener(Self::on_key_down)) .on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down)) .on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up)) .on_scroll_wheel(cx.listener(Self::on_scroll_wheel)) diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 691c2855..17d80421 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -1,5 +1,6 @@ use crate::{ drawer::Drawer, + input::TextInput, modal::Modal, notification::{Notification, NotificationList}, window_border, ActiveTheme, Placement, @@ -47,6 +48,11 @@ pub trait ContextModal: Sized { fn clear_notifications(&mut self, cx: &mut App); /// Returns number of notifications. fn notifications(&mut self, cx: &mut App) -> Rc>>; + + /// Return current focused Input entity. + fn focused_input(&mut self, cx: &mut App) -> Option>; + /// Returns true if there is a focused Input entity. + fn has_focused_input(&mut self, cx: &mut App) -> bool; } impl ContextModal for Window { @@ -160,6 +166,14 @@ impl ContextModal for Window { let entity = Root::read(self, cx).notification.clone(); Rc::new(entity.read(cx).notifications()) } + + fn has_focused_input(&mut self, cx: &mut App) -> bool { + Root::read(self, cx).focused_input.is_some() + } + + fn focused_input(&mut self, cx: &mut App) -> Option> { + Root::read(self, cx).focused_input.clone() + } } // impl ContextModal for Context<'_, V> { @@ -228,6 +242,7 @@ pub struct Root { previous_focus_handle: Option, active_drawer: Option, active_modals: Vec, + pub(super) focused_input: Option>, pub notification: Entity, drawer_size: Option, view: AnyView, @@ -252,6 +267,7 @@ impl Root { previous_focus_handle: None, active_drawer: None, active_modals: Vec::new(), + focused_input: None, notification: cx.new(|cx| NotificationList::new(window, cx)), drawer_size: None, view,