From f8369a340ba0184b59b2354378050160d610ad77 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 23 Jul 2025 16:03:41 -0400 Subject: [PATCH 1/3] fancy open animation, but still no padding --- modules/bar/components/tray/Tray.qml | 2 +- modules/bar/components/tray/TrayItem.qml | 25 +-- modules/bar/components/tray/menu/Menu.qml | 146 ++++++++++++++++-- modules/bar/components/tray/menu/MenuItem.qml | 2 + 4 files changed, 139 insertions(+), 36 deletions(-) diff --git a/modules/bar/components/tray/Tray.qml b/modules/bar/components/tray/Tray.qml index a907abc..0a95718 100644 --- a/modules/bar/components/tray/Tray.qml +++ b/modules/bar/components/tray/Tray.qml @@ -16,7 +16,7 @@ Row { sourceComponent: item property Component item: TrayItem { - trayItem: modelData + trayItem: modelData } } } diff --git a/modules/bar/components/tray/TrayItem.qml b/modules/bar/components/tray/TrayItem.qml index 6e8fff6..055171f 100644 --- a/modules/bar/components/tray/TrayItem.qml +++ b/modules/bar/components/tray/TrayItem.qml @@ -38,32 +38,17 @@ Clickable { anchors.centerIn: parent } - PopupWindow { - id: popup + Menu { + id: menu - visible: root.menuOpened - - color: 'transparent' + opened: root.menuOpened anchor.item: root anchor.rect.x: root.width / 2 - width / 2 anchor.rect.y: root.height + 8 - implicitWidth: menu.width - implicitHeight: menu.height - - Rectangle { - anchors.fill: parent - color: Theme.palette.base300 - radius: 8 - } - - Menu { - id: menu - - menuOpener: QsMenuOpener { - menu: trayItem.menu - } + menuOpener: QsMenuOpener { + menu: trayItem.menu } } } diff --git a/modules/bar/components/tray/menu/Menu.qml b/modules/bar/components/tray/menu/Menu.qml index b7268ff..38d66bd 100644 --- a/modules/bar/components/tray/menu/Menu.qml +++ b/modules/bar/components/tray/menu/Menu.qml @@ -1,29 +1,145 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import Quickshell import "../../../../../config/" import "../../../../../styled/" -ColumnLayout { - id: menu +PopupWindow { + id: window + property QsMenuOpener menuOpener + property bool opened: false - anchors.margins: 8 + color: 'transparent' - Repeater { - model: menuOpener.children - delegate: Loader { - required property QsMenuEntry modelData - active: true + implicitWidth: menu.width + implicitHeight: menu.height - sourceComponent: modelData.isSeparator ? menuSeperator : menuItem - property Component menuSeperator: Rectangle { - implicitHeight: 1 - implicitWidth: menu.width - color: Theme.palette.basecontent + Rectangle { + id: background + + anchors.fill: menu + color: Theme.palette.base300 + border.color: Theme.palette.base200 + border.width: 2 + radius: 8 + + opacity: 0 + + states: State { + name: "opened" + when: window.opened + PropertyChanges { + background { + opacity: 1 + } } - property Component menuItem: MenuItem { - menuEntry: modelData + } + + transitions: [ + Transition { + from: "" + to: "opened" + SequentialAnimation { + ScriptAction { + script: window.visible = true + } + NumberAnimation { + property: "background.opacity" + duration: 200 + } + } + }, + Transition { + from: "opened" + to: "" + SequentialAnimation { + PauseAnimation { + duration: repeater.count * 15 + } + NumberAnimation { + property: "background.opacity" + duration: 200 + } + ScriptAction { + script: window.visible = false + } + } + } + ] + } + + ColumnLayout { + id: menu + + anchors.margins: 30 + + Repeater { + id: repeater + model: window.menuOpener.children + delegate: Loader { + id: loader + + required property QsMenuEntry modelData + required property int index + + active: true + + opacity: 0 + + Layout.minimumWidth: 120 + + sourceComponent: modelData.isSeparator ? menuSeperator : menuItem + property Component menuSeperator: Rectangle { + implicitWidth: menu.width + implicitHeight: 2 + + color: Theme.palette.base100 + } + property Component menuItem: MenuItem { + menuEntry: modelData + } + + states: State { + name: "opened" + when: window.opened + PropertyChanges { + loader { + opacity: 1 + } + } + } + + transitions: [ + Transition { + from: "" + to: "opened" + SequentialAnimation { + PauseAnimation { + duration: 15 * loader.index + } + NumberAnimation { + property: "opacity" + duration: 100 + } + } + }, + Transition { + from: "opened" + to: "" + SequentialAnimation { + PauseAnimation { + duration: 15 * (repeater.count - loader.index) + } + NumberAnimation { + property: "opacity" + duration: 200 + } + } + } + ] } } } diff --git a/modules/bar/components/tray/menu/MenuItem.qml b/modules/bar/components/tray/menu/MenuItem.qml index dbf7272..5624ddc 100644 --- a/modules/bar/components/tray/menu/MenuItem.qml +++ b/modules/bar/components/tray/menu/MenuItem.qml @@ -16,6 +16,8 @@ Clickable { StyledText { id: text + opacity: item.opacity + font.pixelSize: Dimensions.clock.fontSize anchors.verticalCenter: parent.verticalCenter topPadding: Dimensions.clock.verticalPadding From 56cd758dd52c7a74b91049ec5ebe265e6ebbf731 Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 23 Jul 2025 16:03:41 -0400 Subject: [PATCH 2/3] fancy open animation, but still no padding --- config/Dimensions.qml | 2 +- modules/bar/components/Caffeine.qml | 10 +- modules/bar/components/tray/Tray.qml | 2 +- modules/bar/components/tray/TrayItem.qml | 25 +-- modules/bar/components/tray/menu/Menu.qml | 146 ++++++++++++++++-- modules/bar/components/tray/menu/MenuItem.qml | 2 + 6 files changed, 145 insertions(+), 42 deletions(-) diff --git a/config/Dimensions.qml b/config/Dimensions.qml index 81f0a7e..b6ca091 100644 --- a/config/Dimensions.qml +++ b/config/Dimensions.qml @@ -49,7 +49,7 @@ Singleton { component Caffeine: QtObject { id: clock - property int iconSize: 14 + property int fontSize: 16 property int height: 30 property int horizontalPadding: 8 property int verticalPadding: 6 diff --git a/modules/bar/components/Caffeine.qml b/modules/bar/components/Caffeine.qml index 4e5f1cf..18c15a2 100644 --- a/modules/bar/components/Caffeine.qml +++ b/modules/bar/components/Caffeine.qml @@ -26,17 +26,17 @@ Clickable { id: text font.family: Theme.lucide.font.family - font.pixelSize: Dimensions.workspace.iconSize + font.pixelSize: Dimensions.caffeine.fontSize font.bold: true text: Icons.coffee color: clickable.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent anchors.verticalCenter: parent.verticalCenter - topPadding: Dimensions.mpris.verticalPadding - bottomPadding: Dimensions.mpris.verticalPadding - leftPadding: Dimensions.mpris.horizontalPadding - rightPadding: Dimensions.mpris.horizontalPadding + topPadding: Dimensions.caffeine.verticalPadding + bottomPadding: Dimensions.caffeine.verticalPadding + leftPadding: Dimensions.caffeine.horizontalPadding + rightPadding: Dimensions.caffeine.horizontalPadding } Process { diff --git a/modules/bar/components/tray/Tray.qml b/modules/bar/components/tray/Tray.qml index a907abc..0a95718 100644 --- a/modules/bar/components/tray/Tray.qml +++ b/modules/bar/components/tray/Tray.qml @@ -16,7 +16,7 @@ Row { sourceComponent: item property Component item: TrayItem { - trayItem: modelData + trayItem: modelData } } } diff --git a/modules/bar/components/tray/TrayItem.qml b/modules/bar/components/tray/TrayItem.qml index 6e8fff6..055171f 100644 --- a/modules/bar/components/tray/TrayItem.qml +++ b/modules/bar/components/tray/TrayItem.qml @@ -38,32 +38,17 @@ Clickable { anchors.centerIn: parent } - PopupWindow { - id: popup + Menu { + id: menu - visible: root.menuOpened - - color: 'transparent' + opened: root.menuOpened anchor.item: root anchor.rect.x: root.width / 2 - width / 2 anchor.rect.y: root.height + 8 - implicitWidth: menu.width - implicitHeight: menu.height - - Rectangle { - anchors.fill: parent - color: Theme.palette.base300 - radius: 8 - } - - Menu { - id: menu - - menuOpener: QsMenuOpener { - menu: trayItem.menu - } + menuOpener: QsMenuOpener { + menu: trayItem.menu } } } diff --git a/modules/bar/components/tray/menu/Menu.qml b/modules/bar/components/tray/menu/Menu.qml index b7268ff..38d66bd 100644 --- a/modules/bar/components/tray/menu/Menu.qml +++ b/modules/bar/components/tray/menu/Menu.qml @@ -1,29 +1,145 @@ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import Quickshell import "../../../../../config/" import "../../../../../styled/" -ColumnLayout { - id: menu +PopupWindow { + id: window + property QsMenuOpener menuOpener + property bool opened: false - anchors.margins: 8 + color: 'transparent' - Repeater { - model: menuOpener.children - delegate: Loader { - required property QsMenuEntry modelData - active: true + implicitWidth: menu.width + implicitHeight: menu.height - sourceComponent: modelData.isSeparator ? menuSeperator : menuItem - property Component menuSeperator: Rectangle { - implicitHeight: 1 - implicitWidth: menu.width - color: Theme.palette.basecontent + Rectangle { + id: background + + anchors.fill: menu + color: Theme.palette.base300 + border.color: Theme.palette.base200 + border.width: 2 + radius: 8 + + opacity: 0 + + states: State { + name: "opened" + when: window.opened + PropertyChanges { + background { + opacity: 1 + } } - property Component menuItem: MenuItem { - menuEntry: modelData + } + + transitions: [ + Transition { + from: "" + to: "opened" + SequentialAnimation { + ScriptAction { + script: window.visible = true + } + NumberAnimation { + property: "background.opacity" + duration: 200 + } + } + }, + Transition { + from: "opened" + to: "" + SequentialAnimation { + PauseAnimation { + duration: repeater.count * 15 + } + NumberAnimation { + property: "background.opacity" + duration: 200 + } + ScriptAction { + script: window.visible = false + } + } + } + ] + } + + ColumnLayout { + id: menu + + anchors.margins: 30 + + Repeater { + id: repeater + model: window.menuOpener.children + delegate: Loader { + id: loader + + required property QsMenuEntry modelData + required property int index + + active: true + + opacity: 0 + + Layout.minimumWidth: 120 + + sourceComponent: modelData.isSeparator ? menuSeperator : menuItem + property Component menuSeperator: Rectangle { + implicitWidth: menu.width + implicitHeight: 2 + + color: Theme.palette.base100 + } + property Component menuItem: MenuItem { + menuEntry: modelData + } + + states: State { + name: "opened" + when: window.opened + PropertyChanges { + loader { + opacity: 1 + } + } + } + + transitions: [ + Transition { + from: "" + to: "opened" + SequentialAnimation { + PauseAnimation { + duration: 15 * loader.index + } + NumberAnimation { + property: "opacity" + duration: 100 + } + } + }, + Transition { + from: "opened" + to: "" + SequentialAnimation { + PauseAnimation { + duration: 15 * (repeater.count - loader.index) + } + NumberAnimation { + property: "opacity" + duration: 200 + } + } + } + ] } } } diff --git a/modules/bar/components/tray/menu/MenuItem.qml b/modules/bar/components/tray/menu/MenuItem.qml index dbf7272..5624ddc 100644 --- a/modules/bar/components/tray/menu/MenuItem.qml +++ b/modules/bar/components/tray/menu/MenuItem.qml @@ -16,6 +16,8 @@ Clickable { StyledText { id: text + opacity: item.opacity + font.pixelSize: Dimensions.clock.fontSize anchors.verticalCenter: parent.verticalCenter topPadding: Dimensions.clock.verticalPadding From 76d680a95b44b18b13f33a974a5eb626fc833edd Mon Sep 17 00:00:00 2001 From: Benjamin Palko Date: Wed, 23 Jul 2025 16:51:29 -0400 Subject: [PATCH 3/3] basic pipewire, no menu --- config/Dimensions.qml | 10 ++++ modules/bar/Bar.qml | 4 ++ modules/bar/components/Pipewire.qml | 72 +++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 modules/bar/components/Pipewire.qml diff --git a/config/Dimensions.qml b/config/Dimensions.qml index b6ca091..df27c14 100644 --- a/config/Dimensions.qml +++ b/config/Dimensions.qml @@ -11,6 +11,7 @@ Singleton { property Bar bar: Bar {} property Mpris mpris: Mpris {} property Clock clock: Clock {} + property Pipewire pipewire: Pipewire {} property Caffeine caffeine: Caffeine {} property Workspace workspace: Workspace {} property Tray tray: Tray {} @@ -46,6 +47,15 @@ Singleton { property int verticalPadding: 6 } + component Pipewire: QtObject { + id: clock + + property int fontSize: 14 + property int height: 30 + property int horizontalPadding: 8 + property int verticalPadding: 6 + } + component Caffeine: QtObject { id: clock diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index c4b766a..927d7f2 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -92,6 +92,10 @@ Scope { spacing: Dimensions.bar.spacing + Pipewire { + anchors.verticalCenter: parent.verticalCenter + } + Caffeine { anchors.verticalCenter: parent.verticalCenter } diff --git a/modules/bar/components/Pipewire.qml b/modules/bar/components/Pipewire.qml new file mode 100644 index 0000000..0a864ed --- /dev/null +++ b/modules/bar/components/Pipewire.qml @@ -0,0 +1,72 @@ +import QtQuick +import Quickshell.Services.Pipewire +import "../../../config/" +import "../../../styled/" + +Clickable { + id: clickable + + property var sink: Pipewire.defaultAudioSink + + implicitWidth: text.width + implicitHeight: Dimensions.pipewire.height + + PwObjectTracker { + id: bound + objects: [clickable.sink] + } + + onClicked: mouse => { + if (mouse.button == Qt.LeftButton) { + sink.audio.muted = !sink.audio.muted; + } else if (mouse.button == Qt.RightButton) { + // show menu + } + } + + onWheel: event => { + if (event.angleDelta.y > 0) { + sink.audio.volume = Math.min(sink.audio.volume + 0.02, 1.0); + } else if (event.angleDelta.y < 0) { + sink.audio.volume -= 0.02; + } + } + + states: [ + State { + name: "muted" + when: clickable.sink.audio.muted + PropertyChanges { + text { + icon: " " + } + } + }, + State { + name: "off" + when: clickable.sink.audio.volume <= 0 + PropertyChanges { + text { + icon: " " + } + } + } + ] + + StyledText { + id: text + + property string icon: " " + + text: `${icon} ${(Pipewire.defaultAudioSink.audio.volume * 100).toFixed()}%` + font.pixelSize: Dimensions.pipewire.fontSize + + color: clickable.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent + + anchors.verticalCenter: parent.verticalCenter + topPadding: Dimensions.pipewire.verticalPadding + bottomPadding: Dimensions.pipewire.verticalPadding + leftPadding: Dimensions.pipewire.horizontalPadding + rightPadding: Dimensions.pipewire.horizontalPadding + } +}