diff options
| -rw-r--r-- | src/layout/floating.rs | 55 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 12 | ||||
| -rw-r--r-- | src/utils/mod.rs | 49 |
3 files changed, 103 insertions, 13 deletions
diff --git a/src/layout/floating.rs b/src/layout/floating.rs index 342a8d3c..cb2aaafb 100644 --- a/src/layout/floating.rs +++ b/src/layout/floating.rs @@ -16,7 +16,7 @@ use crate::niri_render_elements; use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::RenderTarget; use crate::utils::transaction::TransactionBlocker; -use crate::utils::{ensure_min_max_size, ResizeEdge}; +use crate::utils::{clamp_preferring_top_left_in_area, ensure_min_max_size, ResizeEdge}; use crate::window::ResolvedWindowRules; /// Space for floating windows. @@ -105,6 +105,24 @@ impl Data { logical_pos.x *= self.working_area.size.w; logical_pos.y *= self.working_area.size.h; logical_pos += self.working_area.loc; + + // Make sure the window doesn't go too much off-screen. Numbers taken from Mutter. + let min_on_screen_hor = f64::clamp(self.size.w / 4., 10., 75.); + let min_on_screen_ver = f64::clamp(self.size.h / 4., 10., 75.); + let max_off_screen_hor = f64::max(0., self.size.w - min_on_screen_hor); + let max_off_screen_ver = f64::max(0., self.size.h - min_on_screen_ver); + + logical_pos.x = f64::max(logical_pos.x, -max_off_screen_hor); + logical_pos.y = f64::max(logical_pos.y, -max_off_screen_ver); + logical_pos.x = f64::min( + logical_pos.x, + self.working_area.size.w - self.size.w + max_off_screen_hor, + ); + logical_pos.y = f64::min( + logical_pos.y, + self.working_area.size.h - self.size.h + max_off_screen_ver, + ); + self.logical_pos = logical_pos; } @@ -118,7 +136,13 @@ impl Data { } pub fn update<W: LayoutElement>(&mut self, tile: &Tile<W>) { - self.size = tile.tile_size(); + let size = tile.tile_size(); + if self.size == size { + return; + } + + self.size = size; + self.recompute_logical_pos(); } pub fn set_logical_pos(&mut self, logical_pos: Point<f64, Logical>) { @@ -129,7 +153,7 @@ impl Data { self.pos = pos; - // This should get close to the same result as what we started with. + // This will clamp the logical position to the current working area. self.recompute_logical_pos(); } @@ -361,16 +385,15 @@ impl<W: LayoutElement> FloatingSpace<W> { } } - let mut pos = pos.unwrap_or_else(|| { + let pos = pos.unwrap_or_else(|| { let area_size = self.working_area.size.to_point(); let tile_size = tile.tile_size().to_point(); - let offset = (area_size - tile_size).downscale(2.); + let mut offset = (area_size - tile_size).downscale(2.); + // Prefer top-left corner if larger than the working area. + offset.x = f64::max(offset.x, 0.); + offset.y = f64::max(offset.y, 0.); self.working_area.loc + offset }); - // TODO: also nudge pos + size inside the area. - // TODO: smart padding (if doesn't fit, try without padding). - pos.x = f64::max(pos.x, self.working_area.loc.x + 8.); - pos.y = f64::max(pos.y, self.working_area.loc.y + 8.); let data = Data::new(self.working_area, &tile, pos); self.data.insert(idx, data); @@ -387,7 +410,9 @@ impl<W: LayoutElement> FloatingSpace<W> { let above_pos = self.data[idx].logical_pos; let above_size = self.data[idx].size; - let pos = above_pos + (above_size.to_point() - tile.tile_size().to_point()).downscale(2.); + let tile_size = tile.tile_size(); + let pos = above_pos + (above_size.to_point() - tile_size.to_point()).downscale(2.); + let pos = self.clamp_within_working_area(pos, tile_size); self.add_tile_at(idx, tile, Some(pos), activate); } @@ -796,6 +821,16 @@ impl<W: LayoutElement> FloatingSpace<W> { } } + pub fn clamp_within_working_area( + &self, + pos: Point<f64, Logical>, + size: Size<f64, Logical>, + ) -> Point<f64, Logical> { + let mut rect = Rectangle::from_loc_and_size(pos, size); + clamp_preferring_top_left_in_area(self.working_area, &mut rect); + rect.loc + } + #[cfg(test)] pub fn working_area(&self) -> Rectangle<f64, Logical> { self.working_area diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs index 65ff7863..bfecfcde 100644 --- a/src/layout/workspace.rs +++ b/src/layout/workspace.rs @@ -572,11 +572,14 @@ impl<W: LayoutElement> Workspace<W> { .tiles_with_render_positions() .find(|(tile, _)| tile.window().id() == right_of) .unwrap(); + // Position the new tile in the center above the right_of tile. Think a dialog // opening on top of a window. + let tile_size = tile.tile_size(); let pos = render_pos - + (right_of_tile.tile_size().to_point() - tile.tile_size().to_point()) - .downscale(2.); + + (right_of_tile.tile_size().to_point() - tile_size.to_point()).downscale(2.); + let pos = self.floating.clamp_within_working_area(pos, tile_size); + self.floating.add_tile(tile, Some(pos), activate); if activate { self.floating_is_active = true; @@ -929,7 +932,10 @@ impl<W: LayoutElement> Workspace<W> { } else { let mut removed = self.scrolling.remove_tile(&id, Transaction::new()); removed.tile.stop_move_animations(); - let pos = render_pos + Point::from((50., 50.)); + let pos = self.floating.clamp_within_working_area( + render_pos + Point::from((50., 50.)), + removed.tile.tile_size(), + ); self.floating .add_tile(removed.tile, Some(pos), target_is_active); if target_is_active { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index b4ba1002..e7dff735 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -272,6 +272,18 @@ pub fn ensure_min_max_size(mut x: i32, min_size: i32, max_size: i32) -> i32 { x } +pub fn clamp_preferring_top_left_in_area( + area: Rectangle<f64, Logical>, + rect: &mut Rectangle<f64, Logical>, +) { + rect.loc.x = f64::min(rect.loc.x, area.loc.x + area.size.w - rect.size.w); + rect.loc.y = f64::min(rect.loc.y, area.loc.y + area.size.h - rect.size.h); + + // Clamp by top and left last so it takes precedence. + rect.loc.x = f64::max(rect.loc.x, area.loc.x); + rect.loc.y = f64::max(rect.loc.y, area.loc.y); +} + #[cfg(feature = "dbus")] pub fn show_screenshot_notification(image_path: Option<PathBuf>) { let mut notification = notify_rust::Notification::new(); @@ -309,3 +321,40 @@ pub fn cause_panic() { let b = Duration::from_secs(2); let _ = a - b; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_clamp_preferring_top_left() { + fn check( + (ax, ay, aw, ah): (i32, i32, i32, i32), + (rx, ry, rw, rh): (i32, i32, i32, i32), + (ex, ey): (i32, i32), + ) { + let area = Rectangle::from_loc_and_size((ax, ay), (aw, ah)).to_f64(); + let mut rect = Rectangle::from_loc_and_size((rx, ry), (rw, rh)).to_f64(); + clamp_preferring_top_left_in_area(area, &mut rect); + assert_eq!(rect.loc, Point::from((ex, ey)).to_f64()); + } + + check((0, 0, 10, 20), (2, 3, 4, 5), (2, 3)); + check((0, 0, 10, 20), (-2, 3, 4, 5), (0, 3)); + check((0, 0, 10, 20), (2, -3, 4, 5), (2, 0)); + check((0, 0, 10, 20), (-2, -3, 4, 5), (0, 0)); + + check((1, 1, 10, 20), (2, 3, 4, 5), (2, 3)); + check((1, 1, 10, 20), (-2, 3, 4, 5), (1, 3)); + check((1, 1, 10, 20), (2, -3, 4, 5), (2, 1)); + check((1, 1, 10, 20), (-2, -3, 4, 5), (1, 1)); + + check((0, 0, 10, 20), (20, 3, 4, 5), (6, 3)); + check((0, 0, 10, 20), (2, 30, 4, 5), (2, 15)); + check((0, 0, 10, 20), (20, 30, 4, 5), (6, 15)); + + check((0, 0, 10, 20), (20, 30, 40, 5), (0, 15)); + check((0, 0, 10, 20), (20, 30, 4, 50), (6, 0)); + check((0, 0, 10, 20), (20, 30, 40, 50), (0, 0)); + } +} |
