diff options
Diffstat (limited to 'window-switcher')
| -rw-r--r-- | window-switcher/Switcher.qml | 108 |
1 files changed, 100 insertions, 8 deletions
diff --git a/window-switcher/Switcher.qml b/window-switcher/Switcher.qml index d989e57..4d01b90 100644 --- a/window-switcher/Switcher.qml +++ b/window-switcher/Switcher.qml @@ -25,6 +25,10 @@ Scope { function toggle() { root.open = !root.open; } function close() { root.open = false; } + // Hyprland.toplevels reads empty until it is asked, and a window opened + // since the last showing would otherwise be missing from the grid. + onOpenChanged: if (open) Windows.refresh(); + // A config with no visible window exits, reporting nothing. This 1x1 // click-through window is what holds the shell open while the overlay // is hidden, which is almost always. @@ -54,14 +58,41 @@ Scope { // Keys reach a focused item, not a window: Keys.onEscapePressed on // the PanelWindow above never fires. This item is what listens. Item { + id: keyHandler anchors.fill: parent focus: true - Keys.onEscapePressed: { - console.log("SWITCHER escape"); - root.close(); + // 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) { + Windows.focus(list[selectedIndex]); + root.close(); + } + } + + 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 color: Qt.rgba(Theme.base.r, Theme.base.g, Theme.base.b, 0.82) @@ -71,12 +102,73 @@ Scope { onClicked: root.close() } - Text { + // Nothing to switch to. A full-screen dim with nothing in + // it reads as a hang, so it says so instead. + Column { + anchors.centerIn: parent + spacing: 14 + visible: Windows.windows.length === 0 + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "Nothing here but hopes and dreams..." + color: Theme.text + font.family: Theme.fontFamily + font.pixelSize: 26 + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "go create something beautiful!" + color: Theme.overlay + font.family: Theme.fontFamily + font.pixelSize: 32 + } + } + + Grid { + id: grid anchors.centerIn: parent - text: "press Escape" - color: Theme.text - font.family: Theme.fontFamily - font.pixelSize: 28 + 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 cardWidth: Math.min( + maxCardWidth, + Math.floor((parent.width * 0.92 - (columns - 1) * gap) / columns)) + + spacing: gap + + Repeater { + model: Windows.windows + + WindowCard { + required property int index + required property var modelData + + toplevel: modelData + cardWidth: grid.cardWidth + selected: index === keyHandler.selectedIndex + + onActivated: { + keyHandler.selectedIndex = index; + keyHandler.commit(); + } + // Closing is a side errand, so the overlay + // stays open: a dismissal would mean reopening + // to close a second window. The card goes when + // Hyprland says it went. + onCloseRequested: Windows.closeWindow(modelData) + } + } } } } |
