docking: a canvas that accepts splits stays protected

Hiding panels and dropping the tab strip tested for exactly
Permissions::PROTECTED, so a canvas set up as the docs describe
(accepts_splits on) was hidden and got a tab strip. Permissions::is_protected
decides from what the panel allows.
This commit is contained in:
aiongg
2026-10-20 15:07:24 +00:00
parent 07585888d6
commit 95d5cddb5f
4 changed files with 32 additions and 5 deletions
+23 -2
View File
@@ -48,17 +48,25 @@ fn workspace() -> Layout<u32> {
struct Content {
native: bool,
keep: Vec<u32>,
/// Canvas 0 accepts side splits, as an app that opens panels beside it does.
beside: bool,
drawn: Vec<u32>,
}
impl DockContent<u32> for Content {
fn body(&mut self, ui: &mut egui::Ui, _: &u32) {
fn body(&mut self, ui: &mut egui::Ui, panel: &u32) {
self.drawn.push(*panel);
ui.label("Body");
}
fn logical_label(&self, panel: &u32) -> String {
format!("Panel {panel}")
}
fn permissions(&self, panel: &u32) -> Permissions {
if *panel == 0 { Permissions::PROTECTED } else { Permissions::default() }
match *panel {
0 if self.beside => Permissions { accepts_splits: true, ..Permissions::PROTECTED },
0 => Permissions::PROTECTED,
_ => Permissions::default(),
}
}
fn allow_native(&self, _: &u32) -> bool {
self.native
@@ -459,6 +467,19 @@ fn hide_panels_keeps_protected_panels_and_the_except_set() {
assert_eq!(d.title(6).min, pos2(300.0, 84.0), "below the resize band");
}
#[test]
fn a_canvas_that_accepts_splits_is_still_protected() {
let mut d = Driver::new(workspace(), Profile::Dock);
d.content.beside = true;
d.hide = Hide::All;
d.step(vec![]);
d.content.drawn.clear();
d.step(vec![]);
assert_eq!(d.content.drawn, vec![0], "hiding every panel keeps the canvas");
assert!(d.tab(0).is_none());
assert!(d.response(DockArea::<u32>::hit_id(Id::new(AREA), &Hit::DragBar(0))).is_none());
}
#[test]
fn native_window_falls_back_in_window_when_viewports_are_embedded() {
let mut layout = workspace();
+6
View File
@@ -346,6 +346,12 @@ impl Permissions {
/// uses `Permissions { accepts_splits: true, ..Permissions::PROTECTED }`.
pub const PROTECTED: Self = Self { movable: false, floatable: false, closable: false, accepts_tabs: false, accepts_splits: false };
/// A canvas: it can neither move, close nor take tabs. `accepts_splits` and `floatable`
/// do not matter, so a canvas that opens panels beside it still counts.
pub fn is_protected(self) -> bool {
!self.movable && !self.closable && !self.accepts_tabs
}
/// Lock Workspace: nothing moves, floats or closes; tab and split drops keep their policy.
/// [`super::Workspace::apply`] applies this when the workspace is locked; an application
/// with a global lock preference applies it in its own permissions callback.
+2 -2
View File
@@ -6,7 +6,7 @@ use egui::{Context, Pos2, Rect, pos2, vec2};
use super::{GroupKind, Renderer, floating::StackHit, group::TabGroup};
use crate::docking::{
Action, Column, DockContent, DockEvent, Floating, Group, HidePanels, Layout, Node, Permissions, Profile, Zone,
Action, Column, DockContent, DockEvent, Floating, Group, HidePanels, Layout, Node, Profile, Zone,
dock_model::{Place, node_at},
};
@@ -29,7 +29,7 @@ pub(in crate::docking) fn view<'a, P: Clone + Eq + Hash>(
hide: HidePanels<'_, P>,
content: &dyn DockContent<P>,
) -> (Cow<'a, Layout<P>>, FrameState<P>) {
let protected = |p: &P| content.permissions(p) == Permissions::PROTECTED;
let protected = |p: &P| content.permissions(p).is_protected();
let hidden = |p: &P| match hide {
HidePanels::None => false,
HidePanels::All => !protected(p),
+1 -1
View File
@@ -231,7 +231,7 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> {
/// A lone protected or bare panel draws no tab strip and no drag bar.
pub(in crate::docking) fn stripless<P>(panels: &[P], content: &dyn DockContent<P>) -> bool {
match panels {
[panel] => content.permissions(panel) == Permissions::PROTECTED || content.bare(panel),
[panel] => content.permissions(panel).is_protected() || content.bare(panel),
_ => false,
}
}