diff options
| author | srdusr <[email protected]> | 2025-08-10 14:25:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-08-10 14:25:00 +0200 |
| commit | 7f4740046a004a1ad06aefa5d1d6b1a351571b1a (patch) | |
| tree | 9a22cd7bf999cd2645e7c10907b161f3c347ebe9 | |
| parent | 61d01bb79c6882c950f45a1176ba1f30fbbd4824 (diff) | |
| download | srdwm-7f4740046a004a1ad06aefa5d1d6b1a351571b1a.tar.gz srdwm-7f4740046a004a1ad06aefa5d1d6b1a351571b1a.zip | |
Make corner resize reachable at the button corner, and widen it everywhere else
Reported live: "even where decorations are corner i should still be able
to corner resize, just its... hitbox... does not get in the way of the
close icon" - the titlebar corner holding Close/Maximize/Minimize had no
resize target at all, by design (competing with the close button was
judged worse than losing that one corner). The BUTTON_CLUSTER_MARGIN
strip between the button cluster and the frame's true edge was already
dead space no button claims, regardless of button_count - its own top
DECORATED_TOP_RESIZE_MARGIN rows now register as the diagonal corner
(TopLeft/TopRight) instead of falling through to Top/Drag, without
touching the button's own hitbox at all.
Also requested: widen the other three corners' own resize zone, since a
user reaching for a plain edge-resize instinctively aims for the middle
of that edge, not its corner - a bigger corner zone doesn't compete with
that instinct the way a bigger RESIZE_MARGIN would compete with ordinary
content clicks near an edge. CORNER_MARGIN raised from 3 to 5 (18px to
30px at the default resize_margin).
Updated one existing test whose own per-window resize_margin override
(30px) now put its plain-edge test point inside the widened corner zone
on a window too short for the two to stay apart - taller geometry, same
edge point relative to center, no change to what it actually verifies.
Added coverage for the new button-corner resize target on both sides,
and for the dead strip's own non-corner rows still just dragging as
before.
| -rw-r--r-- | crates/core/src/manager/tests.rs | 14 | ||||
| -rw-r--r-- | crates/core/src/window.rs | 76 |
2 files changed, 86 insertions, 4 deletions
diff --git a/crates/core/src/manager/tests.rs b/crates/core/src/manager/tests.rs index 9af0181..88699de 100644 --- a/crates/core/src/manager/tests.rs +++ b/crates/core/src/manager/tests.rs @@ -432,13 +432,21 @@ wm.set_layout(wm.current_workspace(), "tiling"); // keeps add_window from overriding geometry via SmartPlacement let id = wm.alloc_window_id(); let mut w = Window::new(id, "a"); - w.geometry = Rect::new(100, 100, 400, 300); + // Tall enough that `CORNER_MARGIN * resize_margin`'s own widened + // corner zone (150px at this override, see `CORNER_MARGIN`'s own + // doc comment on why it's deliberately generous) doesn't reach + // anywhere near the plain-edge point tested below - a shorter + // window here used to work purely because the corner zone was + // narrower, not because this test cared about corners at all. + w.geometry = Rect::new(100, 100, 400, 800); w.resize_margin = Some(30); wm.add_window(w); // 15px in from the left edge: well past the WM-wide default (6px), - // but still inside this window's own wider 30px override. - let hit = wm.hit_test(115, 250); + // but still inside this window's own wider 30px override. Deep in + // the window's own vertical middle, well clear of either corner + // zone. + let hit = wm.hit_test(115, 500); assert_eq!(hit.map(|(_, h)| h), Some(TitlebarHit::Resize(ResizeEdge::Left))); } diff --git a/crates/core/src/window.rs b/crates/core/src/window.rs index 6e40486..99d8de7 100644 --- a/crates/core/src/window.rs +++ b/crates/core/src/window.rs @@ -396,7 +396,19 @@ pub const DECORATED_TOP_RESIZE_MARGIN: i32 = RESIZE_MARGIN; /// - see `ResizeEdge::resize_edge_at`'s doc comment for why corners need /// more room than a straight edge at all, not just a proportionally bigger /// dead-simple hit box. -pub const CORNER_MARGIN: i32 = 3; +/// +/// Bumped from `3` (18px at the default `RESIZE_MARGIN`) to `5` (30px): +/// reported live as still too tight to land reliably, and there's real +/// room to widen it further than a plain edge ever could be - someone +/// reaching for a straight edge-resize instinctively aims for the middle +/// of that edge, not its corner, precisely to *avoid* accidentally +/// grabbing a corner instead. A bigger corner zone doesn't compete with +/// that instinct the way a bigger `RESIZE_MARGIN` would compete with +/// ordinary clicks near an edge (`RESIZE_MARGIN`'s own doc comment). +/// Applies to every corner `resize_edge_at` and the titlebar's own +/// non-button corner check use - not the button-side corner, which +/// stays deliberately narrower than this; see its own hit-test comment. +pub const CORNER_MARGIN: i32 = 5; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ResizeEdge { @@ -545,6 +557,22 @@ impl ResizeEdge { } } } + // `x < left` is the same dead `BUTTON_CLUSTER_MARGIN` strip + // excluded above, now put to use: it's already unclaimed by + // any button's own hitbox, by construction, so its own top + // `DECORATED_TOP_RESIZE_MARGIN` rows are a safe, if + // deliberately narrow, diagonal-resize target right at the + // button-side corner - narrower than the non-button + // corner's own `corner_zone` above on purpose, since this + // one has Close sitting immediately next to it and widening + // it any further would start eating into that button's own + // hitbox instead of just the dead space next to it. + // Reported live: this corner had no resize target at all, + // "even where decorations are corner i should still be able + // to corner resize." + if x < left && y <= frame.y + DECORATED_TOP_RESIZE_MARGIN { + return Some(TitlebarHit::Resize(ResizeEdge::TopLeft)); + } if y <= frame.y + DECORATED_TOP_RESIZE_MARGIN { return Some(TitlebarHit::Resize(ResizeEdge::Top)); } @@ -565,6 +593,11 @@ impl ResizeEdge { } } } + // Mirror of the `buttons_left` branch's own identical check + // above - see its comment for the full reasoning. + if x > right && y <= frame.y + DECORATED_TOP_RESIZE_MARGIN { + return Some(TitlebarHit::Resize(ResizeEdge::TopRight)); + } if y <= frame.y + DECORATED_TOP_RESIZE_MARGIN { return Some(TitlebarHit::Resize(ResizeEdge::Top)); } @@ -1085,6 +1118,47 @@ mod tests { } #[test] + fn button_side_corner_still_resizes_from_its_own_dead_strip() { + // Reported live: "even where decorations are corner i should still + // be able to corner resize, just its... hitbox... does not get in + // the way of the close icon." The raw corner pixel sits in the + // `BUTTON_CLUSTER_MARGIN` dead strip outside Close's own hitbox + // (see `decorated_window_top_right_corner_still_closes_not_resizes` + // just above), which used to fall through to plain `Top`/`Drag` + // with no diagonal target at all. Both axes right at the true + // corner - well within the dead strip horizontally (`- 1` from the + // frame's own edge, nowhere near where Close's hitbox starts) and + // within `DECORATED_TOP_RESIZE_MARGIN` vertically. + let f = frame(); + let hit = ResizeEdge::hit_test(f, f.right() - 1, f.y + 1, true, 0, RESIZE_MARGIN, false, None, false); + assert_eq!(hit, Some(TitlebarHit::Resize(ResizeEdge::TopRight))); + } + + #[test] + fn button_side_corner_below_its_own_resize_row_still_drags() { + // The dead strip's own top rows are the new corner-resize target + // (`button_side_corner_still_resizes_from_its_own_dead_strip`), but + // the rest of that same narrow column, below + // `DECORATED_TOP_RESIZE_MARGIN`, is still ordinary titlebar drag -- + // this only ever claims the true corner, not the whole column + // beside Close. + let f = frame(); + let hit = ResizeEdge::hit_test(f, f.right() - 1, f.y + DECORATED_TOP_RESIZE_MARGIN + 5, true, 0, RESIZE_MARGIN, false, None, false); + assert_eq!(hit, Some(TitlebarHit::Drag)); + } + + #[test] + fn button_side_corner_still_resizes_from_its_own_dead_strip_when_buttons_are_left() { + // Mirror of `button_side_corner_still_resizes_from_its_own_dead_ + // strip` with `buttons_left: true` - Close now owns the top-left + // corner, so the dead strip and its own corner-resize target move + // to the frame's left edge instead. + let f = frame(); + let hit = ResizeEdge::hit_test(f, f.x + 1, f.y + 1, true, 0, RESIZE_MARGIN, true, None, false); + assert_eq!(hit, Some(TitlebarHit::Resize(ResizeEdge::TopLeft))); + } + + #[test] fn decorated_window_very_top_edge_of_titlebar_resizes_not_drags() { // The actual regression: reported live as "can't resize tmux's // window from the top, but can in Firefox" - a decorated window's |