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},
|
dock::{Panel, PanelEvent, TabPanel},
|
||||||
h_flex,
|
h_flex,
|
||||||
label::Label,
|
label::Label,
|
||||||
|
notification::Notification,
|
||||||
popup_menu::PopupMenu,
|
popup_menu::PopupMenu,
|
||||||
v_flex, Placement,
|
v_flex, ContextModal, Placement,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
|
@ -87,12 +88,6 @@ pub struct StoryContainer {
|
||||||
closeable: bool,
|
closeable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FocusableView for StoryContainer {
|
|
||||||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
|
||||||
self.focus_handle.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ContainerEvent {
|
pub enum ContainerEvent {
|
||||||
Close,
|
Close,
|
||||||
|
|
@ -162,6 +157,13 @@ impl StoryContainer {
|
||||||
self.story = Some(story);
|
self.story = Some(story);
|
||||||
self
|
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 {
|
impl Panel for StoryContainer {
|
||||||
|
|
@ -174,17 +176,25 @@ impl Panel for StoryContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu {
|
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 EventEmitter<PanelEvent> for StoryContainer {}
|
||||||
|
impl FocusableView for StoryContainer {
|
||||||
|
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Render for StoryContainer {
|
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()
|
v_flex()
|
||||||
.id("story-container")
|
.id("story-container")
|
||||||
.size_full()
|
.size_full()
|
||||||
.overflow_scroll()
|
.overflow_scroll()
|
||||||
|
.track_focus(&self.focus_handle)
|
||||||
|
.on_action(cx.listener(Self::on_action_panel_info))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
|
|
|
||||||
|
|
@ -51,20 +51,6 @@ impl DockArea {
|
||||||
pub fn root(&self) -> View<StackPanel> {
|
pub fn root(&self) -> View<StackPanel> {
|
||||||
self.root.clone()
|
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 {
|
impl Render for DockArea {
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ use crate::{
|
||||||
|
|
||||||
use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel};
|
use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel};
|
||||||
use gpui::{
|
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,
|
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext,
|
||||||
VisualContext, WeakView, WindowContext,
|
VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
@ -62,24 +62,6 @@ impl StackPanel {
|
||||||
.position(|p| p.view().entity_id() == entity_id)
|
.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.
|
/// Add a panel at the end of the stack.
|
||||||
pub fn add_panel<P>(
|
pub fn add_panel<P>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -287,7 +269,7 @@ impl StackPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FocusableView for StackPanel {
|
impl FocusableView for StackPanel {
|
||||||
fn focus_handle(&self, _cx: &gpui::AppContext) -> FocusHandle {
|
fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,21 +103,6 @@ impl TabPanel {
|
||||||
cx.notify();
|
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
|
/// Add a panel to the end of the tabs
|
||||||
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
pub fn add_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
||||||
if self
|
if self
|
||||||
|
|
@ -553,8 +538,7 @@ impl Panel for TabPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl FocusableView for TabPanel {
|
impl FocusableView for TabPanel {
|
||||||
fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle {
|
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
||||||
// FIXME: Delegate to the active panel
|
|
||||||
self.focus_handle.clone()
|
self.focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@ pub struct PopupMenu {
|
||||||
max_width: Pixels,
|
max_width: Pixels,
|
||||||
hovered_menu_ix: Option<usize>,
|
hovered_menu_ix: Option<usize>,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
|
|
||||||
|
action_focus_handle: Option<FocusHandle>,
|
||||||
_subscriptions: [gpui::Subscription; 1],
|
_subscriptions: [gpui::Subscription; 1],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +93,7 @@ impl PopupMenu {
|
||||||
|
|
||||||
let menu = Self {
|
let menu = Self {
|
||||||
focus_handle,
|
focus_handle,
|
||||||
|
action_focus_handle: None,
|
||||||
parent_menu: None,
|
parent_menu: None,
|
||||||
menu_items: Vec::new(),
|
menu_items: Vec::new(),
|
||||||
selected_index: None,
|
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
|
/// Set min width of the popup menu, default is 120px
|
||||||
pub fn min_w(mut self, width: impl Into<Pixels>) -> Self {
|
pub fn min_w(mut self, width: impl Into<Pixels>) -> Self {
|
||||||
self.min_width = width.into();
|
self.min_width = width.into();
|
||||||
|
|
@ -190,12 +199,31 @@ impl PopupMenu {
|
||||||
self.has_icon = true;
|
self.has_icon = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let action_focus_handle = self.action_focus_handle.clone();
|
||||||
|
|
||||||
self.menu_items.push(PopupMenuItem::Item {
|
self.menu_items.push(PopupMenuItem::Item {
|
||||||
icon,
|
icon,
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
action: Some(action.boxed_clone()),
|
action: Some(action.boxed_clone()),
|
||||||
handler: Rc::new(move |cx| {
|
handler: Rc::new(move |cx| {
|
||||||
cx.activate_window();
|
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());
|
cx.dispatch_action(action.boxed_clone());
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue