add dock layout component
This commit is contained in:
@@ -31,9 +31,10 @@ use gpui_native_core::toggle::Toggle;
|
||||
use gpui_native_core::tooltip::Tooltip;
|
||||
use gpui_native_data::list::{List, ListItem};
|
||||
use gpui_native_data::sidebar::{Sidebar, SidebarEntry};
|
||||
use gpui_native_data::tabs::{Tab, Tabs};
|
||||
use gpui_native_data::tabs::{Tab, Tabs, TabsVariant};
|
||||
use gpui_native_data::tree::{Tree, TreeItem};
|
||||
use gpui_native_icons::{IconAssets, IconRole, icon};
|
||||
use gpui_native_layout::dock::{Dock, DockState, PanelDescriptor, SplitDirection};
|
||||
use gpui_native_layout::resize_handle::ResizeHandle;
|
||||
use gpui_native_layout::shell::{LayoutState, Shell};
|
||||
use gpui_native_theme::{ActiveTheme as _, Size, Theme};
|
||||
@@ -51,6 +52,7 @@ struct Gallery {
|
||||
notes_state: Entity<InputState>,
|
||||
split_width: Pixels,
|
||||
layout_state: Entity<LayoutState>,
|
||||
dock_state: Entity<DockState>,
|
||||
shell_selection: Option<usize>,
|
||||
popover_open: bool,
|
||||
menu_open: bool,
|
||||
@@ -67,6 +69,8 @@ struct Gallery {
|
||||
tree_nodes: Vec<GalleryNode>,
|
||||
tree_selected: Option<usize>,
|
||||
tab_selected: usize,
|
||||
card_tabs: Vec<SharedString>,
|
||||
card_tab_selected: usize,
|
||||
sidebar_selected: Option<usize>,
|
||||
}
|
||||
|
||||
@@ -202,19 +206,51 @@ impl Gallery {
|
||||
.text_sm()
|
||||
.text_color(colors.muted_foreground);
|
||||
|
||||
let open_panel = |dock: &Entity<DockState>, id: &'static str, label: &'static str| {
|
||||
let dock = dock.clone();
|
||||
Button::new(SharedString::from(format!("gallery-dock-open-{id}")))
|
||||
.label(label)
|
||||
.variant(ButtonVariant::Secondary)
|
||||
.small()
|
||||
.on_click(move |_, window, cx| {
|
||||
dock.update(cx, |dock, cx| dock.open_panel(id, window, cx));
|
||||
})
|
||||
};
|
||||
|
||||
let center = v_flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.size_full()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.flex_none()
|
||||
.flex_wrap()
|
||||
.gap_2()
|
||||
.child(toggle("left"))
|
||||
.child(toggle("right")),
|
||||
.child(toggle("right"))
|
||||
.child(open_panel(&self.dock_state, "inbox", "Open inbox"))
|
||||
.child(open_panel(&self.dock_state, "settings", "Open settings"))
|
||||
.child({
|
||||
let dock = self.dock_state.clone();
|
||||
Button::new("gallery-dock-split")
|
||||
.label("Split active right")
|
||||
.variant(ButtonVariant::Secondary)
|
||||
.small()
|
||||
.on_click(move |_, _, cx| {
|
||||
dock.update(cx, |dock, cx| {
|
||||
if let Some(active) = dock.active_panel() {
|
||||
dock.split_panel(&active, SplitDirection::Right, cx);
|
||||
}
|
||||
});
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child("Center \u{2014} the dock lands here (L3)")
|
||||
.text_color(colors.muted_foreground);
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.overflow_hidden()
|
||||
.child(Dock::new("gallery-dock", self.dock_state.clone())),
|
||||
);
|
||||
|
||||
Shell::new("gallery-shell", state)
|
||||
.left_sidebar(left_sidebar)
|
||||
@@ -223,6 +259,30 @@ impl Gallery {
|
||||
}
|
||||
}
|
||||
|
||||
/// A stand-in panel view for the dock demo.
|
||||
struct DemoPanel {
|
||||
title: &'static str,
|
||||
body: &'static str,
|
||||
}
|
||||
|
||||
impl Render for DemoPanel {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let colors = cx.theme().colors;
|
||||
v_flex()
|
||||
.size_full()
|
||||
.gap_1()
|
||||
.p_4()
|
||||
.bg(colors.background)
|
||||
.child(div().child(self.title))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(self.body),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Gallery {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
@@ -548,7 +608,7 @@ impl Render for Gallery {
|
||||
.text_xs()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(format!(
|
||||
"left: {} · right: {} \u{2014} buttons toggle, seams resize",
|
||||
"left: {} · right: {} · dock: {} \u{2014} buttons toggle, seams resize, tabs close and split",
|
||||
if self.layout_state.read(cx).is_left_sidebar_visible() {
|
||||
"on"
|
||||
} else {
|
||||
@@ -559,6 +619,11 @@ impl Render for Gallery {
|
||||
} else {
|
||||
"off"
|
||||
},
|
||||
if self.dock_state.read(cx).active_panel().is_some() {
|
||||
"open"
|
||||
} else {
|
||||
"empty"
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
@@ -957,7 +1022,53 @@ impl Render for Gallery {
|
||||
_ => div()
|
||||
.child("Settings: keyboard focus rides the strip; arrows switch tabs.")
|
||||
.into_any_element(),
|
||||
}),
|
||||
})
|
||||
.child({
|
||||
let gallery = gallery.clone();
|
||||
let gallery_for_close = gallery.clone();
|
||||
Tabs::new("gallery-pill-tabs")
|
||||
.variant(TabsVariant::Pill)
|
||||
.selected(self.card_tab_selected)
|
||||
.on_change(move |selected, _, cx| {
|
||||
gallery.update(cx, |gallery, cx| {
|
||||
gallery.card_tab_selected = selected;
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
.on_close(move |index, _, cx| {
|
||||
gallery_for_close.update(cx, |gallery, cx| {
|
||||
if index < gallery.card_tabs.len() {
|
||||
gallery.card_tabs.remove(index);
|
||||
gallery.card_tab_selected = gallery
|
||||
.card_tab_selected
|
||||
.min(gallery.card_tabs.len().saturating_sub(1));
|
||||
cx.notify();
|
||||
}
|
||||
});
|
||||
})
|
||||
.tabs({
|
||||
let labels = self.card_tabs.clone();
|
||||
move |_, _| {
|
||||
labels
|
||||
.iter()
|
||||
.map(|label| {
|
||||
Tab::new(label.clone())
|
||||
.closeable(true)
|
||||
.busy(label == "Syncing")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
})
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(colors.muted_foreground)
|
||||
.child(format!(
|
||||
"pill variant (the signed dock tab) — {} tabs left; the ring on \"Syncing\" is the Spinner placeholder",
|
||||
self.card_tabs.len()
|
||||
)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -1653,6 +1764,39 @@ fn main() {
|
||||
let notes_state = InputState::new(cx);
|
||||
notes_state.update(cx, |state, cx| state.set_multiline(true, cx));
|
||||
let layout_state = LayoutState::new(cx);
|
||||
let dock_state = DockState::new(cx);
|
||||
dock_state.update(cx, |dock, cx| {
|
||||
dock.register_panel(
|
||||
PanelDescriptor::new("overview", "Overview", |_, cx| {
|
||||
cx.new(|_| DemoPanel {
|
||||
title: "Overview",
|
||||
body: "View state survives tab switches and pane moves.",
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
dock.register_panel(
|
||||
PanelDescriptor::new("inbox", "Inbox", |_, cx| {
|
||||
cx.new(|_| DemoPanel {
|
||||
title: "Inbox",
|
||||
body: "Right-click the tab bar for Close, Close Others, Close Pane.",
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
dock.register_panel(
|
||||
PanelDescriptor::new("settings", "Settings", |_, cx| {
|
||||
cx.new(|_| DemoPanel {
|
||||
title: "Settings",
|
||||
body: "Drag the seam between panes; double-click it to reset.",
|
||||
})
|
||||
.into()
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
let bounds = Bounds::centered(None, size(px(720.0), px(560.0)), cx);
|
||||
if let Err(err) = cx.open_window(
|
||||
WindowOptions {
|
||||
@@ -1674,6 +1818,7 @@ fn main() {
|
||||
cx.refresh_windows();
|
||||
})
|
||||
.detach();
|
||||
dock_state.update(cx, |dock, cx| dock.open_panel("overview", window, cx));
|
||||
let toasts = cx.new(|_| Toasts::default());
|
||||
let alerts = cx.new(|_| Alerts::default());
|
||||
cx.new(|cx| {
|
||||
@@ -1693,6 +1838,7 @@ fn main() {
|
||||
notes_state,
|
||||
split_width: px(200.),
|
||||
layout_state,
|
||||
dock_state,
|
||||
shell_selection: None,
|
||||
popover_open: false,
|
||||
menu_open: false,
|
||||
@@ -1708,6 +1854,12 @@ fn main() {
|
||||
list_selected: None,
|
||||
tree_selected: None,
|
||||
tab_selected: 0,
|
||||
card_tabs: vec![
|
||||
SharedString::from("Explorer"),
|
||||
SharedString::from("Syncing"),
|
||||
SharedString::from("Blame"),
|
||||
],
|
||||
card_tab_selected: 0,
|
||||
sidebar_selected: None,
|
||||
tree_nodes: vec![
|
||||
GalleryNode {
|
||||
|
||||
Reference in New Issue
Block a user