From 399ab57d45f21bc2b2c118eb822922c9ebdb0ca1 Mon Sep 17 00:00:00 2001 From: Craig Constable Date: Thu, 24 Sep 2026 09:23:54 +1000 Subject: [PATCH] fix(window): scope taskbar drag clamping to runtime placements - Preserve authored taskbar positions near the tray - Add regression coverage for serialization and docking behavior --- src/theme_engine.rs | 4 ++ src/window/placement_regression_tests.rs | 74 ++++++++++++++++++++++++ src/window/positioning.rs | 4 +- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/theme_engine.rs b/src/theme_engine.rs index 42586965..5164fe11 100644 --- a/src/theme_engine.rs +++ b/src/theme_engine.rs @@ -162,6 +162,9 @@ pub struct Placement { /// Runtime-only layout host retained during undocking; never edits the theme. #[serde(skip)] pub host_dimensions: Option<(u32, u32)>, + /// Runtime-only protection for drag anchoring; authored placements stay exact. + #[serde(skip)] + pub clamp_taskbar_drag: bool, #[serde(default)] pub reference: ReferenceTarget, /// Controls which native shell host owns a root surface. Older themes did @@ -2721,6 +2724,7 @@ impl Default for Placement { fn default() -> Self { Self { host_dimensions: None, + clamp_taskbar_drag: false, reference: ReferenceTarget::default(), nest: SurfaceNest::Taskbar, horizontal: HorizontalAnchor::Left, diff --git a/src/window/placement_regression_tests.rs b/src/window/placement_regression_tests.rs index 820c1921..14a75b47 100644 --- a/src/window/placement_regression_tests.rs +++ b/src/window/placement_regression_tests.rs @@ -712,6 +712,80 @@ fn floating_taskbar_reference_is_not_clamped_to_tray() { assert_eq!(rect.left, 1700); } +#[test] +fn authored_taskbar_placements_keep_their_position_near_the_tray() { + let monitor = RECT { + left: 0, + top: 0, + right: 1920, + bottom: 1080, + }; + for horizontal in [true, false] { + let taskbar = if horizontal { + RECT { + top: 1032, + ..monitor + } + } else { + RECT { + right: 60, + ..monitor + } + }; + let tray = if horizontal { + RECT { + left: 1600, + ..taskbar + } + } else { + RECT { + top: 900, + ..taskbar + } + }; + // Exercise both edge alignment and the same anchors used by drag docking. + for edge_aligned in [true, false] { + let placement: theme_engine::Placement = serde_json::from_value(serde_json::json!({ + "reference": { "region": "taskbar", "display": 0 }, + "nest": "taskbar", + "horizontal": if horizontal && edge_aligned { "right" } else { "left" }, + "vertical": if horizontal || edge_aligned { "bottom" } else { "top" }, + "surface_horizontal": if horizontal && edge_aligned { "right" } else { "left" }, + "surface_vertical": if horizontal || edge_aligned { "bottom" } else { "top" }, + "offset_x": if horizontal && !edge_aligned { 1720 } else { 0 }, + "offset_y": if !horizontal && !edge_aligned { 980 } else { 0 } + })) + .unwrap(); + for tray in [Some(tray), None] { + let rect = positioning::surface_screen_rect( + &placement, + if horizontal { 200 } else { 50 }, + if horizontal { 46 } else { 100 }, + 1.0, + monitor, + Some(taskbar), + tray, + ); + if horizontal { + assert_eq!((rect.left, rect.right), (1720, 1920)); + } else { + assert_eq!((rect.top, rect.bottom), (980, 1080)); + } + } + } + } +} + +#[test] +fn drag_taskbar_clamp_is_not_saved_in_themes() { + let dragged = positioning::taskbar_dock_placement(0, 1700, 1.0, true); + assert!(dragged.clamp_taskbar_drag); + let json = serde_json::to_value(&dragged).unwrap(); + assert!(json.get("clamp_taskbar_drag").is_none()); + let loaded: theme_engine::Placement = serde_json::from_value(json).unwrap(); + assert!(!loaded.clamp_taskbar_drag); +} + #[test] fn smart_anchoring_fractional_dpi_scaling() { let monitor = RECT { diff --git a/src/window/positioning.rs b/src/window/positioning.rs index 16f57eb5..8e77ebea 100644 --- a/src/window/positioning.rs +++ b/src/window/positioning.rs @@ -771,6 +771,7 @@ pub(super) fn taskbar_dock_placement( ) -> theme_engine::Placement { let logical_offset = (screen_offset as f64 / scale).round() as i32; theme_engine::Placement { + clamp_taskbar_drag: true, reference: theme_engine::ReferenceTarget { region: ReferenceRegion::Taskbar, display, @@ -863,7 +864,8 @@ pub(super) fn surface_screen_rect( vertical_anchor_factor(placement.surface_vertical.unwrap_or(placement.vertical)), (placement.offset_y as f64 * scale).round() as i32, ); - let (x, y) = if placement.reference.region == ReferenceRegion::Taskbar + let (x, y) = if placement.clamp_taskbar_drag + && placement.reference.region == ReferenceRegion::Taskbar && placement.nest == SurfaceNest::Taskbar { if let Some(tb) = taskbar {