From 193754502f03d05d8f03c36f8df0dfa99e861189 Mon Sep 17 00:00:00 2001 From: abc2cb Date: Thu, 22 Oct 2026 01:11:05 +0300 Subject: [PATCH] M3: the Tools panel has no tab strip while docked alone (#2244) A "Tools" tab sat over the tool panel although nothing else shared its group; Acrobat's tool pane has no such strip. Tools docked on its own is now drawn bare. Grouped with another panel, or floating, it keeps its tab so it can be dragged away again, and its side stays a preference and a dock command. The docking test groups Inspector with Tools by command, since there is no tab to drop on. Co-authored-by: abc2cb <271337822+abc2cb@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com> --- crates/ui-egui/src/docking.rs | 7 ++++++- crates/ui-egui/tests/control.rs | 16 +++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/ui-egui/src/docking.rs b/crates/ui-egui/src/docking.rs index 58fd71a678..4531709046 100644 --- a/crates/ui-egui/src/docking.rs +++ b/crates/ui-egui/src/docking.rs @@ -396,12 +396,17 @@ pub fn show(app: &mut crate::PdfCraftApp, ui: &mut egui::Ui) { // Availability is a render projection: the saved group, selection and width survive Home. let _ = rendered.apply(Action::Close { panel: Panel::Inspector }); } + // Tools docked on its own is drawn bare, without a tab strip, like Acrobat's tool pane. Grouped + // with another panel, or floating, it keeps its tab so it can be dragged away again; moving it + // to the other side stays a preference and a command. + let bare_tools = rendered.location(&Panel::Tools).is_ok_and(|l| l.floating.is_none() && matches!(l.placement, Placement::Split(_))); + let render_permissions = move |panel: &Panel| if bare_tools && *panel == Panel::Tools { Permissions::PROTECTED } else { permissions(panel) }; let output = DockArea::new(egui::Id::new("pdfcraft-docking")).show_with_limits( ui, &rendered, &style, |panel| crate::i18n::t(panel.label()).into(), - permissions, + render_permissions, panel_limits, |ui, panel| match panel { Panel::Canvas => { diff --git a/crates/ui-egui/tests/control.rs b/crates/ui-egui/tests/control.rs index 20fa4cb230..7de5c59997 100644 --- a/crates/ui-egui/tests/control.rs +++ b/crates/ui-egui/tests/control.rs @@ -1299,19 +1299,13 @@ fn docking_pointer_group_float_close_restore_and_document_commands() { let (mut h, c) = harness(); ok(&mut h, &c, "ui.set", json!({"key":"panel", "value":"bookmarks"})); h.run_steps(4); - let from = h.get_by_role_and_label(egui::accesskit::Role::Tab, "Inspector").rect().center(); - let to = h.get_by_role_and_label(egui::accesskit::Role::Tab, "Tools").rect().center(); - h.event(egui::Event::PointerMoved(from)); - h.run_steps(2); - h.event(egui::Event::PointerButton { pos: from, button: egui::PointerButton::Primary, pressed: true, modifiers: egui::Modifiers::NONE }); - h.run_steps(2); - for step in 1..=8 { - h.event(egui::Event::PointerMoved(from.lerp(to, step as f32 / 8.0))); - h.run_steps(1); - } - h.event(egui::Event::PointerButton { pos: to, button: egui::PointerButton::Primary, pressed: false, modifiers: egui::Modifiers::NONE }); + // Tools docked alone has no tab strip (as Acrobat's tool pane), so it is grouped by command. + assert!(h.query_by_role_and_label(egui::accesskit::Role::Tab, "Tools").is_none()); + ok(&mut h, &c, "ui.dock", json!({"operation":"move", "panel":"inspector", "target":"tools", "zone":"center"})); h.run_steps(4); assert_eq!(h.state().docking.layout.location(&Panel::Inspector).unwrap().anchor, Some(Panel::Tools)); + // Grouped, Tools has its tab again. + h.get_by_role_and_label(egui::accesskit::Role::Tab, "Tools"); h.get_by_role_and_label(egui::accesskit::Role::Tab, "Inspector").click_button(egui::PointerButton::Secondary); h.run_steps(3); h.get_by_label("Float panel").click();