diff --git a/packages/wm-common/src/rect.rs b/packages/wm-common/src/rect.rs index 66308391..1c3e635b 100644 --- a/packages/wm-common/src/rect.rs +++ b/packages/wm-common/src/rect.rs @@ -97,15 +97,21 @@ impl Rect { } /// Returns a new `Rect` that is clamped within the bounds of the given - /// outer rectangle. Attempts to preserve the width and height of the - /// original rectangle. + /// outer rectangle. Adjusts the position and size of the + /// rectangle to ensure it fits entirely within the outer rectangle. #[must_use] pub fn clamp(&self, outer_rect: &Rect) -> Self { + let clamped_x = self.x().max(outer_rect.x()); + let clamped_y = self.y().max(outer_rect.y()); + + let max_width = outer_rect.right - clamped_x; + let max_height = outer_rect.bottom - clamped_y; + Self::from_xy( - self.left.max(outer_rect.left), - self.top.max(outer_rect.top), - self.width().min(outer_rect.width()), - self.height().min(outer_rect.height()), + clamped_x, + clamped_y, + self.width().min(max_width), + self.height().min(max_height), ) } diff --git a/packages/wm/src/commands/general/platform_sync.rs b/packages/wm/src/commands/general/platform_sync.rs index 5e1d6818..1f394779 100644 --- a/packages/wm/src/commands/general/platform_sync.rs +++ b/packages/wm/src/commands/general/platform_sync.rs @@ -257,9 +257,20 @@ fn redraw_containers( }, ); - let rect = window - .to_rect()? - .apply_delta(&window.total_border_delta()?, None); + let scale_factor = window + .monitor() + .and_then(|monitor| monitor.native().scale_factor().ok()); + + let mut rect = window.to_rect()?.apply_delta( + &window.total_border_delta(scale_factor)?, + scale_factor, + ); + + // Constrain tiling windows to their workspace's bounds to prevent + // overflow on multi-monitor setup with different scaling factors. + if window.is_tiling_window() { + rect = rect.clamp(&workspace.to_rect()?); + } let is_visible = matches!( window.display_state(), diff --git a/packages/wm/src/traits/window_getters.rs b/packages/wm/src/traits/window_getters.rs index 80fa7e0c..eb7afad7 100644 --- a/packages/wm/src/traits/window_getters.rs +++ b/packages/wm/src/traits/window_getters.rs @@ -61,27 +61,30 @@ pub trait WindowGetters { fn set_border_delta(&self, border_delta: RectDelta); - fn total_border_delta(&self) -> anyhow::Result { + fn total_border_delta( + &self, + scale_factor: Option, + ) -> anyhow::Result { let border_delta = self.border_delta(); let shadow_border_delta = self.native().shadow_border_delta()?; // TODO: Allow percentage length values. Ok(RectDelta { left: LengthValue::from_px( - border_delta.left.to_px(0, None) - + shadow_border_delta.left.to_px(0, None), + border_delta.left.to_px(0, scale_factor) + + shadow_border_delta.left.to_px(0, scale_factor), ), right: LengthValue::from_px( - border_delta.right.to_px(0, None) - + shadow_border_delta.right.to_px(0, None), + border_delta.right.to_px(0, scale_factor) + + shadow_border_delta.right.to_px(0, scale_factor), ), top: LengthValue::from_px( - border_delta.top.to_px(0, None) - + shadow_border_delta.top.to_px(0, None), + border_delta.top.to_px(0, scale_factor) + + shadow_border_delta.top.to_px(0, scale_factor), ), bottom: LengthValue::from_px( - border_delta.bottom.to_px(0, None) - + shadow_border_delta.bottom.to_px(0, None), + border_delta.bottom.to_px(0, scale_factor) + + shadow_border_delta.bottom.to_px(0, scale_factor), ), }) }