diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-18 12:26:10 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-18 12:26:10 +0200 |
| commit | 1f6bd402b5d2ffef845129a327cae95113b23e55 (patch) | |
| tree | b522d59b159b82de33eb8014dda815c2f5b62fa7 /window-switcher/Expose.js | |
| parent | a880134140f7ab8195c6651c2f64f614855c8dce (diff) | |
| download | quickshell-1f6bd402b5d2ffef845129a327cae95113b23e55.tar.gz quickshell-1f6bd402b5d2ffef845129a327cae95113b23e55.zip | |
window-switcher: animate, respace and label the expose, add arrow keys
Closing a window from the expose now glides the rest into their new
rects instead of jumping. The Repeater's plain array model rebuilt every
card on any change, leaving nothing to animate; ScriptModel diffs by
identity, so only the closed card goes. The Behaviors stay off until the
fade-in settles, or every card would fly in from the corner on open.
The scatter keeps its centre weighting but spaces windows further apart
(GAP 60 -> 200 real px) and may fill 95% of the screen rather than 92%.
Measured with node at 7 windows: about 45px on screen between cards.
The bottom 28px are reserved so the lowest card's name stays on screen.
Each card shows the app's desktop-entry name below its preview, falling
back to the app id, and the app icon moved to a larger overlay in the
preview's bottom-left corner; the hover title starts right of it.
Arrow keys select a card and Enter focuses it. The scatter has no grid,
so Expose.step picks the nearest centre on the arrow's side with
sideways drift counted double, and the first press picks the top-left
card; test_expose.js covers it. The selection is held as the window,
not an index, so a close that shifts the list does not move it, and
hovering selects too, so pointer and keys never show two highlights.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'window-switcher/Expose.js')
| -rw-r--r-- | window-switcher/Expose.js | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/window-switcher/Expose.js b/window-switcher/Expose.js index 4120907..344fdde 100644 --- a/window-switcher/Expose.js +++ b/window-switcher/Expose.js @@ -24,9 +24,9 @@ // pushing a pile apart makes a line that still overlapped after 300 passes // and filled under 15% of the screen. -const GAP = 60; // real pixels kept between two windows +const GAP = 200; // real pixels kept between two windows, room for the name const PASSES = 300; -const FILL = 0.92; // of the screen, each way +const FILL = 0.95; // of the screen, each way const MAX_SCALE = 0.5; // a lone window is not a full-screen mirror const SPREAD = 0.45; // spiral step, as a fraction of the tallest window const ROUNDS = 30; @@ -94,3 +94,30 @@ function layout(rects, W, H) { return r.map(o => ({ x: dx + (o.x - x0) * k, y: dy + (o.y - y0) * k, w: o.w * k, h: o.h * k })); } + +// Keyboard selection over the scatter, which has no rows or columns to step +// through. Returns the index of the card the arrow points at from card i: +// the nearest centre on that side, with sideways drift counted double so +// Right prefers the card level with this one over a closer one far above. +// With no card on that side the selection stays put. With nothing selected +// yet (i < 0) any arrow starts from the top-left card. +function step(rects, i, dx, dy) { + const c = o => ({ x: o.x + o.w / 2, y: o.y + o.h / 2 }); + if (i < 0) { + let best = -1; + for (let k = 0; k < rects.length; k++) + if (best < 0 || rects[k].x + rects[k].y < rects[best].x + rects[best].y) best = k; + return best; + } + const from = c(rects[i]); + let best = i, score = Infinity; + for (let k = 0; k < rects.length; k++) { + if (k === i) continue; + const to = c(rects[k]); + const ahead = (to.x - from.x) * dx + (to.y - from.y) * dy; + if (ahead <= 0) continue; + const side = Math.abs((to.x - from.x) * dy) + Math.abs((to.y - from.y) * dx); + if (ahead + 2 * side < score) { score = ahead + 2 * side; best = k; } + } + return best; +} |
