summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2025-07-15 01:26:01 +0800
committerlittleblack111 <littleblack11111@gmail.com>2025-07-15 01:26:01 +0800
commit939f2e5fdcbccf3148e081934e33d9e27c5af399 (patch)
tree62f87086babf4552d911ae8c25189b3d83a1b01e
parent40dc7a93f228aa94cdbcbaed2cd38150592dd17f (diff)
mpris: progress bar
-rw-r--r--bar/Mpris.qml179
1 files changed, 134 insertions, 45 deletions
diff --git a/bar/Mpris.qml b/bar/Mpris.qml
index b87c003..f520b80 100644
--- a/bar/Mpris.qml
+++ b/bar/Mpris.qml
@@ -1,85 +1,174 @@
-import QtQuick.Layouts
-import QtQuick
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
import Quickshell
import Quickshell.Services.Mpris
import qs.services as Services
import qs.config
import qs.components
-// import Quickshell.Utils
-
Item {
id: root
readonly property MprisPlayer activePlayer: Services.Mpris.activePlayer
- readonly property string cleanedTitle: cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media")
- // length broken (https://github.com/quickshell-mirror/quickshell/issues/109)
- readonly property string position: friendlyTimeForSeconds(activePlayer?.position) || "0:00"
- readonly property string length: friendlyTimeForSeconds(activePlayer?.length) || "0:00"
- readonly property string progress: activePlayer?.positionSupported && position != length ? position + "/" + length : activePlayer.positionSupported && !activePlayer.lengthSupported ? !activePlayer.positionSupported && activePlayer.lengthSupported ? "" : position : activePlayer.lengthSupported ? length : ""
+ readonly property real progress: (activePlayer.positionSupported && activePlayer.lengthSupported && activePlayer.length > 0) ? activePlayer.position / activePlayer.length : 0
+ readonly property string cleanedTitle: cleanMusicTitle(activePlayer?.trackTitle)
readonly property string state: activePlayer?.isPlaying ? Icons.media.pause : Icons.media.play
- function cleanMusicTitle(title: string): string {
+ function cleanMusicTitle(title) {
if (!title)
return "";
- title = title.replace(/\s*\([^)]*\)/g, " "); // Round brackets
- title = title.replace(/\s*\[[^\]]*\]/g, " "); // Square brackets
- title = title.replace(/\s*\{[^\}]*\}/g, " "); // Curly brackets
- title = title.replace(/\s*【[^】]*】/g, " "); // Touhou
+ title = title.replace(/\s*\([^)]*\)/g, " ");
+ title = title.replace(/\s*\[[^\]]*\]/g, " ");
+ title = title.replace(/\s*\{[^\}]*\}/g, " ");
+ title = title.replace(/\s*【[^】]*】/g, " ");
title = title.replace(/\s*《[^》]*》/g, " ");
title = title.replace(/\s*「[^」]*」/g, " ");
title = title.replace(/\s*『[^』]*』/g, " ");
-
return title.trim();
}
- function friendlyTimeForSeconds(seconds) {
- if (isNaN(seconds) || seconds < 0)
- return "0:00";
- seconds = Math.floor(seconds);
- const h = Math.floor(seconds / 3600);
- const m = Math.floor((seconds % 3600) / 60);
- const s = seconds % 60;
- if (h > 0) {
- return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
- } else {
- return `${m}:${s.toString().padStart(2, '0')}`;
- }
- }
-
implicitWidth: rect.width - General.rectMargin / 1.25
height: Bar.height + General.rectMargin / 1.5
- // TODO fancy animation progress bar etc.
Rectangle {
id: rect
anchors.verticalCenter: parent.verticalCenter
- // TODO: move to IRect
implicitWidth: layout.width + General.rectMargin * 4
height: parent.height - General.rectMargin
color: Colors.alt
radius: Style.rounding.smaller
+
RowLayout {
id: layout
anchors.centerIn: parent
spacing: Bar.resourceIconTextSpacing / 1.5
- RowLayout {
- spacing: Bar.resourceIconTextSpacing
- Icon {
- text: root.state
+ Icon {
+ text: root.state
+ }
+ IText {
+ animate: true
+ text: root.cleanedTitle
+ }
+ }
+
+ Canvas {
+ id: canvas
+ anchors.fill: parent
+ onPaint: {
+ var ctx = getContext("2d");
+ ctx.clearRect(0, 0, width, height);
+
+ var t = Math.max(0, Math.min(root.progress, 1));
+ var bw = 2;
+ var r = rect.radius;
+ var w = width, h = height;
+
+ var leftHeight = h - 2 * r;
+ var topWidth = w - 2 * r;
+ var rightHeight = h - 2 * r;
+
+ var seg1 = leftHeight;
+ var seg2 = 2 * topWidth + 4 * (r * Math.PI / 2);
+ var seg3 = rightHeight;
+ var total = seg1 + seg2 + seg3;
+
+ ctx.lineWidth = bw;
+ ctx.strokeStyle = Colors.primary;
+ ctx.lineCap = "butt";
+
+ var rem = t * total;
+
+ // left edge
+ if (rem > 0) {
+ var len = Math.min(rem, seg1);
+ var centerY = h / 2;
+ var halfLen = len / 2;
+
+ ctx.beginPath();
+ ctx.moveTo(bw / 2, centerY - halfLen);
+ ctx.lineTo(bw / 2, centerY + halfLen);
+ ctx.stroke();
+
+ rem -= len;
}
- IText {
- animate: true
- text: root.cleanedTitle
+
+ // top/bottom edges with corners
+ if (rem > 0) {
+ var len = Math.min(rem, seg2);
+ var progress = len / seg2;
+ var edgeLen = topWidth * progress;
+
+ if (progress > 0) {
+ // top-left corner
+ ctx.beginPath();
+ ctx.arc(r, r, r - bw / 2, Math.PI, 3 * Math.PI / 2);
+ ctx.stroke();
+
+ // bottom-left corner
+ ctx.beginPath();
+ ctx.arc(r, h - r, r - bw / 2, Math.PI / 2, Math.PI);
+ ctx.stroke();
+
+ // top edge
+ ctx.beginPath();
+ ctx.moveTo(r, bw / 2);
+ ctx.lineTo(r + edgeLen, bw / 2);
+ ctx.stroke();
+
+ // bottom edge
+ ctx.beginPath();
+ ctx.moveTo(r, h - bw / 2);
+ ctx.lineTo(r + edgeLen, h - bw / 2);
+ ctx.stroke();
+
+ // top-right and bottom-right corners if complete
+ if (progress >= 1) {
+ ctx.beginPath();
+ ctx.arc(w - r, r, r - bw / 2, 3 * Math.PI / 2, 2 * Math.PI);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.arc(w - r, h - r, r - bw / 2, 0, Math.PI / 2);
+ ctx.stroke();
+ }
+ }
+
+ rem -= len;
+ }
+
+ // right edge
+ if (rem > 0) {
+ var len = Math.min(rem, seg3);
+ var centerY = h / 2;
+ var halfLen = len / 2;
+
+ // draw upward from center
+ ctx.beginPath();
+ ctx.moveTo(w - bw / 2, centerY);
+ ctx.lineTo(w - bw / 2, centerY - halfLen);
+ ctx.stroke();
+
+ // draw downward from center
+ ctx.beginPath();
+ ctx.moveTo(w - bw / 2, centerY);
+ ctx.lineTo(w - bw / 2, centerY + halfLen);
+ ctx.stroke();
}
}
- IText {
- text: "[" + root.progress + "]"
+
+ onWidthChanged: requestPaint()
+ onHeightChanged: requestPaint()
+ Connections {
+ target: root
+ function onProgressChanged() {
+ canvas.requestPaint();
+ }
+ }
+ Connections {
+ target: rect
+ function onRadiusChanged() {
+ canvas.requestPaint();
+ }
}
}
}
-
- // hover
- // Text {
- // text: root.activePlayer.trackTitle + " by " + root.activePlayer.trackArtist + " - " + root.activePlayer.trackAlbum + root.activePlayer.position + "/" + root.activePlayer.length
- // }
}