// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. pragma Singleton import Quickshell import Quickshell.Hyprland import QtQuick // Everything that touches Hyprland lives here, so the filter, the sort and // the dispatch syntax are in one place rather than spread across the UI. Singleton { id: root // Hyprland.toplevels reads 0 until this is called. function refresh() { Hyprland.refreshToplevels(); } // Windows on a special: workspace are the scratchpad, which is reached by // its own bind and is not something to tab to. Windows on other // workspaces are kept whatever their visibility: jumping to another // desktop is the point. readonly property var windows: { const out = []; for (const t of Hyprland.toplevels.values) { const ws = t.workspace; if (!ws || String(ws.name).startsWith("special:")) continue; out.push(t); } // Lowest focusHistoryID is the most recently used. HyprlandToplevel // does not expose it as a property, only inside the raw hyprctl // object, where a missing one sorts last rather than as 0. out.sort((a, b) => root.focusOrder(a) - root.focusOrder(b)); return out; } function focusOrder(toplevel) { const o = toplevel.lastIpcObject; const n = o ? Number(o.focusHistoryID) : NaN; return isNaN(n) ? Number.MAX_SAFE_INTEGER : n; } // Dispatch arguments are evaluated as Lua on Hyprland 0.56.2, so the // documented-looking `focuswindow address:0x...` is a syntax error that // fails silently. This form is the one that works. // // HyprlandToplevel.address has no 0x prefix, while the dispatch needs one. function addressOf(toplevel) { const a = String(toplevel.address); return a.startsWith("0x") ? a : "0x" + a; } function focus(toplevel) { Hyprland.dispatch(`hl.dsp.focus({ window = "address:${addressOf(toplevel)}" })`); } function closeWindow(toplevel) { Hyprland.dispatch(`hl.dsp.window.close({ window = "address:${addressOf(toplevel)}" })`); } }