mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-07 07:49:28 -05:00
331 lines
14 KiB
QML
331 lines
14 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 Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
import Quickshell.Widgets
|
|
import Qt5Compat.GraphicalEffects
|
|
|
|
Item {
|
|
id: root
|
|
property bool vertical: false
|
|
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 workspacesShown: Config.options.bar.workspaces.shown
|
|
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown)
|
|
property list<bool> workspaceOccupied: []
|
|
property int widgetPadding: 4
|
|
property int workspaceButtonWidth: 26
|
|
property real activeWorkspaceMargin: 2
|
|
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) % root.workspacesShown
|
|
|
|
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: root.workspacesShown }, (_, i) => {
|
|
return Hyprland.workspaces.values.some(ws => ws.id === workspaceGroup * root.workspacesShown + i + 1);
|
|
})
|
|
}
|
|
|
|
// Occupied workspace updates
|
|
Component.onCompleted: updateWorkspaceOccupied()
|
|
Connections {
|
|
target: Hyprland.workspaces
|
|
function onValuesChanged() {
|
|
updateWorkspaceOccupied();
|
|
}
|
|
}
|
|
Connections {
|
|
target: Hyprland
|
|
function onFocusedWorkspaceChanged() {
|
|
updateWorkspaceOccupied();
|
|
}
|
|
}
|
|
onWorkspaceGroupChanged: {
|
|
updateWorkspaceOccupied();
|
|
}
|
|
|
|
implicitWidth: root.vertical ? Appearance.sizes.verticalBarWidth : (root.workspaceButtonWidth * root.workspacesShown)
|
|
implicitHeight: root.vertical ? (root.workspaceButtonWidth * root.workspacesShown) : 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
|
|
Grid {
|
|
z: 1
|
|
anchors.centerIn: parent
|
|
|
|
rowSpacing: 0
|
|
columnSpacing: 0
|
|
columns: root.vertical ? 1 : root.workspacesShown
|
|
rows: root.vertical ? root.workspacesShown : 1
|
|
|
|
Repeater {
|
|
model: root.workspacesShown
|
|
|
|
Rectangle {
|
|
z: 1
|
|
implicitWidth: workspaceButtonWidth
|
|
implicitHeight: workspaceButtonWidth
|
|
radius: (width / 2)
|
|
property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index))
|
|
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2))
|
|
property var radiusPrev: previousOccupied ? 0 : (width / 2)
|
|
property var radiusNext: rightOccupied ? 0 : (width / 2)
|
|
|
|
topLeftRadius: radiusPrev
|
|
bottomLeftRadius: root.vertical ? radiusNext : radiusPrev
|
|
topRightRadius: root.vertical ? radiusPrev : radiusNext
|
|
bottomRightRadius: radiusNext
|
|
|
|
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 radiusPrev {
|
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
}
|
|
|
|
Behavior on radiusNext {
|
|
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
|
|
radius: Appearance.rounding.full
|
|
color: Appearance.colors.colPrimary
|
|
|
|
anchors {
|
|
verticalCenter: vertical ? undefined : parent.verticalCenter
|
|
horizontalCenter: vertical ? parent.horizontalCenter : undefined
|
|
}
|
|
|
|
// idx1 is the "leading" indicator position, idx2 is the "following" one
|
|
// The former animates faster than the latter, see the NumberAnimations below
|
|
property real idx1: workspaceIndexInGroup
|
|
property real idx2: workspaceIndexInGroup
|
|
property real indicatorPosition: Math.min(idx1, idx2) * workspaceButtonWidth + root.activeWorkspaceMargin
|
|
property real indicatorLength: Math.abs(idx1 - idx2) * workspaceButtonWidth + workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
|
property real indicatorThickness: workspaceButtonWidth - root.activeWorkspaceMargin * 2
|
|
|
|
x: root.vertical ? null : indicatorPosition
|
|
implicitWidth: root.vertical ? indicatorThickness : indicatorLength
|
|
y: root.vertical ? indicatorPosition : null
|
|
implicitHeight: root.vertical ? indicatorLength : indicatorThickness
|
|
|
|
Behavior on idx1 {
|
|
NumberAnimation {
|
|
duration: 100
|
|
easing.type: Easing.OutSine
|
|
}
|
|
}
|
|
Behavior on idx2 {
|
|
NumberAnimation {
|
|
duration: 300
|
|
easing.type: Easing.OutSine
|
|
}
|
|
}
|
|
}
|
|
|
|
// Workspaces - numbers
|
|
Grid {
|
|
z: 3
|
|
|
|
columns: root.vertical ? 1 : root.workspacesShown
|
|
rows: root.vertical ? root.workspacesShown : 1
|
|
columnSpacing: 0
|
|
rowSpacing: 0
|
|
|
|
anchors.fill: parent
|
|
|
|
Repeater {
|
|
model: root.workspacesShown
|
|
|
|
Button {
|
|
id: button
|
|
property int workspaceValue: workspaceGroup * root.workspacesShown + index + 1
|
|
implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight
|
|
implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth
|
|
onPressed: Hyprland.dispatch(`workspace ${workspaceValue}`)
|
|
width: vertical ? undefined : workspaceButtonWidth
|
|
height: vertical ? workspaceButtonWidth : undefined
|
|
|
|
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)
|
|
family: Config.options?.bar.workspaces.useNerdFont ? Appearance.font.family.iconNerd : Appearance.font.family.main
|
|
}
|
|
text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|