dock: Reduce dock item member (#223)

This commit is contained in:
Jason Lee 2024-09-06 17:51:21 +08:00 committed by GitHub
parent 83aecf1293
commit 9baada4fb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 346 additions and 241 deletions

View file

@ -59,18 +59,8 @@ impl StoryWorkspace {
DockItem::split( DockItem::split(
Axis::Vertical, Axis::Vertical,
vec![ vec![
DockItem::panel(StoryContainer::panel( DockItem::tab(StoryContainer::panel::<IconStory>(cx), &dock_area, cx),
"Icon", DockItem::tab(StoryContainer::panel::<CalendarStory>(cx), &dock_area, cx),
"Icon use examples",
IconStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Calendar",
"A calendar component.",
CalendarStory::view(cx).into(),
cx,
)),
], ],
&dock_area, &dock_area,
cx, cx,
@ -80,83 +70,18 @@ impl StoryWorkspace {
vec![ vec![
DockItem::tabs( DockItem::tabs(
vec![ vec![
DockItem::panel(StoryContainer::panel( StoryContainer::panel::<ButtonStory>(cx),
"Button", StoryContainer::panel::<InputStory>(cx),
"Displays a button or a component that looks like a button.", StoryContainer::panel::<DropdownStory>(cx),
ButtonStory::view(cx).into(), StoryContainer::panel::<ModalStory>(cx),
cx, StoryContainer::panel::<PopupStory>(cx),
)), StoryContainer::panel::<ListStory>(cx),
DockItem::panel(StoryContainer::panel( StoryContainer::panel::<SwitchStory>(cx),
"Input", StoryContainer::panel::<ProgressStory>(cx),
"A control that allows the user to input text.", StoryContainer::panel::<TableStory>(cx),
InputStory::view(cx).into(), StoryContainer::panel::<ImageStory>(cx),
cx, StoryContainer::panel::<ResizableStory>(cx),
)), StoryContainer::panel::<ScrollableStory>(cx),
DockItem::panel(StoryContainer::panel(
"Dropdown",
"Displays a list of options for the user to pick from—triggered by a button.",
DropdownStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Modal",
"Modal & Drawer use examples",
ModalStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Popup",
"A popup displays content on top of the main page.",
PopupStory::view(cx).into(),
cx,
)),
DockItem::tabs(
vec![DockItem::panel(StoryContainer::panel(
"List",
"A list displays a series of items.",
ListStory::view(cx).into(),
cx,
))],
None,
&dock_area,
cx,
),
DockItem::panel(StoryContainer::panel(
"Switch",
"A control that allows the user to toggle between two states.",
SwitchStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Progress",
"Progress use examples",
ProgressStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Table",
"Table use examples",
TableStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Image",
"Image use examples",
ImageStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Resizable",
"Resizable use examples",
ResizableStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Scrollable",
"Scrollable use examples",
ScrollableStory::view(cx).into(),
cx,
)),
], ],
None, None,
&dock_area, &dock_area,
@ -164,48 +89,29 @@ impl StoryWorkspace {
), ),
DockItem::tabs( DockItem::tabs(
vec![ vec![
DockItem::panel(StoryContainer::panel( StoryContainer::panel::<ProgressStory>(cx),
"Progress", StoryContainer::panel::<TextStory>(cx),
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.", ],
ProgressStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Text",
"Links, paragraphs, checkboxes, and more.",
TextStory::view(cx).into(),
cx,
))],
None, None,
&dock_area, &dock_area,
cx, cx,
), ),
], ],
vec![None, Some(px(300.))], vec![None, None, Some(px(300.))],
&dock_area,
cx,
),
DockItem::split_with_sizes(
Axis::Vertical,
vec![
DockItem::tab(StoryContainer::panel::<TooltipStory>(cx), &dock_area, cx),
DockItem::tab(StoryContainer::panel::<CalendarStory>(cx), &dock_area, cx),
DockItem::tab(StoryContainer::panel::<ImageStory>(cx), &dock_area, cx),
],
vec![None, None, Some(px(300.))],
&dock_area, &dock_area,
cx, cx,
), ),
DockItem::split_with_sizes(Axis::Vertical, vec![
DockItem::panel(StoryContainer::panel(
"Tooltip",
"Displays a short message when users hover over an element.",
TooltipStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Calendar",
"A calendar component.",
CalendarStory::view(cx).into(),
cx,
)),
DockItem::panel(StoryContainer::panel(
"Image",
"Render SVG image and Chart",
ImageStory::view(cx).into(),
cx,
)),
], vec![None, None, Some(px(300.))], &dock_area, cx),
], ],
vec![Some(px(300.)), None, Some(px(350.))], vec![Some(px(300.)), None, Some(px(350.))],
&dock_area, &dock_area,

View file

@ -37,6 +37,24 @@ impl ButtonStory {
} }
} }
impl super::Story for ButtonStory {
fn title() -> &'static str {
"Button"
}
fn description() -> &'static str {
"Displays a button or a component that looks like a button."
}
fn closeable() -> bool {
false
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl Render for ButtonStory { impl Render for ButtonStory {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let disabled = self.disabled; let disabled = self.disabled;

View file

@ -17,6 +17,20 @@ pub struct CalendarStory {
default_range_mode_picker: View<DatePicker>, default_range_mode_picker: View<DatePicker>,
} }
impl super::Story for CalendarStory {
fn title() -> &'static str {
"Calendar"
}
fn description() -> &'static str {
"A date picker and calendar component."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl CalendarStory { impl CalendarStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -55,6 +55,20 @@ pub struct DropdownStory {
disabled_dropdown: View<Dropdown<Vec<SharedString>>>, disabled_dropdown: View<Dropdown<Vec<SharedString>>>,
} }
impl super::Story for DropdownStory {
fn title() -> &'static str {
"Dropdown"
}
fn description() -> &'static str {
"Displays a list of options for the user to pick from—triggered by a button."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl DropdownStory { impl DropdownStory {
fn new(cx: &mut WindowContext) -> View<Self> { fn new(cx: &mut WindowContext) -> View<Self> {
let countries = vec![ let countries = vec![

View file

@ -18,6 +18,20 @@ impl IconStory {
} }
} }
impl super::Story for IconStory {
fn title() -> &'static str {
"Icon"
}
fn description() -> &'static str {
"Icon use examples"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl Render for IconStory { impl Render for IconStory {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
v_flex().gap_3().child( v_flex().gap_3().child(

View file

@ -10,6 +10,16 @@ pub struct ImageStory {
inbox_img: SvgImg, inbox_img: SvgImg,
} }
impl super::Story for ImageStory {
fn title() -> &'static str {
"Image"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl ImageStory { impl ImageStory {
pub fn new(_: &WindowContext) -> Self { pub fn new(_: &WindowContext) -> Self {
let chart = charts_rs::PieChart::from_json(PIE_JSON).unwrap(); let chart = charts_rs::PieChart::from_json(PIE_JSON).unwrap();

View file

@ -1,4 +1,8 @@
use gpui::{actions, div, px, AppContext, FocusHandle, InteractiveElement, IntoElement, KeyBinding, ParentElement as _, Render, SharedString, Styled, View, ViewContext, VisualContext, WindowContext}; use gpui::{
actions, div, px, AppContext, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
ParentElement as _, Render, SharedString, Styled, View, ViewContext, VisualContext,
WindowContext,
};
use crate::section; use crate::section;
use ui::{ use ui::{
@ -8,11 +12,7 @@ use ui::{
input::{InputEvent, OtpInput, TextInput}, input::{InputEvent, OtpInput, TextInput},
prelude::FluentBuilder as _, prelude::FluentBuilder as _,
scroll::ScrollbarAxis, scroll::ScrollbarAxis,
v_flex, v_flex, FocusableCycle, IconName, Sizable, StyledExt,
FocusableCycle,
IconName,
Sizable,
StyledExt,
}; };
actions!(input_story, [Tab, TabPrev]); actions!(input_story, [Tab, TabPrev]);
@ -44,6 +44,24 @@ pub struct InputStory {
opt_input_sized: View<OtpInput>, opt_input_sized: View<OtpInput>,
} }
impl super::Story for InputStory {
fn title() -> &'static str {
"Input"
}
fn description() -> &'static str {
"A control that allows the user to input text."
}
fn closeable() -> bool {
false
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl InputStory { impl InputStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -16,8 +16,6 @@ mod text_story;
mod tooltip_story; mod tooltip_story;
mod webview_story; mod webview_story;
use std::sync::Arc;
pub use button_story::ButtonStory; pub use button_story::ButtonStory;
pub use calendar_story::CalendarStory; pub use calendar_story::CalendarStory;
pub use dropdown_story::DropdownStory; pub use dropdown_story::DropdownStory;
@ -38,20 +36,19 @@ pub use webview_story::WebViewStory;
use gpui::{ use gpui::{
actions, div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter, actions, div, prelude::FluentBuilder as _, px, AnyView, AppContext, Div, EventEmitter,
FocusableView, Hsla, InteractiveElement, IntoElement, ParentElement, Pixels, Render, FocusableView, Hsla, InteractiveElement, IntoElement, ParentElement, Render, SharedString,
SharedString, StatefulInteractiveElement, Styled as _, View, ViewContext, VisualContext, StatefulInteractiveElement, Styled as _, View, ViewContext, VisualContext, WindowContext,
WindowContext,
}; };
use ui::{ use ui::{
divider::Divider, divider::Divider,
dock::{Panel, PanelEvent, TabPanel, TitleStyle}, dock::{Panel, PanelEvent, TitleStyle},
h_flex, h_flex,
label::Label, label::Label,
notification::Notification, notification::Notification,
popup_menu::PopupMenu, popup_menu::PopupMenu,
theme::ActiveTheme, theme::ActiveTheme,
v_flex, ContextModal, Placement, v_flex, ContextModal,
}; };
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
@ -87,6 +84,7 @@ pub struct StoryContainer {
width: Option<gpui::Pixels>, width: Option<gpui::Pixels>,
height: Option<gpui::Pixels>, height: Option<gpui::Pixels>,
story: Option<AnyView>, story: Option<AnyView>,
story_klass: Option<SharedString>,
closeable: bool, closeable: bool,
} }
@ -95,66 +93,59 @@ pub enum ContainerEvent {
Close, Close,
} }
pub trait Story {
fn klass() -> &'static str {
std::any::type_name::<Self>()
}
fn title() -> &'static str;
fn description() -> &'static str {
""
}
fn closeable() -> bool {
true
}
fn title_bg() -> Option<Hsla> {
None
}
fn new_view(cx: &mut WindowContext) -> AnyView;
}
impl EventEmitter<ContainerEvent> for StoryContainer {} impl EventEmitter<ContainerEvent> for StoryContainer {}
impl StoryContainer { impl StoryContainer {
pub fn new( pub fn new(closeable: bool, cx: &mut WindowContext) -> Self {
name: impl Into<SharedString>,
description: impl Into<SharedString>,
closeable: bool,
cx: &mut WindowContext,
) -> Self {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
Self { Self {
focus_handle, focus_handle,
name: name.into(), name: "".into(),
title_bg: None, title_bg: None,
description: description.into(), description: "".into(),
width: None, width: None,
height: None, height: None,
story: None, story: None,
story_klass: None,
closeable, closeable,
} }
} }
pub fn panel(
name: impl Into<SharedString>,
description: impl Into<SharedString>,
story: AnyView,
cx: &mut WindowContext,
) -> View<Self> {
cx.new_view(|cx| Self::new(name, description, true, cx).story(story))
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn add_panel( pub fn panel<S: Story>(cx: &mut WindowContext) -> View<Self> {
name: impl Into<SharedString>, let name = S::title();
description: impl Into<SharedString>, let description = S::description();
story: AnyView, let story = S::new_view(cx);
tab_panel: View<TabPanel>, let story_klass = S::klass();
placement: Option<Placement>,
size: Option<Pixels>,
closeable: bool,
title_bg: Option<Hsla>,
cx: &mut WindowContext,
) {
let name = name.into();
let description = description.into();
tab_panel.update(cx, |panel, cx| { let view = cx.new_view(|cx| {
let view = cx.new_view(|cx| { let mut story = Self::new(S::closeable(), cx).story(story, story_klass);
Self::new(name, description, closeable, cx) story.name = name.into();
.story(story) story.description = description.into();
.title_bg(title_bg) story.title_bg = S::title_bg();
}); story
if let Some(placement) = placement {
panel.add_panel_at(Arc::new(view.clone()), placement, size, cx);
} else {
panel.add_panel(Arc::new(view.clone()), cx);
}
view
}); });
view
} }
pub fn width(mut self, width: gpui::Pixels) -> Self { pub fn width(mut self, width: gpui::Pixels) -> Self {
@ -167,13 +158,9 @@ impl StoryContainer {
self self
} }
pub fn story(mut self, story: AnyView) -> Self { pub fn story(mut self, story: AnyView, story_klass: impl Into<SharedString>) -> Self {
self.story = Some(story); self.story = Some(story);
self self.story_klass = Some(story_klass.into());
}
pub fn title_bg(mut self, title_bg: Option<Hsla>) -> Self {
self.title_bg = title_bg;
self self
} }

View file

@ -196,6 +196,20 @@ pub struct ListStory {
selected_company: Option<Company>, selected_company: Option<Company>,
} }
impl super::Story for ListStory {
fn title() -> &'static str {
"List"
}
fn description() -> &'static str {
"A list displays a series of items."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl ListStory { impl ListStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -151,6 +151,20 @@ pub struct ModalStory {
model_padding: bool, model_padding: bool,
} }
impl super::Story for ModalStory {
fn title() -> &'static str {
"Modal"
}
fn description() -> &'static str {
"Modal & Drawer use examples"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl ModalStory { impl ModalStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -79,6 +79,20 @@ pub struct PopupStory {
window_mode: bool, window_mode: bool,
} }
impl super::Story for PopupStory {
fn title() -> &'static str {
"Popup"
}
fn description() -> &'static str {
"A popup displays content on top of the main page."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl PopupStory { impl PopupStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)
@ -199,9 +213,10 @@ impl Render for PopupStory {
.small(), .small(),
) )
.into_any() .into_any()
}).max_w(px(600.)) })
.max_w(px(600.))
}) })
}) }),
), ),
) )
.child( .child(
@ -209,7 +224,7 @@ impl Render for PopupStory {
.anchor(AnchorCorner::TopRight) .anchor(AnchorCorner::TopRight)
.trigger(Button::new("info-top-right", cx).label("Top Right")) .trigger(Button::new("info-top-right", cx).label("Top Right"))
.content(|cx| { .content(|cx| {
cx.new_view(|cx| cx.new_view(|cx| {
PopoverContent::new(cx, |cx| { PopoverContent::new(cx, |cx| {
v_flex() v_flex()
.gap_4() .gap_4()
@ -223,7 +238,8 @@ impl Render for PopupStory {
.small(), .small(),
) )
.into_any() .into_any()
})) })
})
}), }),
), ),
) )
@ -284,11 +300,13 @@ impl Render for PopupStory {
.w(px(300.)), .w(px(300.)),
) )
.content(|cx| { .content(|cx| {
cx.new_view(|cx| cx.new_view(|cx| {
PopoverContent::new(cx, |cx| { PopoverContent::new(cx, |cx| {
v_flex() v_flex()
.gap_4() .gap_4()
.child("Hello, this is a Popover on the Bottom Right.") .child(
"Hello, this is a Popover on the Bottom Right.",
)
.child(Divider::horizontal()) .child(Divider::horizontal())
.child( .child(
Button::new("info1", cx) Button::new("info1", cx)
@ -297,7 +315,8 @@ impl Render for PopupStory {
.small(), .small(),
) )
.into_any() .into_any()
})) })
})
}), }),
), ),
), ),

