diff options
| -rw-r--r-- | AGENTS.md | 3 | ||||
| -rw-r--r-- | window-switcher/README.md | 118 |
2 files changed, 83 insertions, 38 deletions
@@ -15,7 +15,8 @@ modules. island/ top-centre pills on DP-1: clock, volume, tray, layout, privacy, controls panel (SUPER alone), calendar, performance appearance/ wallpaper picker and colour scheme switcher - window-switcher/ open windows as live previews in a grid, on ALT+TAB + window-switcher/ expose: open windows scattered as live previews, on ALT+TAB + or the top-right hot corner of DP-1 keybinds/ live keybind reminder, on SUPER+k assistant/ overlay for the voice assistant, fed by its event socket notifications/ balloons for the notification daemon's published files diff --git a/window-switcher/README.md b/window-switcher/README.md index 516d8dd..66e6f69 100644 --- a/window-switcher/README.md +++ b/window-switcher/README.md @@ -1,29 +1,31 @@ # window-switcher -Every open window as a live preview, in one centred grid over a dimmed screen. -ALT+TAB opens it, a click or Enter picks a window, Escape drops it. It replaces -a rofi list that showed the same windows as text. +Every open window as a live preview, scattered over a dimmed DP-1 the way +macOS Mission Control does it. ALT+TAB or the top-right corner of DP-1 opens +it, a click picks a window, Escape drops it. It replaced a uniform grid, which +replaced a rofi list. ┌──────────────────────────────────────────────────────────────┐ - │ │ - │ ┌────────────────────┐ ┌────────────────────┐ │ - │ │ [icon] [x] │ │ [icon] [x] │ │ - │ │ │ │ │ │ - │ │ live preview │ │ live preview │ │ - │ │ fitted in a │ │ │ │ - │ │ 16:10 box │ │ │ │ - │ └────────────────────┘ └────────────────────┘ │ - │ firefox kitty │ - │ a page title, elided... ~/Programming/GIT/... │ - │ [browser icon] [server icon] │ - │ │ + │ ┌───────────────┐ ◜ │ + │ │ [icon][ws] │ ┌────┐ ┌───────────────┐ │ + │ │ │ │ │ │ │ │ + │ └───────────────┘ │ │ │ (hovered) │ │ + │ ┌───────────────┐ ┌──────────┐ │ │ │ title... [x]│ │ + │ │ │ │ most │ │ │ └───────────────┘ │ + │ │ │ │ recent │ └────┘ │ + │ └───────────────┘ └──────────┘ ┌───────────────┐ │ + │ │ │ │ + │ └───────────────┘ │ └──────────────────────────────────────────────────────────────┘ -The cards are ordered most recently used first, so the window you just left is -the first one, the same order ALT+TAB implies. Windows on a `special:` -workspace are the scratchpad, which has its own bind, and are left out. +Every preview keeps its window's real shape and its size relative to the +others: one scale for all, so a portrait DP-3 window is visibly narrower and +taller than a DP-1 one. The most recently used window sits at the centre and +older ones spiral outwards. Where a window lives on its desktop carries no +meaning here, by choice. Windows on a `special:` workspace are the scratchpad, +which has its own bind, and are left out. -Each card's desktop marker is an icon for that workspace rather than a bare +Each preview's desktop marker is an icon for that workspace rather than a bare number. The eight icons come from `Workspaces.qml`, a singleton in `shared/` that this component links, as it does `Theme.qml`, and that the island's flick uses too, so the two agree. The names are resolved through the active icon @@ -52,15 +54,43 @@ shell to expand `~`. The bind lives in `keybindings.lua` on ALT+TAB. `show` and | | | | --- | --- | | ALT+TAB | open, or close if already open | -| click a card, or Enter | focus that window and dismiss | -| arrows | move the selection, up and down by a row | -| Tab / Shift+Tab | move the selection by one | -| the `x` on a card | close that window, overlay stays open | +| top-right corner of DP-1 | open, after the glow (see below) | +| click a preview | focus that window and dismiss | +| the `x` on a hovered preview | close that window, overlay stays open | | Escape, or a click on the backdrop | dismiss, focus unchanged | +The title and the `x` show only on hover, which also draws the accent border. +There is no keyboard selection: this is a pointer feature, and the keyboard +keeps only the way in and Escape. + Closing leaves the overlay up on purpose. Tidying several windows would -otherwise mean reopening between each one, and the card goes when Hyprland says -the window went, not when the button is pressed. +otherwise mean reopening between each one, and the preview goes when Hyprland +says the window went, not when the button is pressed. The rest then move to +fill the space. + +## Hot corner + +`HotCorner.qml` is a 48x48 layer window in the top-right corner of DP-1, which +is a true corner: DP-1 is the right-hand monitor, so the pointer stops there. +Its input region is only the 2x2 pixels in the very corner. The rest is drawn +but click-through, because a larger input area would swallow clicks meant for +the windows underneath. That is also why it cannot light up while the pointer +is merely approaching. + +Touching the corner grows a quarter-circle accent glow out of it over 250ms, +and the expose opens when it is full. Leaving first shrinks it back and +nothing opens. The dwell is what keeps a pointer passing through on its way to +something else from triggering it. + +After it fires it re-arms only once the pointer leaves the corner while the +expose is closed. Opening the expose covers the corner, which reads as +leaving, so without that rule an Escape with the pointer still in the corner +would reopen it 250ms later. + +It is hidden while `status.gaming` is set. It reads that file from +`$XDG_RUNTIME_DIR` itself rather than linking `shared/Status.qml`, which is not +passive: a second instance would run its own game poll and react to +presentation mode a second time, pausing breaktimer twice. ## Hyprland dispatches from QML @@ -107,25 +137,39 @@ that a dispatch requires, so `addressOf()` puts it back. **`toplevel.wayland` can be null.** The Hyprland toplevel and its wlr handle are separate objects created at different moments, so a window that is -appearing or being destroyed has one and not the other. The card guards every +appearing or being destroyed has one and not the other. The preview guards every use of it. -## Card geometry +## The scatter + +`Expose.js` holds the layout, pure JavaScript tested under node: -**A card is a fixed 16:10 box with the preview fitted inside**, not a box -shaped to its preview. The previews are not one shape: DP-1 windows are near -21:9 (2556x1034) and DP-3 reports `transform=1`, so its windows are portrait -9:16 (1076x1916). Sizing each card to its own window gives ragged rows and -breaks the alignment of the three text lines under them. + node window-switcher/test_expose.js -**Card width is clamped by the height as well as the width.** A centred `Grid` -has no way to scroll, so without the height term a busy desktop pushes rows off -the top and bottom where they cannot be reached. The cards shrink instead. -There is a ceiling on the width too, or a single window becomes a full-screen +Windows are seeded on a golden-angle spiral, stretched to the screen's aspect, +then overlapping pairs are pushed apart, each by half the overlap with a 60px +real gap, until nothing overlaps. Thirty gravity rounds follow, each pulling +every window 15% closer to the centre and pushing them apart again: without +them the spiral's gaps stayed and the scatter read as loose, filling 24 to 30% +of the screen; with them 35 to 55%. Twenty windows lay out in about 4ms. A single scale then fits the result into 92% +of the screen, capped at 0.5 so a lone window does not become a full-screen mirror of itself. +**Seeding from real positions does not work on this desk.** It was the first +design: every window here fills its monitor, so they all started at the same +spot, and pushing a pile apart made a line that still overlapped after 300 +passes and filled 8 to 15% of the screen. The spiral converged in 1 to 55 +passes. + +**The push axis is chosen against the screen's shape, not in raw pixels.** In +pixels a wide window overlaps less vertically, so wide windows always +separated up and down and stacked into a column on a 21:9 screen. + +**Real sizes come from `lastIpcObject.size`.** A window without one yet stands +in at 1280x800 until it arrives; the layout is a binding and repaints. + **An uncaptured `ScreencopyView` reports `sourceSize` of `QSize(-1, -1)`**, not -`(0, 0)`. The aspect ratio guard therefore tests for a positive height. The +`(0, 0)`. The preview's aspect ratio guard therefore tests for a positive height. The obvious defensive rewrites, `!== 0` or a plain truthiness check, all pass on `-1` and produce a negative ratio. |
