feat: add small mpris widget to dash
This commit is contained in:
parent
d63a102e36
commit
4e9a6eaa4a
12 changed files with 259 additions and 101 deletions
|
@ -11,6 +11,7 @@ pkgs.mkShell {
|
||||||
# Required for qmlls to find the correct type declarations
|
# Required for qmlls to find the correct type declarations
|
||||||
# Sadly Quickshell doesn't export some types declaratively
|
# Sadly Quickshell doesn't export some types declaratively
|
||||||
export QMLLS_BUILD_DIRS=${pkgs.kdePackages.qtdeclarative}/lib/qt-6/qml/:${quickshell}/lib/qt-6/qml/
|
export QMLLS_BUILD_DIRS=${pkgs.kdePackages.qtdeclarative}/lib/qt-6/qml/:${quickshell}/lib/qt-6/qml/
|
||||||
|
export QML_IMPORT_PATH=$PWD/src
|
||||||
${pkgs.pre-commit}/bin/pre-commit install -f
|
${pkgs.pre-commit}/bin/pre-commit install -f
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQml
|
import QtQml
|
||||||
import "base"
|
import "base"
|
||||||
|
import "widgets/mpris"
|
||||||
|
import "provider"
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: homeWindow
|
id: homeWindow
|
||||||
|
@ -23,6 +25,7 @@ PanelWindow {
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouse
|
id: mouse
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: homeWindow.root.enabled = false
|
onClicked: homeWindow.root.enabled = false
|
||||||
|
@ -44,6 +47,23 @@ PanelWindow {
|
||||||
|
|
||||||
Component.onCompleted: () => maxSize = homeWindow.screen.width * (2 / 7)
|
Component.onCompleted: () => maxSize = homeWindow.screen.width * (2 / 7)
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
model: Notifications.incoming
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
width: 100
|
||||||
|
height: 50
|
||||||
|
Text {
|
||||||
|
text: parent.modelData.appName
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
id: anim
|
id: anim
|
||||||
|
@ -69,26 +89,7 @@ PanelWindow {
|
||||||
Layout.margins: 15
|
Layout.margins: 15
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
BRectangle {
|
MprisSmall {}
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 200
|
|
||||||
radius: 15
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
clip: true
|
|
||||||
Rectangle {
|
|
||||||
Layout.margins: 20
|
|
||||||
Layout.preferredWidth: parent.height - (Layout.margins * 2)
|
|
||||||
Layout.preferredHeight: parent.height - (Layout.margins * 2)
|
|
||||||
Layout.maximumWidth: {
|
|
||||||
const mWidth = parent.width - (Layout.margins * 2);
|
|
||||||
return mWidth > 0 ? mWidth : 0;
|
|
||||||
}
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,18 +25,13 @@ Rectangle {
|
||||||
maskSource: mask
|
maskSource: mask
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
id: mask
|
id: mask
|
||||||
width: image.width
|
width: image.width
|
||||||
height: image.height
|
height: image.height
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
visible: false
|
visible: false
|
||||||
|
radius: roundedImage.radius
|
||||||
Rectangle {
|
color: "black"
|
||||||
width: image.width
|
|
||||||
height: image.height
|
|
||||||
radius: roundedImage.radius
|
|
||||||
color: "black"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
44
src/base/BlurredImage.qml
Normal file
44
src/base/BlurredImage.qml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property var source
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
source: root.source
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
id: image
|
||||||
|
autoPaddingEnabled: false
|
||||||
|
source: background
|
||||||
|
anchors.fill: background
|
||||||
|
blurEnabled: true
|
||||||
|
blurMax: 64
|
||||||
|
blurMultiplier: 2
|
||||||
|
blur: 1
|
||||||
|
brightness: -0.15
|
||||||
|
contrast: -0.35
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: mask
|
||||||
|
anchors.margins: root.border.width - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mask
|
||||||
|
width: image.width
|
||||||
|
height: image.height
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
radius: root.radius
|
||||||
|
color: "black"
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,14 +15,13 @@ Singleton {
|
||||||
bodyMarkupSupported: false
|
bodyMarkupSupported: false
|
||||||
bodySupported: true
|
bodySupported: true
|
||||||
imageSupported: true
|
imageSupported: true
|
||||||
}
|
|
||||||
Item {
|
|
||||||
|
|
||||||
Component.onCompleted: () => {
|
onNotification: n => {
|
||||||
notif._.notification.connect(n => {
|
n.tracked = true;
|
||||||
list.push(n);
|
incoming.push(n);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property var list: []
|
|
||||||
|
property list<Notification> backlog: notif._.trackedNotifications
|
||||||
|
property list<Notification> incoming: []
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,22 @@ Singleton {
|
||||||
|
|
||||||
property var current: player.all[player.index]
|
property var current: player.all[player.index]
|
||||||
|
|
||||||
property var all: Mpris.players.values
|
property list<MprisPlayer> all: Mpris.players.values
|
||||||
|
|
||||||
property int index: {
|
property int index: {
|
||||||
const ind = Mpris.players.values.findIndex(p => p.playbackState === MprisPlaybackState.Playing);
|
const ind = Mpris.players.values.findIndex(p => p.playbackState === MprisPlaybackState.Playing);
|
||||||
return ind >= 0 ? ind : 0;
|
return ind >= 0 ? ind : 0;
|
||||||
}
|
}
|
||||||
property var next: () => {
|
|
||||||
|
onIndexChanged: () => {
|
||||||
|
player.current = player.all[player.index] ?? player.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function next(): void {
|
||||||
player.index = (player.index + 1) % all.length;
|
player.index = (player.index + 1) % all.length;
|
||||||
}
|
}
|
||||||
property var prev: () => {
|
|
||||||
|
function prev(): void {
|
||||||
const newInd = player.index - 1;
|
const newInd = player.index - 1;
|
||||||
player.index = newInd < 0 ? all.length - 1 : newInd;
|
player.index = newInd < 0 ? all.length - 1 : newInd;
|
||||||
}
|
}
|
||||||
|
|
5
src/provider/qmldir
Normal file
5
src/provider/qmldir
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module Provider
|
||||||
|
singleton Player 0.1 Player.qml
|
||||||
|
singleton Inhibitor 0.1 Inhibitor.qml
|
||||||
|
singleton Notifications 0.1 Notifications.qml
|
||||||
|
singleton Time 0.1 Time.qml
|
|
@ -72,7 +72,7 @@ ColumnLayout {
|
||||||
, ["media-playlist-repeat", MprisLoopState.Playlist] //
|
, ["media-playlist-repeat", MprisLoopState.Playlist] //
|
||||||
]
|
]
|
||||||
property int index: map.findIndex(e => e[1] === Player.current?.loopState)
|
property int index: map.findIndex(e => e[1] === Player.current?.loopState)
|
||||||
source: loopButton.visible ? Quickshell.iconPath(map[index][0]) : ""
|
source: loopButton.visible && map[index] && map[index][0] ? Quickshell.iconPath(map[index][0]) : ""
|
||||||
onClicked: {
|
onClicked: {
|
||||||
const ind = (index + 1) % map.length;
|
const ind = (index + 1) % map.length;
|
||||||
Player.current.loopState = map[ind][1];
|
Player.current.loopState = map[ind][1];
|
||||||
|
|
135
src/widgets/mpris/MprisSmall.qml
Normal file
135
src/widgets/mpris/MprisSmall.qml
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell
|
||||||
|
import "../../base"
|
||||||
|
import "../../provider"
|
||||||
|
|
||||||
|
BRectangle {
|
||||||
|
id: mprisSmall
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 200
|
||||||
|
radius: 15
|
||||||
|
|
||||||
|
visible: Player.current ?? false
|
||||||
|
|
||||||
|
BlurredImage {
|
||||||
|
source: Player.current?.trackArtUrl ?? ""
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: parent.radius
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
BRoundedImage {
|
||||||
|
id: im
|
||||||
|
color: "transparent"
|
||||||
|
visible: false
|
||||||
|
source: Player.current?.trackArtUrl ?? ""
|
||||||
|
radius: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
id: effect
|
||||||
|
source: im
|
||||||
|
autoPaddingEnabled: true
|
||||||
|
shadowBlur: 1.0
|
||||||
|
shadowColor: 'black'
|
||||||
|
shadowEnabled: true
|
||||||
|
Layout.margins: 20
|
||||||
|
Layout.preferredWidth: parent.height - (Layout.margins * 2)
|
||||||
|
Layout.preferredHeight: parent.height - (Layout.margins * 2)
|
||||||
|
Layout.maximumWidth: {
|
||||||
|
const mWidth = parent.width - (Layout.margins * 2);
|
||||||
|
return mWidth > 0 ? mWidth : 0;
|
||||||
|
}
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.maximumWidth: parent.width / 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: Player.current?.trackTitle ?? "Unknown Track"
|
||||||
|
color: "white"
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: Player.current?.trackAlbum ?? "Unknown Album"
|
||||||
|
color: "white"
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: Player.current?.trackAlbumArtist ?? "Unknown Artist"
|
||||||
|
color: "white"
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
BIconButton {
|
||||||
|
source: Quickshell.iconPath("media-seek-backward")
|
||||||
|
onClicked: Player.current.previous()
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
BIconButton {
|
||||||
|
source: Quickshell.iconPath(Player.isPlaying ? "media-playback-pause" : "media-playback-start")
|
||||||
|
onClicked: Player.current.togglePlaying()
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
BIconButton {
|
||||||
|
source: Quickshell.iconPath("media-seek-forward")
|
||||||
|
onClicked: Player.current.next()
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: slider
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 10
|
||||||
|
Layout.minimumHeight: 3
|
||||||
|
from: 0
|
||||||
|
to: Player.current?.length ?? 0
|
||||||
|
value: Player.current?.position ?? 0
|
||||||
|
enabled: (Player.current?.canSeek && Player.current?.positionSupported) ?? false
|
||||||
|
|
||||||
|
onMoved: {
|
||||||
|
if (Player.current)
|
||||||
|
Player.current.position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
const con = () => mprisSmall.player?.positionChanged.connect(() => {
|
||||||
|
slider.value = Player.current?.position;
|
||||||
|
});
|
||||||
|
con();
|
||||||
|
Player.currentChanged.connect(() => {
|
||||||
|
con();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameAnimation {
|
||||||
|
// only emit the signal when the position is actually changing.
|
||||||
|
running: Player.current?.playbackState == MprisPlaybackState.Playing
|
||||||
|
// emit the positionChanged signal every frame.
|
||||||
|
onTriggered: Player.current?.positionChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,11 +3,9 @@ import Quickshell.Services.Pipewire
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell.Services.Mpris
|
import "../../base"
|
||||||
import "root:base"
|
import "../../provider"
|
||||||
import "root:provider"
|
import "../../widgets/MprisBig"
|
||||||
import QtQuick.Effects
|
|
||||||
import "root:widgets/MprisBig"
|
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: audioman
|
id: audioman
|
||||||
|
@ -25,53 +23,15 @@ PanelWindow {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: audioman.visible = false
|
onClicked: audioman.visible = false
|
||||||
|
|
||||||
BRectangle {
|
BlurredImage {
|
||||||
id: display
|
id: display
|
||||||
|
|
||||||
x: 10
|
x: 10
|
||||||
y: 10
|
y: 10
|
||||||
width: 500
|
width: 500
|
||||||
height: 600
|
height: 600
|
||||||
radius: 10
|
radius: 10
|
||||||
|
source: Player.current?.trackArtUrl ?? ""
|
||||||
Image {
|
color: "#BD93F9"
|
||||||
id: background
|
|
||||||
anchors.fill: parent
|
|
||||||
source: Player.current?.trackArtUrl ?? ""
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
visible: false
|
|
||||||
anchors.margins: display.border.width - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiEffect {
|
|
||||||
id: image
|
|
||||||
autoPaddingEnabled: false
|
|
||||||
source: background
|
|
||||||
anchors.fill: background
|
|
||||||
blurEnabled: true
|
|
||||||
blurMax: 64
|
|
||||||
blurMultiplier: 2
|
|
||||||
blur: 1
|
|
||||||
brightness: -0.15
|
|
||||||
contrast: -0.35
|
|
||||||
maskEnabled: true
|
|
||||||
maskSource: mask
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: mask
|
|
||||||
width: image.width
|
|
||||||
height: image.height
|
|
||||||
layer.enabled: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: image.width
|
|
||||||
height: image.height
|
|
||||||
radius: display.radius
|
|
||||||
color: "black"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: test
|
id: test
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Quickshell.Services.Notifications
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: toast
|
id: toast
|
||||||
property int lifetime: 5000
|
readonly property int lifetime: 5000
|
||||||
property int countdownTime: lifetime
|
property int countdownTime: lifetime
|
||||||
|
|
||||||
required property string appName
|
required property string appName
|
||||||
|
@ -21,6 +21,7 @@ MouseArea {
|
||||||
required property bool hasActionIcons
|
required property bool hasActionIcons
|
||||||
required property var actions
|
required property var actions
|
||||||
required property int index
|
required property int index
|
||||||
|
Component.onCompleted: () => console.log(toast.actions.values)
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
popupcol.model.remove(toast.index, 1);
|
popupcol.model.remove(toast.index, 1);
|
||||||
|
@ -125,22 +126,10 @@ MouseArea {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: toast.actions
|
model: toast.actions
|
||||||
|
|
||||||
delegate: Button {
|
delegate: ToastAction {
|
||||||
id: actionButton
|
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
notifAction: modelData
|
||||||
IconImage {
|
hasIcons: toast.hasActionIcons
|
||||||
visible: toast.hasActionIcons
|
|
||||||
Component.onCompleted: () => {
|
|
||||||
if (toast.hasActionIcons) {
|
|
||||||
source = actionButton.modelData.identifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text: modelData.text
|
|
||||||
onClicked: () => modelData?.invoke()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
src/windows/notificationtoast/ToastAction.qml
Normal file
22
src/windows/notificationtoast/ToastAction.qml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: actionButton
|
||||||
|
|
||||||
|
required property var notifAction
|
||||||
|
required property bool hasIcons
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
visible: parent.hasIcons
|
||||||
|
Component.onCompleted: () => {
|
||||||
|
if (parent.hasIcons) {
|
||||||
|
source = actionButton.notifAction?.identifier ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text: notifAction?.text ?? ""
|
||||||
|
onClicked: () => notifAction?.invoke()
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue