dock: Add popup_menu delegate to custom panel menu. (#202)

This commit is contained in:
Jason Lee 2024-09-02 14:55:37 +08:00 committed by GitHub
parent f5cae46e42
commit af09525c1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 105 additions and 34 deletions

View file

@ -76,101 +76,112 @@ impl StoryWorkspace {
})
});
StoryContainer::add_pane(
StoryContainer::add_panel(
"Buttons",
"Displays a button or a component that looks like a button.",
ButtonStory::view(cx).into(),
tab_panel.clone(),
false,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Input",
"A control that allows the user to input text.",
InputStory::view(cx).into(),
tab_panel.clone(),
false,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Text",
"Links, paragraphs, checkboxes, and more.",
TextStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Switch",
"A control that allows the user to toggle between two states.",
SwitchStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Dropdowns",
"Displays a list of options for the user to pick from—triggered by a button.",
DropdownStory::new(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Modal",
"Modal & Drawer use examples",
ModalStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Popup",
"A popup displays content on top of the main page.",
PopupStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Tooltip",
"Displays a short message when users hover over an element.",
TooltipStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"List",
"A list displays a series of items.",
ListStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Icon",
"Icon use examples",
IconStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Image",
"Render SVG image and Chart",
ImageStory::view(cx).into(),
right_tab_panel1.clone(),
true,
cx,
)
.detach();
@ -183,47 +194,52 @@ impl StoryWorkspace {
// cx,
// );
StoryContainer::add_pane(
StoryContainer::add_panel(
"Table",
"Powerful table and datagrids built.",
TableStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Progress",
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
ProgressStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Resizable",
"Accessible resizable panel groups and layouts with keyboard support.",
ResizableStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Scrollable",
"A scrollable area with scroll bar.",
ScrollableStory::view(cx).into(),
tab_panel.clone(),
true,
cx,
)
.detach();
StoryContainer::add_pane(
StoryContainer::add_panel(
"Calendar",
"A calendar component.",
CalendarStory::view(cx).into(),
right_tab_panel.clone(),
true,
cx,
)
.detach();

View file

@ -37,8 +37,8 @@ pub use tooltip_story::TooltipStory;
pub use webview_story::WebViewStory;
use gpui::{
div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter, FocusableView,
InteractiveElement, IntoElement, ParentElement, Render, SharedString,
actions, div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter,
FocusableView, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
StatefulInteractiveElement, Styled as _, Task, View, ViewContext, VisualContext, WindowContext,
};
@ -48,6 +48,7 @@ use ui::{
dock::{Panel, PanelEvent, TabPanel},
h_flex,
label::Label,
popup_menu::PopupMenu,
v_flex,
};
@ -57,6 +58,8 @@ pub fn init(cx: &mut AppContext) {
popup_story::init(cx);
}
actions!(story, [PanelInfo]);
pub fn section(title: impl IntoElement, cx: &WindowContext) -> Div {
use ui::theme::ActiveTheme;
let theme = cx.theme();
@ -81,6 +84,7 @@ pub struct StoryContainer {
width: Option<gpui::Pixels>,
height: Option<gpui::Pixels>,
story: Option<AnyView>,
closeable: bool,
}
impl FocusableView for StoryContainer {
@ -100,6 +104,7 @@ impl StoryContainer {
pub fn new(
name: impl Into<SharedString>,
description: impl Into<SharedString>,
closeable: bool,
cx: &mut WindowContext,
) -> Self {
let focus_handle = cx.focus_handle();
@ -111,14 +116,16 @@ impl StoryContainer {
width: None,
height: None,
story: None,
closeable,
}
}
pub fn add_pane(
pub fn add_panel(
name: impl Into<SharedString>,
description: impl Into<SharedString>,
story: AnyView,
tab_panel: View<TabPanel>,
closeable: bool,
cx: &mut WindowContext,
) -> Task<Result<View<Self>>> {
let name = name.into();
@ -126,7 +133,8 @@ impl StoryContainer {
cx.spawn(|mut cx| async move {
tab_panel.update(&mut cx, |panel, cx| {
let view = cx.new_view(|cx| Self::new(name, description, cx).story(story));
let view =
cx.new_view(|cx| Self::new(name, description, closeable, cx).story(story));
panel.add_panel(Arc::new(view.clone()), cx);
view
})
@ -153,6 +161,14 @@ impl Panel for StoryContainer {
fn title(&self, _cx: &WindowContext) -> SharedString {
self.name.clone()
}
fn closeable(&self, _cx: &WindowContext) -> bool {
self.closeable
}
fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu {
menu.menu("Panel Info", Box::new(PanelInfo))
}
}
impl EventEmitter<PanelEvent> for StoryContainer {}

View file

@ -1,6 +1,8 @@
use gpui::{AnyView, EventEmitter, FocusableView, SharedString, View, WindowContext};
use rust_i18n::t;
use crate::popup_menu::PopupMenu;
use super::PanelEvent;
pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
@ -13,6 +15,11 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
fn closeable(&self, _cx: &WindowContext) -> bool {
true
}
/// The addition popup menu of the panel, default is `None`.
fn popup_menu(&self, this: PopupMenu, _cx: &WindowContext) -> PopupMenu {
this
}
}
pub trait PanelView: 'static + Send + Sync {
@ -21,6 +28,10 @@ pub trait PanelView: 'static + Send + Sync {
t!("Dock.Unnamed").into()
}
fn closeable(&self, cx: &WindowContext) -> bool;
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu;
fn view(&self) -> AnyView;
}
@ -29,6 +40,14 @@ impl<T: Panel> PanelView for View<T> {
self.read(cx).title(cx)
}
fn closeable(&self, cx: &WindowContext) -> bool {
self.read(cx).closeable(cx)
}
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
self.read(cx).popup_menu(menu, cx)
}
fn view(&self) -> AnyView {
self.clone().into()
}

View file

@ -4,14 +4,14 @@ use gpui::{
div, prelude::FluentBuilder, rems, AnchorCorner, AppContext, DefiniteLength, DismissEvent,
DragMoveEvent, Empty, EventEmitter, FocusHandle, FocusableView, InteractiveElement as _,
IntoElement, ParentElement, Render, ScrollHandle, StatefulInteractiveElement, Styled, View,
ViewContext, VisualContext as _, WeakView,
ViewContext, VisualContext as _, WeakView, WindowContext,
};
use rust_i18n::t;
use crate::{
button::Button,
h_flex,
popup_menu::PopupMenuExt,
popup_menu::{PopupMenu, PopupMenuExt},
tab::{Tab, TabBar},
theme::ActiveTheme,
tooltip::Tooltip,
@ -164,6 +164,9 @@ impl TabPanel {
fn render_menu_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let is_zoomed = self.is_zoomed;
let closeable = self.closeable(cx);
let view = cx.view().clone();
let build_popup_menu = move |this, cx: &WindowContext| view.read(cx).popup_menu(this, cx);
h_flex()
.gap_2()
@ -186,17 +189,20 @@ impl TabPanel {
.icon(IconName::Ellipsis)
.xsmall()
.ghost()
.popup_menu(move |this, _| {
this.menu(
if is_zoomed {
t!("Dock.Zoom Out")
} else {
t!("Dock.Zoom In")
},
Box::new(ToggleZoom),
)
.separator()
.menu(t!("Dock.Close"), Box::new(ClosePanel))
.popup_menu(move |this, cx| {
build_popup_menu(this, cx)
.menu(
if is_zoomed {
t!("Dock.Zoom Out")
} else {
t!("Dock.Zoom In")
},
Box::new(ToggleZoom),
)
.when(closeable, |this| {
this.separator()
.menu(t!("Dock.Close"), Box::new(ClosePanel))
})
})
.anchor(AnchorCorner::TopRight),
)
@ -498,7 +504,21 @@ impl TabPanel {
}
}
impl Panel for TabPanel {}
impl Panel for TabPanel {
fn closeable(&self, cx: &WindowContext) -> bool {
self.active_panel()
.map(|panel| panel.closeable(cx))
.unwrap_or(false)
}
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu {
if let Some(panel) = self.active_panel() {
panel.popup_menu(menu, cx)
} else {
menu
}
}
}
impl FocusableView for TabPanel {
fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle {
// FIXME: Delegate to the active panel