import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland import QtQuick import QtQuick.Layouts // ───────────────────────────────────────────── // Shell root – spawns one bar per screen // ───────────────────────────────────────────── ShellRoot { id: root // ── Global polled data ─────────────────── property string clockTime: "" property string clockDate: "" property string cpuUsage: "" property string memUsed: "" property string networkInfo: "" property string volumeInfo: "" property string themeInfo: "" // Clock – every second Process { id: clockTimeProc command: ["date", "+%H:%M:%S"] running: true stdout: StdioCollector { onStreamFinished: clockTime = this.text.trim() } } Timer { interval: 1000; running: true; repeat: true onTriggered: clockTimeProc.running = true } Process { id: clockDateProc command: ["date", "+%A, %B %d %Y"] running: true stdout: StdioCollector { onStreamFinished: clockDate = this.text.trim() } } Timer { interval: 1000; running: true; repeat: true onTriggered: clockDateProc.running = true } // CPU – every 2 s Process { id: cpuProc command: ["bash", "-c", "top -bn1 | grep 'Cpu(s)' | awk '{print int($2+$4)}'"] running: true stdout: StdioCollector { onStreamFinished: cpuUsage = this.text.trim() } } Timer { interval: 2000; running: true; repeat: true onTriggered: cpuProc.running = true } // Memory – every 2 s Process { id: memProc command: ["bash", "-c", "free -g --si | awk '/Mem:/{printf \"%.1f\", $3}'"] running: true stdout: StdioCollector { onStreamFinished: memUsed = this.text.trim() } } Timer { interval: 2000; running: true; repeat: true onTriggered: memProc.running = true } // Network – every 5 s Process { id: netProc command: ["bash", Qt.resolvedUrl("scripts/get-network.sh")] running: true stdout: StdioCollector { onStreamFinished: networkInfo = this.text.trim() } } Timer { interval: 5000; running: true; repeat: true onTriggered: netProc.running = true } // Volume – every 1 s Process { id: volProc command: ["bash", Qt.resolvedUrl("scripts/get-volume.sh")] running: true stdout: StdioCollector { onStreamFinished: volumeInfo = this.text.trim() } } Timer { interval: 1000; running: true; repeat: true onTriggered: volProc.running = true } // Theme – every 2 s Process { id: themeProc command: ["bash", Qt.resolvedUrl("scripts/get-theme.sh")] running: true stdout: StdioCollector { onStreamFinished: themeInfo = this.text.trim() } } Timer { interval: 2000; running: true; repeat: true onTriggered: themeProc.running = true } // ── One bar per screen ─────────────────── Variants { model: Quickshell.screens PanelWindow { id: bar required property var modelData screen: modelData color: '#1c1d20' anchors { top: true; left: true; right: true } implicitHeight: 30 exclusiveZone: implicitHeight // reserve space (replaces :exclusive true) // Which screen slot is this? (0 = primary → ws 1–5, 1 = secondary → ws 6–10) property int screenIndex: { var screens = Quickshell.screens for (var i = 0; i < screens.length; i++) { if (screens[i] === modelData) return i } return 0 } // The 5 workspace IDs that belong to this bar readonly property int wsFirst: screenIndex === 0 ? 1 : 6 readonly property int wsLast: screenIndex === 0 ? 5 : 10 // ── Bar layout (centerbox equivalent) ── RowLayout { anchors.fill: parent spacing: 0 anchors.leftMargin: 8 anchors.rightMargin: 8 // LEFT – workspaces (native Hyprland model, filtered per screen) Item { Layout.fillWidth: true Layout.fillHeight: true Row { anchors { left: parent.left; verticalCenter: parent.verticalCenter } spacing: 4 Repeater { // Always show slots wsFirst..wsLast, even if Hyprland // hasn't created the workspace yet (shows as empty). model: bar.wsLast - bar.wsFirst + 1 delegate: Rectangle { required property int index readonly property int wsId: bar.wsFirst + index // Look up live workspace from Hyprland readonly property HyprlandWorkspace liveWs: { for (var i = 0; i < Hyprland.workspaces.values.length; i++) { if (Hyprland.workspaces.values[i].id === wsId) return Hyprland.workspaces.values[i] } return null } readonly property bool active: Hyprland.focusedMonitor !== null && Hyprland.focusedMonitor.activeWorkspace !== null && Hyprland.focusedMonitor.activeWorkspace.id === wsId && Hyprland.focusedMonitor.name === bar.modelData.name readonly property bool occupied: liveWs !== null && liveWs.windowCount > 0 implicitWidth: 22 implicitHeight: 22 radius: 4 color: active ? "#89b4fa" // focused : occupied ? "#313244" // has windows : "transparent" // empty border.color: active ? "transparent" : "#585b70" border.width: active ? 0 : 1 Text { anchors.centerIn: parent text: wsId color: active ? "#1e1e2e" : "#cdd6f4" font.pixelSize: 11 font.family: "monospace" } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: Hyprland.dispatch("workspace " + wsId) } } } } } // CENTER – clock Item { Layout.fillWidth: true Layout.fillHeight: true ClockWidget { anchors.centerIn: parent clockTime: root.clockTime clockDate: root.clockDate } } // RIGHT – stats + menus Item { Layout.fillWidth: true Layout.fillHeight: true RowLayout { anchors { right: parent.right; verticalCenter: parent.verticalCenter } spacing: 2 // Primary screen (0) gets full menus; secondary gets stats only SettingsMenu { visible: bar.screenIndex === 0 volumeInfo: root.volumeInfo networkInfo: root.networkInfo themeInfo: root.themeInfo } LogoutMenu { visible: bar.screenIndex === 0 } CpuWidget { cpuUsage: root.cpuUsage } MemWidget { memUsed: root.memUsed } } } } } } }