doc: Fix TabPanel popup menu can't dispatch action to TabContent. (#208)

Co-authored-by: Floyd Wang <gassnake999@gmail.com>
This commit is contained in:
Jason Lee 2024-09-03 19:51:35 +08:00 committed by GitHub
parent 5e86c9cfa0
commit e318a9d8dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 61 deletions

View file

@ -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<Self>) {
struct Info;
let note = Notification::new(format!("You have clicked panel info on: {}", self.name))
.id::<Info>();
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<PanelEvent> 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<Self>) -> impl IntoElement {
fn render(&mut self, cx: &mut ViewContext<Self>) -> 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()

View file

@ -51,20 +51,6 @@ impl DockArea {
pub fn root(&self) -> View<StackPanel> {
self.root.clone()
}
/// Return the index of the panel.
pub fn index_of_panel<P: Panel>(
&self,
panel: View<P>,
cx: &mut ViewContext<Self>,
) -> Option<usize> {
self.root.read(cx).index_of_panel(panel)
}
/// Return the existing panel by type.
pub fn panel<P: Panel>(&self, cx: &mut ViewContext<Self>) -> Option<View<P>> {
self.root.read(cx).panel::<P>(cx)
}
}
impl Render for DockArea {

View file

@ -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<P>(&self, cx: &WindowContext) -> Option<View<P>>
where
P: Panel,
{
self.panels.iter().find_map(|p| {
if let Ok(p) = p.view().downcast::<P>() {
Some(p)
} else {
if let Ok(tab) = p.view().downcast::<TabPanel>() {
tab.read(cx).panel(cx)
} else {
None
}
}
})
}
/// Add a panel at the end of the stack.
pub fn add_panel<P>(
&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()
}
}

View file

@ -103,21 +103,6 @@ impl TabPanel {
cx.notify();
}
/// Return the existing panel view by type.
pub fn panel<P: Panel>(&self, cx: &WindowContext) -> Option<View<P>> {
self.panels.iter().find_map(|p| {
if let Ok(p) = p.view().downcast::<P>() {
Some(p)
} else {
if let Ok(stack) = p.view().downcast::<StackPanel>() {
stack.read(cx).panel::<P>(cx)
} else {
None
}
}
})
}
/// Add a panel to the end of the tabs
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
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()
}
}

View file

@ -75,6 +75,8 @@ pub struct PopupMenu {
max_width: Pixels,
hovered_menu_ix: Option<usize>,
bounds: Bounds<Pixels>,
action_focus_handle: Option<FocusHandle>,
_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<Pixels>) -> 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());
}),
});