aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/layout/floating.rs55
-rw-r--r--src/layout/workspace.rs12
-rw-r--r--src/utils/mod.rs49
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));
+ }
+}