// WorkspacesWidget.qml - shows workspaces and allows switching between them import QtQuick import QtQuick.Layouts import Quickshell.Hyprland import ".." Rectangle { id: root required property var screen property var sortedWorkspaces: Array.from(Hyprland.workspaces.values).sort((a, b) => a.id - b.id) property int activeIndex: { var fid = Hyprland.focusedWorkspace?.id ?? -1 for (var i = 0; i < sortedWorkspaces.length; i++) if (sortedWorkspaces[i].id === fid) return i return -1 } // Get the active workspace for this monitor property var monitorActiveWorkspace: { var activeWs = Hyprland.focusedWorkspace // Check if focused workspace is on this monitor if (activeWs && activeWs.monitor?.id === root.screen.id) { return activeWs } // Otherwise find the first workspace on this monitor for (var i = 0; i < sortedWorkspaces.length; i++) { if (sortedWorkspaces[i].monitor?.id === root.screen.id) { return sortedWorkspaces[i] } } return null } function isWorkspaceOnThisMonitor(workspace) { // Find the monitor in Hyprland.monitors that matches this screen's name for (const monitor of Hyprland.monitors.values) { if (monitor.name === root.screen.name && monitor.id === workspace.monitor.id) { return true } } return false } readonly property int btnWidth: 32 color: Theme.pill radius: Theme.radius clip: true implicitWidth: wsRow.implicitWidth implicitHeight: Theme.barHeight Behavior on implicitWidth { NumberAnimation { duration: Theme.workspaceSlideTime; easing.type: Easing.InOutCubic } } // Sliding active indicator - single Rectangle that travels between buttons Rectangle { visible: root.activeIndex >= 0 x: root.activeIndex * root.btnWidth width: root.btnWidth height: parent.height color: Theme.wsActive Behavior on x { NumberAnimation { duration: Theme.workspaceSlideTime; easing.type: Easing.InOutCubic } } } RowLayout { id: wsRow anchors.fill: parent spacing: 0 Repeater { model: root.sortedWorkspaces delegate: Rectangle { required property var modelData Layout.fillHeight: true implicitWidth: root.btnWidth color: wsBtn.containsMouse ? Theme.pillHover : "transparent" // Reduce opacity for workspaces not on this monitor opacity: root.isWorkspaceOnThisMonitor(modelData) ? 1.0 : 0.4 Behavior on opacity { NumberAnimation { duration: 150 } } Behavior on color { ColorAnimation { duration: 150 } } Text { anchors.centerIn: parent text: modelData.id font { family: Theme.fontSans; pixelSize: Theme.fontSizeSecondary } color: Theme.pillText } MouseArea { id: wsBtn anchors.fill: parent hoverEnabled: true onClicked: Hyprland.dispatch("hl.dsp.focus({ workspace = '" + modelData.id + "' })") onWheel: (w) => Hyprland.dispatch( "hl.dsp.focus({ workspace = \"" + (w.angleDelta.y > 0 ? "e+1" : "e-1") + "\" })" ) } } } } }