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:
parent
5e86c9cfa0
commit
e318a9d8dc
5 changed files with 51 additions and 61 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue