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.
This commit is contained in:
Jason Lee 2025-03-21 17:34:32 +08:00
parent 9d2a70be48
commit ffa36c1c5b
6 changed files with 59 additions and 18 deletions

View file

@ -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::<Info>();
window.push_notification(note, cx);
}
fn on_action_toggle_search(
&mut self,
_: &ToggleSearch,
window: &mut Window,
cx: &mut Context<Self>,
) {
cx.propagate();
if window.has_focused_input(cx) {
return;
}
struct Search;
let note =
Notification::new(format!("You have toggled search on: {}", self.name)).id::<Search>();
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()

View file

@ -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);
});
}
}

View file

@ -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)

View file

@ -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>) {
self.toggle_button_visible = visible;
}

View file

@ -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<Self>) {
fn on_focus(&mut self, window: &mut Window, cx: &mut Context<Self>) {
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<Self>) {
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>,
) {
self.pause_blink_cursor(cx)
fn on_key_down(&mut self, _: &KeyDownEvent, _: &mut Window, cx: &mut Context<Self>) {
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))

View file

@ -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<Vec<Entity<Notification>>>;
/// Return current focused Input entity.
fn focused_input(&mut self, cx: &mut App) -> Option<Entity<TextInput>>;
/// 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<Entity<TextInput>> {
Root::read(self, cx).focused_input.clone()
}
}
// impl<V> ContextModal for Context<'_, V> {
@ -228,6 +242,7 @@ pub struct Root {
previous_focus_handle: Option<FocusHandle>,
active_drawer: Option<ActiveDrawer>,
active_modals: Vec<ActiveModal>,
pub(super) focused_input: Option<Entity<TextInput>>,
pub notification: Entity<NotificationList>,
drawer_size: Option<DefiniteLength>,
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,