dock: Simplify DockItem API. (#1732)

## Break Change

- The `DockItem::tabs` has removed `active_ix` argument, use
`.active_index()` method instead.

```diff
- DockItem::tabs(vec![panel], Some(1), &weak_self, window, cx)
+ DockItem::tabs(vec![panel], &weak_self, window, cx).active_index(1)
```
This commit is contained in:
Jason Lee 2025-12-03 17:53:37 +08:00 committed by GitHub
parent e2f022cb59
commit b70a0633ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 109 additions and 43 deletions

View file

@ -258,8 +258,7 @@ impl StoryWorkspace {
fn reset_default_layout(dock_area: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
let dock_item = Self::init_default_layout(&dock_area, window, cx);
let left_panels = DockItem::split_with_sizes(
Axis::Vertical,
let left_panels = DockItem::v_split(
vec![
DockItem::tab(
StoryContainer::panel::<ListStory>(window, cx),
@ -272,38 +271,33 @@ impl StoryWorkspace {
Arc::new(StoryContainer::panel::<ScrollbarStory>(window, cx)),
Arc::new(StoryContainer::panel::<AccordionStory>(window, cx)),
],
None,
&dock_area,
window,
cx,
),
)
.size(px(360.)),
],
vec![None, Some(px(360.))],
&dock_area,
window,
cx,
);
let bottom_panels = DockItem::split_with_sizes(
Axis::Vertical,
let bottom_panels = DockItem::v_split(
vec![DockItem::tabs(
vec![
Arc::new(StoryContainer::panel::<TooltipStory>(window, cx)),
Arc::new(StoryContainer::panel::<IconStory>(window, cx)),
],
None,
&dock_area,
window,
cx,
)],
vec![None],
&dock_area,
window,
cx,
);
let right_panels = DockItem::split_with_sizes(
Axis::Vertical,
let right_panels = DockItem::v_split(
vec![
DockItem::tab(
StoryContainer::panel::<ImageStory>(window, cx),
@ -318,7 +312,6 @@ impl StoryWorkspace {
cx,
),
],
vec![None],
&dock_area,
window,
cx,
@ -340,8 +333,7 @@ impl StoryWorkspace {
window: &mut Window,
cx: &mut App,
) -> DockItem {
DockItem::split_with_sizes(
Axis::Vertical,
DockItem::v_split(
vec![DockItem::tabs(
vec![
Arc::new(StoryContainer::panel::<ButtonStory>(window, cx)),
@ -364,12 +356,10 @@ impl StoryWorkspace {
Arc::new(StoryContainer::panel::<FormStory>(window, cx)),
Arc::new(StoryContainer::panel::<NotificationStory>(window, cx)),
],
None,
&dock_area,
window,
cx,
)],
vec![None],
&dock_area,
window,
cx,

View file

@ -3,15 +3,15 @@
use std::{ops::Deref, sync::Arc};
use gpui::{
div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Empty, Entity,
IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, Style,
StyleRefinement, Styled as _, WeakEntity, Window,
App, AppContext, Axis, Context, Element, Empty, Entity, IntoElement, MouseMoveEvent,
MouseUpEvent, ParentElement as _, Pixels, Point, Render, Style, StyleRefinement, Styled as _,
WeakEntity, Window, div, prelude::FluentBuilder as _, px,
};
use serde::{Deserialize, Serialize};
use crate::{
resizable::{resize_handle, PANEL_MIN_SIZE},
StyledExt,
resizable::{PANEL_MIN_SIZE, resize_handle},
};
use super::{DockArea, DockItem, PanelView, TabPanel};
@ -91,6 +91,7 @@ impl Dock {
});
let panel = DockItem::Tabs {
size: None,
items: Vec::new(),
active_ix: 0,
view: panel.clone(),

View file

@ -78,20 +78,31 @@ pub enum DockItem {
/// Split layout
Split {
axis: Axis,
/// Self size, only used for build split panels
size: Option<Pixels>,
items: Vec<DockItem>,
/// Items sizes
sizes: Vec<Option<Pixels>>,
view: Entity<StackPanel>,
},
/// Tab layout
Tabs {
/// Self size, only used for build split panels
size: Option<Pixels>,
items: Vec<Arc<dyn PanelView>>,
active_ix: usize,
view: Entity<TabPanel>,
},
/// Panel layout
Panel { view: Arc<dyn PanelView> },
Panel {
/// Self size, only used for build split panels
size: Option<Pixels>,
view: Arc<dyn PanelView>,
},
/// Tiles layout
Tiles {
/// Self size, only used for build split panels
size: Option<Pixels>,
items: Vec<TileItem>,
view: Entity<Tiles>,
},
@ -122,7 +133,45 @@ impl std::fmt::Debug for DockItem {
}
impl DockItem {
/// Create DockItem with split layout, each item of panel have equal size.
/// Get the size of the DockItem.
fn get_size(&self) -> Option<Pixels> {
match self {
Self::Split { size, .. } => *size,
Self::Tabs { size, .. } => *size,
Self::Panel { size, .. } => *size,
Self::Tiles { size, .. } => *size,
}
}
/// Set size for the DockItem.
pub fn size(mut self, new_size: impl Into<Pixels>) -> Self {
let new_size: Option<Pixels> = Some(new_size.into());
match self {
Self::Split { ref mut size, .. } => *size = new_size,
Self::Tabs { ref mut size, .. } => *size = new_size,
Self::Tiles { ref mut size, .. } => *size = new_size,
Self::Panel { ref mut size, .. } => *size = new_size,
}
self
}
/// Set active index for the DockItem, only valid for [`DockItem::Tabs`].
pub fn active_index(mut self, new_active_ix: usize) -> Self {
debug_assert!(
matches!(self, Self::Tabs { .. }),
"active_ix can only be set for DockItem::Tabs"
);
if let Self::Tabs {
ref mut active_ix, ..
} = self
{
*active_ix = new_active_ix;
}
self
}
/// Create DockItem::Split with given split layout.
pub fn split(
axis: Axis,
items: Vec<DockItem>,
@ -130,10 +179,30 @@ impl DockItem {
window: &mut Window,
cx: &mut App,
) -> Self {
let sizes = vec![None; items.len()];
let sizes = items.iter().map(|item| item.get_size()).collect();
Self::split_with_sizes(axis, items, sizes, dock_area, window, cx)
}
/// Create DockItem with vertical split layout.
pub fn v_split(
items: Vec<DockItem>,
dock_area: &WeakEntity<DockArea>,
window: &mut Window,
cx: &mut App,
) -> Self {
Self::split(Axis::Vertical, items, dock_area, window, cx)
}
/// Create DockItem with horizontal split layout.
pub fn h_split(
items: Vec<DockItem>,
dock_area: &WeakEntity<DockArea>,
window: &mut Window,
cx: &mut App,
) -> Self {
Self::split(Axis::Horizontal, items, dock_area, window, 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.
@ -176,6 +245,7 @@ impl DockItem {
Self::Split {
axis,
size: None,
items,
sizes,
view: stack_panel,
@ -184,7 +254,10 @@ impl DockItem {
/// Create DockItem with panel layout
pub fn panel(panel: Arc<dyn PanelView>) -> Self {
Self::Panel { view: panel }
Self::Panel {
size: None,
view: panel,
}
}
/// Create DockItem with tiles layout
@ -209,7 +282,7 @@ impl DockItem {
TileItem::new(Arc::new(view), meta.bounds).z_index(meta.z_index);
tiles.add_item(tile_item, dock_area, window, cx);
}
DockItem::Panel { view } => {
DockItem::Panel { view, .. } => {
let meta: TileMeta = metas[ix].into();
let tile_item =
TileItem::new(view.clone(), meta.bounds).z_index(meta.z_index);
@ -235,6 +308,7 @@ impl DockItem {
});
Self::Tiles {
size: None,
items: tile_panel.read(cx).panels.clone(),
view: tile_panel,
}
@ -245,7 +319,6 @@ impl DockItem {
/// The `active_ix` is the index of the active tab, if `None` the first tab is active.
pub fn tabs(
items: Vec<Arc<dyn PanelView>>,
active_ix: Option<usize>,
dock_area: &WeakEntity<DockArea>,
window: &mut Window,
cx: &mut App,
@ -254,7 +327,7 @@ impl DockItem {
for item in items.into_iter() {
new_items.push(item)
}
Self::new_tabs(new_items, active_ix, dock_area, window, cx)
Self::new_tabs(new_items, None, dock_area, window, cx)
}
pub fn tab<P: Panel>(
@ -284,6 +357,7 @@ impl DockItem {
});
Self::Tabs {
size: None,
items,
active_ix,
view: tab_panel,
@ -307,7 +381,7 @@ impl DockItem {
items.iter().find_map(|item| item.find_panel(panel.clone()))
}
Self::Tabs { items, .. } => items.iter().find(|item| *item == &panel).cloned(),
Self::Panel { view } => Some(view.clone()),
Self::Panel { view, .. } => Some(view.clone()),
Self::Tiles { items, .. } => items.iter().find_map(|item| {
if &item.panel == &panel {
Some(item.panel.clone())
@ -346,13 +420,13 @@ impl DockItem {
}
// Unable to find tabs, create new tabs
let new_item = Self::tabs(vec![panel.clone()], None, dock_area, window, cx);
let new_item = Self::tabs(vec![panel.clone()], dock_area, window, cx);
items.push(new_item.clone());
view.update(cx, |stack_panel, cx| {
stack_panel.add_panel(new_item.view(), None, dock_area.clone(), window, cx);
});
}
Self::Tiles { view, items } => {
Self::Tiles { view, items, .. } => {
let tile_item = TileItem::new(
Arc::new(cx.new(|cx| {
let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
@ -411,7 +485,7 @@ impl DockItem {
}
}
DockItem::Tiles { .. } => {}
DockItem::Panel { view } => view.set_active(!collapsed, window, cx),
DockItem::Panel { view, .. } => view.set_active(!collapsed, window, cx),
}
}
@ -447,6 +521,7 @@ impl DockArea {
let dock_item = DockItem::Split {
axis: Axis::Horizontal,
size: None,
items: vec![],
sizes: vec![],
view: stack_panel.clone(),
@ -726,7 +801,7 @@ impl DockArea {
dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
} else {
self.set_left_dock(
DockItem::tabs(vec![panel], None, &weak_self, window, cx),
DockItem::tabs(vec![panel], &weak_self, window, cx),
None,
true,
window,
@ -739,7 +814,7 @@ impl DockArea {
dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
} else {
self.set_bottom_dock(
DockItem::tabs(vec![panel], None, &weak_self, window, cx),
DockItem::tabs(vec![panel], &weak_self, window, cx),
None,
true,
window,
@ -752,7 +827,7 @@ impl DockArea {
dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
} else {
self.set_right_dock(
DockItem::tabs(vec![panel], None, &weak_self, window, cx),
DockItem::tabs(vec![panel], &weak_self, window, cx),
None,
true,
window,

View file

@ -1,4 +1,4 @@
use gpui::{point, px, size, App, AppContext, Axis, Bounds, Entity, Pixels, WeakEntity, Window};
use gpui::{App, AppContext, Axis, Bounds, Entity, Pixels, WeakEntity, Window, point, px, size};
use itertools::Itertools as _;
use serde::{Deserialize, Serialize};
@ -219,7 +219,7 @@ impl PanelState {
})
.collect_vec();
DockItem::tabs(items, Some(active_index), &dock_area, window, cx)
DockItem::tabs(items, &dock_area, window, cx).active_index(active_index)
}
PanelInfo::Panel(_) => {
let view = PanelRegistry::build_panel(
@ -230,7 +230,7 @@ impl PanelState {
window,
cx,
);
DockItem::tabs(vec![view.into()], None, &dock_area, window, cx)
DockItem::tabs(vec![view.into()], &dock_area, window, cx)
}
PanelInfo::Tiles { metas } => DockItem::tiles(items, metas, &dock_area, window, cx),
}

View file

@ -57,7 +57,7 @@ impl AppMenuBar {
} else {
selected_ix.saturating_sub(1)
};
self.set_selected_ix(Some(new_ix), window, cx);
self.set_selected_index(Some(new_ix), window, cx);
}
fn on_move_right(&mut self, _: &SelectRight, window: &mut Window, cx: &mut Context<Self>) {
@ -70,14 +70,14 @@ impl AppMenuBar {
} else {
selected_ix + 1
};
self.set_selected_ix(Some(new_ix), window, cx);
self.set_selected_index(Some(new_ix), window, cx);
}
fn on_cancel(&mut self, _: &Cancel, window: &mut Window, cx: &mut Context<Self>) {
self.set_selected_ix(None, window, cx);
self.set_selected_index(None, window, cx);
}
fn set_selected_ix(&mut self, ix: Option<usize>, _: &mut Window, cx: &mut Context<Self>) {
fn set_selected_index(&mut self, ix: Option<usize>, _: &mut Window, cx: &mut Context<Self>) {
self.selected_ix = ix;
cx.notify();
}
@ -189,7 +189,7 @@ impl AppMenu {
_ = self.menu_bar.update(cx, |state, cx| {
let new_ix = if is_selected { None } else { Some(self.ix) };
state.set_selected_ix(new_ix, window, cx);
state.set_selected_index(new_ix, window, cx);
});
}
@ -204,7 +204,7 @@ impl AppMenu {
}
_ = self.menu_bar.update(cx, |state, cx| {
state.set_selected_ix(Some(self.ix), window, cx);
state.set_selected_index(Some(self.ix), window, cx);
});
}
}