update dock
This commit is contained in:
Generated
+87
-4
@@ -1363,6 +1363,15 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -1714,7 +1723,7 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case 0.10.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
@@ -3486,6 +3495,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-task",
|
"async-task",
|
||||||
|
"backtrace",
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"bitflags 2.13.1",
|
"bitflags 2.13.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3516,6 +3526,7 @@ dependencies = [
|
|||||||
"pollster 0.4.0",
|
"pollster 0.4.0",
|
||||||
"postage",
|
"postage",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
"proptest",
|
||||||
"rand 0.9.5",
|
"rand 0.9.5",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"refineable",
|
"refineable",
|
||||||
@@ -4498,7 +4509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
|
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder-lite",
|
"byteorder-lite",
|
||||||
"quick-error",
|
"quick-error 2.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6613,6 +6624,36 @@ dependencies = [
|
|||||||
"syn 2.0.119",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proptest"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set 0.8.0",
|
||||||
|
"bit-vec 0.8.0",
|
||||||
|
"bitflags 2.13.1",
|
||||||
|
"num-traits",
|
||||||
|
"proptest-macro",
|
||||||
|
"rand 0.9.5",
|
||||||
|
"rand_chacha 0.9.0",
|
||||||
|
"rand_xorshift",
|
||||||
|
"regex-syntax",
|
||||||
|
"rusty-fork",
|
||||||
|
"tempfile",
|
||||||
|
"unarray",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proptest-macro"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case 0.11.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psm"
|
name = "psm"
|
||||||
version = "0.1.32"
|
version = "0.1.32"
|
||||||
@@ -6660,6 +6701,12 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-error"
|
name = "quick-error"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -6838,6 +6885,15 @@ dependencies = [
|
|||||||
"rand_core 0.10.1",
|
"rand_core 0.10.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "range-alloc"
|
name = "range-alloc"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -6894,7 +6950,7 @@ dependencies = [
|
|||||||
"avif-serialize",
|
"avif-serialize",
|
||||||
"imgref",
|
"imgref",
|
||||||
"loop9",
|
"loop9",
|
||||||
"quick-error",
|
"quick-error 2.0.1",
|
||||||
"rav1e",
|
"rav1e",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rgb",
|
"rgb",
|
||||||
@@ -7446,6 +7502,18 @@ version = "1.0.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rusty-fork"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"quick-error 1.2.3",
|
||||||
|
"tempfile",
|
||||||
|
"wait-timeout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustybuzz"
|
name = "rustybuzz"
|
||||||
version = "0.20.1"
|
version = "0.20.1"
|
||||||
@@ -8592,7 +8660,7 @@ dependencies = [
|
|||||||
"fax",
|
"fax",
|
||||||
"flate2",
|
"flate2",
|
||||||
"half",
|
"half",
|
||||||
"quick-error",
|
"quick-error 2.0.1",
|
||||||
"weezl",
|
"weezl",
|
||||||
"zune-jpeg 0.5.15",
|
"zune-jpeg 0.5.15",
|
||||||
]
|
]
|
||||||
@@ -9398,6 +9466,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unarray"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
@@ -9701,6 +9775,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wait-timeout"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "waker-fn"
|
name = "waker-fn"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|||||||
@@ -9,3 +9,6 @@ publish.workspace = true
|
|||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
gpui-component.workspace = true
|
gpui-component.workspace = true
|
||||||
gpui-base.workspace = true
|
gpui-base.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
|
|||||||
@@ -97,15 +97,6 @@ fn zoom_control(group: &TabGroupContext, cx: &App) -> Option<PanelControl> {
|
|||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The payload for dragging the tab at `ix` out of its group, or `None` when
|
|
||||||
/// this group must not be rearranged.
|
|
||||||
fn tab_drag(group: &TabGroupContext, ix: usize, cx: &App) -> Option<DragPanel> {
|
|
||||||
group
|
|
||||||
.is_draggable()
|
|
||||||
.then(|| group.drag_panel(ix, cx))
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The left-most, top-most tab group in a container — where a left dock's
|
/// The left-most, top-most tab group in a container — where a left dock's
|
||||||
/// collapse affordance goes. Mirrors the old `StackPanel::left_top_tab_panel`.
|
/// collapse affordance goes. Mirrors the old `StackPanel::left_top_tab_panel`.
|
||||||
fn left_top_group(node: &PaneNode) -> Option<NodeId> {
|
fn left_top_group(node: &PaneNode) -> Option<NodeId> {
|
||||||
@@ -183,6 +174,37 @@ impl SignedTabGroupSkin {
|
|||||||
left == group.node() && group.panels().len() == 1
|
left == group.node() && group.panels().len() == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The bottom or right dock whose root tab group this group is, if any.
|
||||||
|
///
|
||||||
|
/// Base bars a dock's only group from being dragged or closed, so the
|
||||||
|
/// dock cannot be emptied. A bottom/right panel is supposed to be
|
||||||
|
/// closable and movable, though — the vendored dock allowed exactly that
|
||||||
|
/// — so the skin recognizes the group and routes around the bar.
|
||||||
|
fn is_dock_root_group(&self, group: &TabGroupContext, cx: &App) -> Option<DockPlacement> {
|
||||||
|
let area = self.shared.area().upgrade()?;
|
||||||
|
let area = area.read(cx);
|
||||||
|
[DockPlacement::Bottom, DockPlacement::Right]
|
||||||
|
.into_iter()
|
||||||
|
.find(|placement| {
|
||||||
|
area.layout(*placement)
|
||||||
|
.is_some_and(|tree| tree.root().id() == group.node())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The drag payload for the tab at `ix`, or `None` when this group must
|
||||||
|
/// not be rearranged. A locked group is never draggable; a group that is
|
||||||
|
/// a bottom/right dock's only content still is, because the center is
|
||||||
|
/// always there to land in.
|
||||||
|
fn tab_drag(&self, group: &TabGroupContext, ix: usize, cx: &App) -> Option<DragPanel> {
|
||||||
|
if group.is_locked() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !group.is_draggable() && self.is_dock_root_group(group, cx).is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
group.drag_panel(ix, cx)
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether a dock's collapse affordance belongs in *this* group's tab
|
/// Whether a dock's collapse affordance belongs in *this* group's tab
|
||||||
/// bar, and which way it points. `None` means this group draws none.
|
/// bar, and which way it points. `None` means this group draws none.
|
||||||
fn dock_toggle_button(
|
fn dock_toggle_button(
|
||||||
@@ -309,7 +331,12 @@ impl SignedTabGroupSkin {
|
|||||||
let control = zoom_control(group, cx);
|
let control = zoom_control(group, cx);
|
||||||
let toolbar_zoom = control.is_some_and(|control| control.toolbar_visible());
|
let toolbar_zoom = control.is_some_and(|control| control.toolbar_visible());
|
||||||
let menu_zoom = control.is_some_and(|control| control.menu_visible());
|
let menu_zoom = control.is_some_and(|control| control.menu_visible());
|
||||||
let closable = group.can_close();
|
// A bottom/right dock's only panel cannot be closed through the
|
||||||
|
// group (base keeps a dock's last group), but the skin handles that
|
||||||
|
// close by removing the whole dock, so the item is offered.
|
||||||
|
let closable = group.can_close()
|
||||||
|
|| (self.is_dock_root_group(group, cx).is_some()
|
||||||
|
&& group.active_panel().is_some_and(|panel| panel.closable(cx)));
|
||||||
let buttons = handle.and_then(|handle| handle.toolbar_buttons(window, cx));
|
let buttons = handle.and_then(|handle| handle.toolbar_buttons(window, cx));
|
||||||
let panel = handle.map(|handle| handle.panel());
|
let panel = handle.map(|handle| handle.panel());
|
||||||
|
|
||||||
@@ -395,7 +422,7 @@ impl SignedTabGroupSkin {
|
|||||||
) -> Tab {
|
) -> Tab {
|
||||||
let collapsed = group.is_collapsed();
|
let collapsed = group.is_collapsed();
|
||||||
let droppable = group.is_droppable();
|
let droppable = group.is_droppable();
|
||||||
let drag = tab_drag(group, ix, cx);
|
let drag = self.tab_drag(group, ix, cx);
|
||||||
let handle = PanelHandle::of(&panel);
|
let handle = PanelHandle::of(&panel);
|
||||||
|
|
||||||
Tab::new(ix)
|
Tab::new(ix)
|
||||||
@@ -546,6 +573,19 @@ impl SignedTabGroupSkin {
|
|||||||
impl TabGroupRenderer for SignedTabGroupSkin {
|
impl TabGroupRenderer for SignedTabGroupSkin {
|
||||||
fn frame(&self, group: &TabGroupContext, _: &mut Window, cx: &mut App) -> Stateful<Div> {
|
fn frame(&self, group: &TabGroupContext, _: &mut Window, cx: &mut App) -> Stateful<Div> {
|
||||||
let control = zoom_control(group, cx);
|
let control = zoom_control(group, cx);
|
||||||
|
// An emptied group — its last panel was dragged away — draws nothing,
|
||||||
|
// so an emptied dock does not leave a bare tab bar behind.
|
||||||
|
if group.panels().is_empty() {
|
||||||
|
return div().id("tab-panel");
|
||||||
|
}
|
||||||
|
// Closing the only panel of a bottom/right dock would leave an empty
|
||||||
|
// dock, which base refuses through the group. The skin removes the
|
||||||
|
// whole dock instead — the vendored dock's close took its split
|
||||||
|
// group away just the same.
|
||||||
|
let dock_to_remove = (group.panels().len() <= 1)
|
||||||
|
.then(|| self.is_dock_root_group(group, cx))
|
||||||
|
.flatten();
|
||||||
|
let shared = self.shared.clone();
|
||||||
|
|
||||||
// `v_flex`, not `div`: gpui's default display is Block, and in block
|
// `v_flex`, not `div`: gpui's default display is Block, and in block
|
||||||
// layout a child's `flex_grow` is ignored — the content region below
|
// layout a child's `flex_grow` is ignored — the content region below
|
||||||
@@ -574,12 +614,23 @@ impl TabGroupRenderer for SignedTabGroupSkin {
|
|||||||
})
|
})
|
||||||
.on_action({
|
.on_action({
|
||||||
let group = group.clone();
|
let group = group.clone();
|
||||||
|
let shared = shared.clone();
|
||||||
move |_: &ClosePanel, window, cx| {
|
move |_: &ClosePanel, window, cx| {
|
||||||
let Some(panel) = group.active_panel() else {
|
let Some(panel) = group.active_panel() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if !panel.closable(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let panel = panel.panel_id(cx);
|
let panel = panel.panel_id(cx);
|
||||||
group.close(panel, window, cx);
|
match dock_to_remove {
|
||||||
|
Some(placement) => {
|
||||||
|
_ = shared.area().update(cx, |area, cx| {
|
||||||
|
area.remove_dock(placement, window, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => group.close(panel, window, cx),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -599,6 +650,12 @@ impl TabGroupRenderer for SignedTabGroupSkin {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
|
// An emptied group draws no tab bar; the app prunes the emptied
|
||||||
|
// bottom/right dock a moment later.
|
||||||
|
if group.panels().is_empty() {
|
||||||
|
return Empty.into_any_element();
|
||||||
|
}
|
||||||
|
|
||||||
// The sidebar group draws no chrome at all, like the vendored dock's
|
// The sidebar group draws no chrome at all, like the vendored dock's
|
||||||
// bare `DockItem::Panel`.
|
// bare `DockItem::Panel`.
|
||||||
if self.is_plain_sidebar_group(group, cx) {
|
if self.is_plain_sidebar_group(group, cx) {
|
||||||
@@ -614,15 +671,21 @@ impl TabGroupRenderer for SignedTabGroupSkin {
|
|||||||
let right_dock_button = self.dock_toggle_button(DockPlacement::Right, group, cx);
|
let right_dock_button = self.dock_toggle_button(DockPlacement::Right, group, cx);
|
||||||
let is_bottom_dock = bottom_dock_button.is_some();
|
let is_bottom_dock = bottom_dock_button.is_some();
|
||||||
|
|
||||||
// macOS: the traffic lights overlay the window's top-left corner. The
|
// macOS: the traffic lights overlay the window's top-left corner. Only
|
||||||
// left dock (sidebar) normally clears them; when it is closed or
|
// the group whose tab bar actually sits under them must reserve the
|
||||||
// absent, the center tab bar must reserve the space itself.
|
// space: the left dock (sidebar) normally clears them, and when it is
|
||||||
|
// closed or absent it is the center's left-most, top-most tab group
|
||||||
|
// that is in the corner. A bottom or right dock is never there, and
|
||||||
|
// neither is the right panel of a center split.
|
||||||
let needs_traffic_light_padding = cfg!(target_os = "macos")
|
let needs_traffic_light_padding = cfg!(target_os = "macos")
|
||||||
&& self
|
&& self.shared.area().upgrade().is_some_and(|area| {
|
||||||
.shared
|
let area = area.read(cx);
|
||||||
.area()
|
!area.is_dock_open(DockPlacement::Left)
|
||||||
.upgrade()
|
&& area
|
||||||
.is_none_or(|area| !area.read(cx).is_dock_open(DockPlacement::Left));
|
.layout(DockPlacement::Center)
|
||||||
|
.and_then(|tree| left_top_group(tree.root()))
|
||||||
|
== Some(group.node())
|
||||||
|
});
|
||||||
|
|
||||||
// Bring a newly displayed tab into view. The group owns selection
|
// Bring a newly displayed tab into view. The group owns selection
|
||||||
// now, so the skin notices the change rather than being told about it.
|
// now, so the skin notices the change rather than being told about it.
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
//! Render-path smoke tests: the skin reads the dock area while rendering, and
|
||||||
|
//! GPUI panics if an entity is read while it is leased (being updated). These
|
||||||
|
//! pin that the first frame — docks, groups, tab bars — renders without
|
||||||
|
//! tripping the lease check.
|
||||||
|
|
||||||
|
use dock::{BasePanel, Panel, SignedDockSkin, panel_handle};
|
||||||
|
use gpui::{
|
||||||
|
App, AppContext, Context, Empty, EventEmitter, FocusHandle, Focusable, IntoElement, Render,
|
||||||
|
TestAppContext, Window,
|
||||||
|
};
|
||||||
|
use gpui_base::dock::{DockArea, DockLayout, DockPlacement, PanelEvent};
|
||||||
|
|
||||||
|
struct Probe {
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Probe {
|
||||||
|
fn new(cx: &mut Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
focus_handle: cx.focus_handle(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasePanel for Probe {
|
||||||
|
fn panel_name(&self) -> &'static str {
|
||||||
|
"Probe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Panel for Probe {
|
||||||
|
fn title(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
"Probe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<PanelEvent> for Probe {}
|
||||||
|
|
||||||
|
impl Focusable for Probe {
|
||||||
|
fn focus_handle(&self, _: &App) -> FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Probe {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn the_first_frame_renders_the_area_and_its_docks(cx: &mut TestAppContext) {
|
||||||
|
cx.update(|cx| {
|
||||||
|
gpui_component::init(cx);
|
||||||
|
});
|
||||||
|
let (area, cx) = cx.add_window_view(|window, cx| {
|
||||||
|
let skin = SignedDockSkin::new(cx);
|
||||||
|
DockArea::new("test", None, window, cx).with_renderer(skin)
|
||||||
|
});
|
||||||
|
|
||||||
|
let bottom = cx.update(|_, cx| cx.new(Probe::new));
|
||||||
|
cx.update(|window, cx| {
|
||||||
|
let left = cx.new(Probe::new);
|
||||||
|
let center = cx.new(Probe::new);
|
||||||
|
|
||||||
|
area.update(cx, |area, cx| {
|
||||||
|
area.set_dock(
|
||||||
|
DockPlacement::Left,
|
||||||
|
DockLayout::tabs().panel_view(panel_handle(left), cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
area.set_center(
|
||||||
|
DockLayout::tabs().panel_view(panel_handle(center), cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
area.set_dock(
|
||||||
|
DockPlacement::Bottom,
|
||||||
|
DockLayout::tabs().panel_view(panel_handle(bottom.clone()), cx),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// The first frame walks every render hook — the dock frame, each group's
|
||||||
|
// tab bar, the toolbar — all of which read the dock area.
|
||||||
|
cx.update(|window, cx| window.draw(cx).clear(cx));
|
||||||
|
|
||||||
|
// Emptying a dock leaves an empty group behind; its render must also be
|
||||||
|
// safe (and draw nothing).
|
||||||
|
cx.update(|window, cx| {
|
||||||
|
area.update(cx, |area, cx| {
|
||||||
|
area.remove_panel(bottom, window, cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cx.update(|window, cx| window.draw(cx).clear(cx));
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use dock::{DockArea, DockLayout, DockPlacement, SignedDockSkin, panel_handle};
|
use dock::{DockArea, DockEvent, DockLayout, DockPlacement, SignedDockSkin, panel_handle};
|
||||||
use gpui::prelude::*;
|
use gpui::prelude::*;
|
||||||
use gpui::{Context, Entity, Render, Subscription, Window, div, px};
|
use gpui::{Context, Entity, Render, Subscription, Window, div, px};
|
||||||
use gpui_component::{Root, StyledExt, Theme};
|
use gpui_component::{Root, StyledExt, Theme};
|
||||||
@@ -40,6 +40,32 @@ impl Workspace {
|
|||||||
|
|
||||||
let mut subscriptions = vec![];
|
let mut subscriptions = vec![];
|
||||||
|
|
||||||
|
// A bottom/right dock whose last panel was dragged away is removed
|
||||||
|
// entirely: base keeps the emptied region, which would otherwise
|
||||||
|
// linger as a bare strip. Deferred, because the event arrives while
|
||||||
|
// the area is mid-update.
|
||||||
|
let dock_for_pruning = dock.clone();
|
||||||
|
subscriptions.push(cx.subscribe_in(
|
||||||
|
&dock,
|
||||||
|
window,
|
||||||
|
move |_, _, event: &DockEvent, window, cx| {
|
||||||
|
if !matches!(event, DockEvent::LayoutChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let dock = dock_for_pruning.clone();
|
||||||
|
cx.spawn_in(window, async move |_, window| {
|
||||||
|
_ = dock.update_in(window, |area, window, cx| {
|
||||||
|
for placement in [DockPlacement::Bottom, DockPlacement::Right] {
|
||||||
|
if area.is_empty(placement, cx) {
|
||||||
|
area.remove_dock(placement, window, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
subscriptions.push(cx.observe_window_appearance(window, |_this, window, cx| {
|
subscriptions.push(cx.observe_window_appearance(window, |_this, window, cx| {
|
||||||
Theme::sync_system_appearance(Some(window), cx);
|
Theme::sync_system_appearance(Some(window), cx);
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user