View file

@ -21,6 +21,16 @@ pub struct ProgressStory {
slider2_value: f32, slider2_value: f32,
} }
impl super::Story for ProgressStory {
fn title() -> &'static str {
"Progress"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl ProgressStory { impl ProgressStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -13,6 +13,20 @@ pub struct ResizableStory {
group2: View<ResizablePanelGroup>, group2: View<ResizablePanelGroup>,
} }
impl super::Story for ResizableStory {
fn title() -> &'static str {
"Resizable"
}
fn description() -> &'static str {
"The resizable panels."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl ResizableStory { impl ResizableStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(|cx| Self::new(cx)) cx.new_view(|cx| Self::new(cx))

View file

@ -60,6 +60,20 @@ impl ScrollableStory {
} }
} }
impl super::Story for ScrollableStory {
fn title() -> &'static str {
"Scrollable"
}
fn description() -> &'static str {
"Add vertical or horizontal, or both scrollbars to a container."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl Render for ScrollableStory { impl Render for ScrollableStory {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
let view = cx.view().clone(); let view = cx.view().clone();

View file

@ -18,6 +18,20 @@ pub struct SwitchStory {
switch3: bool, switch3: bool,
} }
impl super::Story for SwitchStory {
fn title() -> &'static str {
"Switch"
}
fn description() -> &'static str {
"A control that allows the user to toggle between two states."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl SwitchStory { impl SwitchStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(|cx| Self::new(cx)) cx.new_view(|cx| Self::new(cx))

View file

@ -362,6 +362,20 @@ pub struct TableStory {
table: View<Table<CustomerTableDelegate>>, table: View<Table<CustomerTableDelegate>>,
} }
impl super::Story for TableStory {
fn title() -> &'static str {
"Table"
}
fn description() -> &'static str {
"A complex data table with selection, sorting, column moving, and loading more."
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl TableStory { impl TableStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.new_view(Self::new) cx.new_view(Self::new)

View file

@ -25,6 +25,20 @@ pub struct TextStory {
masked: bool, masked: bool,
} }
impl super::Story for TextStory {
fn title() -> &'static str {
"Text"
}
fn description() -> &'static str {
"The text render testing and examples"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl TextStory { impl TextStory {
pub(crate) fn new(_cx: &mut WindowContext) -> Self { pub(crate) fn new(_cx: &mut WindowContext) -> Self {
Self { Self {

View file

@ -24,6 +24,16 @@ impl TooltipStory {
} }
} }
impl super::Story for TooltipStory {
fn title() -> &'static str {
"Tooltip"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl Render for TooltipStory { impl Render for TooltipStory {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
v_flex() v_flex()

View file

@ -18,6 +18,16 @@ pub struct WebViewStory {
address_input: View<TextInput>, address_input: View<TextInput>,
} }
impl super::Story for WebViewStory {
fn title() -> &'static str {
"WebView"
}
fn new_view(cx: &mut WindowContext) -> gpui::AnyView {
Self::view(cx).into()
}
}
impl WebViewStory { impl WebViewStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();

View file

@ -32,13 +32,10 @@ pub enum DockItem {
view: View<StackPanel>, view: View<StackPanel>,
}, },
Tabs { Tabs {
items: Vec<DockItem>, items: Vec<Arc<dyn PanelView>>,
active_ix: usize, active_ix: usize,
view: View<TabPanel>, view: View<TabPanel>,
}, },
Panel {
view: Arc<dyn PanelView>,
},
} }
impl DockItem { impl DockItem {
@ -68,26 +65,12 @@ impl DockItem {
let stack_panel = cx.new_view(|cx| { let stack_panel = cx.new_view(|cx| {
let mut stack_panel = StackPanel::new(axis, cx); let mut stack_panel = StackPanel::new(axis, cx);
for (i, item) in items.iter_mut().enumerate() { for (i, item) in items.iter_mut().enumerate() {
// Always convert DockItem::Panel to DockItem::Tabs in split layout.
// This makes they can subscribe the Zoom event.
match item {
DockItem::Panel { .. } => {
*item = DockItem::tabs(vec![item.clone()], None, &dock_area, cx);
}
_ => {}
}
let view = item.view(); let view = item.view();
let size = sizes.get(i).copied().flatten(); let size = sizes.get(i).copied().flatten();
stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx) stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx)
} }
for (i, item) in items.iter().enumerate() { for (i, item) in items.iter().enumerate() {
let item = match item {
DockItem::Panel { .. } => Self::tabs(vec![item.clone()], None, &dock_area, cx),
_ => item.clone(),
};
let view = item.view(); let view = item.view();
let size = sizes.get(i).copied().flatten(); let size = sizes.get(i).copied().flatten();
stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx) stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx)
@ -106,8 +89,30 @@ impl DockItem {
/// Create DockItem with tabs layout, items are displayed as tabs. /// Create DockItem with tabs layout, items are displayed as tabs.
/// ///
/// The `active_ix` is the index of the active tab, if `None` the first tab is active. /// The `active_ix` is the index of the active tab, if `None` the first tab is active.
pub fn tabs( pub fn tabs<P: Panel>(
items: Vec<DockItem>, items: Vec<View<P>>,
active_ix: Option<usize>,
dock_area: &View<DockArea>,
cx: &mut WindowContext,
) -> Self {
let mut new_items: Vec<Arc<dyn PanelView>> = vec![];
for item in items.into_iter() {
let item: Arc<dyn PanelView> = Arc::new(item);
new_items.push(item)
}
Self::new_tabs(new_items, active_ix, dock_area, cx)
}
pub fn tab<P: Panel>(
item: View<P>,
dock_area: &View<DockArea>,
cx: &mut WindowContext,
) -> Self {
Self::new_tabs(vec![Arc::new(item.clone())], None, dock_area, cx)
}
fn new_tabs(
items: Vec<Arc<dyn PanelView>>,
active_ix: Option<usize>, active_ix: Option<usize>,
dock_area: &View<DockArea>, dock_area: &View<DockArea>,
cx: &mut WindowContext, cx: &mut WindowContext,
@ -115,10 +120,8 @@ impl DockItem {
let active_ix = active_ix.unwrap_or(0); let active_ix = active_ix.unwrap_or(0);
let tab_panel = cx.new_view(|cx| { let tab_panel = cx.new_view(|cx| {
let mut tab_panel = TabPanel::new(None, dock_area.downgrade(), cx); let mut tab_panel = TabPanel::new(None, dock_area.downgrade(), cx);
for item in items.iter() { for item in items.iter() {
let view = item.view(); tab_panel.add_panel(item.clone(), cx)
tab_panel.add_panel(view, cx)
} }
tab_panel tab_panel
@ -131,22 +134,11 @@ impl DockItem {
} }
} }
/// Create DockItem with a single panel, the `view` must implement `Panel`.
pub fn panel<P>(view: View<P>) -> Self
where
P: Panel,
{
Self::Panel {
view: Arc::new(view),
}
}
/// Returns the views of the dock item. /// Returns the views of the dock item.
fn view(&self) -> Arc<dyn PanelView> { fn view(&self) -> Arc<dyn PanelView> {
match self { match self {
Self::Split { view, .. } => Arc::new(view.clone()), Self::Split { view, .. } => Arc::new(view.clone()),
Self::Tabs { view, .. } => Arc::new(view.clone()), Self::Tabs { view, .. } => Arc::new(view.clone()),
Self::Panel { view } => view.clone(),
} }
} }
@ -156,16 +148,7 @@ impl DockItem {
Self::Split { items, .. } => { Self::Split { items, .. } => {
items.iter().find_map(|item| item.find_panel(panel.clone())) items.iter().find_map(|item| item.find_panel(panel.clone()))
} }
Self::Tabs { items, .. } => { Self::Tabs { items, .. } => items.iter().find(|item| *item == &panel).cloned(),
items.iter().find_map(|item| item.find_panel(panel.clone()))
}
Self::Panel { view } => {
if view == &panel {
Some(view.clone())
} else {
None
}
}
} }
} }
} }
@ -245,11 +228,6 @@ impl DockArea {
// Because we always wrap the DockItem::Panel in a DockItem::Tabs // Because we always wrap the DockItem::Panel in a DockItem::Tabs
subscribe_zoom(view, dock_area.clone(), cx); subscribe_zoom(view, dock_area.clone(), cx);
} }
DockItem::Panel { .. } => {
// The DockItem::Panel is not need to handle the zoom events
// Because the DockItem::Panel is always wrapped in a DockItem::Tabs
// So we only need to subscribe the zoom events on the TabPanel
}
} }
} }
@ -272,7 +250,6 @@ impl DockArea {
match &self.items { match &self.items {
DockItem::Split { view, .. } => view.clone().into_any_element(), DockItem::Split { view, .. } => view.clone().into_any_element(),
DockItem::Tabs { view, .. } => view.clone().into_any_element(), DockItem::Tabs { view, .. } => view.clone().into_any_element(),
DockItem::Panel { view } => view.view().into_any_element(),
} }
} }
} }