From 6e84d0ca214c4051bd2bc22a50748fc9a2b3288e Mon Sep 17 00:00:00 2001 From: voidarc Date: Sat, 13 Jun 2026 20:53:00 +0100 Subject: [PATCH 01/13] clean --- Bar.qml | 56 +----- Components/Color.js => Color.js | 0 Components/Clock.qml | 42 ----- Components/Pill.qml | 140 --------------- Components/Pill/Default.qml | 16 -- Components/Pill/Expanded.qml | 29 ---- Components/Utils.qml | 45 ----- Components/Utils/Battery.qml | 46 ----- Components/Utils/Container.qml | 64 ------- Components/Utils/Mpris.qml | 4 - Components/Workspace.qml | 290 -------------------------------- 11 files changed, 2 insertions(+), 730 deletions(-) rename Components/Color.js => Color.js (100%) delete mode 100644 Components/Clock.qml delete mode 100644 Components/Pill.qml delete mode 100644 Components/Pill/Default.qml delete mode 100644 Components/Pill/Expanded.qml delete mode 100644 Components/Utils.qml delete mode 100644 Components/Utils/Battery.qml delete mode 100644 Components/Utils/Container.qml delete mode 100644 Components/Utils/Mpris.qml delete mode 100644 Components/Workspace.qml diff --git a/Bar.qml b/Bar.qml index dd10837..576b4b7 100644 --- a/Bar.qml +++ b/Bar.qml @@ -4,8 +4,6 @@ import Quickshell import Quickshell.Hyprland import "Components/Color.js" as Colors -import "Components" - Scope { Variants { model: Quickshell.screens @@ -19,67 +17,17 @@ Scope { color: "transparent" exclusionMode: ExclusionMode.Normal - exclusiveZone: 20 + exclusiveZone: 15 implicitHeight: 1000 screen: modelData - Behavior on implicitHeight { - NumberAnimation { - duration: 0 - easing.type: Easing.InOutQuad - } - } - mask: Region { - Region { - item: workspaceSwitcher - } - - Region { - item: pillItem - } - - Region { - item: utilsItem - } - } + mask: Region {} anchors { left: true right: true top: true } - - Workspace { - id: workspaceSwitcher - - monitor: win.monitor - } - - RectangularShadow { - anchors.fill: pillItem - blur: 20 - color: Colors.crust - offset.x: 3 - offset.y: 3 - radius: pillItem.radius - spread: 7 - } - - Pill { - id: pillItem - - monitor: win.monitor - } - - Item { - anchors.fill: parent - - Utils { - id: utilsItem - - monitor: win.monitor - } - } } } } diff --git a/Components/Color.js b/Color.js similarity index 100% rename from Components/Color.js rename to Color.js diff --git a/Components/Clock.qml b/Components/Clock.qml deleted file mode 100644 index b14621d..0000000 --- a/Components/Clock.qml +++ /dev/null @@ -1,42 +0,0 @@ -// Time.qml - -// with this line our type becomes a Singleton -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick - -// your singletons should always have Singleton as the type -Singleton { - id: root - property string time - property string day - - Process { - id: timeProc - command: ["date", "+%T"] - running: true - - stdout: StdioCollector { - onStreamFinished: root.time = this.text - } - } - - Process { - id: dateProc - command: ["date", "+%A %B %d %Y"] - running: true - - stdout: StdioCollector { - onStreamFinished: root.day = this.text - } - } - - Timer { - interval: 500 - running: true - repeat: true - onTriggered: timeProc.running = true, dateProc.running = true - } -} diff --git a/Components/Pill.qml b/Components/Pill.qml deleted file mode 100644 index 5af3d2e..0000000 --- a/Components/Pill.qml +++ /dev/null @@ -1,140 +0,0 @@ -import Quickshell -import Quickshell.Hyprland -import QtQuick -import QtQuick.Controls -import QtQuick.Effects -import "Color.js" as Colors - -import "./Pill" - -Rectangle { - required property HyprlandMonitor monitor - id: container - state: "" - visible: true - - property bool shown: monitor !== null && monitor.focused - - Behavior on anchors.topMargin { - SmoothedAnimation { duration: 70; easing.type: Easing.OutBounce } - } - - opacity: shown ? 1 : 0 - - Behavior on opacity { - NumberAnimation {duration: 50} - } - - anchors { - top: parent.top - topMargin: shown ? 5 : -implicitHeight - horizontalCenter: parent.horizontalCenter - } - - MouseArea { - id: mouseArea - anchors.fill: parent - - onClicked: container.state == 'expanded' ? container.state = "" : container.state = 'expanded'; - - } - - implicitWidth: 130 - implicitHeight: 30 - - radius: 30 - - property real gradientMidpoint: 0.4 - - gradient: Gradient { - GradientStop { position: 0.0; color: Colors.surface0 } - GradientStop { position: container.gradientMidpoint; color: Colors.surface0 } - GradientStop { position: 1.0; color: Colors.surface2 } - } - - border {color: Colors.lavender; width: 2} - - component SmoothAnim: SpringAnimation { - spring: 3 - mass: 0.5 - damping: 0.18 - } - - Behavior on implicitHeight { - SmoothAnim {} - } - - Behavior on implicitWidth { - SmoothAnim {} - } - - Behavior on gradientMidpoint { - NumberAnimation { - duration: 100 - } - } - - states: [ - State { - name: "" - StateChangeScript { - script: mainLoader.replace(defaultComponent) - } - }, - State { - name: "expanded" - StateChangeScript { - script: mainLoader.replace(expandedComponent) - } - PropertyChanges { container.gradientMidpoint: 0.7} - PropertyChanges { container.implicitHeight: 130} - PropertyChanges { container.implicitWidth: 300} - } - ] - - StackView { - id: mainLoader - anchors.fill: parent - - MouseArea { - id: mouseAreaStack - anchors.fill: parent - hoverEnabled: true - - // FIX part 1: Prevent internal StackView items from changing cursor grab - propagateComposedEvents: true - - onClicked: { - container.state == 'expanded' ? container.state = "" : container.state = 'expanded'; - } - - onExited: { - container.state = "" - } - } - - enabled:false - - initialItem: defaultComponent - - // Custom animations for switching components - replaceEnter: Transition { - PropertyAnimation { property: "opacity"; from: 0; to: 1; duration: 100 } - PropertyAnimation { property: "scale"; from: 0.9; to: 1.0; duration: 100 } - } - replaceExit: Transition { - PropertyAnimation { property: "opacity"; from: 1; to: 0; duration: 100 } - } - } - - Component { - id: defaultComponent - Default {} - } - - Component { - id: expandedComponent - Expanded {} - } - -} diff --git a/Components/Pill/Default.qml b/Components/Pill/Default.qml deleted file mode 100644 index 7ffa2d9..0000000 --- a/Components/Pill/Default.qml +++ /dev/null @@ -1,16 +0,0 @@ -import Quickshell -import Quickshell.Hyprland -import QtQuick -import QtQuick.Controls -import "../Color.js" as Colors -import "../" - -Item { - Text { - font.family: "FiraMono Nerd Font" - anchors.centerIn: parent - font.pixelSize: 15 - color: Colors.text - text: Clock.time - } -} diff --git a/Components/Pill/Expanded.qml b/Components/Pill/Expanded.qml deleted file mode 100644 index 7450533..0000000 --- a/Components/Pill/Expanded.qml +++ /dev/null @@ -1,29 +0,0 @@ -import Quickshell -import Quickshell.Hyprland -import QtQuick -import QtQuick.Layouts -import "../Color.js" as Colors -import "../" - -Item { - anchors.fill: parent - - Column { - spacing: 5 - anchors.centerIn: parent - - Text { - font.family: "FiraMono Nerd Font" - font.pixelSize: 40 - color: Colors.text - anchors.horizontalCenter: parent.horizontalCenter - text: Clock.time - } - Text { - font.family: "FiraMono Nerd Font" - color: Colors.subtext0 - anchors.horizontalCenter: parent.horizontalCenter - text: Clock.day - } - } -} diff --git a/Components/Utils.qml b/Components/Utils.qml deleted file mode 100644 index 0002578..0000000 --- a/Components/Utils.qml +++ /dev/null @@ -1,45 +0,0 @@ -pragma ComponentBehavior: Bound -import QtQuick -import QtQuick.Layouts - -import Quickshell -import Quickshell.Hyprland - -import "./Utils" - -RowLayout { - id: utilsContainer - - required property HyprlandMonitor monitor - property bool shown: monitor !== null && monitor.focused - - opacity: shown ? 1 : 0 - state: "" - - Behavior on anchors.topMargin { - SmoothedAnimation { - duration: 300 - } - } - Behavior on opacity { - NumberAnimation { - duration: 50 - } - } - states: [ - State { - name: "" - } - ] - - anchors { - right: parent.right - rightMargin: 7 - top: parent.top - topMargin: shown ? -33 : -40 - } - - Battery {} - // Mpris {} - -} diff --git a/Components/Utils/Battery.qml b/Components/Utils/Battery.qml deleted file mode 100644 index ed377b5..0000000 --- a/Components/Utils/Battery.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick -import Quickshell -import Quickshell.Services.UPower - -import "../Color.js" as Colors - -import "./" - -Container { - id: batteryRoot - - boxColor: Colors.green - boxState: "" - sourceComponent: batteryClosed - visible: UPower.devices[0] ? true : false - - MouseArea { - anchors.fill: parent - enabled: true - hoverEnabled: true - propagateComposedEvents: true - - onClicked: batteryRoot.boxState == "" ? batteryRoot.boxState = "open" : batteryRoot.boxState = "" - onExited: batteryRoot.boxState = "" - } - - Component { - id: batteryClosed - - Item { - id: batteryRoot - - anchors.fill: parent - - Text { - color: Colors.base - text: "something" - - anchors { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - } - } - } -} diff --git a/Components/Utils/Container.qml b/Components/Utils/Container.qml deleted file mode 100644 index c10edf1..0000000 --- a/Components/Utils/Container.qml +++ /dev/null @@ -1,64 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import Quickshell - -Item { - id: container - - required property color boxColor - property double boxHeight - property double boxRadius - required property string boxState - property double boxWidth - property double openHeight - property double openWidth - required property Component sourceComponent - - Layout.topMargin: 0 - implicitHeight: boxHeight || 50 - implicitWidth: boxWidth || 30 - state: boxState - - Behavior on anchors.topMargin { - SmoothAnim {} - } - Behavior on implicitHeight { - SmoothAnim {} - } - Behavior on implicitWidth { - SmoothAnim {} - } - states: [ - State { - name: "" - }, - State { - name: "open" - - PropertyChanges { - container.Layout.topMargin: 35 - container.implicitHeight: openHeight || 150 - container.implicitWidth: openWidth || 200 - } - } - ] - - Rectangle { - id: mainBackground - - anchors.fill: parent - color: container.boxColor - radius: container.boxRadius || 10 - - Loader { - anchors.fill: parent - sourceComponent: container.sourceComponent - } - } - - component SmoothAnim: SpringAnimation { - damping: 0.18 - mass: 0.5 - spring: 3 - } -} diff --git a/Components/Utils/Mpris.qml b/Components/Utils/Mpris.qml deleted file mode 100644 index bc1c4ec..0000000 --- a/Components/Utils/Mpris.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQuick -import Quickshell - -Item {} diff --git a/Components/Workspace.qml b/Components/Workspace.qml deleted file mode 100644 index d718117..0000000 --- a/Components/Workspace.qml +++ /dev/null @@ -1,290 +0,0 @@ -pragma ComponentBehavior: Bound -import QtQuick -import QtQuick.Effects -import QtQuick.Layouts - -import Quickshell -import Quickshell.Hyprland - -import "Color.js" as Colors - -Item { - id: workspaceRowContainer - - required property HyprlandMonitor monitor - property int textBottomMargin: 0 - - function getNextEmptyWorkspaceId() { - let maxId = 0; - let workspaces = Hyprland.workspaces.values; - - for (let i = 0; i < workspaces.length; i++) { - let ws = workspaces[i]; - // Look at all numerical workspaces globally (ignoring special workspaces like scratchpads) - if (ws.id > maxId && ws.id < 99) { - maxId = ws.id; - } - } - - // Returns the next global workspace ID (e.g., if max is 3, returns 4) - console.log(maxId + 1); - return maxId === 0 ? 1 : maxId + 1; - } - - implicitHeight: 65 - implicitWidth: 200 - state: "" - - Behavior on anchors.topMargin { - NumberAnimation { - duration: 100 - } - } - states: [ - State { - name: "" - // When not hovered, stay at default - when: !workspaceHoverHandler.hovered - - PropertyChanges { - workspaceRowContainer.anchors.topMargin: -33 - } - }, - State { - name: "hovered" - // Keep the workspace dropped whenever hovered is true - when: workspaceHoverHandler.hovered - - PropertyChanges { - workspaceRowContainer.anchors.topMargin: -20 - } - - PropertyChanges { - workspaceRowContainer.textBottomMargin: 7 - } - } - ] - Behavior on textBottomMargin { - NumberAnimation { - duration: 80 - } - } - - anchors { - left: parent.left - leftMargin: 15 - top: parent.top - topMargin: -33 - } - - HoverHandler { - id: workspaceHoverHandler - } - - Row { - id: workspaceRow - - spacing: 5 - - Repeater { - model: Hyprland.workspaces.values - - Item { - id: selectorContainer - - property HyprlandWorkspace activeWorkspace: workspaceRowContainer.monitor.activeWorkspace - property HyprlandWorkspace currentWorkspace: Hyprland.workspaces.values[index] - property HyprlandWorkspace globalFocusedWorkspace: Hyprland.focusedWorkspace - required property int index - - function getImplicitWidth() { - let implicitWidth = 25; - - if (workspaceRowContainer.state == "hovered") { - implicitWidth += 5; - if (currentWorkspace.focused) { - implicitWidth += 20; - } - } - return implicitWidth; - } - - function shouldBeVisible() { - let isSpecialWorkspace = !selectorContainer.currentWorkspace.name.includes("special:"); - let isCurrentMonitor = Hyprland.workspaces.values[index].monitor - == workspaceRowContainer.monitor; - return isSpecialWorkspace && isCurrentMonitor; - } - - implicitHeight: 50 - implicitWidth: getImplicitWidth() - visible: shouldBeVisible() - - Behavior on anchors.topMargin { - NumberAnimation { - duration: 100 - } - } - Behavior on implicitWidth { - NumberAnimation { - duration: 100 - } - } - states: [ - State { - name: "" - when: !selectorHoverHandler.hovered - - PropertyChanges { - selectorContainer.anchors.topMargin: 0 - } - }, - State { - name: "individualHovered" - when: selectorHoverHandler.hovered - - PropertyChanges { - selectorContainer.anchors.topMargin: 10 - } - }, - State { - name: "workspaceSwitched" - - PropertyChanges { - selectorContainer.anchors.topMargin: 5 - } - } - ] - - // onActiveWorkspaceChanged: { - // if (workspaceRowContainer.monitor.activeWorkspace.id == "1") {} - // if (workspaceRowContainer.monitor.activeWorkspace.id == "4") {} - // } - onGlobalFocusedWorkspaceChanged: { - if (globalFocusedWorkspace.name === currentWorkspace.name && globalFocusedWorkspace.monitor - === workspaceRowContainer.monitor && workspaceRowContainer.monitor.activeWorkspace.name - === currentWorkspace.name) { - selectorContainer.state = "workspaceSwitched"; - switchFeedbackTimer.restart(); - } - } - - Timer { - id: switchFeedbackTimer - - interval: 200 - repeat: false - - onTriggered: selectorContainer.state = "" - } - - anchors { - top: parent.top - topMargin: 0 - } - - HoverHandler { - id: selectorHoverHandler - } - - MouseArea { - id: selectorMouseArea - - anchors.fill: parent - hoverEnabled: true - propagateComposedEvents: true - - onClicked: selectorContainer.currentWorkspace.activate() - } - - RectangularShadow { - anchors.fill: selector - blur: 20 - color: Colors.crust - offset.x: 3 - offset.y: 3 - radius: selector.radius - spread: 7 - z: -1 - } - - Rectangle { - id: selector - - anchors.fill: parent - color: selectorContainer.currentWorkspace.focused ? Colors.mauve : - workspaceRowContainer.monitor.activeWorkspace - == selectorContainer.currentWorkspace ? Colors.lavender : Colors.overlay1 - radius: 8 - state: "" - z: 1 - - Behavior on color { - ColorAnimation { - duration: 100 - } - } - - Text { - id: workspaceIdentifier - - font.family: "FiraMono Nerd Font" - font.pixelSize: workspaceRowContainer.state == "hovered" ? 15 : 13 - text: selectorContainer.currentWorkspace.name - - anchors { - bottom: parent.bottom - bottomMargin: workspaceRowContainer.textBottomMargin - horizontalCenter: parent.horizontalCenter - } - } - } - } - } - - Item { - implicitHeight: 40 - implicitWidth: 20 - - Behavior on anchors.topMargin { - NumberAnimation { - duration: 100 - } - } - - anchors { - top: parent.top - topMargin: workspaceRowContainer.state == "hovered" ? 0 : -10 - } - - MouseArea { - id: newWorkspaceButton - - anchors.fill: parent - - onClicked: Hyprland.dispatch(`hl.dsp.focus({workspace = -${workspaceRowContainer.getNextEmptyWorkspaceId()}})`) - } - - Rectangle { - anchors.fill: parent - border.color: Colors.text - border.width: 2 - color: Colors.surface2 - radius: 10 - - Text { - color: Colors.text - font.family: "FiraMono Nerd Font" - font.pixelSize: 20 - text: "+" - - anchors { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - } - } - } - } -} From 269b806af538f70508300f385af35f55181d5a79 Mon Sep 17 00:00:00 2001 From: voidarc Date: Sun, 14 Jun 2026 15:01:16 +0100 Subject: [PATCH 02/13] getting things done --- Bar.qml | 63 ++++++++++++++++++++++++++-- Bar/Center.qml | 44 ++++++++++++++++++++ Components/CenteredText.qml | 14 +++++++ Components/Container.qml | 82 +++++++++++++++++++++++++++++++++++++ Services/Time.qml | 46 +++++++++++++++++++++ shell.qml | 7 +--- 6 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 Bar/Center.qml create mode 100644 Components/CenteredText.qml create mode 100644 Components/Container.qml create mode 100644 Services/Time.qml diff --git a/Bar.qml b/Bar.qml index 576b4b7..392779d 100644 --- a/Bar.qml +++ b/Bar.qml @@ -1,8 +1,10 @@ import QtQuick import QtQuick.Effects +import QtQuick.Layouts import Quickshell import Quickshell.Hyprland -import "Components/Color.js" as Colors +import "Bar" +import "Color.js" as Colors Scope { Variants { @@ -12,22 +14,75 @@ Scope { PanelWindow { id: win + property double barExclusionZone: 15 + property double barMaxHeight: 1000 required property var modelData property HyprlandMonitor monitor: Hyprland.monitorFor(modelData) color: "transparent" exclusionMode: ExclusionMode.Normal - exclusiveZone: 15 - implicitHeight: 1000 + exclusiveZone: barExclusionZone + implicitHeight: barMaxHeight screen: modelData - mask: Region {} + mask: Region { + Region { + item: left + } + + Region { + item: center + } + + Region { + item: right + } + } anchors { left: true right: true top: true } + + Item { + anchors { + fill: parent + topMargin: 2 + } + + Row { + id: left + + height: childrenRect.height + + anchors { + right: parent.right + } + } + + Row { + id: center + + height: childrenRect.height + + anchors { + horizontalCenter: parent.horizontalCenter + } + + Center {} + } + + Row { + id: right + + height: childrenRect.height + + anchors { + left: parent.left + } + } + } } } } diff --git a/Bar/Center.qml b/Bar/Center.qml new file mode 100644 index 0000000..c6801b5 --- /dev/null +++ b/Bar/Center.qml @@ -0,0 +1,44 @@ +import QtQuick +import Quickshell +import "../Color.js" as Colors +import "../Components/" +import "../Services/" + +Container { + id: centerPill + + boxColor: Colors.mauve + defaultItem: time + exclusiveToScreen: true + state: "" + + states: [ + State { + name: "hovered" + + PropertyChanges { + centerPill.boxColor: Colors.teal + centerPill.boxHeight: 200 + } + } + ] + + mouse.onEntered: { + print("entered"); + centerPill.state = "hovered"; + } + mouse.onExited: { + print("something"); + centerPill.state = ""; + } + + Component { + id: time + + Item { + CenteredText { + text: Time.time + } + } + } +} diff --git a/Components/CenteredText.qml b/Components/CenteredText.qml new file mode 100644 index 0000000..963ea8a --- /dev/null +++ b/Components/CenteredText.qml @@ -0,0 +1,14 @@ +import QtQuick +import "../Color.js" as Colors + +Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: Colors.base + + font { + family: "FiraMono Nerd Font" + pointSize: 12 + weight: 600 + } +} diff --git a/Components/Container.qml b/Components/Container.qml new file mode 100644 index 0000000..972f2c2 --- /dev/null +++ b/Components/Container.qml @@ -0,0 +1,82 @@ +import QtQuick +import QtQuick.Controls +import Quickshell + +import "../Color.js" as Colors + +Item { + id: containerRoot + + property color boxColor: Colors.surface0 + property double boxHeight: 28 + property double boxRadius: 9 + property double boxWidth: 100 + property Component defaultItem + property bool exclusiveToScreen: false + property alias mouse: mouseArea + property alias rect: container + property alias stack: containerContent + + function getVisible() { + if (containerRoot.exclusiveToScreen) { + let minMargin = -2 - containerRoot.boxHeight; + console.log(minMargin); + return minMargin - 5; + } else { + return 0; + } + } + + implicitHeight: boxHeight + implicitWidth: boxWidth + + Behavior on boxHeight { + animation: defaultAnim + } + Behavior on boxWidth { + animation: defaultAnim + } + + anchors { + top: parent.top + topMargin: getVisible() + } + + SpringAnimation { + id: defaultAnim + + damping: 0.3 + spring: 4 + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: true + z: 1 + } + + Rectangle { + id: container + + anchors.fill: parent + color: containerRoot.boxColor + radius: containerRoot.boxRadius + + StackView { + id: containerContent + + anchors.fill: parent + + Component.onCompleted: containerContent.push(containerRoot.defaultItem) + onCurrentItemChanged: { + if (currentItem) { + // Dynamically center the incoming child to the StackView + currentItem.anchors.horizontalCenter = containerContent.horizontalCenter; + currentItem.anchors.verticalCenter = containerContent.verticalCenter; + } + } + } + } +} diff --git a/Services/Time.qml b/Services/Time.qml new file mode 100644 index 0000000..9eaa1e6 --- /dev/null +++ b/Services/Time.qml @@ -0,0 +1,46 @@ +// Time.qml + +// with this line our type becomes a Singleton +pragma Singleton +import QtQuick + +import Quickshell +import Quickshell.Io + +// your singletons should always have Singleton as the type +Singleton { + id: root + + property string date + property string time + + Process { + id: timeProc + + command: ["date", "+%T"] + running: true + + stdout: StdioCollector { + onStreamFinished: root.time = this.text + } + } + + Process { + id: dateProc + + command: ["date"] + running: true + + stdout: StdioCollector { + onStreamFinished: root.time = this.text + } + } + + Timer { + interval: 1000 + repeat: true + running: true + + onTriggered: timeProc.running = true + } +} diff --git a/shell.qml b/shell.qml index fc6f2ae..ec9eb18 100644 --- a/shell.qml +++ b/shell.qml @@ -1,10 +1,7 @@ +import QtQuick import Quickshell import Quickshell.Hyprland -import QtQuick -import "Components/Color.js" as Colors - -import "Components" Scope { - Bar {} + Bar {} } From a8a69c116f9401d29308ac42b36d8fe122ae5233 Mon Sep 17 00:00:00 2001 From: voidarc Date: Sun, 14 Jun 2026 15:52:46 +0100 Subject: [PATCH 03/13] some state nonsense --- Bar.qml | 4 ++- Bar/Center.qml | 70 +++++++++++++++++++++++++++++++++++++--- Components/Container.qml | 69 +++++++++++++++++++++++++++++++++------ Services/Time.qml | 9 ++++-- 4 files changed, 135 insertions(+), 17 deletions(-) diff --git a/Bar.qml b/Bar.qml index 392779d..e413c09 100644 --- a/Bar.qml +++ b/Bar.qml @@ -70,7 +70,9 @@ Scope { horizontalCenter: parent.horizontalCenter } - Center {} + Center { + monitor: win.monitor + } } Row { diff --git a/Bar/Center.qml b/Bar/Center.qml index c6801b5..ad012c4 100644 --- a/Bar/Center.qml +++ b/Bar/Center.qml @@ -1,5 +1,7 @@ import QtQuick +import QtQuick.Layouts import Quickshell +import Quickshell.Hyprland import "../Color.js" as Colors import "../Components/" import "../Services/" @@ -7,28 +9,67 @@ import "../Services/" Container { id: centerPill + required property HyprlandMonitor monitor + property string previousState + boxColor: Colors.mauve defaultItem: time + exclusiveMonitor: centerPill.monitor exclusiveToScreen: true state: "" states: [ + State { + name: "" + + StateChangeScript { + script: { + if (centerPill.previousState != "hovered") { + centerPill.stack.replace(time); + } + centerPill.previousState = ""; + } + } + }, State { name: "hovered" PropertyChanges { - centerPill.boxColor: Colors.teal - centerPill.boxHeight: 200 + centerPill.boxHeight: 35 + centerPill.boxWidth: 110 + } + + StateChangeScript { + script: { + centerPill.previousState = "hovered"; + } + } + }, + State { + name: "expanded" + + PropertyChanges { + centerPill.boxHeight: 140 + centerPill.boxRadius: 30 + centerPill.boxWidth: 300 + } + + StateChangeScript { + script: { + centerPill.stack.replace(fullTime); + centerPill.previousState = "expanded"; + } } } ] + mouse.onClicked: { + centerPill.state == "expanded" ? centerPill.state = "" : centerPill.state = "expanded"; + } mouse.onEntered: { - print("entered"); centerPill.state = "hovered"; } mouse.onExited: { - print("something"); centerPill.state = ""; } @@ -41,4 +82,25 @@ Container { } } } + + Component { + id: fullTime + + Column { + Layout.alignment: Qt.AlignCenter + spacing: 5 + + Item { + CenteredText { + text: Time.time + } + } + + Item { + CenteredText { + text: Time.date + } + } + } + } } diff --git a/Components/Container.qml b/Components/Container.qml index 972f2c2..34b8f1e 100644 --- a/Components/Container.qml +++ b/Components/Container.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import Quickshell +import Quickshell.Hyprland import "../Color.js" as Colors @@ -12,6 +13,7 @@ Item { property double boxRadius: 9 property double boxWidth: 100 property Component defaultItem + required property HyprlandMonitor exclusiveMonitor property bool exclusiveToScreen: false property alias mouse: mouseArea property alias rect: container @@ -19,9 +21,11 @@ Item { function getVisible() { if (containerRoot.exclusiveToScreen) { - let minMargin = -2 - containerRoot.boxHeight; - console.log(minMargin); - return minMargin - 5; + if (Hyprland.focusedMonitor != containerRoot.exclusiveMonitor) { + return -2 - containerRoot.boxHeight - 5; + } else { + return 0; + } } else { return 0; } @@ -30,11 +34,25 @@ Item { implicitHeight: boxHeight implicitWidth: boxWidth + Behavior on anchors.topMargin { + animation: defaultCurve + } Behavior on boxHeight { - animation: defaultAnim + SpringAnimation { + damping: 0.3 + spring: 4 + } + } + Behavior on boxRadius { + NumberAnimation { + duration: 100 + } } Behavior on boxWidth { - animation: defaultAnim + SpringAnimation { + damping: 0.3 + spring: 4 + } } anchors { @@ -42,11 +60,11 @@ Item { topMargin: getVisible() } - SpringAnimation { - id: defaultAnim + NumberAnimation { + id: defaultCurve - damping: 0.3 - spring: 4 + duration: 200 + easing: Easing.InOutBack } MouseArea { @@ -69,6 +87,39 @@ Item { anchors.fill: parent + popEnter: Transition { + PropertyAnimation { + duration: 200 + from: 0 + property: "opacity" + to: 1 + } + } + popExit: Transition { + PropertyAnimation { + duration: 200 + from: 1 + property: "opacity" + to: 0 + } + } + pushEnter: Transition { + PropertyAnimation { + duration: 200 + from: 0 + property: "opacity" + to: 1 + } + } + pushExit: Transition { + PropertyAnimation { + duration: 200 + from: 1 + property: "opacity" + to: 0 + } + } + Component.onCompleted: containerContent.push(containerRoot.defaultItem) onCurrentItemChanged: { if (currentItem) { diff --git a/Services/Time.qml b/Services/Time.qml index 9eaa1e6..527dbba 100644 --- a/Services/Time.qml +++ b/Services/Time.qml @@ -28,11 +28,11 @@ Singleton { Process { id: dateProc - command: ["date"] + command: ["date", "+%A %B %d %Y"] running: true stdout: StdioCollector { - onStreamFinished: root.time = this.text + onStreamFinished: root.date = this.text } } @@ -41,6 +41,9 @@ Singleton { repeat: true running: true - onTriggered: timeProc.running = true + onTriggered: { + timeProc.running = true; + dateProc.running = true; + } } } From 23407ea36b6747d89e5841767f45f310e132923d Mon Sep 17 00:00:00 2001 From: voidarc Date: Sun, 14 Jun 2026 16:21:49 +0100 Subject: [PATCH 04/13] made the pill again but better --- Bar.qml | 2 +- Bar/Center.qml | 52 +++++++++++++++++++++++++++++++------ Components/CenteredText.qml | 9 +------ Components/Container.qml | 33 ++++++++++------------- Components/StyledText.qml | 12 +++++++++ 5 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 Components/StyledText.qml diff --git a/Bar.qml b/Bar.qml index e413c09..35afe91 100644 --- a/Bar.qml +++ b/Bar.qml @@ -48,7 +48,7 @@ Scope { Item { anchors { fill: parent - topMargin: 2 + topMargin: 3 } Row { diff --git a/Bar/Center.qml b/Bar/Center.qml index ad012c4..42664c6 100644 --- a/Bar/Center.qml +++ b/Bar/Center.qml @@ -22,6 +22,12 @@ Container { State { name: "" + PropertyChanges { + centerPill.boxHeight: 28 + centerPill.boxRadius: 9 + centerPill.boxWidth: 100 + } + StateChangeScript { script: { if (centerPill.previousState != "hovered") { @@ -36,6 +42,7 @@ Container { PropertyChanges { centerPill.boxHeight: 35 + centerPill.boxRadius: 15 centerPill.boxWidth: 110 } @@ -86,19 +93,48 @@ Container { Component { id: fullTime - Column { - Layout.alignment: Qt.AlignCenter - spacing: 5 + Item { + Rectangle { + anchors.fill: parent + radius: 30 - Item { - CenteredText { - text: Time.time + gradient: Gradient { + GradientStop { + color: "transparent" + position: 0.0 + } + + GradientStop { + color: "transparent" + position: 0.6 + } + + GradientStop { + color: Colors.red + position: 1.0 + } } } - Item { - CenteredText { + Column { + anchors.centerIn: parent + spacing: 5 + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + font.pointSize: 30 + text: Time.time + } + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + color: Colors.surface0 text: Time.date + + font { + pointSize: 9 + weight: 400 + } } } } diff --git a/Components/CenteredText.qml b/Components/CenteredText.qml index 963ea8a..1599b64 100644 --- a/Components/CenteredText.qml +++ b/Components/CenteredText.qml @@ -1,14 +1,7 @@ import QtQuick import "../Color.js" as Colors -Text { +StyledText { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - color: Colors.base - - font { - family: "FiraMono Nerd Font" - pointSize: 12 - weight: 600 - } } diff --git a/Components/Container.qml b/Components/Container.qml index 34b8f1e..3d0fd78 100644 --- a/Components/Container.qml +++ b/Components/Container.qml @@ -22,7 +22,7 @@ Item { function getVisible() { if (containerRoot.exclusiveToScreen) { if (Hyprland.focusedMonitor != containerRoot.exclusiveMonitor) { - return -2 - containerRoot.boxHeight - 5; + return -4 - containerRoot.boxHeight - 5; } else { return 0; } @@ -31,6 +31,7 @@ Item { } } + clip: true implicitHeight: boxHeight implicitWidth: boxWidth @@ -44,8 +45,9 @@ Item { } } Behavior on boxRadius { - NumberAnimation { - duration: 100 + SpringAnimation { + damping: 0.3 + spring: 4 } } Behavior on boxWidth { @@ -87,33 +89,24 @@ Item { anchors.fill: parent - popEnter: Transition { + replaceEnter: Transition { PropertyAnimation { - duration: 200 + duration: 100 from: 0 property: "opacity" to: 1 } - } - popExit: Transition { + PropertyAnimation { - duration: 200 - from: 1 - property: "opacity" + duration: 100 + from: -5 + property: "anchors.topMargin" to: 0 } } - pushEnter: Transition { + replaceExit: Transition { PropertyAnimation { - duration: 200 - from: 0 - property: "opacity" - to: 1 - } - } - pushExit: Transition { - PropertyAnimation { - duration: 200 + duration: 100 from: 1 property: "opacity" to: 0 diff --git a/Components/StyledText.qml b/Components/StyledText.qml new file mode 100644 index 0000000..80bbd52 --- /dev/null +++ b/Components/StyledText.qml @@ -0,0 +1,12 @@ +import QtQuick +import "../Color.js" as Colors + +Text { + color: Colors.base + + font { + family: "FiraMono Nerd Font" + pointSize: 12 + weight: 600 + } +} From 603da980355f6119d8937934a5e828848ecb503d Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 16 Jun 2026 22:01:15 +0100 Subject: [PATCH 05/13] notifications working (totally) perfectly --- Bar/Center.qml | 169 ++++++++++------ Bar/Center/Expanded.qml | 55 ++++++ Bar/Center/NotificationDisplay.qml | 47 +++++ Bar/Center/NotificationList.qml | 302 +++++++++++++++++++++++++++++ Components/Container.qml | 46 +++-- Components/MidpointGradient.qml | 23 +++ Components/StyledText.qml | 1 + Services/NotificationManager.qml | 31 +++ Services/qmldir | 3 + 9 files changed, 598 insertions(+), 79 deletions(-) create mode 100644 Bar/Center/Expanded.qml create mode 100644 Bar/Center/NotificationDisplay.qml create mode 100644 Bar/Center/NotificationList.qml create mode 100644 Components/MidpointGradient.qml create mode 100644 Services/NotificationManager.qml create mode 100644 Services/qmldir diff --git a/Bar/Center.qml b/Bar/Center.qml index 42664c6..7c3e41a 100644 --- a/Bar/Center.qml +++ b/Bar/Center.qml @@ -1,20 +1,25 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Hyprland +import Quickshell.Services.Notifications import "../Color.js" as Colors import "../Components/" import "../Services/" +import "./Center" Container { - id: centerPill + id: root + property Notification latestNotification: NotificationManager.getLatestNotification() || null required property HyprlandMonitor monitor + property NotificationServer notifServer: NotificationManager.notif property string previousState boxColor: Colors.mauve defaultItem: time - exclusiveMonitor: centerPill.monitor + exclusiveMonitor: root.monitor exclusiveToScreen: true state: "" @@ -23,17 +28,18 @@ Container { name: "" PropertyChanges { - centerPill.boxHeight: 28 - centerPill.boxRadius: 9 - centerPill.boxWidth: 100 + root.boxHeight: 28 + root.boxRadius: 9 + root.boxWidth: 100 + root.visibleTopMargin: 0 } StateChangeScript { script: { - if (centerPill.previousState != "hovered") { - centerPill.stack.replace(time); + if (root.previousState != "hovered") { + root.stack.replace(time); } - centerPill.previousState = ""; + root.previousState = ""; } } }, @@ -41,14 +47,15 @@ Container { name: "hovered" PropertyChanges { - centerPill.boxHeight: 35 - centerPill.boxRadius: 15 - centerPill.boxWidth: 110 + root.boxHeight: 35 + root.boxRadius: 15 + root.boxWidth: 110 + root.visibleTopMargin: 0 } StateChangeScript { script: { - centerPill.previousState = "hovered"; + root.previousState = "hovered"; } } }, @@ -56,28 +63,74 @@ Container { name: "expanded" PropertyChanges { - centerPill.boxHeight: 140 - centerPill.boxRadius: 30 - centerPill.boxWidth: 300 + root.boxHeight: 140 + root.boxRadius: 30 + root.boxWidth: 300 + root.visibleTopMargin: 0 } StateChangeScript { script: { - centerPill.stack.replace(fullTime); - centerPill.previousState = "expanded"; + if (root.state != "notifications") { + root.stack.replace(fullTime); + root.previousState = "expanded"; + } + } + } + }, + State { + extend: "expanded" + name: "notifications" + + PropertyChanges { + root.boxHeight: 320 + } + }, + State { + name: "notified" + + PropertyChanges { + root.boxHeight: 90 + root.boxRadius: 30 + root.boxWidth: 330 + root.visibleTopMargin: 20 + } + + StateChangeScript { + script: { + root.stack.replace(notification); + root.previousState = "notified"; } } } ] - mouse.onClicked: { - centerPill.state == "expanded" ? centerPill.state = "" : centerPill.state = "expanded"; + hover.onHoveredChanged: { + hover.hovered == true ? root.state == "notified" ? root.state = "notified" : root.state = "hovered" : + root.state = ""; } - mouse.onEntered: { - centerPill.state = "hovered"; + onLatestNotificationChanged: { + if (latestNotification != null && Hyprland.focusedMonitor == monitor) { + if (root.latestNotification.lastGeneration == false) { + root.state = "notified"; + notificationViewTimer.start(); + } + } } - mouse.onExited: { - centerPill.state = ""; + tap.onTapped: { + root.state == "hovered" ? root.state = "expanded" : undefined; + } + + Timer { + id: notificationViewTimer + + interval: 3000 + repeat: false + running: false || !root.hover.hovered + + onTriggered: { + root.state = ""; + } } Component { @@ -93,50 +146,42 @@ Container { Component { id: fullTime - Item { - Rectangle { - anchors.fill: parent - radius: 30 + Expanded { + NotificationList { + id: notifList - gradient: Gradient { - GradientStop { - color: "transparent" - position: 0.0 - } + animEnabled: false + radius: root.boxRadius + rootState: root.state + server: root.notifServer + state: "" - GradientStop { - color: "transparent" - position: 0.6 - } - - GradientStop { - color: Colors.red - position: 1.0 - } - } - } - - Column { - anchors.centerIn: parent - spacing: 5 - - StyledText { - anchors.horizontalCenter: parent.horizontalCenter - font.pointSize: 30 - text: Time.time - } - - StyledText { - anchors.horizontalCenter: parent.horizontalCenter - color: Colors.surface0 - text: Time.date - - font { - pointSize: 9 - weight: 400 + TapHandler { + onTapped: { + notifList.animEnabled = true; + root.state = "notifications"; + Qt.callLater(() => { + notifList.animEnabled = false; + }); } } } } } + + Component { + id: notification + + NotificationDisplay { + body: root.latestNotification.body + summary: root.latestNotification.summary + + TapHandler { + onTapped: { + root.state = "notifications"; + root.stack.replace(fullTime); + } + } + } + } } diff --git a/Bar/Center/Expanded.qml b/Bar/Center/Expanded.qml new file mode 100644 index 0000000..2908700 --- /dev/null +++ b/Bar/Center/Expanded.qml @@ -0,0 +1,55 @@ +import QtQuick +import Quickshell +import "../../Color.js" as Colors +import "../../Components/" +import "../../Services/" + +Item { + id: fullTimeRoot + + Rectangle { + anchors.fill: parent + radius: 30 + + gradient: MidpointGradient { + color: Colors.red + } + } + + Item { + id: timeColumnContainer + + anchors.fill: parent + + Column { + id: timeColumn + + spacing: 5 + + anchors { + centerIn: parent + } + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + color: Colors.surface0 + text: Time.time + + font { + pointSize: 30 + } + } + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + color: Colors.surface1 + text: Time.date + + font { + pointSize: 9 + weight: 400 + } + } + } + } +} diff --git a/Bar/Center/NotificationDisplay.qml b/Bar/Center/NotificationDisplay.qml new file mode 100644 index 0000000..8e5cc5d --- /dev/null +++ b/Bar/Center/NotificationDisplay.qml @@ -0,0 +1,47 @@ +import QtQuick +import Quickshell +import Quickshell.Services.Notifications +import "../../Color.js" as Colors +import "../../Components/" +import "../../Services/" + +Item { + id: root + + required property string body + required property string summary + + Rectangle { + anchors.fill: parent + radius: 30 + + gradient: MidpointGradient { + color: Colors.green + midpoint: 0.7 + } + } + + Column { + anchors.centerIn: parent + spacing: 5 + + StyledText { + anchors.horizontalCenter: parent.horizontalCenter + font.pointSize: 11 + text: root.summary + } + + StyledText { + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + text: root.body + verticalAlignment: Text.AlignVCenter + width: root.width - 20 + + font { + pointSize: 10 + weight: 500 + } + } + } +} diff --git a/Bar/Center/NotificationList.qml b/Bar/Center/NotificationList.qml new file mode 100644 index 0000000..41529d3 --- /dev/null +++ b/Bar/Center/NotificationList.qml @@ -0,0 +1,302 @@ +import QtQuick +import QtQuick.Effects +import Quickshell +import Quickshell.Services.Notifications +import "../../Color.js" as Colors +import "../../Components/" +import "../../Services/" + +Item { + id: root + + required property bool animEnabled + required property double radius + required property string rootState + required property NotificationServer server + + Behavior on anchors.topMargin { + enabled: root.animEnabled + + SpringAnimation { + damping: 0.35 + spring: 4 + } + } + states: [ + State { + name: "" + when: root.rootState != "notifications" + + PropertyChanges { + root.anchors.topMargin: root.parent.height - 21 + } + }, + State { + name: "expanded" + when: root.rootState == "notifications" + + PropertyChanges { + root.anchors.topMargin: 5 + } + } + ] + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + top: parent.top + topMargin: parent.height - 21 + } + + Rectangle { + id: background + + anchors.fill: parent + color: Colors.surface0 + radius: root.radius - 3 + + states: [ + State { + name: "" + when: root.rootState != "notifications" + + PropertyChanges {} + }, + State { + name: "expanded" + when: root.rootState == "notifications" + + PropertyChanges { + background.anchors.bottomMargin: 5 + background.anchors.leftMargin: 5 + background.anchors.rightMargin: 5 + } + + StateChangeScript { + script: { + console.log("some"); + } + } + } + ] + + anchors { + bottomMargin: 15 + leftMargin: 20 + rightMargin: 20 + } + } + + Item { + id: notifListContainer + + anchors.fill: parent + clip: true + opacity: 0 + + anchors { + bottomMargin: 5 + leftMargin: 5 + rightMargin: 5 + topMargin: 5 + } + + CenteredText { + color: Colors.overlay0 + text: "No Notifications" + visible: root.server.trackedNotifications.values.length <= 0 + } + + ListView { + id: notifScrollingList + + anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + height: Math.min(contentHeight, parent.height) + implicitHeight: background.height + model: root.server.trackedNotifications.values.length + spacing: 7 + + // verticalLayoutDirection: ListView.BottomToTop + + delegate: Item { + id: notifRoot + + property Notification currentNotif: root.server.trackedNotifications.values[(notifRoot.notifLen + - 1) - index] + required property real index + property real notifLen: root.server.trackedNotifications.values.length + + height: 50 + width: ListView.view.width + + states: [ + State { + name: "" + when: notifHover.hovered == false + }, + State { + name: "hovered" + when: notifHover.hovered == false + + PropertyChanges {} + } + ] + + HoverHandler { + id: notifHover + } + + Rectangle { + id: dismissButton + + color: Colors.red + implicitWidth: 100 + radius: 10 + + Behavior on anchors.rightMargin { + NumberAnimation { + duration: 100 + } + } + states: [ + State { + name: "" + when: notifHover.hovered == false + }, + State { + name: "hovered" + when: notifHover.hovered == true + + PropertyChanges { + dismissButton.anchors.rightMargin: 5 + } + } + ] + + TapHandler { + onTapped: { + notifRoot.currentNotif.dismiss(); + } + } + + anchors { + bottom: parent.bottom + bottomMargin: 3 + right: parent.right + rightMargin: 10 + top: parent.top + topMargin: 3 + } + } + + Rectangle { + id: textContainer + + anchors.fill: parent + clip: true + color: Colors.surface2 + radius: 9 + + Behavior on anchors.rightMargin { + NumberAnimation { + duration: 100 + } + } + states: [ + State { + name: "" + when: notifHover.hovered == false + }, + State { + name: "hovered" + when: notifHover.hovered == true + + PropertyChanges { + textContainer.anchors.rightMargin: 40 + } + } + ] + + Column { + id: textColumn + + anchors { + centerIn: parent + left: parent.left + right: parent.right + } + + StyledText { + color: Colors.text + horizontalAlignment: Text.AlignHCenter + text: notifRoot.currentNotif.summary + verticalAlignment: Text.AlignVCenter + width: notifRoot.width - 10 + } + + StyledText { + color: Colors.subtext1 + horizontalAlignment: Text.AlignHCenter + text: notifRoot.currentNotif.body + verticalAlignment: Text.AlignVCenter + width: notifRoot.width - 10 + } + } + } + } + + anchors { + bottomMargin: 5 + leftMargin: 10 + rightMargin: 10 + topMargin: 5 + } + } + } + + MultiEffect { + id: notifListClipper + + anchors.fill: notifListContainer + maskEnabled: true + maskSource: clipsource + opacity: 0 + source: notifListContainer + + Behavior on opacity { + NumberAnimation { + duration: 100 + } + } + states: [ + State { + name: "" + when: root.rootState != "notifications" + + PropertyChanges { + notifListClipper.opacity: 0 + } + }, + State { + name: "expanded" + when: root.rootState == "notifications" + + PropertyChanges { + notifListClipper.opacity: 1 + } + } + ] + } + + Rectangle { + id: clipsource + + anchors.fill: background + color: "black" + layer.enabled: true + radius: background.radius + visible: false + } +} diff --git a/Components/Container.qml b/Components/Container.qml index 3d0fd78..d3fd96c 100644 --- a/Components/Container.qml +++ b/Components/Container.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import Quickshell import Quickshell.Hyprland @@ -12,26 +13,26 @@ Item { property double boxHeight: 28 property double boxRadius: 9 property double boxWidth: 100 - property Component defaultItem - required property HyprlandMonitor exclusiveMonitor - property bool exclusiveToScreen: false - property alias mouse: mouseArea - property alias rect: container - property alias stack: containerContent - - function getVisible() { + readonly property double calculatedTopMargin: { if (containerRoot.exclusiveToScreen) { if (Hyprland.focusedMonitor != containerRoot.exclusiveMonitor) { return -4 - containerRoot.boxHeight - 5; } else { - return 0; + return visibleTopMargin; } } else { - return 0; + return visibleTopMargin; } } + property Component defaultItem + required property HyprlandMonitor exclusiveMonitor + property bool exclusiveToScreen: false + property alias hover: hoverHandler + property alias rect: container + property alias stack: containerContent + property alias tap: tapHandler + property double visibleTopMargin: 0 - clip: true implicitHeight: boxHeight implicitWidth: boxWidth @@ -59,7 +60,7 @@ Item { anchors { top: parent.top - topMargin: getVisible() + topMargin: calculatedTopMargin } NumberAnimation { @@ -69,18 +70,29 @@ Item { easing: Easing.InOutBack } - MouseArea { - id: mouseArea + HoverHandler { + id: hoverHandler + } - anchors.fill: parent - hoverEnabled: true - z: 1 + TapHandler { + id: tapHandler + } + + RectangularShadow { + anchors.fill: container + blur: 30 + color: Colors.mantle + offset.x: 7 + offset.y: 3 + radius: container.radius + spread: 10 } Rectangle { id: container anchors.fill: parent + clip: true color: containerRoot.boxColor radius: containerRoot.boxRadius diff --git a/Components/MidpointGradient.qml b/Components/MidpointGradient.qml new file mode 100644 index 0000000..333c5fd --- /dev/null +++ b/Components/MidpointGradient.qml @@ -0,0 +1,23 @@ +import QtQuick + +Gradient { + id: root + + required property color color + property double midpoint: 0.6 + + GradientStop { + color: "transparent" + position: 0.0 + } + + GradientStop { + color: "transparent" + position: root.midpoint + } + + GradientStop { + color: root.color + position: 1.0 + } +} diff --git a/Components/StyledText.qml b/Components/StyledText.qml index 80bbd52..db0271b 100644 --- a/Components/StyledText.qml +++ b/Components/StyledText.qml @@ -3,6 +3,7 @@ import "../Color.js" as Colors Text { color: Colors.base + elide: Text.ElideRight font { family: "FiraMono Nerd Font" diff --git a/Services/NotificationManager.qml b/Services/NotificationManager.qml new file mode 100644 index 0000000..ece9287 --- /dev/null +++ b/Services/NotificationManager.qml @@ -0,0 +1,31 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Services.Notifications + +Singleton { + id: root + + property alias notif: server + + function getLatestNotification() { + let notificationList = server.trackedNotifications.values; + let len = notificationList.length; + if (len <= 0) { + len = 1; + } + let latestNotification = notificationList[len - 1]; + return latestNotification; + } + + NotificationServer { + id: server + + bodyMarkupSupported: true + bodySupported: true + + onNotification: notification => { + notification.tracked = true; + } + } +} diff --git a/Services/qmldir b/Services/qmldir new file mode 100644 index 0000000..ad604ed --- /dev/null +++ b/Services/qmldir @@ -0,0 +1,3 @@ +module Services +singleton Time 1.0 Time.qml +singleton NotificationManager 1.0 NotificationManager.qml From be6647e6d6c26d81567ec72d2ec0f7d396a2b01a Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 16 Jun 2026 22:01:53 +0100 Subject: [PATCH 06/13] removed console nonsense --- Bar/Center/NotificationList.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Bar/Center/NotificationList.qml b/Bar/Center/NotificationList.qml index 41529d3..d4648c1 100644 --- a/Bar/Center/NotificationList.qml +++ b/Bar/Center/NotificationList.qml @@ -72,12 +72,6 @@ Item { background.anchors.leftMargin: 5 background.anchors.rightMargin: 5 } - - StateChangeScript { - script: { - console.log("some"); - } - } } ] From 10ff3c170ea644e2448e5f3c805e77b398bbfdac Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 23 Jun 2026 12:48:51 +0100 Subject: [PATCH 07/13] switched from flake to devenv (better trust) --- .gitignore | 11 +++++++++ devenv.lock | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 16 +++++++++++++ devenv.yaml | 4 ++++ flake.lock | 61 ------------------------------------------------- flake.nix | 42 ---------------------------------- 6 files changed, 96 insertions(+), 103 deletions(-) create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml delete mode 100644 flake.lock delete mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 1f8011a..36a8c6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,12 @@ .session + +# Devenv +.devenv* +devenv.local.nix +devenv.local.yaml + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..e34c720 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,65 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1782153911, + "narHash": "sha256-Z7BG4cSJX1ybJeZztEMV8WlE6GrXJsSd34x9eHvpzU8=", + "owner": "cachix", + "repo": "devenv", + "rev": "d894e487bb23ed87e0555d2158710844bde65322", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1782132010, + "narHash": "sha256-ZnAVHdVrotp80iIMm5CSR1fdxPlw7Uwmwxb+O/wsgZ8=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "12866ae2dddbc0ab8b329915f8072bb9c75bde89", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1781607440, + "narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3e41b24abd260e8f71dbe2f5737d24122f972158", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} \ No newline at end of file diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..4e65aa1 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,16 @@ +{ + pkgs, + lib, + config, + inputs, + ... +}: + +{ + # https://devenv.sh/packages/ + packages = with pkgs; [ + quickshell + kdePackages.qtdeclarative + ]; + env.DEVSHELL_NAME = "󰏖 devenv/#fab387| quickshell/green"; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..68616a4 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,4 @@ +# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 0d3e526..0000000 --- a/flake.lock +++ /dev/null @@ -1,61 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1780243769, - "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "utils": "utils" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 962ccf1..0000000 --- a/flake.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ - description = "A simple Rust development environment"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - utils.url = "github:numtide/flake-utils"; - }; - - outputs = - { - self, - nixpkgs, - utils, - }: - utils.lib.eachDefaultSystem ( - system: - let - pkgs = import nixpkgs { inherit system; }; - in - { - devShells.default = pkgs.mkShell { - - name = "flake"; - - buildInputs = with pkgs; [ - quickshell - kdePackages.qtdeclarative - ]; - - # Environment variables - shellHook = '' - export DEVSHELL_NAME="󱄅 flake/#89dceb| quickshell/green" - - # Trigger zsh - if [[ -z "$ZSH_VERSION" ]]; then - exec zsh - fi - ''; - }; - } - ); -} From 175cea641bddb635122dec833294e4221bfb156e Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 23 Jun 2026 13:21:29 +0100 Subject: [PATCH 08/13] started on workspaces --- Bar.qml | 10 ++++++++-- Bar/Center/NotificationList.qml | 15 +++++++++++++-- Bar/Left.qml | 24 ++++++++++++++++++++++++ Services/NotificationManager.qml | 1 + Services/WorkspaceManager.qml | 22 ++++++++++++++++++++++ Services/qmldir | 1 + devenv.nix | 11 ++++++----- 7 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 Bar/Left.qml create mode 100644 Services/WorkspaceManager.qml diff --git a/Bar.qml b/Bar.qml index 35afe91..21dbcfc 100644 --- a/Bar.qml +++ b/Bar.qml @@ -57,7 +57,12 @@ Scope { height: childrenRect.height anchors { - right: parent.right + left: parent.left + leftMargin: 7 + } + + Left { + monitor: win.monitor } } @@ -81,7 +86,8 @@ Scope { height: childrenRect.height anchors { - left: parent.left + right: parent.right + rightMargin: 7 } } } diff --git a/Bar/Center/NotificationList.qml b/Bar/Center/NotificationList.qml index d4648c1..c6372c9 100644 --- a/Bar/Center/NotificationList.qml +++ b/Bar/Center/NotificationList.qml @@ -112,8 +112,6 @@ Item { model: root.server.trackedNotifications.values.length spacing: 7 - // verticalLayoutDirection: ListView.BottomToTop - delegate: Item { id: notifRoot @@ -169,6 +167,19 @@ Item { } ] + StyledText { + color: Colors.surface0 + font.pointSize: 15 + horizontalAlignment: Qt.AlignRight + text: "󰩹" + verticalAlignment: Qt.AlignVCenter + + anchors { + fill: parent + rightMargin: 14 + } + } + TapHandler { onTapped: { notifRoot.currentNotif.dismiss(); diff --git a/Bar/Left.qml b/Bar/Left.qml new file mode 100644 index 0000000..ba4ab81 --- /dev/null +++ b/Bar/Left.qml @@ -0,0 +1,24 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import Quickshell.Services.Notifications +import "../Color.js" as Colors +import "../Components/" +import "../Services/" +import "./Center" + +Container { + required property HyprlandMonitor monitor + + boxHeight: 25 + boxWidth: 40 + exclusiveMonitor: monitor + + Item { + StyledText { + text: WorkspaceManager.getWorkspacesForMonitor(monitor) + } + } +} diff --git a/Services/NotificationManager.qml b/Services/NotificationManager.qml index ece9287..02981e3 100644 --- a/Services/NotificationManager.qml +++ b/Services/NotificationManager.qml @@ -21,6 +21,7 @@ Singleton { NotificationServer { id: server + actionsSupported: true bodyMarkupSupported: true bodySupported: true diff --git a/Services/WorkspaceManager.qml b/Services/WorkspaceManager.qml new file mode 100644 index 0000000..0bd91e2 --- /dev/null +++ b/Services/WorkspaceManager.qml @@ -0,0 +1,22 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Hyprland + +Singleton { + id: root + + function getWorkspacesForMonitor(monitor) { + let allWorkspaces = Hyprland.workspaces; + let monitorWorkspaces = []; + for (let workspace in allWorkspaces) { + let currentWorkspace = allWorkspaces[workspace]; + + if (currentWorkspace.monitor == monitor) { + console.log("correct"); + } else { + console.log("incorrect"); + } + } + } +} diff --git a/Services/qmldir b/Services/qmldir index ad604ed..586fa0f 100644 --- a/Services/qmldir +++ b/Services/qmldir @@ -1,3 +1,4 @@ module Services singleton Time 1.0 Time.qml singleton NotificationManager 1.0 NotificationManager.qml +singleton WorkspaceManager 1.0 WorkspaceManager.qml diff --git a/devenv.nix b/devenv.nix index 4e65aa1..c0112ee 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,16 +1,17 @@ { pkgs, - lib, - config, - inputs, ... }: { - # https://devenv.sh/packages/ + env.DEVSHELL_NAME = "󰏖 devenv/#fab387| quickshell/green"; + packages = with pkgs; [ quickshell kdePackages.qtdeclarative ]; - env.DEVSHELL_NAME = "󰏖 devenv/#fab387| quickshell/green"; + + processes = { + qs.exec = "quickshell -p ."; + }; } From 2669f7e144c9e5f2f6f4e46c3e203c7ccbc234bb Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 23 Jun 2026 13:45:14 +0100 Subject: [PATCH 09/13] some items --- Bar/Left.qml | 9 +++++---- Bar/Left/WorkspaceContainer.qml | 29 +++++++++++++++++++++++++++++ Bar/Left/WorkspaceSelector.qml | 17 +++++++++++++++++ Services/WorkspaceManager.qml | 31 +++++++++++++++++++++++-------- 4 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 Bar/Left/WorkspaceContainer.qml create mode 100644 Bar/Left/WorkspaceSelector.qml diff --git a/Bar/Left.qml b/Bar/Left.qml index ba4ab81..c872390 100644 --- a/Bar/Left.qml +++ b/Bar/Left.qml @@ -8,17 +8,18 @@ import "../Color.js" as Colors import "../Components/" import "../Services/" import "./Center" +import "./Left/" Container { + id: root + required property HyprlandMonitor monitor boxHeight: 25 boxWidth: 40 exclusiveMonitor: monitor - Item { - StyledText { - text: WorkspaceManager.getWorkspacesForMonitor(monitor) - } + defaultItem: WorkspaceContainer { + monitor: root.monitor } } diff --git a/Bar/Left/WorkspaceContainer.qml b/Bar/Left/WorkspaceContainer.qml new file mode 100644 index 0000000..ae5101f --- /dev/null +++ b/Bar/Left/WorkspaceContainer.qml @@ -0,0 +1,29 @@ +import QtQuick +import Quickshell +import Quickshell.Hyprland +import "../../Components/" +import "../../Services/" + +Item { + id: root + + required property HyprlandMonitor monitor + + Row { + anchors.fill: parent + + Repeater { + model: WorkspaceManager.getWorkspacesForMonitor(root.monitor) + + CenteredText { + text: console.log("some") + } + + // WorkspaceSelector { + // required property HyprlandWorkspace modelData + // + // workspace: modelData + // } + } + } +} diff --git a/Bar/Left/WorkspaceSelector.qml b/Bar/Left/WorkspaceSelector.qml new file mode 100644 index 0000000..523ad4f --- /dev/null +++ b/Bar/Left/WorkspaceSelector.qml @@ -0,0 +1,17 @@ +import QtQuick +import Quickshell +import Quickshell.Hyprland +import "../../Components/" +import "../../Services/" + +Item { + id: root + + required property HyprlandWorkspace workspace + + Rectangle { + StyledText { + text: "some" + } + } +} diff --git a/Services/WorkspaceManager.qml b/Services/WorkspaceManager.qml index 0bd91e2..93b2f8d 100644 --- a/Services/WorkspaceManager.qml +++ b/Services/WorkspaceManager.qml @@ -6,17 +6,32 @@ import Quickshell.Hyprland Singleton { id: root - function getWorkspacesForMonitor(monitor) { - let allWorkspaces = Hyprland.workspaces; - let monitorWorkspaces = []; + function getAllNumberedWorkspaces() { + let allWorkspaces = Hyprland.workspaces.values; + let filteredWorkspaces = []; for (let workspace in allWorkspaces) { let currentWorkspace = allWorkspaces[workspace]; - - if (currentWorkspace.monitor == monitor) { - console.log("correct"); - } else { - console.log("incorrect"); + if (!currentWorkspace.name.includes("special")) { + if (currentWorkspace) { + filteredWorkspaces.push(currentWorkspace); + } } } } + + function getWorkspacesForMonitor(monitor) { + let allWorkspaces = getAllNumberedWorkspaces(); + let monitorWorkspaces = []; + + for (let workspace in allWorkspaces) { + let currentWorkspace = allWorkspaces[workspace]; + if (currentWorkspace.monitor == monitor) { + console.log(currentWorkspace.id); + if (currentWorkspace) { + monitorWorkspaces.push(currentWorkspace); + } + } + } + return monitorWorkspaces; + } } From 26767ae588a3d6eaa8b295856bb4482cad2dbefd Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 23 Jun 2026 13:54:38 +0100 Subject: [PATCH 10/13] wow numbers --- Bar/Left.qml | 2 +- Bar/Left/WorkspaceContainer.qml | 16 +++++++--------- Bar/Left/WorkspaceSelector.qml | 6 ++---- Services/WorkspaceManager.qml | 1 + 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Bar/Left.qml b/Bar/Left.qml index c872390..8fa7d0d 100644 --- a/Bar/Left.qml +++ b/Bar/Left.qml @@ -16,7 +16,7 @@ Container { required property HyprlandMonitor monitor boxHeight: 25 - boxWidth: 40 + boxWidth: 200 exclusiveMonitor: monitor defaultItem: WorkspaceContainer { diff --git a/Bar/Left/WorkspaceContainer.qml b/Bar/Left/WorkspaceContainer.qml index ae5101f..6f46877 100644 --- a/Bar/Left/WorkspaceContainer.qml +++ b/Bar/Left/WorkspaceContainer.qml @@ -1,4 +1,5 @@ import QtQuick +import QtQuick.Layouts import Quickshell import Quickshell.Hyprland import "../../Components/" @@ -9,21 +10,18 @@ Item { required property HyprlandMonitor monitor - Row { + RowLayout { anchors.fill: parent + Repeater { model: WorkspaceManager.getWorkspacesForMonitor(root.monitor) - CenteredText { - text: console.log("some") - } + WorkspaceSelector { + required property HyprlandWorkspace modelData - // WorkspaceSelector { - // required property HyprlandWorkspace modelData - // - // workspace: modelData - // } + workspace: modelData + } } } } diff --git a/Bar/Left/WorkspaceSelector.qml b/Bar/Left/WorkspaceSelector.qml index 523ad4f..ad8ab27 100644 --- a/Bar/Left/WorkspaceSelector.qml +++ b/Bar/Left/WorkspaceSelector.qml @@ -9,9 +9,7 @@ Item { required property HyprlandWorkspace workspace - Rectangle { - StyledText { - text: "some" - } + StyledText { + text: root.workspace.id } } diff --git a/Services/WorkspaceManager.qml b/Services/WorkspaceManager.qml index 93b2f8d..22acc3b 100644 --- a/Services/WorkspaceManager.qml +++ b/Services/WorkspaceManager.qml @@ -17,6 +17,7 @@ Singleton { } } } + return filteredWorkspaces; } function getWorkspacesForMonitor(monitor) { From 2b492113153c512a3c7a30889975a7285605a9d7 Mon Sep 17 00:00:00 2001 From: voidarc Date: Tue, 23 Jun 2026 15:33:25 +0100 Subject: [PATCH 11/13] finished workspaces --- Bar/Left.qml | 47 +++++++++++++++++++- Bar/Left/WorkspaceContainer.qml | 61 +++++++++++++++++++++++++- Bar/Left/WorkspaceSelector.qml | 77 ++++++++++++++++++++++++++++++++- Services/WorkspaceManager.qml | 43 +++++++++++++++++- 4 files changed, 222 insertions(+), 6 deletions(-) diff --git a/Bar/Left.qml b/Bar/Left.qml index 8fa7d0d..99ff888 100644 --- a/Bar/Left.qml +++ b/Bar/Left.qml @@ -13,13 +13,58 @@ import "./Left/" Container { id: root + property double extraHoveredWidth: 0 required property HyprlandMonitor monitor + property double rowLeftMargin: 4 + property double rowSpacing: 3 + property double rowYmargin: 3 + property double selectorFocusedExtra: 15 + property double selectorHeight: 20 + property double selectorWidth: 20 + property double spacing: 3 + property double workspaceNumber: WorkspaceManager.getNumberOfWorkspaces(monitor) boxHeight: 25 - boxWidth: 200 + boxWidth: (selectorWidth * workspaceNumber) + ((2 * rowLeftMargin) + (rowSpacing * ( + workspaceNumber - 1))) + extraHoveredWidth + (hoverHandler.hovered == true + ? selectorFocusedExtra : 0) exclusiveMonitor: monitor defaultItem: WorkspaceContainer { + extraHoveredWidth: root.extraHoveredWidth monitor: root.monitor + rowLeftMargin: root.rowLeftMargin + rowSpacing: root.rowSpacing + rowYmargin: root.rowYmargin + selectorFocusedExtra: root.selectorFocusedExtra + selectorHeight: root.selectorHeight + selectorWidth: root.selectorWidth + } + Behavior on rowYmargin { + SpringAnimation { + damping: 0.3 + spring: 4 + } + } + states: [ + State { + name: "" + when: !hoverHandler.hovered + }, + State { + name: "hovered" + when: hoverHandler.hovered + + PropertyChanges { + root.boxHeight: 40 + root.extraHoveredWidth: 25 + root.rowYmargin: 5 + root.selectorWidth: 40 + } + } + ] + + HoverHandler { + id: hoverHandler } } diff --git a/Bar/Left/WorkspaceContainer.qml b/Bar/Left/WorkspaceContainer.qml index 6f46877..5f5f0ef 100644 --- a/Bar/Left/WorkspaceContainer.qml +++ b/Bar/Left/WorkspaceContainer.qml @@ -2,17 +2,70 @@ import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Hyprland +import "../../Color.js" as Colors import "../../Components/" import "../../Services/" Item { id: root + required property double extraHoveredWidth required property HyprlandMonitor monitor + required property double rowLeftMargin + required property double rowSpacing + required property double rowYmargin + required property double selectorFocusedExtra + required property double selectorHeight + required property double selectorWidth - RowLayout { - anchors.fill: parent + Item { + implicitWidth: 20 + opacity: root.extraHoveredWidth > 0 ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: 100 + } + } + + TapHandler { + id: tapHandler + + onTapped: WorkspaceManager.activateNextFreeWorkspace() + } + + anchors { + bottom: parent.bottom + bottomMargin: 5 + root.rowYmargin + right: parent.right + rightMargin: 4 + top: parent.top + topMargin: 5 + root.rowYmargin + } + + Rectangle { + anchors.fill: parent + color: Colors.red + radius: 4 + + StyledText { + anchors.centerIn: parent + text: "+" + } + } + } + + Row { + spacing: root.rowSpacing + + anchors { + bottom: parent.bottom + bottomMargin: root.rowYmargin + left: parent.left + leftMargin: root.rowLeftMargin + top: parent.top + topMargin: root.rowYmargin + } Repeater { model: WorkspaceManager.getWorkspacesForMonitor(root.monitor) @@ -20,6 +73,10 @@ Item { WorkspaceSelector { required property HyprlandWorkspace modelData + extraHoveredWidth: root.extraHoveredWidth + selectorFocusedExtra: root.selectorFocusedExtra + selectorHeight: root.selectorHeight + selectorWidth: root.selectorWidth workspace: modelData } } diff --git a/Bar/Left/WorkspaceSelector.qml b/Bar/Left/WorkspaceSelector.qml index ad8ab27..abc556f 100644 --- a/Bar/Left/WorkspaceSelector.qml +++ b/Bar/Left/WorkspaceSelector.qml @@ -1,15 +1,88 @@ import QtQuick +import QtQuick.Controls import Quickshell import Quickshell.Hyprland +import "../../Color.js" as Colors import "../../Components/" import "../../Services/" Item { id: root + required property double extraHoveredWidth + property bool hovered: extraHoveredWidth > 0 ? true : false + property color selectorColor: Colors.surface2 + required property double selectorFocusedExtra + required property double selectorHeight + required property double selectorWidth required property HyprlandWorkspace workspace + property string workspaceState: WorkspaceManager.getWorkspaceState(workspace) - StyledText { - text: root.workspace.id + implicitWidth: selectorWidth + (root.hovered == true ? (root.workspaceState == "focused" + ? selectorFocusedExtra : 0) : 0) + + Behavior on implicitWidth { + SpringAnimation { + damping: 0.3 + spring: 4 + } + } + states: [ + State { + name: "" + when: root.workspaceState == "inactive" + + PropertyChanges { + root.selectorColor: Colors.surface2 + } + }, + State { + name: "active" + when: root.workspaceState == "active" + + PropertyChanges { + root.selectorColor: Colors.overlay2 + } + }, + State { + name: "focused" + when: root.workspaceState == "focused" + + PropertyChanges { + root.selectorColor: Colors.lavender + } + } + ] + + TapHandler { + id: tapHandler + + onTapped: root.workspace.activate() + } + + anchors { + bottom: parent.bottom + top: parent.top + } + + Rectangle { + color: root.selectorColor + radius: 6 + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + + anchors { + fill: parent + } + + StyledText { + anchors.centerIn: parent + font.pointSize: 10.5 + text: root.workspace.id + } } } diff --git a/Services/WorkspaceManager.qml b/Services/WorkspaceManager.qml index 22acc3b..d238bc9 100644 --- a/Services/WorkspaceManager.qml +++ b/Services/WorkspaceManager.qml @@ -6,6 +6,15 @@ import Quickshell.Hyprland Singleton { id: root + function activateNextFreeWorkspace() { + let nextFree = getNextFreeWorkspace(); + activateWorkspaceById(nextFree); + } + + function activateWorkspaceById(id) { + Hyprland.dispatch(`hl.dsp.focus({workspace = ${id}})`); + } + function getAllNumberedWorkspaces() { let allWorkspaces = Hyprland.workspaces.values; let filteredWorkspaces = []; @@ -20,6 +29,39 @@ Singleton { return filteredWorkspaces; } + function getNextFreeWorkspace() { + let workspaceList = getAllNumberedWorkspaces(); + + if (workspaceList[workspaceList.length - 1].id == workspaceList.length) { + return workspaceList.length + 1; + } else { + let prevWorkspace = 0; + for (let workspace in workspaceList) { + let currentWorkspace = workspaceList[workspace]; + if (currentWorkspace.id != (prevWorkspace + 1)) { + return currentWorkspace.id - 1; + } else { + prevWorkspace = currentWorkspace.id; + } + } + } + } + + function getNumberOfWorkspaces(monitor) { + let workspaceList = getWorkspacesForMonitor(monitor); + return workspaceList.length; + } + + function getWorkspaceState(workspace) { + if (workspace.focused == true) { + return "focused"; + } else if (workspace.active == true) { + return "active"; + } else { + return "inactive"; + } + } + function getWorkspacesForMonitor(monitor) { let allWorkspaces = getAllNumberedWorkspaces(); let monitorWorkspaces = []; @@ -27,7 +69,6 @@ Singleton { for (let workspace in allWorkspaces) { let currentWorkspace = allWorkspaces[workspace]; if (currentWorkspace.monitor == monitor) { - console.log(currentWorkspace.id); if (currentWorkspace) { monitorWorkspaces.push(currentWorkspace); } From 07ec781b688422feb2fb07ab64ee09abf03273f5 Mon Sep 17 00:00:00 2001 From: voidarc Date: Fri, 26 Jun 2026 17:34:25 +0100 Subject: [PATCH 12/13] working mpris and bluetooth menu --- Bar.qml | 4 + Bar/Center.qml | 55 ++++- Bar/Center/Mpris.qml | 62 +++++ Bar/Left/WorkspaceContainer.qml | 4 +- Bar/Left/WorkspaceSelector.qml | 3 +- Bar/Right.qml | 21 ++ Bar/Right/Bluetooth.qml | 72 ++++++ Bar/Right/Bluetooth/Expanded.qml | 384 +++++++++++++++++++++++++++++++ Components/Container.qml | 18 +- Services/BluetoothManager.qml | 69 ++++++ Services/MprisManager.qml | 27 +++ Services/qmldir | 2 + 12 files changed, 708 insertions(+), 13 deletions(-) create mode 100644 Bar/Center/Mpris.qml create mode 100644 Bar/Right.qml create mode 100644 Bar/Right/Bluetooth.qml create mode 100644 Bar/Right/Bluetooth/Expanded.qml create mode 100644 Services/BluetoothManager.qml create mode 100644 Services/MprisManager.qml diff --git a/Bar.qml b/Bar.qml index 21dbcfc..45a8464 100644 --- a/Bar.qml +++ b/Bar.qml @@ -89,6 +89,10 @@ Scope { right: parent.right rightMargin: 7 } + + Right { + monitor: win.monitor + } } } } diff --git a/Bar/Center.qml b/Bar/Center.qml index 7c3e41a..4fb86b0 100644 --- a/Bar/Center.qml +++ b/Bar/Center.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Hyprland +import Quickshell.Services.Mpris import Quickshell.Services.Notifications import "../Color.js" as Colors import "../Components/" @@ -49,13 +50,18 @@ Container { PropertyChanges { root.boxHeight: 35 root.boxRadius: 15 - root.boxWidth: 110 + root.boxWidth: MprisManager.getPlaying() == true ? 350 : 110 root.visibleTopMargin: 0 } StateChangeScript { script: { - root.previousState = "hovered"; + if (MprisManager.getPlaying()) { + root.previousState = "mpris"; + root.stack.replace(mpris); + } else { + root.previousState = "hovered"; + } } } }, @@ -121,6 +127,17 @@ Container { root.state == "hovered" ? root.state = "expanded" : undefined; } + Connections { + function onTrackChanged() { + if (Hyprland.focusedMonitor == root.monitor) { + musicToastTimer.start(); + root.state = "hovered"; + } + } + + target: MprisManager.defaultPlayer + } + Timer { id: notificationViewTimer @@ -133,6 +150,18 @@ Container { } } + Timer { + id: musicToastTimer + + interval: 2000 + repeat: false + running: false + + onTriggered: { + root.state = ""; + } + } + Component { id: time @@ -143,6 +172,28 @@ Container { } } + Component { + id: mpris + + Mpris { + TapHandler { + onTapped: root.state = "expanded" + } + + TapHandler { + acceptedButtons: Qt.RightButton + + onTapped: MprisManager.skip() + } + + TapHandler { + acceptedButtons: Qt.MiddleButton + + onTapped: MprisManager.prev() + } + } + } + Component { id: fullTime diff --git a/Bar/Center/Mpris.qml b/Bar/Center/Mpris.qml new file mode 100644 index 0000000..51adfe2 --- /dev/null +++ b/Bar/Center/Mpris.qml @@ -0,0 +1,62 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Services.Mpris +import "../../Color.js" as Colors +import "../../Components/" +import "../../Services/" + +Item { + id: root + + property MprisPlayer player: MprisManager.defaultPlayer + + Rectangle { + color: Colors.red + radius: 5 + + anchors { + bottomMargin: 15 + fill: parent + leftMargin: 10 + rightMargin: parent.width - ((parent.width * MprisManager.pos) - 10) + topMargin: 15 + } + } + + RowLayout { + anchors { + bottomMargin: 2 + fill: parent + leftMargin: 7 + rightMargin: 7 + topMargin: 2 + } + + Item { + Layout.fillHeight: true + Layout.preferredWidth: height + + Image { + anchors.fill: parent + fillMode: Image.PreserveAspectFit + source: MprisManager.defaultPlayer.trackArtUrl + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + + StyledText { + elide: Qt.ElideRight + horizontalAlignment: Qt.AlignHCenter + text: MprisManager.defaultPlayer.trackArtist + " - " + MprisManager.defaultPlayer.trackTitle + verticalAlignment: Qt.AlignVCenter + + anchors { + fill: parent + } + } + } + } +} diff --git a/Bar/Left/WorkspaceContainer.qml b/Bar/Left/WorkspaceContainer.qml index 5f5f0ef..65ccc38 100644 --- a/Bar/Left/WorkspaceContainer.qml +++ b/Bar/Left/WorkspaceContainer.qml @@ -19,6 +19,8 @@ Item { required property double selectorWidth Item { + id: newWorkspaceButton + implicitWidth: 20 opacity: root.extraHoveredWidth > 0 ? 1 : 0 @@ -45,7 +47,7 @@ Item { Rectangle { anchors.fill: parent - color: Colors.red + color: Colors.green radius: 4 StyledText { diff --git a/Bar/Left/WorkspaceSelector.qml b/Bar/Left/WorkspaceSelector.qml index abc556f..3111d51 100644 --- a/Bar/Left/WorkspaceSelector.qml +++ b/Bar/Left/WorkspaceSelector.qml @@ -79,8 +79,7 @@ Item { fill: parent } - StyledText { - anchors.centerIn: parent + CenteredText { font.pointSize: 10.5 text: root.workspace.id } diff --git a/Bar/Right.qml b/Bar/Right.qml new file mode 100644 index 0000000..a4608d4 --- /dev/null +++ b/Bar/Right.qml @@ -0,0 +1,21 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import Quickshell.Services.Notifications +import "../Color.js" as Colors +import "../Components/" +import "../Services/" +import "./Center" +import "./Right/" + +Row { + id: root + + required property HyprlandMonitor monitor + + Bluetooth { + exclusiveMonitor: root.monitor + } +} diff --git a/Bar/Right/Bluetooth.qml b/Bar/Right/Bluetooth.qml new file mode 100644 index 0000000..875949c --- /dev/null +++ b/Bar/Right/Bluetooth.qml @@ -0,0 +1,72 @@ +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell +import Quickshell.Bluetooth +import "../../Color.js" as Colors +import "../../Components/" +import "../../Services/" +import "Bluetooth" + +Container { + id: root + + animOffset: 200 + boxColor: Colors.peach + boxHeight: 25 + boxWidth: 30 + defaultItem: icon + exclusiveToScreen: true + forceHidden: BluetoothManager.getConnected() + state: "" + + states: [ + State { + name: "" + + PropertyChanges { + root.boxHeight: 25 + root.boxWidth: 30 + } + + StateChangeScript { + script: { + root.stack.replace(icon); + } + } + }, + State { + name: "expanded" + + PropertyChanges { + root.boxHeight: 250 + root.boxRadius: 20 + root.boxWidth: 350 + } + + StateChangeScript { + script: { + root.stack.replace(expanded); + } + } + } + ] + + hover.onHoveredChanged: hover.hovered == false ? state = "" : undefined + tap.onTapped: state = "expanded" + + Component { + id: icon + + Item { + CenteredText { + text: "󰂱" + } + } + } + + Component { + id: expanded + + Expanded {} + } +} diff --git a/Bar/Right/Bluetooth/Expanded.qml b/Bar/Right/Bluetooth/Expanded.qml new file mode 100644 index 0000000..71b4f61 --- /dev/null +++ b/Bar/Right/Bluetooth/Expanded.qml @@ -0,0 +1,384 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Bluetooth +import "../../../Color.js" as Colors +import "../../../Components/" +import "../../../Services/" + +Item { + id: root + + property double topBarHeight: 35 + + Rectangle { + color: Colors.surface0 + radius: 17 + + anchors { + bottomMargin: 5 + fill: parent + leftMargin: 5 + rightMargin: 5 + topMargin: 5 + } + + Rectangle { + id: topBar + + property double topBarHeight: root.topBarHeight + + color: Colors.peach + implicitHeight: topBarHeight + radius: 9 + + anchors { + left: parent.left + leftMargin: 5 + right: parent.right + rightMargin: 5 + top: parent.top + topMargin: 5 + } + + RowLayout { + id: topBarRow + + property double topBarRowPadding: 4 + + spacing: 3 + + anchors { + left: parent.left + leftMargin: topBarRowPadding + right: parent.right + rightMargin: topBarRowPadding + top: parent.top + topMargin: topBarRowPadding + } + + Rectangle { + Layout.fillWidth: true + // color: Qt.lighter(Colors.peach, 1.2) + color: Colors.surface1 + implicitHeight: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2) + radius: 6 + + StyledText { + anchors.verticalCenter: parent.verticalCenter + color: Colors.text + horizontalAlignment: Qt.AlignLeft + text: " " + BluetoothManager.defaultAdapterName + } + } + + Rectangle { + id: scanButton + + color: Qt.lighter(Colors.peach, 1.15) + implicitHeight: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2) + implicitWidth: topBar.topBarHeight - (topBarRow.topBarRowPadding * 2) + radius: 6 + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + states: [ + State { + name: "hovered" + + PropertyChanges { + scanButton.color: Colors.green + } + } + ] + + border { + color: Colors.surface0 + width: 2 + } + + HoverHandler { + onHoveredChanged: hovered == true ? scanButton.state = "hovered" : scanButton.state = "" + } + + TapHandler { + onTapped: { + BluetoothManager.toggleDiscover(); + } + } + + CenteredText { + id: scanButtonText + + text: "" + + NumberAnimation { + id: animateRotation + + duration: 1000 + from: 0 + loops: Animation.Infinite + properties: "rotation" + running: BluetoothManager.defaultAdapter.discovering + target: scanButtonText + to: 360 + + onStopped: { + scanButton.rotation = 0; + } + } + } + } + } + } + + ListView { + boundsBehavior: Flickable.StopAtBounds + clip: true + model: BluetoothManager.getDevicesList() + spacing: 5 + + delegate: Item { + id: deviceRoot + + required property real index + required property BluetoothDevice modelData + + height: 55 + width: ListView.view.width + + Rectangle { + id: containerBox + + anchors.fill: parent + clip: true + color: Colors.peach + radius: 7 + + Rectangle { + anchors.centerIn: parent + color: Colors.green + implicitHeight: parent.height - 10 + implicitWidth: parent.width * (deviceRoot.modelData.batteryAvailable == true + ? deviceRoot.modelData.battery : 0) - 10 + opacity: 0.7 + radius: 7 + visible: deviceRoot.modelData.batteryAvailable == true + } + + RowLayout { + anchors { + bottomMargin: 4 + fill: parent + leftMargin: 4 + rightMargin: 4 + topMargin: 4 + } + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: height + color: deviceRoot.modelData.connected ? Colors.green : Qt.lighter(Colors.peach, 1.25) + radius: 5 + + CenteredText { + color: Colors.surface0 + font.pointSize: 30 + text: BluetoothManager.getIcon(deviceRoot.modelData.icon) + } + } + + ColumnLayout { + Layout.fillWidth: true + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + color: Qt.lighter(Colors.peach, 1.15) + radius: 4 + + StyledText { + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + elide: Qt.ElideRight + text: deviceRoot.modelData.name + } + } + + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + color: "transparent" + + StyledText { + anchors.fill: parent + anchors.verticalCenter: parent.verticalCenter + color: Colors.surface2 + font.weight: 300 + text: deviceRoot.modelData.address + } + } + } + + Item { + Layout.fillHeight: true + Layout.preferredWidth: height + + RowLayout { + anchors.fill: parent + + Rectangle { + id: itemEntry + + Layout.fillHeight: true + Layout.fillWidth: true + color: Colors.red + radius: 3 + + Loader { + id: deleteButtonLoader + + anchors.fill: parent + sourceComponent: forgetButton + + states: [ + State { + name: "" + when: deviceRoot.modelData.connected == false + + PropertyChanges { + deleteButtonLoader.sourceComponent: forgetButton + } + }, + State { + name: "connected" + when: deviceRoot.modelData.connected == true + + PropertyChanges { + deleteButtonLoader.sourceComponent: disconnectButton + } + } + ] + } + + Component { + id: disconnectButton + + Item { + anchors.fill: parent + + TapHandler { + onTapped: deviceRoot.modelData.disconnect() + } + + CenteredText { + text: "" + } + } + } + + Component { + id: forgetButton + + Item { + anchors.fill: parent + + TapHandler { + onTapped: deviceRoot.modelData.forget() + } + + CenteredText { + text: "󰩹" + } + } + } + } + + Rectangle { + id: greenButton + + property Component loadedItem: connectButton + + Layout.fillHeight: true + Layout.fillWidth: true + color: Colors.green + radius: 3 + + states: [ + State { + name: "" + when: deviceRoot.modelData.paired == true + + PropertyChanges { + greenButton.color: Colors.green + greenButton.loadedItem: connectButton + } + }, + State { + name: "unpaired" + when: deviceRoot.modelData.paired == false + + PropertyChanges { + greenButton.color: Colors.blue + greenButton.loadedItem: pairButton + } + } + ] + + Loader { + anchors.fill: parent + sourceComponent: greenButton.loadedItem + } + + Component { + id: connectButton + + Item { + anchors.fill: parent + + CenteredText { + text: "󱘖" + } + + TapHandler { + onTapped: deviceRoot.modelData.connect() + } + } + } + + Component { + id: pairButton + + Item { + anchors.fill: parent + + CenteredText { + text: "" + } + + TapHandler { + onTapped: { + deviceRoot.modelData.pair(); + } + } + } + } + } + } + } + } + } + } + + anchors { + bottomMargin: 5 + fill: parent + leftMargin: 5 + rightMargin: 5 + topMargin: 10 + root.topBarHeight + } + } + } +} diff --git a/Components/Container.qml b/Components/Container.qml index d3fd96c..d906226 100644 --- a/Components/Container.qml +++ b/Components/Container.qml @@ -7,16 +7,17 @@ import Quickshell.Hyprland import "../Color.js" as Colors Item { - id: containerRoot + id: root + property double animOffset: 0 property color boxColor: Colors.surface0 property double boxHeight: 28 property double boxRadius: 9 property double boxWidth: 100 readonly property double calculatedTopMargin: { - if (containerRoot.exclusiveToScreen) { - if (Hyprland.focusedMonitor != containerRoot.exclusiveMonitor) { - return -4 - containerRoot.boxHeight - 5; + if (root.exclusiveToScreen) { + if (Hyprland.focusedMonitor != root.exclusiveMonitor || forceHidden == true) { + return -4 - root.boxHeight - 5; } else { return visibleTopMargin; } @@ -27,6 +28,7 @@ Item { property Component defaultItem required property HyprlandMonitor exclusiveMonitor property bool exclusiveToScreen: false + property bool forceHidden: false property alias hover: hoverHandler property alias rect: container property alias stack: containerContent @@ -66,7 +68,7 @@ Item { NumberAnimation { id: defaultCurve - duration: 200 + duration: 200 + root.animOffset easing: Easing.InOutBack } @@ -93,8 +95,8 @@ Item { anchors.fill: parent clip: true - color: containerRoot.boxColor - radius: containerRoot.boxRadius + color: root.boxColor + radius: root.boxRadius StackView { id: containerContent @@ -125,7 +127,7 @@ Item { } } - Component.onCompleted: containerContent.push(containerRoot.defaultItem) + Component.onCompleted: containerContent.push(root.defaultItem) onCurrentItemChanged: { if (currentItem) { // Dynamically center the incoming child to the StackView diff --git a/Services/BluetoothManager.qml b/Services/BluetoothManager.qml new file mode 100644 index 0000000..86f3611 --- /dev/null +++ b/Services/BluetoothManager.qml @@ -0,0 +1,69 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Bluetooth + +Singleton { + id: root + + property BluetoothAdapter defaultAdapter: Bluetooth.defaultAdapter + property string defaultAdapterName: Bluetooth.defaultAdapter.adapterId + + function getConnected() { + let deviceList = getDevicesList(); + for (let device in deviceList) { + let currentDevice = deviceList[device]; + if (currentDevice.connected == true) { + return false; + } + } + return true; + } + + function getDevicesList() { + return root.defaultAdapter.devices.values; + } + + function getIcon(name) { + const icons = { + "audio-card": "󰓃", + "audio-input-microphone": "", + "audio-headphones": "󰋋", + "audio-headset": "󰋋", + "battery": "󰂀", + "camera-photo": "󰻛", + "computer": "", + "input-keyboard": "󰌌", + "input-mouse": "󰍽", + "input-gaming": "󰊴", + "phone": "󰏲" + }; + + return icons[name] || "󰾰"; + } + + function pairTrustConnect(device) { + device.pair(); + device.trusted = true; + device.connect(); + } + + function toggleDiscover() { + if (defaultAdapter.discovering == true) { + defaultAdapter.discovering = false; + } else { + defaultAdapter.discovering = true; + discoveringTimeout.running = true; + } + } + + Timer { + id: discoveringTimeout + + interval: 15000 + repeat: false + running: false + + onTriggered: defaultAdapter.discovering = false + } +} diff --git a/Services/MprisManager.qml b/Services/MprisManager.qml new file mode 100644 index 0000000..95cf458 --- /dev/null +++ b/Services/MprisManager.qml @@ -0,0 +1,27 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Services.Mpris + +Singleton { + id: root + + property MprisPlayer defaultPlayer: Mpris.players.values[0] + property real pos: defaultPlayer.position / defaultPlayer.length + + function getPlaying() { + if (!defaultPlayer) { + return false; + } else { + return defaultPlayer.playbackState == MprisPlaybackState.Playing; + } + } + + function prev() { + defaultPlayer.previous(); + } + + function skip() { + defaultPlayer.next(); + } +} diff --git a/Services/qmldir b/Services/qmldir index 586fa0f..b6984ea 100644 --- a/Services/qmldir +++ b/Services/qmldir @@ -2,3 +2,5 @@ module Services singleton Time 1.0 Time.qml singleton NotificationManager 1.0 NotificationManager.qml singleton WorkspaceManager 1.0 WorkspaceManager.qml +singleton BluetoothManager 1.0 BluetoothManager.qml +singleton MprisManager 1.0 MprisManager.qml From 33cfb4b5391e481e673834ef856670f60d548a3c Mon Sep 17 00:00:00 2001 From: voidarc Date: Fri, 26 Jun 2026 21:05:35 +0100 Subject: [PATCH 13/13] fixed weird mpris and notification state issues --- Bar/Center.qml | 56 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/Bar/Center.qml b/Bar/Center.qml index 4fb86b0..0e94c30 100644 --- a/Bar/Center.qml +++ b/Bar/Center.qml @@ -22,7 +22,6 @@ Container { defaultItem: time exclusiveMonitor: root.monitor exclusiveToScreen: true - state: "" states: [ State { @@ -50,18 +49,30 @@ Container { PropertyChanges { root.boxHeight: 35 root.boxRadius: 15 - root.boxWidth: MprisManager.getPlaying() == true ? 350 : 110 + root.boxWidth: 110 root.visibleTopMargin: 0 } StateChangeScript { script: { - if (MprisManager.getPlaying()) { - root.previousState = "mpris"; - root.stack.replace(mpris); - } else { - root.previousState = "hovered"; - } + root.previousState = "hovered"; + } + } + }, + State { + name: "mprisToast" + + PropertyChanges { + root.boxHeight: 35 + root.boxRadius: 15 + root.boxWidth: 350 + root.visibleTopMargin: 0 + } + + StateChangeScript { + script: { + root.previousState = "mpris"; + root.stack.replace(mpris); } } }, @@ -112,8 +123,18 @@ Container { ] hover.onHoveredChanged: { - hover.hovered == true ? root.state == "notified" ? root.state = "notified" : root.state = "hovered" : - root.state = ""; + if (hover.hovered == true) { + if (root.state == "notified") { + root.state = "notified"; + notificationViewTimer.stop(); + } else if (MprisManager.getPlaying() == true) { + root.state = "mprisToast"; + } else { + root.state = "hovered"; + } + } else { + root.state = ""; + } } onLatestNotificationChanged: { if (latestNotification != null && Hyprland.focusedMonitor == monitor) { @@ -130,8 +151,10 @@ Container { Connections { function onTrackChanged() { if (Hyprland.focusedMonitor == root.monitor) { - musicToastTimer.start(); - root.state = "hovered"; + musicToastTimer.restart(); + if (root.state == "") { + root.state = "mprisToast"; + } } } @@ -143,7 +166,7 @@ Container { interval: 3000 repeat: false - running: false || !root.hover.hovered + running: false onTriggered: { root.state = ""; @@ -153,12 +176,15 @@ Container { Timer { id: musicToastTimer - interval: 2000 + interval: 3000 repeat: false running: false onTriggered: { - root.state = ""; + if (!root.hover.hovered) { + console.log("some"); + root.state = ""; + } } }