dock: Define a better way to init Dock layout. (#218)
This commit is contained in:
parent
d6015f636c
commit
adf6f70185
6 changed files with 476 additions and 330 deletions
|
|
@ -12,13 +12,13 @@ use std::sync::Arc;
|
|||
use ui::{
|
||||
button::Button,
|
||||
color_picker::{ColorPicker, ColorPickerEvent},
|
||||
dock::{DockArea, StackPanel, TabPanel},
|
||||
dock::{DockArea, DockItem},
|
||||
drawer::Drawer,
|
||||
h_flex,
|
||||
modal::Modal,
|
||||
popup_menu::PopupMenuExt,
|
||||
theme::{ActiveTheme, Colorize as _, Theme},
|
||||
ContextModal, IconName, Placement, Root, Sizable,
|
||||
ContextModal, IconName, Root, Sizable,
|
||||
};
|
||||
|
||||
use crate::app_state::AppState;
|
||||
|
|
@ -51,240 +51,168 @@ impl StoryWorkspace {
|
|||
})
|
||||
.detach();
|
||||
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx));
|
||||
let dock_area = cx.new_view(|cx| DockArea::new("main-dock", stack_panel.clone(), cx));
|
||||
let weak_dock_area = dock_area.downgrade();
|
||||
let dock_area = cx.new_view(|cx| DockArea::new("main-dock", cx));
|
||||
|
||||
let center_tab_panel = cx.new_view(|cx| {
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx)
|
||||
});
|
||||
let left_tab_panel = cx.new_view(|cx| {
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx)
|
||||
});
|
||||
let dock_item = DockItem::split_with_sizes(
|
||||
Axis::Horizontal,
|
||||
vec![
|
||||
DockItem::split(
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
DockItem::panel(StoryContainer::panel(
|
||||
"Icon",
|
||||
"Icon use examples",
|
||||
IconStory::view(cx).into(),
|
||||
cx,
|
||||
)),
|
||||
DockItem::panel(StoryContainer::panel(
|
||||
"Calendar",
|
||||
"A calendar component.",
|
||||
CalendarStory::view(cx).into(),
|
||||
cx,
|
||||
)),
|
||||
],
|
||||
&dock_area,
|
||||
cx,
|
||||
),
|
||||
DockItem::split_with_sizes(
|
||||
Axis::Vertical,
|
||||
vec![
|
||||
DockItem::tabs(
|
||||
vec![
|
||||
DockItem::panel(StoryContainer::panel(
|
||||
"Button",
|
||||
"Displays a button or a component that looks like a button.",
|
||||
ButtonStory::view(cx).into(),
|
||||
cx,
|
||||
)),
|
||||
DockItem::panel(StoryContainer::panel(
|
||||
"Input",
|
||||
"A control that allows the user to input text.",
|
||||
InputStory::view(cx).into(),
|
||||
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,
|
||||
&dock_area,
|
||||
cx,
|
||||
),
|
||||
DockItem::tabs(
|
||||
vec![
|
||||
DockItem::panel(StoryContainer::panel(
|
||||
"Progress",
|
||||
"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,
|
||||
&dock_area,
|
||||
cx,
|
||||
),
|
||||
],
|
||||
vec![None, Some(px(300.))],
|
||||
&dock_area,
|
||||
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),
|
||||
|
||||
let right_tab_panel = cx.new_view(|cx| {
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx));
|
||||
TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx)
|
||||
});
|
||||
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.add_panel(
|
||||
left_tab_panel.clone(),
|
||||
Some(px(300.)),
|
||||
weak_dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
|
||||
view.add_panel(center_tab_panel.clone(), None, weak_dock_area.clone(), cx);
|
||||
view.add_panel(
|
||||
right_tab_panel.clone(),
|
||||
Some(px(350.)),
|
||||
weak_dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Buttons",
|
||||
"Displays a button or a component that looks like a button.",
|
||||
ButtonStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
],
|
||||
vec![Some(px(300.)), None, Some(px(350.))],
|
||||
&dock_area,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Input",
|
||||
"A control that allows the user to input text.",
|
||||
InputStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Text",
|
||||
"Links, paragraphs, checkboxes, and more.",
|
||||
TextStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Switch",
|
||||
"A control that allows the user to toggle between two states.",
|
||||
SwitchStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Dropdowns",
|
||||
"Displays a list of options for the user to pick from—triggered by a button.",
|
||||
DropdownStory::new(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Modal",
|
||||
"Modal & Drawer use examples",
|
||||
ModalStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Popup",
|
||||
"A popup displays content on top of the main page.",
|
||||
PopupStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Tooltip",
|
||||
"Displays a short message when users hover over an element.",
|
||||
TooltipStory::view(cx).into(),
|
||||
right_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"List",
|
||||
"A list displays a series of items.",
|
||||
ListStory::view(cx).into(),
|
||||
left_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Icon",
|
||||
"Icon use examples",
|
||||
IconStory::view(cx).into(),
|
||||
left_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Image",
|
||||
"Render SVG image and Chart",
|
||||
ImageStory::view(cx).into(),
|
||||
right_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
None,
|
||||
true,
|
||||
Some(cx.theme().muted),
|
||||
cx,
|
||||
);
|
||||
|
||||
// StoryContainer::add_panel(
|
||||
// WebViewStory::view(cx).into(),
|
||||
// stack_panel.clone(),
|
||||
// DockPosition::Right,
|
||||
// px(450.),
|
||||
// cx,
|
||||
// );
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Table",
|
||||
"Powerful table and datagrids built.",
|
||||
TableStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Progress",
|
||||
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
|
||||
ProgressStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
Some(px(200.)),
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Resizable",
|
||||
"Accessible resizable panel groups and layouts with keyboard support.",
|
||||
ResizableStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Scrollable",
|
||||
"A scrollable area with scroll bar.",
|
||||
ScrollableStory::view(cx).into(),
|
||||
center_tab_panel.clone(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
|
||||
StoryContainer::add_panel(
|
||||
"Calendar",
|
||||
"A calendar component.",
|
||||
CalendarStory::view(cx).into(),
|
||||
right_tab_panel.clone(),
|
||||
Some(Placement::Bottom),
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
dock_area.update(cx, |view, cx| view.set_root(dock_item, cx));
|
||||
|
||||
let locale_selector = cx.new_view(LocaleSelector::new);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub struct DropdownStory {
|
|||
}
|
||||
|
||||
impl DropdownStory {
|
||||
pub fn new(cx: &mut WindowContext) -> View<Self> {
|
||||
fn new(cx: &mut WindowContext) -> View<Self> {
|
||||
let countries = vec![
|
||||
Country::new("United States", "US"),
|
||||
Country::new("Canada", "CA"),
|
||||
|
|
@ -144,6 +144,10 @@ impl DropdownStory {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn view(cx: &mut WindowContext) -> View<Self> {
|
||||
Self::new(cx)
|
||||
}
|
||||
|
||||
fn on_dropdown_event(
|
||||
&mut self,
|
||||
_: View<Dropdown<Vec<Country>>>,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,15 @@ impl StoryContainer {
|
|||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
pub fn add_panel(
|
||||
name: impl Into<SharedString>,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ mod panel;
|
|||
mod stack_panel;
|
||||
mod tab_panel;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder, AnyView, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, Render, SharedString, Styled, View, ViewContext,
|
||||
actions, div, prelude::FluentBuilder, AnyElement, AnyView, Axis, InteractiveElement as _,
|
||||
IntoElement, ParentElement as _, Pixels, Render, SharedString, Styled, View, ViewContext,
|
||||
VisualContext, WindowContext,
|
||||
};
|
||||
pub use panel::*;
|
||||
pub use stack_panel::*;
|
||||
|
|
@ -15,23 +18,241 @@ actions!(dock, [ToggleZoom, ClosePanel]);
|
|||
/// The main area of the dock.
|
||||
pub struct DockArea {
|
||||
id: SharedString,
|
||||
root: View<StackPanel>,
|
||||
items: DockItem,
|
||||
zoom_view: Option<AnyView>,
|
||||
}
|
||||
|
||||
impl DockArea {
|
||||
pub fn new(
|
||||
id: impl Into<SharedString>,
|
||||
root: View<StackPanel>,
|
||||
_cx: &mut ViewContext<Self>,
|
||||
/// DockItem is a tree structure that represents the layout of the dock.
|
||||
#[derive(Clone)]
|
||||
pub enum DockItem {
|
||||
Split {
|
||||
axis: gpui::Axis,
|
||||
items: Vec<DockItem>,
|
||||
sizes: Vec<Option<Pixels>>,
|
||||
view: View<StackPanel>,
|
||||
},
|
||||
Tabs {
|
||||
items: Vec<DockItem>,
|
||||
active_ix: usize,
|
||||
view: View<TabPanel>,
|
||||
},
|
||||
Panel {
|
||||
view: Arc<dyn PanelView>,
|
||||
},
|
||||
}
|
||||
|
||||
impl DockItem {
|
||||
/// Create DockItem with split layout, each item of panel have equal size.
|
||||
pub fn split(
|
||||
axis: Axis,
|
||||
items: Vec<DockItem>,
|
||||
dock_area: &View<DockArea>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Self {
|
||||
let sizes = vec![None; items.len()];
|
||||
Self::split_with_sizes(axis, items, sizes, dock_area, cx)
|
||||
}
|
||||
|
||||
/// Create DockItem with split layout, each item of panel have specified size.
|
||||
///
|
||||
/// Please note that the `items` and `sizes` must have the same length.
|
||||
/// Set `None` in `sizes` to make the index of panel have auto size.
|
||||
pub fn split_with_sizes(
|
||||
axis: Axis,
|
||||
items: Vec<DockItem>,
|
||||
sizes: Vec<Option<Pixels>>,
|
||||
dock_area: &View<DockArea>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Self {
|
||||
let mut items = items;
|
||||
let stack_panel = cx.new_view(|cx| {
|
||||
let mut stack_panel = StackPanel::new(axis, cx);
|
||||
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 size = sizes.get(i).copied().flatten();
|
||||
stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx)
|
||||
}
|
||||
|
||||
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 size = sizes.get(i).copied().flatten();
|
||||
stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx)
|
||||
}
|
||||
|
||||
stack_panel
|
||||
});
|
||||
Self::Split {
|
||||
axis,
|
||||
items,
|
||||
sizes,
|
||||
view: stack_panel,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn tabs(
|
||||
items: Vec<DockItem>,
|
||||
active_ix: Option<usize>,
|
||||
dock_area: &View<DockArea>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Self {
|
||||
let active_ix = active_ix.unwrap_or(0);
|
||||
let tab_panel = cx.new_view(|cx| {
|
||||
let mut tab_panel = TabPanel::new(None, dock_area.downgrade(), cx);
|
||||
|
||||
for item in items.iter() {
|
||||
let view = item.view();
|
||||
tab_panel.add_panel(view, cx)
|
||||
}
|
||||
|
||||
tab_panel
|
||||
});
|
||||
|
||||
Self::Tabs {
|
||||
items,
|
||||
active_ix,
|
||||
view: tab_panel,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn view(&self) -> Arc<dyn PanelView> {
|
||||
match self {
|
||||
Self::Split { view, .. } => Arc::new(view.clone()),
|
||||
Self::Tabs { view, .. } => Arc::new(view.clone()),
|
||||
Self::Panel { view } => view.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find existing panel in the dock item.
|
||||
pub fn find_panel(&self, panel: Arc<dyn PanelView>) -> Option<Arc<dyn PanelView>> {
|
||||
match self {
|
||||
Self::Split { items, .. } => {
|
||||
items.iter().find_map(|item| item.find_panel(panel.clone()))
|
||||
}
|
||||
Self::Tabs { items, .. } => {
|
||||
items.iter().find_map(|item| item.find_panel(panel.clone()))
|
||||
}
|
||||
Self::Panel { view } => {
|
||||
if view == &panel {
|
||||
Some(view.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DockArea {
|
||||
pub fn new(id: impl Into<SharedString>, cx: &mut WindowContext) -> Self {
|
||||
let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx));
|
||||
let dock_item = DockItem::Split {
|
||||
axis: Axis::Horizontal,
|
||||
items: vec![],
|
||||
sizes: vec![],
|
||||
view: stack_panel.clone(),
|
||||
};
|
||||
|
||||
Self {
|
||||
id: id.into(),
|
||||
root,
|
||||
items: dock_item,
|
||||
zoom_view: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The the DockItem as the root of the dock area.
|
||||
#[must_use]
|
||||
pub fn set_root(&mut self, item: DockItem, cx: &mut ViewContext<Self>) {
|
||||
self.subscribe_item(&item, cx);
|
||||
self.items = item;
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Subscribe event on the panels
|
||||
#[allow(clippy::only_used_in_recursion)]
|
||||
fn subscribe_item(&self, item: &DockItem, cx: &mut ViewContext<Self>) {
|
||||
let dock_area = cx.view();
|
||||
|
||||
/// Subscribe zoom event on the panel
|
||||
fn subscribe_zoom<P: Panel>(
|
||||
view: &View<P>,
|
||||
dock_area: View<DockArea>,
|
||||
cx: &mut ViewContext<DockArea>,
|
||||
) {
|
||||
cx.subscribe(view, move |_, panel, event, cx| match event {
|
||||
PanelEvent::ZoomIn => {
|
||||
let dock_area = dock_area.clone();
|
||||
let panel = panel.clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let _ = cx.update(|cx| {
|
||||
let _ = dock_area.update(cx, |dock, cx| {
|
||||
dock.set_zoomed_in(panel, cx);
|
||||
cx.notify();
|
||||
});
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
PanelEvent::ZoomOut => {
|
||||
let dock_area = dock_area.clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let _ = cx.update(|cx| {
|
||||
let _ = dock_area.update(cx, |view, cx| view.set_zoomed_out(cx));
|
||||
});
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
match item {
|
||||
DockItem::Split { items, .. } => {
|
||||
for item in items {
|
||||
self.subscribe_item(item, cx);
|
||||
}
|
||||
}
|
||||
DockItem::Tabs { view, .. } => {
|
||||
// We need, only subscribe to the zoom events on the TabPanel
|
||||
// Because we always wrap the DockItem::Panel in a DockItem::Tabs
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the dock area.
|
||||
pub fn id(&self) -> SharedString {
|
||||
self.id.clone()
|
||||
|
|
@ -47,14 +268,17 @@ impl DockArea {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
/// Returns the root stack panel.
|
||||
pub fn root(&self) -> View<StackPanel> {
|
||||
self.root.clone()
|
||||
fn render_items(&self, _cx: &mut ViewContext<Self>) -> AnyElement {
|
||||
match &self.items {
|
||||
DockItem::Split { view, .. } => view.clone().into_any_element(),
|
||||
DockItem::Tabs { view, .. } => view.clone().into_any_element(),
|
||||
DockItem::Panel { view } => view.view().into_any_element(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for DockArea {
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
// println!("Rendering dock area");
|
||||
div()
|
||||
.id("dock-area")
|
||||
|
|
@ -64,7 +288,7 @@ impl Render for DockArea {
|
|||
if let Some(zoom_view) = self.zoom_view.clone() {
|
||||
this.child(zoom_view)
|
||||
} else {
|
||||
this.child(self.root.clone())
|
||||
this.child(self.render_items(cx))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
|
||||
use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel};
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, Entity, EventEmitter, FocusHandle,
|
||||
prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, EventEmitter, FocusHandle,
|
||||
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
};
|
||||
|
|
@ -19,7 +19,7 @@ pub struct StackPanel {
|
|||
pub(super) parent: Option<View<StackPanel>>,
|
||||
pub(super) axis: Axis,
|
||||
focus_handle: FocusHandle,
|
||||
panels: SmallVec<[Arc<dyn PanelView>; 2]>,
|
||||
pub(crate) panels: SmallVec<[Arc<dyn PanelView>; 2]>,
|
||||
panel_group: View<ResizablePanelGroup>,
|
||||
}
|
||||
|
||||
|
|
@ -56,53 +56,41 @@ impl StackPanel {
|
|||
}
|
||||
|
||||
/// Return the index of the panel.
|
||||
pub(crate) fn index_of_panel<P>(&self, panel: &View<P>) -> Option<usize>
|
||||
where
|
||||
P: Panel,
|
||||
{
|
||||
let entity_id = panel.entity_id();
|
||||
self.panels
|
||||
.iter()
|
||||
.position(|p| p.view().entity_id() == entity_id)
|
||||
pub(crate) fn index_of_panel(&self, panel: Arc<dyn PanelView>) -> Option<usize> {
|
||||
self.panels.iter().position(|p| p == &panel)
|
||||
}
|
||||
|
||||
/// Add a panel at the end of the stack.
|
||||
pub fn add_panel<P>(
|
||||
pub fn add_panel(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
self.insert_panel(panel, self.panels.len(), size, dock_area, cx);
|
||||
}
|
||||
|
||||
pub fn add_panel_at<P>(
|
||||
pub fn add_panel_at(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
self.insert_panel_at(panel, self.panels_len(), placement, size, dock_area, cx);
|
||||
}
|
||||
|
||||
pub fn insert_panel_at<P>(
|
||||
pub fn insert_panel_at(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
ix: usize,
|
||||
placement: Placement,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
match placement {
|
||||
Placement::Top | Placement::Left => {
|
||||
self.insert_panel_before(panel, ix, size, dock_area, cx)
|
||||
|
|
@ -114,69 +102,48 @@ impl StackPanel {
|
|||
}
|
||||
|
||||
/// Insert a panel at the index.
|
||||
pub fn insert_panel_before<P>(
|
||||
pub fn insert_panel_before(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
ix: usize,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
self.insert_panel(panel, ix, size, dock_area, cx);
|
||||
}
|
||||
|
||||
/// Insert a panel after the index.
|
||||
pub fn insert_panel_after<P>(
|
||||
pub fn insert_panel_after(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
ix: usize,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
self.insert_panel(panel, ix + 1, size, dock_area, cx);
|
||||
}
|
||||
|
||||
fn new_resizable_panel<P>(panel: View<P>, size: Option<Pixels>) -> ResizablePanel
|
||||
where
|
||||
P: Panel,
|
||||
{
|
||||
fn new_resizable_panel(panel: Arc<dyn PanelView>, size: Option<Pixels>) -> ResizablePanel {
|
||||
resizable_panel()
|
||||
.content_view(panel.into())
|
||||
.content_view(panel.view())
|
||||
.when_some(size, |this, size| this.size(size))
|
||||
}
|
||||
|
||||
fn insert_panel<P>(
|
||||
fn insert_panel(
|
||||
&mut self,
|
||||
panel: View<P>,
|
||||
panel: Arc<dyn PanelView>,
|
||||
ix: usize,
|
||||
size: Option<Pixels>,
|
||||
dock_area: WeakView<DockArea>,
|
||||
_dock_area: WeakView<DockArea>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
) {
|
||||
// If the panel is already in the stack, return.
|
||||
if let Some(_) = self.index_of_panel(&panel) {
|
||||
if let Some(_) = self.index_of_panel(panel.clone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cx.subscribe(&panel, move |_, panel, event, cx| match event {
|
||||
PanelEvent::ZoomIn => {
|
||||
let _ = dock_area.update(cx, |dock, cx| {
|
||||
dock.set_zoomed_in(panel.clone(), cx);
|
||||
});
|
||||
}
|
||||
PanelEvent::ZoomOut => {
|
||||
let _ = dock_area.update(cx, |dock, cx| dock.set_zoomed_out(cx));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let view = cx.view().clone();
|
||||
cx.window_context().defer({
|
||||
let panel = panel.clone();
|
||||
|
|
@ -197,7 +164,7 @@ impl StackPanel {
|
|||
ix
|
||||
};
|
||||
|
||||
self.panels.insert(ix, Arc::new(panel.clone()));
|
||||
self.panels.insert(ix, panel.clone());
|
||||
self.panel_group.update(cx, |view, cx| {
|
||||
view.insert_child(Self::new_resizable_panel(panel, size), ix, cx)
|
||||
});
|
||||
|
|
@ -206,11 +173,8 @@ impl StackPanel {
|
|||
}
|
||||
|
||||
/// Remove panel from the stack.
|
||||
pub fn remove_panel<P>(&mut self, panel: View<P>, cx: &mut ViewContext<Self>)
|
||||
where
|
||||
P: Panel,
|
||||
{
|
||||
if let Some(ix) = self.index_of_panel(&panel) {
|
||||
pub fn remove_panel(&mut self, panel: Arc<dyn PanelView>, cx: &mut ViewContext<Self>) {
|
||||
if let Some(ix) = self.index_of_panel(panel.clone()) {
|
||||
self.panels.remove(ix);
|
||||
self.panel_group.update(cx, |view, cx| {
|
||||
view.remove_child(ix, cx);
|
||||
|
|
@ -223,18 +187,20 @@ impl StackPanel {
|
|||
}
|
||||
|
||||
/// Replace the old panel with the new panel at same index.
|
||||
pub(super) fn replace_panel<P>(
|
||||
pub(super) fn replace_panel(
|
||||
&mut self,
|
||||
old_panel: View<P>,
|
||||
old_panel: Arc<dyn PanelView>,
|
||||
new_panel: View<StackPanel>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
P: Panel,
|
||||
{
|
||||
if let Some(ix) = self.index_of_panel(&old_panel) {
|
||||
) {
|
||||
if let Some(ix) = self.index_of_panel(old_panel.clone()) {
|
||||
self.panels[ix] = Arc::new(new_panel.clone());
|
||||
self.panel_group.update(cx, |view, cx| {
|
||||
view.replace_child(Self::new_resizable_panel(new_panel.clone(), None), ix, cx);
|
||||
view.replace_child(
|
||||
Self::new_resizable_panel(Arc::new(new_panel.clone()), None),
|
||||
ix,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -252,7 +218,7 @@ impl StackPanel {
|
|||
let view = cx.view().clone();
|
||||
if let Some(parent) = self.parent.as_ref() {
|
||||
parent.update(cx, |parent, cx| {
|
||||
parent.remove_panel(view, cx);
|
||||
parent.remove_panel(Arc::new(view), cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ pub struct TabPanel {
|
|||
focus_handle: FocusHandle,
|
||||
dock_area: WeakView<DockArea>,
|
||||
stack_panel: Option<View<StackPanel>>,
|
||||
panels: Vec<Arc<dyn PanelView>>,
|
||||
active_ix: usize,
|
||||
pub(crate) panels: Vec<Arc<dyn PanelView>>,
|
||||
pub(crate) active_ix: usize,
|
||||
tab_bar_scroll_handle: ScrollHandle,
|
||||
|
||||
is_zoomed: bool,
|
||||
|
|
@ -185,7 +185,7 @@ impl TabPanel {
|
|||
let tab_view = cx.view().clone();
|
||||
if let Some(stack_panel) = self.stack_panel.as_ref() {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.remove_panel(tab_view, cx);
|
||||
view.remove_panel(Arc::new(tab_view), cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -462,18 +462,33 @@ impl TabPanel {
|
|||
|
||||
let stack_panel = self.stack_panel.as_ref().unwrap();
|
||||
let parent_axis = stack_panel.read(cx).axis;
|
||||
|
||||
let ix = stack_panel
|
||||
.read(cx)
|
||||
.index_of_panel(&cx.view())
|
||||
.index_of_panel(Arc::new(cx.view().clone()))
|
||||
.unwrap_or_default();
|
||||
|
||||
if parent_axis.is_vertical() && placement.is_vertical() {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
|
||||
view.insert_panel_at(
|
||||
Arc::new(new_tab_panel),
|
||||
ix,
|
||||
placement,
|
||||
size,
|
||||
dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
} else if parent_axis.is_horizontal() && placement.is_horizontal() {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx);
|
||||
view.insert_panel_at(
|
||||
Arc::new(new_tab_panel),
|
||||
ix,
|
||||
placement,
|
||||
size,
|
||||
dock_area.clone(),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// 1. Create new StackPanel with new axis
|
||||
|
|
@ -499,18 +514,18 @@ impl TabPanel {
|
|||
|
||||
new_stack_panel.update(cx, |view, cx| match placement {
|
||||
Placement::Left | Placement::Top => {
|
||||
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
|
||||
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
|
||||
view.add_panel(Arc::new(new_tab_panel), size, dock_area.clone(), cx);
|
||||
view.add_panel(Arc::new(tab_panel.clone()), None, dock_area.clone(), cx);
|
||||
}
|
||||
Placement::Right | Placement::Bottom => {
|
||||
view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx);
|
||||
view.add_panel(new_tab_panel, size, dock_area.clone(), cx);
|
||||
view.add_panel(Arc::new(tab_panel.clone()), None, dock_area.clone(), cx);
|
||||
view.add_panel(Arc::new(new_tab_panel), size, dock_area.clone(), cx);
|
||||
}
|
||||
});
|
||||
|
||||
if *stack_panel != new_stack_panel {
|
||||
stack_panel.update(cx, |view, cx| {
|
||||
view.replace_panel(tab_panel.clone(), new_stack_panel.clone(), cx);
|
||||
view.replace_panel(Arc::new(tab_panel.clone()), new_stack_panel.clone(), cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue