diff --git a/modules/BarIcon.qml b/modules/BarIcon.qml index a9d0e89..a034e04 100644 --- a/modules/BarIcon.qml +++ b/modules/BarIcon.qml @@ -14,12 +14,9 @@ Text { text: _displayIcon onIconChanged: { - if (_crossfade.running) { - _pendingIcon = icon; - } else { - _pendingIcon = icon; + _pendingIcon = icon; + if (!_crossfade.running) _crossfade.start(); - } } SequentialAnimation { diff --git a/modules/MprisMenu.qml b/modules/MprisMenu.qml index b4bfb29..c81176e 100644 --- a/modules/MprisMenu.qml +++ b/modules/MprisMenu.qml @@ -9,7 +9,12 @@ M.PopupPanel { required property MprisPlayer player - // Album art + function _fmtTime(ms) { + const s = Math.floor(ms / 1000); + const m = Math.floor(s / 60); + return m + ":" + String(s % 60).padStart(2, "0"); + } + Item { width: menuWindow.panelWidth height: _artImg.status === Image.Ready ? 140 : 60 @@ -28,7 +33,6 @@ M.PopupPanel { visible: status === Image.Ready } - // Gradient fade at the bottom so art blends into the panel Rectangle { anchors.left: parent.left anchors.right: parent.right @@ -41,7 +45,6 @@ M.PopupPanel { } } - // Fallback icon when no art Text { anchors.centerIn: parent text: "\uF001" @@ -52,7 +55,6 @@ M.PopupPanel { } } - // Track info Item { width: menuWindow.panelWidth height: titleCol.implicitHeight + 8 @@ -82,7 +84,7 @@ M.PopupPanel { const p = menuWindow.player; if (!p) return ""; const artist = Array.isArray(p.trackArtists) ? p.trackArtists.join(", ") : (p.trackArtists || ""); - return [artist, p.trackAlbum].filter(s => s).join(" — "); + return [artist, p.trackAlbum].filter(s => s).join(" \u2014 "); } color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 1 @@ -93,7 +95,6 @@ M.PopupPanel { } } - // Progress bar Item { width: menuWindow.panelWidth height: 20 @@ -102,39 +103,25 @@ M.PopupPanel { readonly property real dur: menuWindow.player?.length ?? 0 readonly property real frac: dur > 0 ? pos / dur : 0 - // Time labels Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter - text: _fmt(parent.pos) + text: menuWindow._fmtTime(parent.pos) color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily - - function _fmt(ms) { - const s = Math.floor(ms / 1000); - const m = Math.floor(s / 60); - return m + ":" + String(s % 60).padStart(2, "0"); - } } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter - text: _fmt(parent.dur) + text: menuWindow._fmtTime(parent.dur) color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily - - function _fmt(ms) { - const s = Math.floor(ms / 1000); - const m = Math.floor(s / 60); - return m + ":" + String(s % 60).padStart(2, "0"); - } } - // Bar Item { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter @@ -155,7 +142,6 @@ M.PopupPanel { } } - // Transport controls Item { width: menuWindow.panelWidth height: 36 @@ -164,7 +150,6 @@ M.PopupPanel { anchors.centerIn: parent spacing: 24 - // Previous Text { text: "\uF048" color: menuWindow.player?.canGoPrevious ? M.Theme.base05 : M.Theme.base03 @@ -180,7 +165,6 @@ M.PopupPanel { } } - // Play/Pause Text { text: menuWindow.player?.playbackState === MprisPlaybackState.Playing ? "\uF04C" : "\uF04B" color: M.Theme.base0E @@ -195,7 +179,6 @@ M.PopupPanel { } } - // Next Text { text: "\uF051" color: menuWindow.player?.canGoNext ? M.Theme.base05 : M.Theme.base03 @@ -213,7 +196,6 @@ M.PopupPanel { } } - // Player name Item { width: menuWindow.panelWidth height: 20 diff --git a/modules/Volume.qml b/modules/Volume.qml index c631191..08b24a2 100644 --- a/modules/Volume.qml +++ b/modules/Volume.qml @@ -7,7 +7,6 @@ import "." as M M.BarSection { id: root spacing: M.Theme.moduleSpacing - // No tooltip — the panel replaces it tooltip: "" PwObjectTracker { @@ -17,20 +16,32 @@ M.BarSection { readonly property var sink: Pipewire.defaultAudioSink readonly property real volume: sink?.audio?.volume ?? 0 readonly property bool muted: sink?.audio?.muted ?? false + readonly property string _volumeIcon: muted ? "\uF026" : (volume > 0.5 ? "\uF028" : (volume > 0 ? "\uF027" : "\uF026")) + readonly property color _volumeColor: muted ? M.Theme.base04 : M.Theme.base0E + + readonly property var _sinkList: { + const sinks = []; + if (Pipewire.nodes) { + for (const node of Pipewire.nodes.values) + if (!node.isStream && node.isSink) + sinks.push(node); + } + return sinks; + } property bool _expanded: false property bool _panelHovered: false readonly property bool _showPanel: root._hovered || _panelHovered || _expanded M.BarIcon { - icon: root.muted ? "\uF026" : (root.volume > 0.5 ? "\uF028" : (root.volume > 0 ? "\uF027" : "\uF026")) - color: root.muted ? M.Theme.base04 : M.Theme.base0E + icon: root._volumeIcon + color: root._volumeColor anchors.verticalCenter: parent.verticalCenter } M.BarLabel { label: Math.round(root.volume * 100) + "%" minText: "100%" - color: root.muted ? M.Theme.base04 : M.Theme.base0E + color: root._volumeColor anchors.verticalCenter: parent.verticalCenter } @@ -140,8 +151,8 @@ M.BarSection { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter - text: root.muted ? "\uF026" : (root.volume > 0.5 ? "\uF028" : "\uF027") - color: root.muted ? M.Theme.base04 : M.Theme.base0E + text: root._volumeIcon + color: root._volumeColor font.pixelSize: M.Theme.fontSize + 2 font.family: M.Theme.iconFontFamily @@ -171,7 +182,7 @@ M.BarSection { Rectangle { width: parent.width * Math.min(1, Math.max(0, root.volume)) height: parent.height - color: root.muted ? M.Theme.base04 : M.Theme.base0E + color: root._volumeColor radius: 3 Behavior on width { NumberAnimation { duration: 80 } } @@ -249,16 +260,7 @@ M.BarSection { } Repeater { - model: { - const sinks = []; - if (Pipewire.nodes) { - for (const node of Pipewire.nodes.values) { - if (!node.isStream && node.isSink) - sinks.push(node); - } - } - return sinks; - } + model: root._sinkList delegate: Item { required property var modelData