From e318a9d8dc71257e4cda4ce4c7f9244f41cfdd31 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 3 Sep 2024 19:51:35 +0800 Subject: [PATCH] doc: Fix TabPanel popup menu can't dispatch action to TabContent. (#208) Co-authored-by: Floyd Wang --- crates/story/src/lib.rs | 28 +++++++++++++++++++--------- crates/ui/src/dock/mod.rs | 14 -------------- crates/ui/src/dock/stack_panel.rs | 24 +++--------------------- crates/ui/src/dock/tab_panel.rs | 18 +----------------- crates/ui/src/popup_menu.rs | 28 ++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 61 deletions(-) diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index d1e7e20d..4712c7e6 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -48,8 +48,9 @@ use ui::{ dock::{Panel, PanelEvent, TabPanel}, h_flex, label::Label, + notification::Notification, popup_menu::PopupMenu, - v_flex, Placement, + v_flex, ContextModal, Placement, }; pub fn init(cx: &mut AppContext) { @@ -87,12 +88,6 @@ pub struct StoryContainer { closeable: bool, } -impl FocusableView for StoryContainer { - fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { - self.focus_handle.clone() - } -} - #[derive(Debug)] pub enum ContainerEvent { Close, @@ -162,6 +157,13 @@ impl StoryContainer { self.story = Some(story); self } + + fn on_action_panel_info(&mut self, _: &PanelInfo, cx: &mut ViewContext) { + struct Info; + let note = Notification::new(format!("You have clicked panel info on: {}", self.name)) + .id::(); + cx.push_notification(note); + } } impl Panel for StoryContainer { @@ -174,17 +176,25 @@ impl Panel for StoryContainer { } fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu { - menu.menu("Panel Info", Box::new(PanelInfo)) + menu.track_focus(&self.focus_handle) + .menu("Info", Box::new(PanelInfo)) } } impl EventEmitter for StoryContainer {} +impl FocusableView for StoryContainer { + fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { + self.focus_handle.clone() + } +} impl Render for StoryContainer { - fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_flex() .id("story-container") .size_full() .overflow_scroll() + .track_focus(&self.focus_handle) + .on_action(cx.listener(Self::on_action_panel_info)) .child( div() .flex() diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index 72afb1ff..af354f57 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -51,20 +51,6 @@ impl DockArea { pub fn root(&self) -> View { self.root.clone() } - - /// Return the index of the panel. - pub fn index_of_panel( - &self, - panel: View

, - cx: &mut ViewContext, - ) -> Option { - self.root.read(cx).index_of_panel(panel) - } - - /// Return the existing panel by type. - pub fn panel(&self, cx: &mut ViewContext) -> Option> { - self.root.read(cx).panel::

(cx) - } } impl Render for DockArea { diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index e0088dd4..4b3e3643 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -9,9 +9,9 @@ use crate::{ use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel}; use gpui::{ - prelude::FluentBuilder as _, Axis, DismissEvent, Entity, EventEmitter, FocusHandle, + prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, Entity, EventEmitter, FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext, - VisualContext, WeakView, WindowContext, + VisualContext, WeakView, }; use smallvec::SmallVec; @@ -62,24 +62,6 @@ impl StackPanel { .position(|p| p.view().entity_id() == entity_id) } - /// Return the existing panel view by type. - pub fn panel

(&self, cx: &WindowContext) -> Option> - where - P: Panel, - { - self.panels.iter().find_map(|p| { - if let Ok(p) = p.view().downcast::

() { - Some(p) - } else { - if let Ok(tab) = p.view().downcast::() { - tab.read(cx).panel(cx) - } else { - None - } - } - }) - } - /// Add a panel at the end of the stack. pub fn add_panel

( &mut self, @@ -287,7 +269,7 @@ impl StackPanel { } impl FocusableView for StackPanel { - fn focus_handle(&self, _cx: &gpui::AppContext) -> FocusHandle { + fn focus_handle(&self, _cx: &AppContext) -> FocusHandle { self.focus_handle.clone() } } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index b5cc7348..22c3f330 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -103,21 +103,6 @@ impl TabPanel { cx.notify(); } - /// Return the existing panel view by type. - pub fn panel(&self, cx: &WindowContext) -> Option> { - self.panels.iter().find_map(|p| { - if let Ok(p) = p.view().downcast::

() { - Some(p) - } else { - if let Ok(stack) = p.view().downcast::() { - stack.read(cx).panel::

(cx) - } else { - None - } - } - }) - } - /// Add a panel to the end of the tabs pub fn add_panel(&mut self, panel: Arc, cx: &mut ViewContext) { if self @@ -553,8 +538,7 @@ impl Panel for TabPanel { } } impl FocusableView for TabPanel { - fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle { - // FIXME: Delegate to the active panel + fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { self.focus_handle.clone() } } diff --git a/crates/ui/src/popup_menu.rs b/crates/ui/src/popup_menu.rs index 3fd1dfc8..37b9f601 100644 --- a/crates/ui/src/popup_menu.rs +++ b/crates/ui/src/popup_menu.rs @@ -75,6 +75,8 @@ pub struct PopupMenu { max_width: Pixels, hovered_menu_ix: Option, bounds: Bounds, + + action_focus_handle: Option, _subscriptions: [gpui::Subscription; 1], } @@ -91,6 +93,7 @@ impl PopupMenu { let menu = Self { focus_handle, + action_focus_handle: None, parent_menu: None, menu_items: Vec::new(), selected_index: None, @@ -106,6 +109,12 @@ impl PopupMenu { }) } + /// Bind the focus handle of the menu, when clicked, it will focus back to this handle and then dispath the action + pub fn track_focus(mut self, focus_handle: &FocusHandle) -> Self { + self.action_focus_handle = Some(focus_handle.clone()); + self + } + /// Set min width of the popup menu, default is 120px pub fn min_w(mut self, width: impl Into) -> Self { self.min_width = width.into(); @@ -190,12 +199,31 @@ impl PopupMenu { self.has_icon = true; } + let action_focus_handle = self.action_focus_handle.clone(); + self.menu_items.push(PopupMenuItem::Item { icon, label: label.into(), action: Some(action.boxed_clone()), handler: Rc::new(move |cx| { cx.activate_window(); + + // Focus back to the user expected focus handle + // Then the actions listened on that focus handle can be received + // + // For example: + // + // TabPanel + // |- PopupMenu + // |- PanelContent (actions are listened here) + // + // The `PopupMenu` and `PanelContent` are at the same level in the TabPanel + // If the actions are listened on the `PanelContent`, + // it can't receive the actions from the `PopupMenu`, unless we focus on `PanelContent`. + if let Some(handle) = action_focus_handle.as_ref() { + cx.focus(&handle); + } + cx.dispatch_action(action.boxed_clone()); }), });