srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core
diff options
context:
space:
mode:
Diffstat (limited to 'crates/core')
-rw-r--r--crates/core/src/manager.rs15
-rw-r--r--crates/core/src/window.rs58
2 files changed, 47 insertions, 26 deletions
diff --git a/crates/core/src/manager.rs b/crates/core/src/manager.rs
index 8696508..2c92ac1 100644
--- a/crates/core/src/manager.rs
+++ b/crates/core/src/manager.rs
@@ -4,7 +4,7 @@ use crate::monitor::{Monitor, MonitorId};
use crate::placement::{PlacementConfig, SmartPlacement, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH};
use crate::rules::WindowRule;
use crate::theme::ThemeConfig;
-use crate::window::{ResizeEdge, TitlebarHit, Window, WindowId};
+use crate::window::{ResizeEdge, TitlebarHit, Window, WindowId, RESIZE_MARGIN};
use crate::workspace::{Workspace, WorkspaceId};
use std::collections::HashMap;
@@ -61,6 +61,15 @@ pub struct WindowManager {
pub animations_enabled: bool,
/// Tween duration in milliseconds, read from `general.animation_duration`.
pub animation_duration_ms: u32,
+ /// Whether windows get a drop shadow. Read from `general.shadows`. A
+ /// maximized or fullscreen window never gets one regardless of this --
+ /// see the Wayland backend's shadow render call site - so this only
+ /// ever turns it off entirely, not on for those.
+ pub shadows_enabled: bool,
+ /// Width, in pixels, of the resize grab band along a window's edges,
+ /// read from `general.resize_margin`. See [`crate::window::RESIZE_MARGIN`]'s
+ /// doc comment for the default and why it's what it is.
+ pub resize_margin: i32,
/// Default decoration colours and border width, read from `theme.colors.*`/
/// `theme.decorations.*`. See `ThemeConfig`'s own doc comment.
pub theme: ThemeConfig,
@@ -103,6 +112,8 @@ impl WindowManager {
placement: PlacementConfig::default(),
animations_enabled: true,
animation_duration_ms: 200,
+ shadows_enabled: true,
+ resize_margin: RESIZE_MARGIN,
theme: ThemeConfig::default(),
drag: None,
resize: None,
@@ -759,7 +770,7 @@ impl WindowManager {
if w.minimized {
continue;
}
- if let Some(hit) = ResizeEdge::hit_test(w.geometry, x, y, w.decorated, w.border_width) {
+ if let Some(hit) = ResizeEdge::hit_test(w.geometry, x, y, w.decorated, w.border_width, self.resize_margin) {
return Some((w.id, hit));
}
}
diff --git a/crates/core/src/window.rs b/crates/core/src/window.rs
index 1a4cab5..e30f501 100644
--- a/crates/core/src/window.rs
+++ b/crates/core/src/window.rs
@@ -147,15 +147,25 @@ impl Window {
/// The height, in pixels, of the drawn title bar. Shared between backends so
/// hit-testing and rendering agree on the same band.
pub const TITLEBAR_HEIGHT: u32 = 30;
-/// Width of the resize grab band along each window edge.
+/// Default width, in pixels, of the resize grab band along each window
+/// edge - `WindowManager::resize_margin`'s starting value, read from
+/// `general.resize_margin`, and what every `hit_test` call in this file's
+/// own tests still passes directly.
///
-/// 10px rather than a hairline: this is grabbed with a mouse, and a border
-/// only a couple of pixels wide is genuinely hard to hit - which is why
-/// Hyprland ships `extend_border_grab_area` and why every desktop widens
-/// this beyond the visible border. The band is inside the window, so it
-/// costs a few pixels of client edge; that is the right trade for making
-/// resize reliably grabbable without a keyboard.
-pub const RESIZE_MARGIN: i32 = 10;
+/// Originally 10px on the reasoning that a border only a couple of pixels
+/// wide is genuinely hard to grab with a mouse - which is why Hyprland
+/// ships `extend_border_grab_area` and why every desktop widens this
+/// beyond the visible border. But the band is *inside* the window
+/// (`resize_edge_at` measures from `frame`, the client's own content rect,
+/// inward), and at 10px that traded too much: reported live as ordinary
+/// clicks near any edge - a link near a browser's edge, a button near a
+/// panel's edge - regularly landing as a resize-edge grab instead of
+/// reaching the client at all, not just an occasional near-miss. Halved to
+/// 6px, which is still comfortably grabbable (about the same as a native
+/// X11 border on a legacy WM) while giving content much more of its own
+/// edge back. Still configurable per the doc comment above if 6px turns
+/// out to be too little in the other direction for someone.
+pub const RESIZE_MARGIN: i32 = 6;
/// Top-edge resize margin for an *undecorated* window specifically --
/// narrower than [`RESIZE_MARGIN`] on purpose.
///
@@ -206,7 +216,7 @@ impl ResizeEdge {
/// client. Resize-from-edge still applies either way: an undecorated
/// window is still a window, and dragging its (invisible) edge to
/// resize is still expected to work.
- pub fn hit_test(frame: Rect, x: i32, y: i32, decorated: bool, border_width: u32) -> Option<TitlebarHit> {
+ pub fn hit_test(frame: Rect, x: i32, y: i32, decorated: bool, border_width: u32, resize_margin: i32) -> Option<TitlebarHit> {
// Border strips render *outside* `frame` (`decoration::
// border_strips`, `border_width` pixels past each edge) - without
// widening the containment check to match, those visible pixels
@@ -236,12 +246,12 @@ impl ResizeEdge {
}
return Some(TitlebarHit::Drag);
}
- let edge = Self::resize_edge_at(frame, x, y, decorated)?;
+ let edge = Self::resize_edge_at(frame, x, y, decorated, resize_margin)?;
Some(TitlebarHit::Resize(edge))
}
- fn resize_edge_at(frame: Rect, x: i32, y: i32, decorated: bool) -> Option<ResizeEdge> {
- let m = RESIZE_MARGIN;
+ fn resize_edge_at(frame: Rect, x: i32, y: i32, decorated: bool, resize_margin: i32) -> Option<ResizeEdge> {
+ let m = resize_margin;
let top_m = if decorated { m } else { UNDECORATED_TOP_RESIZE_MARGIN };
let near_left = x <= frame.x + m;
let near_right = x >= frame.right() - m;
@@ -318,14 +328,14 @@ mod tests {
#[test]
fn close_button_is_top_right_corner_of_titlebar() {
let f = frame();
- let hit = ResizeEdge::hit_test(f, f.right() - 5, f.y + 5, true, 0);
+ let hit = ResizeEdge::hit_test(f, f.right() - 5, f.y + 5, true, 0, RESIZE_MARGIN);
assert_eq!(hit, Some(TitlebarHit::Close));
}
#[test]
fn maximize_is_left_of_close() {
let f = frame();
- let hit = ResizeEdge::hit_test(f, f.right() - TITLEBAR_HEIGHT as i32 - 5, f.y + 5, true, 0);
+ let hit = ResizeEdge::hit_test(f, f.right() - TITLEBAR_HEIGHT as i32 - 5, f.y + 5, true, 0, RESIZE_MARGIN);
assert_eq!(hit, Some(TitlebarHit::Maximize));
}
@@ -333,14 +343,14 @@ mod tests {
fn middle_of_titlebar_is_drag() {
let f = frame();
let (cx, _) = f.center();
- let hit = ResizeEdge::hit_test(f, cx, f.y + 5, true, 0);
+ let hit = ResizeEdge::hit_test(f, cx, f.y + 5, true, 0, RESIZE_MARGIN);
assert_eq!(hit, Some(TitlebarHit::Drag));
}
#[test]
fn bottom_right_corner_is_resize() {
let f = frame();
- let hit = ResizeEdge::hit_test(f, f.right() - 1, f.bottom() - 1, true, 0);
+ let hit = ResizeEdge::hit_test(f, f.right() - 1, f.bottom() - 1, true, 0, RESIZE_MARGIN);
assert_eq!(hit, Some(TitlebarHit::Resize(ResizeEdge::BottomRight)));
}
@@ -357,7 +367,7 @@ mod tests {
// outside RESIZE_MARGIN, so a real resize edge can't also explain a
// `None` here - undecorated, this must not be treated as
// decoration (or a resize edge) at all, just plain content.
- let hit = ResizeEdge::hit_test(f, cx, f.y + 20, false, 0);
+ let hit = ResizeEdge::hit_test(f, cx, f.y + 20, false, 0, RESIZE_MARGIN);
assert_eq!(hit, None);
}
@@ -365,7 +375,7 @@ mod tests {
fn undecorated_window_still_resizes_from_every_edge_including_top() {
let f = frame();
let (cx, _) = f.center();
- let hit = ResizeEdge::hit_test(f, cx, f.y + 1, false, 0);
+ let hit = ResizeEdge::hit_test(f, cx, f.y + 1, false, 0, RESIZE_MARGIN);
assert_eq!(hit, Some(TitlebarHit::Resize(ResizeEdge::Top)));
}
@@ -381,9 +391,9 @@ mod tests {
// above where `RESIZE_MARGIN` even starts to matter.
let f = frame();
let (cx, _) = f.center();
- assert_eq!(ResizeEdge::hit_test(f, cx, f.y + 5, false, 0), None, "5px in: past the narrow undecorated band, must reach the client");
+ assert_eq!(ResizeEdge::hit_test(f, cx, f.y + 5, false, 0, RESIZE_MARGIN), None, "5px in: past the narrow undecorated band, must reach the client");
assert_eq!(
- ResizeEdge::hit_test(f, cx, f.y + 5, true, 0),
+ ResizeEdge::hit_test(f, cx, f.y + 5, true, 0, RESIZE_MARGIN),
Some(TitlebarHit::Drag),
"decorated: 5px in is still well inside the titlebar band, not a resize edge"
);
@@ -392,7 +402,7 @@ mod tests {
#[test]
fn outside_frame_is_none() {
let f = frame();
- assert_eq!(ResizeEdge::hit_test(f, 0, 0, true, 0), None);
+ assert_eq!(ResizeEdge::hit_test(f, 0, 0, true, 0, RESIZE_MARGIN), None);
}
#[test]
@@ -407,16 +417,16 @@ mod tests {
let border_width = 2;
// One pixel into the border strip, past the left edge.
let x = f.x - 1;
- assert_eq!(ResizeEdge::hit_test(f, x, cy, true, 0), None, "sanity check: with no border, this point really is outside the window");
+ assert_eq!(ResizeEdge::hit_test(f, x, cy, true, 0, RESIZE_MARGIN), None, "sanity check: with no border, this point really is outside the window");
assert_eq!(
- ResizeEdge::hit_test(f, x, cy, true, border_width),
+ ResizeEdge::hit_test(f, x, cy, true, border_width, RESIZE_MARGIN),
Some(TitlebarHit::Resize(ResizeEdge::Left)),
"one pixel into the actual drawn border must still register as the left edge"
);
// Just past the border entirely (border_width + 1 outside frame) is
// still nothing - the fix widens the dead zone's boundary, it
// doesn't remove it.
- assert_eq!(ResizeEdge::hit_test(f, f.x - border_width as i32 - 1, cy, true, border_width), None);
+ assert_eq!(ResizeEdge::hit_test(f, f.x - border_width as i32 - 1, cy, true, border_width, RESIZE_MARGIN), None);
}
#[test]