import QtQuick import Quickshell import Quickshell.Io import "." as M import "../services" as S import NovaStats as NS M.BarModule { id: root active: NS.ModulesService.workspacesEnable spacing: 4 cursorShape: Qt.ArrowCursor property var _allWorkspaces: [] property int _activeId: -1 readonly property string _output: QsWindow.window?.screen?.name ?? "" readonly property var _workspaces: _allWorkspaces.filter(w => w.output === root._output) // Initial state Process { id: initProc running: true command: ["niri", "msg", "--json", "workspaces"] stdout: StdioCollector { onStreamFinished: { try { const ws = JSON.parse(text); root._allWorkspaces = ws.sort((a, b) => a.idx - b.idx); for (const w of ws) { if (w.is_focused) root._activeId = w.id; } } catch (e) {} } } } // Live updates via shared NiriIpc singleton Connections { target: S.NiriIpc function onWorkspacesChanged(workspaces) { root._allWorkspaces = workspaces.sort((a, b) => a.idx - b.idx); } function onWorkspaceActivated(id, focused) { if (focused) root._activeId = id; } } Process { id: switchProc property int wsId: -1 command: ["niri", "msg", "action", "focus-workspace", String(wsId)] } Repeater { model: root._workspaces delegate: Rectangle { id: pill required property var modelData readonly property bool active: modelData.id === root._activeId property bool _hovered: false HoverHandler { onHoveredChanged: { pill._hovered = hovered; const name = pill.modelData.name || ("Workspace " + pill.modelData.idx); if (hovered) { M.TooltipState.text = name; M.TooltipState.itemX = pill.mapToGlobal(pill.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0); M.TooltipState.screen = QsWindow.window?.screen ?? null; M.TooltipState.accentColor = root.accentColor; M.TooltipState.visible = true; } else { M.TooltipState.visible = false; } } } width: NS.ThemeService.fontSize + 4 height: NS.ThemeService.fontSize + 4 radius: width / 2 color: pill.active ? root.accentColor : (pill._hovered ? NS.ThemeService.base03 : NS.ThemeService.base02) Behavior on color { ColorAnimation { duration: 150 } } Text { anchors.centerIn: parent text: pill.modelData.idx color: pill.active ? NS.ThemeService.base00 : root.accentColor font.pixelSize: NS.ThemeService.fontSize - 2 font.family: NS.ThemeService.fontFamily font.bold: pill.active } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { switchProc.wsId = pill.modelData.id; switchProc.running = true; } } } } }