diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-19 00:59:15 +0800 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-05-19 00:59:15 +0800 |
| commit | 7c5a23d3348a6a868204b6f2969b7ceddca0e675 (patch) | |
| tree | 955c2cadf86b30499eedc823836844f6e3738423 | |
| parent | launcher: fix actions (diff) | |
| download | caelestia-shell-7c5a23d3348a6a868204b6f2969b7ceddca0e675.tar.gz caelestia-shell-7c5a23d3348a6a868204b6f2969b7ceddca0e675.tar.bz2 caelestia-shell-7c5a23d3348a6a868204b6f2969b7ceddca0e675.zip | |
dashboard: add media
| -rw-r--r-- | config/DashboardConfig.qml | 4 | ||||
| -rw-r--r-- | modules/dashboard/Dash.qml | 5 | ||||
| -rw-r--r-- | modules/dashboard/dash/DateTime.qml | 3 | ||||
| -rw-r--r-- | modules/dashboard/dash/Media.qml | 237 | ||||
| -rw-r--r-- | widgets/StateLayer.qml | 7 |
5 files changed, 247 insertions, 9 deletions
diff --git a/config/DashboardConfig.qml b/config/DashboardConfig.qml index 1461ff7..f0d34cd 100644 --- a/config/DashboardConfig.qml +++ b/config/DashboardConfig.qml @@ -4,6 +4,7 @@ import Quickshell import QtQuick Singleton { + readonly property int mediaUpdateInterval: 500 readonly property Sizes sizes: Sizes {} component Sizes: QtObject { @@ -12,5 +13,8 @@ Singleton { readonly property int infoWidth: 200 readonly property int infoIconSize: 25 readonly property int dateTimeWidth: 110 + readonly property int mediaWidth: 200 + readonly property int mediaProgressSweep: 180 + readonly property int mediaProgressThickness: 8 } } diff --git a/modules/dashboard/Dash.qml b/modules/dashboard/Dash.qml index 0d8b760..1af0d70 100644 --- a/modules/dashboard/Dash.qml +++ b/modules/dashboard/Dash.qml @@ -51,13 +51,12 @@ GridLayout { } Rect { - text: "media" - Layout.row: 0 Layout.column: 5 Layout.rowSpan: 2 - Layout.preferredWidth: 250 Layout.fillHeight: true + + Media {} } component Rect: StyledRect { diff --git a/modules/dashboard/dash/DateTime.qml b/modules/dashboard/dash/DateTime.qml index 0c851fb..7b4e277 100644 --- a/modules/dashboard/dash/DateTime.qml +++ b/modules/dashboard/dash/DateTime.qml @@ -1,9 +1,6 @@ import "root:/widgets" import "root:/services" import "root:/config" -import Quickshell -import Quickshell.Io -import Quickshell.Widgets import QtQuick Item { diff --git a/modules/dashboard/dash/Media.qml b/modules/dashboard/dash/Media.qml new file mode 100644 index 0000000..655fde5 --- /dev/null +++ b/modules/dashboard/dash/Media.qml @@ -0,0 +1,237 @@ +import "root:/widgets" +import "root:/services" +import "root:/config" +import QtQuick +import QtQuick.Effects +import QtQuick.Shapes + +Item { + id: root + + property real playerProgress: { + const active = Players.active; + return active ? active.position / active.length : 0; + } + + implicitWidth: DashboardConfig.sizes.mediaWidth + + Behavior on playerProgress { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + Timer { + running: root.visible && (Players.active?.isPlaying ?? false) + interval: DashboardConfig.mediaUpdateInterval + triggeredOnStart: true + repeat: true + onTriggered: Players.active?.positionChanged() + } + + Shape { + preferredRendererType: Shape.CurveRenderer + + ShapePath { + fillColor: "transparent" + strokeColor: Colours.palette.m3surfaceContainerHigh + strokeWidth: DashboardConfig.sizes.mediaProgressThickness + capStyle: ShapePath.RoundCap + + PathAngleArc { + centerX: cover.x + cover.width / 2 + centerY: cover.y + cover.height / 2 + radiusX: (cover.width + DashboardConfig.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small + radiusY: (cover.height + DashboardConfig.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small + startAngle: -90 - DashboardConfig.sizes.mediaProgressSweep / 2 + sweepAngle: DashboardConfig.sizes.mediaProgressSweep + } + } + + ShapePath { + fillColor: "transparent" + strokeColor: Colours.palette.m3primary + strokeWidth: DashboardConfig.sizes.mediaProgressThickness + capStyle: ShapePath.RoundCap + + PathAngleArc { + centerX: cover.x + cover.width / 2 + centerY: cover.y + cover.height / 2 + radiusX: (cover.width + DashboardConfig.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small + radiusY: (cover.height + DashboardConfig.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small + startAngle: -90 - DashboardConfig.sizes.mediaProgressSweep / 2 + sweepAngle: DashboardConfig.sizes.mediaProgressSweep * root.playerProgress + } + } + } + + StyledRect { + id: cover + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Appearance.padding.large + DashboardConfig.sizes.mediaProgressThickness + Appearance.spacing.small + + implicitHeight: width + color: Colours.palette.m3surfaceContainerHigh + radius: Appearance.rounding.full + + MaterialIcon { + anchors.centerIn: parent + + text: "art_track" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: (parent.width * 0.4) || 1 + } + + Image { + id: image + + anchors.fill: parent + + visible: false + source: Players.active?.trackArtUrl ?? "" + asynchronous: true + fillMode: Image.PreserveAspectCrop + } + + Rectangle { + id: mask + + layer.enabled: true + layer.smooth: true + visible: false + anchors.fill: image + radius: parent.radius + } + + MultiEffect { + anchors.fill: image + source: image + maskEnabled: true + maskSource: mask + maskSpreadAtMin: 1 + maskThresholdMin: 0.5 + } + } + + StyledText { + id: title + + anchors.top: cover.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: Appearance.spacing.normal + + horizontalAlignment: Text.AlignHCenter + text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title") + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.normal + + width: parent.implicitWidth - Appearance.padding.large * 2 + elide: Text.ElideRight + } + + StyledText { + id: album + + anchors.top: title.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: Appearance.spacing.small + + horizontalAlignment: Text.AlignHCenter + text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + + width: parent.implicitWidth - Appearance.padding.large * 2 + elide: Text.ElideRight + } + + StyledText { + id: artist + + anchors.top: album.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: Appearance.spacing.small + + horizontalAlignment: Text.AlignHCenter + text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist") + color: Colours.palette.m3secondary + + width: parent.implicitWidth - Appearance.padding.large * 2 + elide: Text.ElideRight + } + + Row { + id: controls + + anchors.top: artist.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: Appearance.spacing.smaller + + spacing: Appearance.spacing.small + + Control { + icon: "skip_previous" + canUse: Players.active?.canGoPrevious ?? false + + function onClicked(): void { + Players.active?.previous(); + } + } + + Control { + icon: Players.active?.isPlaying ? "pause" : "play_arrow" + canUse: Players.active?.canTogglePlaying ?? false + + function onClicked(): void { + Players.active?.togglePlaying(); + } + } + + Control { + icon: "skip_next" + canUse: Players.active?.canGoNext ?? false + + function onClicked(): void { + Players.active?.next(); + } + } + } + + component Control: StyledRect { + id: control + + required property string icon + required property bool canUse + function onClicked(): void { + } + + implicitWidth: Math.max(icon.implicitHeight, icon.implicitHeight) + Appearance.padding.small + implicitHeight: implicitWidth + + StateLayer { + disabled: !control.canUse + radius: Appearance.rounding.full + + function onClicked(): void { + control.onClicked(); + } + } + + MaterialIcon { + id: icon + + anchors.centerIn: parent + anchors.verticalCenterOffset: font.pointSize * 0.05 + + animate: true + text: control.icon + color: control.canUse ? Colours.palette.m3onSurface : Colours.palette.m3outline + font.pointSize: Appearance.font.size.large + } + } +} diff --git a/widgets/StateLayer.qml b/widgets/StateLayer.qml index bebf230..97d1d50 100644 --- a/widgets/StateLayer.qml +++ b/widgets/StateLayer.qml @@ -8,6 +8,7 @@ Rectangle { readonly property alias hovered: mouse.hovered readonly property alias pressed: mouse.pressed + property bool disabled function onClicked(event: MouseEvent): void { } @@ -15,7 +16,7 @@ Rectangle { anchors.fill: parent color: Colours.palette.m3onSurface - opacity: mouse.pressed ? 0.1 : mouse.hovered ? 0.08 : 0 + opacity: disabled ? 0 : mouse.pressed ? 0.1 : mouse.hovered ? 0.08 : 0 MouseArea { id: mouse @@ -23,13 +24,13 @@ Rectangle { property bool hovered anchors.fill: parent - cursorShape: Qt.PointingHandCursor + cursorShape: root.disabled ? undefined : Qt.PointingHandCursor hoverEnabled: true onEntered: hovered = true onExited: hovered = false - onClicked: event => root.onClicked(event) + onClicked: event => !root.disabled && root.onClicked(event) } Behavior on opacity { |