import QtQuick import QtQuick.Controls // button import QtQuick.Layouts import Quickshell import Quickshell.Hyprland import Quickshell.Wayland import Quickshell.Widgets import qs.common import qs.common.functions import qs.common.widgets import Qt5Compat.GraphicalEffects Item { id: root property bool borderless: Config.options.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(QsWindow.window?.screen) readonly property list monitors: Hyprland.monitors.values readonly property HyprlandToplevel activeWindow: Hyprland.activeToplevel readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown) property list workspaceOccupied: [] property int widgetPadding: 4 property int workspaceButtonWidth: 26 property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % Config.options.bar.workspaces.shown function updateWorkspaceOccupied() { workspaceOccupied = Array.from({ length: Config.options.bar.workspaces.shown }, (_, i) => { return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * Config.options.bar.workspaces.shown + i + 1); }); } function isMonitorWorkspace(id) { for (var i = 0; i < monitors.length; i++){ if (id === monitors[i].id) { return true; } } } Component.onCompleted: updateWorkspaceOccupied() Connections { target: Hyprland.workspaces function onValuesChanged() { root.updateWorkspaceOccupied(); } } implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 implicitHeight: Appearance.sizes.barHeight // Workspaces - background RowLayout { id: rowLayout z: 1 spacing: 0 anchors.fill: parent implicitHeight: Appearance.sizes.barHeight // https://doc.qt.io/qt-6/qml-qtquick-repeater.html Repeater { model: Config.options.bar.workspaces.shown Rectangle { z: 1 implicitHeight: root.workspaceButtonWidth implicitWidth: root.workspaceButtonWidth radius: Appearance.rounding.full property var leftOccupied: index - 1 >= 0 && (root.workspaceOccupied[index-1] || root.isMonitorWorkspace(index)) property var rightOccupied: index + 1 < workspaceOccupied.length && (root.workspaceOccupied[index+1] || root.isMonitorWorkspace(index+2)) property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full topLeftRadius: radiusLeft bottomLeftRadius: radiusLeft topRightRadius: radiusRight bottomRightRadius: radiusRight color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) opacity: (root.workspaceOccupied[index] || root.isMonitorWorkspace(index+1)) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on radiusLeft { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on radiusRight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } } } // Active workspace Rectangle { z: 2 property real activeWorkspaceMargin: 2 implicitHeight: root.workspaceButtonWidth - activeWorkspaceMargin * 2 radius: Appearance.rounding.full color: Appearance.colors.colPrimary anchors.verticalCenter: parent.verticalCenter property real idx1: root.workspaceIndexInGroup property real idx2: root.workspaceIndexInGroup x: Math.min(idx1, idx2) * root.workspaceButtonWidth + activeWorkspaceMargin implicitWidth: Math.abs(idx1 - idx2) * root.workspaceButtonWidth + root.workspaceButtonWidth - activeWorkspaceMargin * 2 Behavior on activeWorkspaceMargin { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } Behavior on idx1 { // leading animation NumberAnimation { duration: 100 easing.type: Easing.OutSine } } /* Behavior on idx2 { // following animation NumberAnimation { duration: 100 easing.type: Easing.OutSine } } */ } // workspaces - numbers RowLayout { id: rowLayoutNumbers z: 3 spacing: 0 anchors.fill: parent implicitHeight: Appearance.sizes.barHeight Repeater { model: Config.options.bar.workspaces.shown Button { id: button property int workspaceValue: workspaceGroup * Config.options.bar.workspaces.shown + index + 1 Layout.fillHeight: true onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`) width: root.workspaceButtonWidth background: Item { id: workspaceButtonBackground implicitWidth: root.workspaceButtonWidth implicitHeight: root.workspaceButtonWidth StyledText { // Workspace number text opacity: (Config.options?.bar.workspaces.alwaysShowNumbers) ? 1 : 0 z: 3 anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pixelSize: Appearance.font.pixelSize.small - ((text.length - 1) * (text !== "10") * 2) text: `${button.workspaceValue}` elide: Text.ElideRight color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1Inactive Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } } } } } } }