feat(quickshell): Alt+Tab overview of every monitor and workspace

Each monitor is a row of workspace cards at its own aspect ratio, with
live captures of the windows where they sit, including windows on
hidden workspaces. Alt+Tab opens it with the previous window picked,
further Tabs walk the focus history, and releasing Alt switches.
Super+W opens it without the hold. Hover, click, Enter and the
workspace digits work too.

Group cycling moves from Alt+Tab to Super+Tab and Super+Shift+Tab.
This commit is contained in:
nvrl committed 2026-10-04 23:27:48 +02:00
1 parent e9735d1129
commit b69f4bcf0d
7 files changed
+637 -2

No files matched your search

+35
View File
@@ -19,6 +19,9 @@ Singleton {
property bool launcherOpen: false
property bool askOpen: false
property bool dashboardOpen: false
property bool overviewOpen: false
// Opened by Alt+Tab, so letting go of Alt picks the window.
property bool overviewHold: false
// Seed text for the next launcher open. Lets a keybind or a script hand the
// launcher a query it did not have to type — `qs ipc call shell prompt
@@ -40,6 +43,9 @@ Singleton {
// …and for the dashboard drawer.
property string dashboardScreen: ""
// …and for the overview.
property string overviewScreen: ""
// When the dashboard was last closed from outside. A click on the bar's
// clock first clears the drawer's focus grab, which closes it, and then
// lands on the clock, which would toggle it straight back open. A toggle
@@ -51,6 +57,8 @@ Singleton {
property string askSeed: ""
signal osdRequested(string kind)
signal overviewStepped(int delta)
signal overviewCommitted()
function togglePowerMenu() {
if (!root.powerMenuOpen)
@@ -88,6 +96,29 @@ Singleton {
root.dashboardClosedAt = Date.now();
}
function toggleOverview() {
if (!root.overviewOpen) {
root.overviewHold = false;
root.overviewScreen = root.focusedScreenName();
}
root.overviewOpen = !root.overviewOpen;
}
function closeOverview() { root.overviewOpen = false; }
// Alt+Tab and Alt+Shift+Tab. The first press opens the overview with the
// previous window picked; Hyprland keeps the bind while it is open, so
// later presses arrive here too and walk the focus history.
function overviewStep(delta: int) {
if (!root.overviewOpen) {
root.overviewHold = true;
root.overviewScreen = root.focusedScreenName();
root.overviewOpen = true;
if (delta < 0) root.overviewStepped(-2);
return;
}
root.overviewStepped(delta);
}
function toggleAsk() {
if (!root.askOpen) {
root.askSeed = "";
@@ -164,6 +195,10 @@ Singleton {
function toggleDnd(): void { Notifs.toggleDnd(); }
function clearNotifications(): void { Notifs.clearAll(); }
function toggleStayAwake(): void { Idle.toggleStayAwake(); }
function overview(): void { root.toggleOverview(); }
function overviewNext(): void { root.overviewStep(1); }
function overviewPrev(): void { root.overviewStep(-1); }
function overviewCommit(): void { if (root.overviewHold) root.overviewCommitted(); }
function wallpaper(path: string): void { Wallpaper.set(path); }
}
}