dock: Add popup_menu delegate to custom panel menu. (#202)
This commit is contained in:
parent
f5cae46e42
commit
af09525c1e
4 changed files with 105 additions and 34 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue