Files
illogical-impulse/.config/quickshell/ii/modules/bar/Workspaces.qml
T

307 lines
13 KiB
QML

import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
Item {
id: root
property bool borderless: Config.options.bar.borderless
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / Config.options.bar.workspaces.shown)
property list<bool> workspaceOccupied: []
property int widgetPadding: 4
property int workspaceButtonWidth: 26
property real workspaceIconSize: workspaceButtonWidth * 0.69
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
property bool showNumbers: false
Timer {
id: showNumbersTimer
interval: (Config?.options.bar.autoHide.showWhenPressingSuper.delay ?? 100)
repeat: false
onTriggered: {
root.showNumbers = true
}
}
Connections {
target: GlobalStates
function onSuperDownChanged() {
if (!Config?.options.bar.autoHide.showWhenPressingSuper.enable) return;
if (GlobalStates.superDown) showNumbersTimer.restart();
else {
showNumbersTimer.stop();
root.showNumbers = false;
}
}
function onSuperReleaseMightTriggerChanged() {
showNumbersTimer.stop()
}
}
// Function to update workspaceOccupied
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);
})
}
// Initialize workspaceOccupied when the component is created
Component.onCompleted: updateWorkspaceOccupied()
// Listen for changes in Hyprland.workspaces.values
Connections {
target: Hyprland.workspaces
function onValuesChanged() {
updateWorkspaceOccupied();
}
}
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
implicitHeight: Appearance.sizes.barHeight
// Scroll to switch workspaces
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0)
Hyprland.dispatch(`workspace r+1`);
else if (event.angleDelta.y > 0)
Hyprland.dispatch(`workspace r-1`);
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.BackButton
onPressed: (event) => {
if (event.button === Qt.BackButton) {
Hyprland.dispatch(`togglespecialworkspace`);
}
}
}
// Workspaces - background
RowLayout {
id: rowLayout
z: 1
spacing: 0
anchors.fill: parent
implicitHeight: Appearance.sizes.barHeight
Repeater {
model: Config.options.bar.workspaces.shown
Rectangle {
z: 1
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
radius: Appearance.rounding.full
property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index))
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === 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: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === 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
// Make active ws indicator, which has a brighter color, smaller to look like it is of the same size as ws occupied highlight
property real activeWorkspaceMargin: 2
implicitHeight: workspaceButtonWidth - activeWorkspaceMargin * 2
radius: Appearance.rounding.full
color: Appearance.colors.colPrimary
anchors.verticalCenter: parent.verticalCenter
property real idx1: workspaceIndexInGroup
property real idx2: workspaceIndexInGroup
x: Math.min(idx1, idx2) * workspaceButtonWidth + activeWorkspaceMargin
implicitWidth: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - activeWorkspaceMargin * 2
Behavior on activeWorkspaceMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on idx1 { // Leading anim
NumberAnimation {
duration: 100
easing.type: Easing.OutSine
}
}
Behavior on idx2 { // Following anim
NumberAnimation {
duration: 300
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: workspaceButtonWidth
background: Item {
id: workspaceButtonBackground
implicitWidth: workspaceButtonWidth
implicitHeight: workspaceButtonWidth
property var biggestWindow: HyprlandData.biggestWindowForWorkspace(button.workspaceValue)
property var mainAppIconSource: Quickshell.iconPath(AppSearch.guessIcon(biggestWindow?.class), "image-missing")
StyledText { // Workspace number text
opacity: root.showNumbers
|| ((Config.options?.bar.workspaces.alwaysShowNumbers && (!Config.options?.bar.workspaces.showAppIcons || !workspaceButtonBackground.biggestWindow || root.showNumbers))
|| (root.showNumbers && !Config.options?.bar.workspaces.showAppIcons)
) ? 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 :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
Rectangle { // Dot instead of ws number
id: wsDot
opacity: (Config.options?.bar.workspaces.alwaysShowNumbers
|| root.showNumbers
|| (Config.options?.bar.workspaces.showAppIcons && workspaceButtonBackground.biggestWindow)
) ? 0 : 1
visible: opacity > 0
anchors.centerIn: parent
width: workspaceButtonWidth * 0.18
height: width
radius: width / 2
color: (monitor?.activeWorkspace?.id == button.workspaceValue) ?
Appearance.m3colors.m3onPrimary :
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
Appearance.colors.colOnLayer1Inactive)
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
Item { // Main app icon
anchors.centerIn: parent
width: workspaceButtonWidth
height: workspaceButtonWidth
opacity: !Config.options?.bar.workspaces.showAppIcons ? 0 :
(workspaceButtonBackground.biggestWindow && !root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
1 : workspaceButtonBackground.biggestWindow ? workspaceIconOpacityShrinked : 0
visible: opacity > 0
IconImage {
id: mainAppIcon
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
anchors.rightMargin: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ?
(workspaceButtonWidth - workspaceIconSize) / 2 : workspaceIconMarginShrinked
source: workspaceButtonBackground.mainAppIconSource
implicitSize: (!root.showNumbers && Config.options?.bar.workspaces.showAppIcons) ? workspaceIconSize : workspaceIconSizeShrinked
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on anchors.bottomMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on anchors.rightMargin {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Behavior on implicitSize {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
Loader {
active: Config.options.bar.workspaces.monochromeIcons
anchors.fill: mainAppIcon
sourceComponent: Item {
Desaturate {
id: desaturatedIcon
visible: false // There's already color overlay
anchors.fill: parent
source: mainAppIcon
desaturation: 0.8
}
ColorOverlay {
anchors.fill: desaturatedIcon
source: desaturatedIcon
color: ColorUtils.transparentize(wsDot.color, 0.9)
}
}
}
}
}
}
}
}
}