aboutsummaryrefslogtreecommitdiffstats
path: root/window-switcher/Switcher.qml
diff options
context:
space:
mode:
Diffstat (limited to 'window-switcher/Switcher.qml')
-rw-r--r--window-switcher/Switcher.qml95
1 files changed, 29 insertions, 66 deletions
diff --git a/window-switcher/Switcher.qml b/window-switcher/Switcher.qml
index d907ab7..bd5c607 100644
--- a/window-switcher/Switcher.qml
+++ b/window-switcher/Switcher.qml
@@ -12,6 +12,7 @@
import Quickshell
import Quickshell.Wayland
import QtQuick
+import "Expose.js" as Expose
Scope {
id: root
@@ -80,39 +81,11 @@ Scope {
// Keys reach a focused item, not a window: Keys.onEscapePressed on
// the PanelWindow above never fires. This item is what listens.
+ // Escape is the only key: the expose is a pointer feature.
Item {
- id: keyHandler
anchors.fill: parent
focus: true
-
- // The LazyLoader destroys the overlay when it closes and
- // builds a new one on the next opening, verified by a build
- // counter: two openings, two constructions. So this starts at
- // 0 every time on its own, and there is nothing to reset. A
- // Component.onCompleted assigning 0 here would be a no-op.
- property int selectedIndex: 0
-
- function commit() {
- const list = Windows.windows;
- if (selectedIndex >= 0 && selectedIndex < list.length)
- root.focusAfterClose(list[selectedIndex]);
- }
-
- function move(delta) {
- const n = Windows.windows.length;
- if (n === 0) return;
- selectedIndex = (selectedIndex + delta + n) % n;
- }
-
Keys.onEscapePressed: root.close()
- Keys.onReturnPressed: commit()
- Keys.onEnterPressed: commit()
- Keys.onLeftPressed: move(-1)
- Keys.onRightPressed: move(1)
- Keys.onUpPressed: move(-grid.columns)
- Keys.onDownPressed: move(grid.columns)
- Keys.onTabPressed: move(1)
- Keys.onBacktabPressed: move(-1)
Rectangle {
anchors.fill: parent
@@ -146,38 +119,26 @@ Scope {
}
}
- Grid {
- id: grid
- anchors.centerIn: parent
- visible: Windows.windows.length > 0
-
- readonly property int count: Windows.windows.length
- readonly property int gap: 30
- readonly property int maxCardWidth: 640
-
- // Columns are the window count capped at six, so one
- // card sits dead centre and two straddle the middle.
- columns: Math.max(1, Math.min(count, 6))
-
- // Cards grow to fill their row, up to a ceiling.
- // Without the ceiling a lone preview becomes a
- // full-screen mirror of the window it stands for.
- readonly property int rows: Math.ceil(count / columns)
-
- // The three text lines and the spacing above them,
- // which the box height has to leave room for.
- readonly property int textHeight: 54
-
- // Clamped by the height as well as the width. Without
- // the second term a busy desktop pushes rows off the
- // top and bottom, and a centred Grid has no way to
- // scroll to them, so the cards shrink instead.
- readonly property int cardWidth: Math.min(
- maxCardWidth,
- Math.floor((parent.width * 0.92 - (columns - 1) * gap) / columns),
- Math.floor(((parent.height * 0.92 - (rows - 1) * gap) / rows - textHeight) * 16 / 10))
+ // The scatter. Recomputed whenever the window list or a
+ // window's size changes, so closing one moves the rest.
+ Item {
+ id: scatter
+ anchors.fill: parent
- spacing: gap
+ // Real sizes come from the raw hyprctl object. A window
+ // that has none yet stands in at 1280x800 until it does.
+ readonly property var rects: Expose.layout(
+ Windows.windows.map(t => {
+ const o = t.lastIpcObject;
+ const s = o && o.size;
+ return s ? { w: s[0], h: s[1] } : { w: 1280, h: 800 };
+ }), width, height)
+
+ // ponytail: a fade, not a flight from each window's real
+ // position; add the flight if the fade feels abrupt.
+ opacity: 0
+ Component.onCompleted: opacity = 1
+ Behavior on opacity { NumberAnimation { duration: 150 } }
Repeater {
model: Windows.windows
@@ -186,14 +147,16 @@ Scope {
required property int index
required property var modelData
+ readonly property var rect: scatter.rects[index]
+ ?? { x: 0, y: 0, w: 0, h: 0 }
+
toplevel: modelData
- cardWidth: grid.cardWidth
- selected: index === keyHandler.selectedIndex
+ x: rect.x
+ y: rect.y
+ width: rect.w
+ height: rect.h
- onActivated: {
- keyHandler.selectedIndex = index;
- keyHandler.commit();
- }
+ onActivated: root.focusAfterClose(modelData)
// Closing is a side errand, so the overlay
// stays open: a dismissal would mean reopening
// to close a second window. The card goes when