This commit is contained in:
2026-02-08 13:26:24 +00:00
commit 8dfbb0dea7
10 changed files with 546 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
import QtQuick 2.12
import org.kde.plasma.configuration 2.0
ConfigModel {
ConfigCategory {
name: i18n("General")
icon: "preferences-desktop"
source: "GeneralConfig.qml"
}
}
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name=""/>
<group name="General">
<entry name="opacity" type="Int">
<default>100</default>
<label>Opacity</label>
</entry>
<entry name="fontFamily" type="String">
<default>"Noto Sans"</default>
<label>Font family</label>
</entry>
</group>
</kcfg>
+46
View File
@@ -0,0 +1,46 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.plasma.core as PlasmaCore
import org.kde.kirigami as Kirigami
Item {
id: configRoot
signal configurationChanged
property alias cfg_opacity: opacitySpinBox.value
property string cfg_fontFamily
ColumnLayout {
spacing: Kirigami.Units.mediumSpacing
RowLayout{
Label {
text: i18n("Opacity percent")
}
SpinBox {
id: opacitySpinBox
from: 0
to: 100
}
}
RowLayout{
Label {
text: i18n("Font")
}
ComboBox{
id: fontFamilyComboBox
model: Qt.fontFamilies()
onCurrentIndexChanged: {
var current = Qt.fontFamilies()[currentIndex]
cfg_fontFamily=current
configRoot.configurationChanged()
}
}
}
}
}
+107
View File
@@ -0,0 +1,107 @@
/**
Credits: https://github.com/ccatterina/plasmusic-toolbar
looked handy, ty
*/
import QtQuick 2.15
import QtQml.Models 2.3
import org.kde.plasma.private.mpris as Mpris
QtObject {
id: root
property var mpris2Model: Mpris.Mpris2Model {
onRowsInserted: (_, rowIndex) => {
const CONTAINER_ROLE = Qt.UserRole + 1
const player = this.data(this.index(rowIndex, 0), CONTAINER_ROLE)
if (player.desktopEntry === root.sourceName) {
this.currentIndex = rowIndex;
}
}
}
property string sourceName: "any"
readonly property bool ready: {
if (!mpris2Model.currentPlayer) {
return false
}
return mpris2Model.currentPlayer.desktopEntry === sourceName || sourceName === "any";
}
readonly property string artists: ready ? mpris2Model.currentPlayer.artist : ""
readonly property string title: ready ? mpris2Model.currentPlayer.track : ""
readonly property int playbackStatus: ready ? mpris2Model.currentPlayer.playbackStatus : Mpris.PlaybackStatus.Unknown
readonly property int shuffle: ready ? mpris2Model.currentPlayer.shuffle : Mpris.ShuffleStatus.Unknown
readonly property string artUrl: ready ? mpris2Model.currentPlayer.artUrl : ""
readonly property int loopStatus: ready ? mpris2Model.currentPlayer.loopStatus : Mpris.LoopStatus.Unknown
readonly property double songPosition: ready ? mpris2Model.currentPlayer.position : 0
readonly property double songLength: ready ? mpris2Model.currentPlayer.length : 0
readonly property real volume: ready ? mpris2Model.currentPlayer.volume : 0
readonly property string identity: ready ? mpris2Model.currentPlayer.identity : ""
readonly property bool canGoNext: ready ? mpris2Model.currentPlayer.canGoNext : false
readonly property bool canGoPrevious: ready ? mpris2Model.currentPlayer.canGoPrevious : false
readonly property bool canPlay: ready ? mpris2Model.currentPlayer.canPlay : false
readonly property bool canPause: ready ? mpris2Model.currentPlayer.canPause : false
readonly property bool canSeek: ready ? mpris2Model.currentPlayer.canSeek : false
readonly property bool canRaise: ready ? mpris2Model.currentPlayer.canRaise : false
// To know whether Shuffle and Loop can be changed we have to check if the property is defined,
// unlike the other commands, LoopStatus and Shuffle hasn't a specific propety such as
// CanPause, CanSeek, etc.
readonly property bool canChangeShuffle: ready ? mpris2Model.currentPlayer.shuffle != undefined : false
readonly property bool canChangeLoopStatus: ready ? mpris2Model.currentPlayer.loopStatus != undefined : false
function togglePlayPause() {
mpris2Model.currentPlayer?.PlayPause();
}
function setPosition(position) {
mpris2Model.currentPlayer.position = position;
}
function next() {
mpris2Model.currentPlayer?.Next();
}
function previous() {
mpris2Model.currentPlayer?.Previous();
}
function updatePosition() {
mpris2Model.currentPlayer?.updatePosition();
}
function setVolume(volume) {
mpris2Model.currentPlayer.volume = volume
}
function changeVolume(delta, showOSD) {
mpris2Model.currentPlayer.changeVolume(delta, showOSD);
}
function setShuffle(shuffle) {
mpris2Model.currentPlayer.shuffle = shuffle
}
function setLoopStatus(loopStatus) {
mpris2Model.currentPlayer.loopStatus = loopStatus
}
function raise() {
mpris2Model.currentPlayer.Raise();
}
function formatTrackTime(s) {
var hours = Math.floor(s / 3600)
var minutes = Math.floor((s - (hours * 3600)) / 60)
var seconds = Math.ceil(s - (hours * 3600) - (minutes * 60))
minutes = (minutes < 10) ? "0" + minutes : minutes
seconds = (seconds < 10) ? "0" + seconds : seconds
var time = minutes + ":" + seconds
return time
}
}
+179
View File
@@ -0,0 +1,179 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.plasma.components as PlasmaComponents
import org.kde.plasma.core as PlasmaCore
import org.kde.kirigami as Kirigami
MouseArea {
id: mediaControlsMouseArea
focus: true
Keys.onPressed: {
if (!event.modifiers) {
event.accepted = true
if (event.key === Qt.Key_Space || event.key === Qt.Key_K) {
player.togglePlayPause()
} else if (event.key === Qt.Key_P) {
player.previous()
} else if (event.key === Qt.Key_N) {
player.next()
} else {
event.accepted = false
}
}
}
hoverEnabled: true
readonly property double buttonSize: 16
RowLayout {
ColumnLayout {
Layout.fillHeight: true
id: leftColumn
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
ColumnLayout {
id: nowPlayingLabels
Layout.alignment: Qt.AlignRight
spacing: 0
Label {
Layout.alignment: Qt.AlignRight
text: "NOW"
lineHeight: 0.8
font.pixelSize: 16
font.bold: true
font.family: plasmoid.configuration.fontFamily
}
Label {
id: nowPlayingLabel2
Layout.alignment: Qt.AlignRight
text: "PLAYING"
lineHeight: 0.8
font.bold: true
font.pixelSize: 16
font.family: plasmoid.configuration.fontFamily
}
}
RowLayout {
id: mediaControls
Layout.alignment: Qt.AlignHCenter
opacity: mediaControlsMouseArea.containsMouse ? 1 : 0
visible: opacity > 0
Behavior on opacity {
PropertyAnimation {
easing.type: Easing.InOutQuad
duration: 250
}
}
Button {
Layout.preferredWidth: buttonSize
Layout.preferredHeight: buttonSize
contentItem: Kirigami.Icon {
source: "media-skip-backward"
}
padding: 0
background: null
onClicked: {
player.previous()
console.log("prev clicked")
}
}
Button {
Layout.preferredWidth: buttonSize
Layout.preferredHeight: buttonSize
id: playButton
contentItem: Kirigami.Icon {
source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start"
}
padding: 0
background: null
onClicked: {
player.togglePlayPause()
console.log("pause clicked")
}
}
Button {
Layout.preferredWidth: buttonSize
Layout.preferredHeight: buttonSize
contentItem: Kirigami.Icon {
source: "media-skip-forward"
}
onClicked: {
player.next()
}
padding: 0
background: null
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
states: [
State {
name: "buttonsVisible"
when: mediaControlsMouseArea.containsMouse
PropertyChanges {
target: nowPlayingLabels
Layout.bottomMargin: 10
}
},
State {
name: "buttonsHidden"
when: !mediaControlsMouseArea.containsMouse
PropertyChanges {
target: nowPlayingLabels
Layout.bottomMargin: 0
}
}
]
transitions: Transition {
NumberAnimation {
properties: "Layout.bottomMargin"
duration: 250
easing.type: Easing.InOutQuad
}
}
}
Rectangle {
id: separator
width: 1
Layout.fillHeight: true
}
ColumnLayout {
Layout.fillWidth: true
id: infoColumn
PlasmaComponents.Label {
font.family: plasmoid.configuration.fontFamily
text: player.title
Layout.fillWidth: true
font.pixelSize: 28
lineHeight: 0.8
font.bold: true
elide: Text.ElideRight
}
PlasmaComponents.Label {
font.family: plasmoid.configuration.fontFamily
elide: Text.ElideRight
Layout.maximumWidth: 300
Layout.fillWidth: true
text: player.artists
font.pixelSize: 26
lineHeight: 0.8
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.plasma.plasmoid
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasma5support as Plasma5Support
import org.kde.plasma.private.mpris as Mpris
import org.kde.kirigami as Kirigami
PlasmoidItem {
id: root
Layout.minimumWidth: Kirigami.Units.gridUnit*25
Layout.minimumHeight: Kirigami.Units.gridUnit*5
Plasmoid.backgroundHints: "NoBackground"
opacity: plasmoid.configuration.opacity/100
Representation{
anchors.fill: parent
}
Player{
id: player
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"KPlugin": {
"Authors": [
{
"Name": "ruinivist"
}
],
"Category": "Multimedia",
"Description": "An aesthetic song name widget with media controls. Meant to be placed on Desktop not in panel.",
"Icon": "applications-multimedia",
"Id": "org.ruiny.NowPlaying",
"License": "GPLv3",
"Name": "NowPlaying",
"Version": "1.0",
"Website": "https://github.com/ruinivist/NowPlaying"
},
"KPackageStructure": "Plasma/Applet",
"X-Plasma-API-Minimum-Version": "6.0",
"X-Plasma-Provides": [ "org.kde.plasma.multimediacontrols" ],
"X-Plasma-DBusActivationService": "org.mpris.MediaPlayer2.*"
}