summaryrefslogtreecommitdiffstats
path: root/home-configuration/config/quickshell/modules/bar
diff options
context:
space:
mode:
Diffstat (limited to 'home-configuration/config/quickshell/modules/bar')
-rw-r--r--home-configuration/config/quickshell/modules/bar/Bar.qml154
-rw-r--r--home-configuration/config/quickshell/modules/bar/Music.qml219
-rw-r--r--home-configuration/config/quickshell/modules/bar/QuickControl.qml519
-rw-r--r--home-configuration/config/quickshell/modules/bar/Time.qml144
-rw-r--r--home-configuration/config/quickshell/modules/bar/Workspaces.qml169
5 files changed, 1205 insertions, 0 deletions
diff --git a/home-configuration/config/quickshell/modules/bar/Bar.qml b/home-configuration/config/quickshell/modules/bar/Bar.qml
new file mode 100644
index 0000000..3fc6b36
--- /dev/null
+++ b/home-configuration/config/quickshell/modules/bar/Bar.qml
@@ -0,0 +1,154 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+PanelWindow {
+ id: bar
+
+ anchors {
+ top: true
+ left: true
+ right: true
+ }
+
+ implicitHeight: Config.barHeight
+ color: "transparent"
+
+ Rectangle {
+ id: content
+
+ anchors.fill: parent
+ color: Config.barBackground
+ opacity: 0
+ y: -Config.barHeight
+
+ RowLayout {
+ anchors {
+ left: parent.left
+ leftMargin: Config.barMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ spacing: Config.workspaceSpacing
+
+ Loader {
+ active: true
+ sourceComponent: Workspaces {}
+ }
+ }
+
+ Rectangle {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+
+ height: Config.blockHeight
+ width: windowTitle.contentWidth + Config.blockPadding
+ radius: Config.blockRadius
+ color: "transparent"
+
+ Text {
+ id: windowTitle
+
+ anchors.centerIn: parent
+
+ text: niri.focusedWindow?.title ?? ""
+
+ color: Config.textWindow
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ elide: Text.ElideRight
+ }
+ }
+
+ RowLayout {
+ id: rightLayout
+
+ anchors {
+ right: parent.right
+ rightMargin: Config.barMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ spacing: Config.workspaceSpacing
+
+ Loader {
+ active: true
+ sourceComponent: Music {}
+ }
+
+ Loader {
+ active: true
+ sourceComponent: Time {}
+ }
+
+ Loader {
+ active: true
+ sourceComponent: QuickControl {}
+ }
+ }
+ }
+
+ ParallelAnimation {
+ id: showAnimation
+
+ NumberAnimation {
+ target: content
+ property: "y"
+ from: -Config.barHeight
+ to: 0
+ duration: Config.barAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+
+ NumberAnimation {
+ target: content
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: Config.barAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ ParallelAnimation {
+ id: hideAnimation
+
+ NumberAnimation {
+ target: content
+ property: "y"
+ from: 0
+ to: -Config.barHeight
+ duration: Config.barAnimationDuration
+ easing.type: Easing.InCubic
+ }
+
+ NumberAnimation {
+ target: content
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: Config.barAnimationDuration
+ easing.type: Easing.InCubic
+ }
+
+ onFinished: {
+ showAnimation.start()
+ }
+ }
+
+ Component.onCompleted: {
+ showAnimation.start()
+ }
+
+ function restart() {
+ showAnimation.stop()
+ hideAnimation.stop()
+ hideAnimation.start()
+ }
+}
diff --git a/home-configuration/config/quickshell/modules/bar/Music.qml b/home-configuration/config/quickshell/modules/bar/Music.qml
new file mode 100644
index 0000000..ec16fed
--- /dev/null
+++ b/home-configuration/config/quickshell/modules/bar/Music.qml
@@ -0,0 +1,219 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Services.Mpris
+import "../../config.js" as Config
+
+Rectangle {
+ id: musicBlock
+
+ property var player: null
+
+ implicitWidth: visible
+ ? Math.min(textMetrics.width + Config.blockPadding + 8, 400)
+ : 0
+
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ visible: player !== null && player.isPlaying
+
+ function findFeishin() {
+ player = null
+
+ for (var i = 0; i < Mpris.players.values.length; i++) {
+ var p = Mpris.players.values[i]
+
+ console.log(
+ "MPRIS:",
+ "identity =", p.identity,
+ "desktopEntry =", p.desktopEntry
+ )
+
+ var identity = (p.identity || "").toLowerCase()
+ var desktopEntry = (p.desktopEntry || "").toLowerCase()
+
+ if (
+ identity === "feishin" ||
+ desktopEntry === "feishin"
+ ) {
+ player = p
+ console.log("FEISHIN FOUND")
+ return
+ }
+ }
+
+ console.log("FEISHIN NOT FOUND")
+ }
+
+ function cleanTitle(title) {
+ if (!title)
+ return ""
+
+ return title
+ .replace(/\s*[\(\[][^)\]]*\blive\b[^)\]]*[\)\]]/gi, "")
+ .replace(/\s*[-–—]\s*\blive\b.*$/i, "")
+ .trim()
+ }
+
+ Component.onCompleted: {
+ findFeishin()
+ }
+
+ Connections {
+ target: Mpris.players
+
+ function onValuesChanged() {
+ findFeishin()
+ }
+ }
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ TextMetrics {
+ id: textMetrics
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ text: player
+ ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle)
+ : ""
+ }
+
+ Text {
+ id: musicText
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+
+ leftPadding: Config.blockPadding / 2
+ rightPadding: Config.blockPadding / 2
+
+ text: player
+ ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle)
+ : ""
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ elide: Text.ElideRight
+ maximumLineCount: 1
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ if (player && player.canRaise)
+ player.raise()
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/home-configuration/config/quickshell/modules/bar/QuickControl.qml b/home-configuration/config/quickshell/modules/bar/QuickControl.qml
new file mode 100644
index 0000000..ffe6f6b
--- /dev/null
+++ b/home-configuration/config/quickshell/modules/bar/QuickControl.qml
@@ -0,0 +1,519 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Services.Pipewire
+import Quickshell.Services.SystemTray
+import "../../config.js" as Config
+
+Rectangle {
+ id: root
+
+ property bool expanded: false
+
+ implicitWidth: layout.implicitWidth + 24
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ Behavior on implicitWidth {
+ NumberAnimation {
+ duration: 250
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ Component.onCompleted: {
+ console.info("QuickControl: loaded")
+ console.info(
+ "QuickControl: tray items =",
+ SystemTray.items.values.length
+ )
+
+ if (Pipewire.defaultAudioSink) {
+ console.info(
+ "QuickControl: audio sink =",
+ Pipewire.defaultAudioSink.name
+ )
+
+ if (Pipewire.defaultAudioSink.audio) {
+ console.info(
+ "QuickControl: volume =",
+ Math.round(
+ Pipewire.defaultAudioSink.audio.volume * 100
+ ) + "%",
+ "muted =",
+ Pipewire.defaultAudioSink.audio.muted
+ )
+ }
+ } else {
+ console.warn("QuickControl: no default audio sink")
+ }
+ }
+
+ onExpandedChanged: {
+ console.info(
+ "QuickControl: expanded =",
+ expanded
+ )
+ }
+
+ RowLayout {
+ id: layout
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ leftMargin: 12
+ rightMargin: 12
+ }
+
+ spacing: 6
+
+ Text {
+ id: volumeIcon
+
+ text: Pipewire.defaultAudioSink?.audio?.muted
+ ? "󰖁"
+ : "󰕾"
+
+ visible: root.expanded
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ if (!Pipewire.defaultAudioSink?.audio) {
+ console.warn(
+ "QuickControl: no audio sink"
+ )
+ return
+ }
+
+ var audio = Pipewire.defaultAudioSink.audio
+
+ audio.muted = !audio.muted
+
+ console.info(
+ "QuickControl: mute =",
+ audio.muted
+ )
+ }
+ }
+ }
+
+ Text {
+ id: volumeText
+
+ text: Pipewire.defaultAudioSink?.audio
+ ? Math.round(
+ Pipewire.defaultAudioSink.audio.volume * 100
+ ) + "%"
+ : "0%"
+
+ visible: root.expanded
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ if (!Pipewire.defaultAudioSink?.audio) {
+ console.warn(
+ "QuickControl: no audio sink"
+ )
+ return
+ }
+
+ var audio = Pipewire.defaultAudioSink.audio
+
+ audio.volume = Math.min(
+ audio.volume + 0.05,
+ 1.0
+ )
+
+ console.info(
+ "QuickControl: volume =",
+ Math.round(audio.volume * 100) + "%"
+ )
+ }
+ }
+ }
+
+ Text {
+ id: brightnessIcon
+
+ text: "󰃠"
+
+ visible: root.expanded
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ console.info(
+ "QuickControl: brightness clicked"
+ )
+
+ Quickshell.execDetached([
+ "brightnessctl",
+ "set",
+ "+5%"
+ ])
+ }
+ }
+ }
+
+ Text {
+ id: networkIcon
+
+ text: {
+ var item = SystemTray.items.values.find(function(item) {
+ return item.id === "nm-applet"
+ })
+
+ if (!item)
+ return "󰤨"
+
+ var title = (item.title || "").toLowerCase()
+
+ if (title.indexOf("ethernet") !== -1 ||
+ title.indexOf("wired") !== -1 ||
+ title.indexOf("lan") !== -1)
+ return "󰈀"
+
+ return "󰤨"
+ }
+
+ visible: root.expanded
+ color: Config.textDark
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ console.info("QuickControl: network clicked")
+ Quickshell.execDetached(["nm-connection-editor"])
+ }
+ }
+}
+
+ Repeater {
+ model: root.expanded
+ ? SystemTray.items
+ : null
+
+ delegate: Item {
+ id: trayItem
+
+ required property var modelData
+
+ implicitWidth: 18
+ implicitHeight: 18
+
+ Layout.preferredWidth: 18
+ Layout.preferredHeight: 18
+
+ Component.onCompleted: {
+ console.info(
+ "QuickControl: tray item:",
+ modelData.title,
+ "id =",
+ modelData.id,
+ "hasMenu =",
+ modelData.hasMenu,
+ "onlyMenu =",
+ modelData.onlyMenu
+ )
+ }
+
+ QsMenuAnchor {
+ id: menuAnchor
+
+ anchor.item: trayItem
+
+ menu: modelData.menu
+
+ onOpened: {
+ console.info(
+ "QuickControl: context menu opened:",
+ modelData.title
+ )
+ }
+
+ onClosed: {
+ console.info(
+ "QuickControl: context menu closed:",
+ modelData.title
+ )
+ }
+ }
+
+ Image {
+ anchors.fill: parent
+
+ source: modelData.icon
+
+ sourceSize.width: 18
+ sourceSize.height: 18
+
+ fillMode: Image.PreserveAspectFit
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ acceptedButtons:
+ Qt.LeftButton |
+ Qt.RightButton |
+ Qt.MiddleButton
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: function(mouse) {
+ if (mouse.button === Qt.RightButton) {
+ console.info(
+ "QuickControl: right click:",
+ modelData.title,
+ "hasMenu =",
+ modelData.hasMenu
+ )
+
+ if (modelData.hasMenu) {
+ console.info(
+ "QuickControl: opening menu:",
+ modelData.title
+ )
+
+ menuAnchor.open()
+ } else {
+ console.info(
+ "QuickControl: no context menu:",
+ modelData.title
+ )
+ }
+
+ return
+ }
+
+ if (mouse.button === Qt.MiddleButton) {
+ console.info(
+ "QuickControl: middle click:",
+ modelData.title
+ )
+
+ modelData.secondaryActivate()
+ return
+ }
+
+ console.info(
+ "QuickControl: left click:",
+ modelData.title
+ )
+
+ if (modelData.onlyMenu && modelData.hasMenu) {
+ console.info(
+ "QuickControl: onlyMenu item, opening menu:",
+ modelData.title
+ )
+
+ menuAnchor.open()
+ } else {
+ modelData.activate()
+ }
+ }
+ }
+ }
+ }
+
+ Text {
+ id: dots
+
+ text: "•••"
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ Layout.alignment: Qt.AlignVCenter
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ root.expanded = !root.expanded
+
+ console.info(
+ "QuickControl: toggled, expanded =",
+ root.expanded
+ )
+ }
+ }
+ }
+ }
+
+ Connections {
+ target: SystemTray.items
+
+ function onValuesChanged() {
+ console.info(
+ "QuickControl: system tray changed, items =",
+ SystemTray.items.values.length
+ )
+ }
+ }
+
+ Connections {
+ target: Pipewire.defaultAudioSink?.audio ?? null
+
+ function onVolumeChanged() {
+ if (Pipewire.defaultAudioSink?.audio) {
+ console.info(
+ "QuickControl: volume changed:",
+ Math.round(
+ Pipewire.defaultAudioSink.audio.volume * 100
+ ) + "%"
+ )
+ }
+ }
+
+ function onMutedChanged() {
+ if (Pipewire.defaultAudioSink?.audio) {
+ console.info(
+ "QuickControl: muted changed:",
+ Pipewire.defaultAudioSink.audio.muted
+ )
+ }
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/home-configuration/config/quickshell/modules/bar/Time.qml b/home-configuration/config/quickshell/modules/bar/Time.qml
new file mode 100644
index 0000000..dfa3f0d
--- /dev/null
+++ b/home-configuration/config/quickshell/modules/bar/Time.qml
@@ -0,0 +1,144 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+Rectangle {
+ id: clockBlock
+
+ implicitWidth: timeText.contentWidth + Config.blockPadding
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ property bool alternateFormat: false
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ SystemClock {
+ id: clock
+ precision: SystemClock.Seconds
+ }
+
+ Text {
+ id: timeText
+
+ anchors.centerIn: parent
+
+ text: alternateFormat
+ ? Qt.formatDateTime(clock.date, "󱑃 dddd, dd MMMM yyyy")
+ : Qt.formatDateTime(clock.date, " hh:mm AP • ddd, dd/MM")
+
+ color: Config.textDark
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ alternateFormat = !alternateFormat
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/home-configuration/config/quickshell/modules/bar/Workspaces.qml b/home-configuration/config/quickshell/modules/bar/Workspaces.qml
new file mode 100644
index 0000000..3962a63
--- /dev/null
+++ b/home-configuration/config/quickshell/modules/bar/Workspaces.qml
@@ -0,0 +1,169 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+Rectangle {
+ id: workspaceBlock
+
+ implicitWidth: workspaceRow.width + 16
+ implicitHeight: Config.blockHeight
+
+ color: "transparent"
+
+ RowLayout {
+ id: workspaceRow
+
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Config.workspaceSpacing
+
+ Repeater {
+ model: niri.workspaces
+
+ delegate: Rectangle {
+ id: workspace
+
+ visible: model.output === Config.barOutput && index < 11
+
+ implicitWidth: Config.workspaceWidth
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ color: model.isActive
+ ? "transparent"
+ : Config.workspaceBackground
+
+ layer.enabled: true
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ visible: model.isActive
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+ }
+
+ Text {
+ anchors.centerIn: parent
+
+ text: index
+ color: model.isActive
+ ? Config.textDark
+ : Config.textInactive
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ niri.focusWorkspaceById(model.id)
+ }
+ }
+ }
+ }
+ }
+}