diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-15 09:07:44 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2024-12-30 20:12:37 +0300 |
| commit | c008e1c5bcab360425780997f70818fa4252c938 (patch) | |
| tree | fe8b7138ddc3b7bc9a3616cbf42cf8d8defb807e /src/layout | |
| parent | 1aa60f0da3e7f1a0bf50d6b88d59db6ba094d4f0 (diff) | |
| download | niri-c008e1c5bcab360425780997f70818fa4252c938.tar.gz niri-c008e1c5bcab360425780997f70818fa4252c938.tar.bz2 niri-c008e1c5bcab360425780997f70818fa4252c938.zip | |
floating: Implement smarter clamping for window location
A small part of the window always remains on-screen regardless of the working
area changes.
Interactive move lets the user position the window anywhere; automatic actions
like toggle-window-floating and dialog opening try to put the window fully
on-screen.
The size-fraction canonical floating window position remains unclamped, and
clamping happens when recomputing the logical position.
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/floating.rs | 55 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 12 |
2 files changed, 54 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 { |
