Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9e693c0a5 | |||
| 85d822346f | |||
| bd85a4fd4f | |||
| 50cfc9e66e | |||
| 88074ccb47 |
@@ -70,6 +70,10 @@ PLAYING]]></default>
|
||||
<label>Show media controls</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="sourcePriority" type="String">
|
||||
<label>Source priority</label>
|
||||
<default></default>
|
||||
</entry>
|
||||
<entry name="trackTextVerticalSpacing" type="Int">
|
||||
<label>Track text vertical spacing</label>
|
||||
<default>5</default>
|
||||
|
||||
@@ -7,13 +7,13 @@ Item {
|
||||
required property string artworkSource
|
||||
required property var labelLines
|
||||
required property string fontFamily
|
||||
required property int fontPixelSize
|
||||
required property real fontPixelSize
|
||||
required property string textColor
|
||||
required property bool textShadowEnabled
|
||||
required property int verticalSpacing
|
||||
required property int horizontalAlignment
|
||||
required property int borderRadius
|
||||
required property int artworkSize
|
||||
required property real artworkSize
|
||||
property string currentArtworkSource: ""
|
||||
property string incomingArtworkSource: ""
|
||||
property real currentArtworkOpacity: 0
|
||||
@@ -104,7 +104,8 @@ Item {
|
||||
}
|
||||
|
||||
clip: holdsArtworkSlot
|
||||
implicitHeight: holdsArtworkSlot ? artworkSize : labelTextColumn.implicitHeight
|
||||
implicitWidth: artworkSize
|
||||
implicitHeight: artworkSize
|
||||
onArtworkSourceChanged: handleArtworkSourceChange()
|
||||
Component.onCompleted: handleArtworkSourceChange()
|
||||
|
||||
@@ -184,6 +185,7 @@ Item {
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: labelContent.verticalSpacing
|
||||
opacity: labelContent.textOpacity
|
||||
visible: opacity > 0
|
||||
|
||||
+112
-41
@@ -11,82 +11,155 @@ import org.kde.plasma.private.mpris as Mpris
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property int containerRole: Qt.UserRole + 1
|
||||
property string sourceName: "any"
|
||||
property string sourcePriority: ""
|
||||
property var mpris2Model
|
||||
property int modelRevision: 0
|
||||
readonly property var normalizedSourcePriority: parseSourcePriority(sourcePriority)
|
||||
readonly property var resolvedPlayer: resolvePlayer()
|
||||
readonly property bool ready: {
|
||||
if (!mpris2Model.currentPlayer)
|
||||
if (!resolvedPlayer)
|
||||
return false;
|
||||
|
||||
return mpris2Model.currentPlayer.desktopEntry === sourceName || sourceName === "any";
|
||||
return sourceName === "any" || entriesMatch(resolvedPlayer.desktopEntry, sourceName);
|
||||
}
|
||||
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
|
||||
readonly property string artists: ready ? resolvedPlayer.artist : ""
|
||||
readonly property string title: ready ? resolvedPlayer.track : ""
|
||||
readonly property int playbackStatus: ready ? resolvedPlayer.playbackStatus : Mpris.PlaybackStatus.Unknown
|
||||
readonly property int shuffle: ready ? resolvedPlayer.shuffle : Mpris.ShuffleStatus.Unknown
|
||||
readonly property string artUrl: ready ? resolvedPlayer.artUrl : ""
|
||||
readonly property int loopStatus: ready ? resolvedPlayer.loopStatus : Mpris.LoopStatus.Unknown
|
||||
readonly property double songPosition: ready ? resolvedPlayer.position : 0
|
||||
readonly property double songLength: ready ? resolvedPlayer.length : 0
|
||||
readonly property real volume: ready ? resolvedPlayer.volume : 0
|
||||
readonly property string identity: ready ? resolvedPlayer.identity : ""
|
||||
readonly property bool canGoNext: ready ? resolvedPlayer.canGoNext : false
|
||||
readonly property bool canGoPrevious: ready ? resolvedPlayer.canGoPrevious : false
|
||||
readonly property bool canPlay: ready ? resolvedPlayer.canPlay : false
|
||||
readonly property bool canPause: ready ? resolvedPlayer.canPause : false
|
||||
readonly property bool canSeek: ready ? resolvedPlayer.canSeek : false
|
||||
readonly property bool canRaise: ready ? resolvedPlayer.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 do not have specific properties 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
|
||||
readonly property bool canChangeShuffle: ready ? resolvedPlayer.shuffle !== undefined : false
|
||||
readonly property bool canChangeLoopStatus: ready ? resolvedPlayer.loopStatus !== undefined : false
|
||||
|
||||
function normalizedEntry(value) {
|
||||
return (value || "").toString().trim().toLowerCase();
|
||||
}
|
||||
|
||||
function entriesMatch(left, right) {
|
||||
const normalizedLeft = normalizedEntry(left);
|
||||
const normalizedRight = normalizedEntry(right);
|
||||
if (!normalizedLeft || !normalizedRight)
|
||||
return false;
|
||||
|
||||
return normalizedLeft === normalizedRight;
|
||||
}
|
||||
|
||||
function parseSourcePriority(value) {
|
||||
return (value || "").split(",").map((entry) => {
|
||||
return normalizedEntry(entry);
|
||||
}).filter((entry) => {
|
||||
return entry.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function playerAt(row) {
|
||||
if (!mpris2Model || row < 0 || row >= mpris2Model.rowCount())
|
||||
return null;
|
||||
|
||||
return mpris2Model.data(mpris2Model.index(row, 0), containerRole);
|
||||
}
|
||||
|
||||
function findPlayerByEntry(targetEntry) {
|
||||
if (!targetEntry || !mpris2Model)
|
||||
return null;
|
||||
|
||||
const playerCount = mpris2Model.rowCount();
|
||||
for (let row = 0; row < playerCount; ++row) {
|
||||
const player = playerAt(row);
|
||||
if (player && entriesMatch(player.desktopEntry, targetEntry))
|
||||
return player;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function resolvePlayer() {
|
||||
modelRevision;
|
||||
if (!mpris2Model)
|
||||
return null;
|
||||
|
||||
for (const targetEntry of normalizedSourcePriority) {
|
||||
const matchedPlayer = findPlayerByEntry(targetEntry);
|
||||
if (matchedPlayer)
|
||||
return matchedPlayer;
|
||||
|
||||
}
|
||||
return mpris2Model.currentPlayer || null;
|
||||
}
|
||||
|
||||
function togglePlayPause() {
|
||||
if (mpris2Model.currentPlayer)
|
||||
mpris2Model.currentPlayer.PlayPause();
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.PlayPause();
|
||||
|
||||
}
|
||||
|
||||
function setPosition(position) {
|
||||
mpris2Model.currentPlayer.position = position;
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.position = position;
|
||||
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (mpris2Model.currentPlayer)
|
||||
mpris2Model.currentPlayer.Next();
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.Next();
|
||||
|
||||
}
|
||||
|
||||
function previous() {
|
||||
if (mpris2Model.currentPlayer)
|
||||
mpris2Model.currentPlayer.Previous();
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.Previous();
|
||||
|
||||
}
|
||||
|
||||
function updatePosition() {
|
||||
if (mpris2Model.currentPlayer)
|
||||
mpris2Model.currentPlayer.updatePosition();
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.updatePosition();
|
||||
|
||||
}
|
||||
|
||||
function setVolume(volume) {
|
||||
mpris2Model.currentPlayer.volume = volume;
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.volume = volume;
|
||||
|
||||
}
|
||||
|
||||
function changeVolume(delta, showOSD) {
|
||||
mpris2Model.currentPlayer.changeVolume(delta, showOSD);
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.changeVolume(delta, showOSD);
|
||||
|
||||
}
|
||||
|
||||
function setShuffle(shuffle) {
|
||||
mpris2Model.currentPlayer.shuffle = shuffle;
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.shuffle = shuffle;
|
||||
|
||||
}
|
||||
|
||||
function setLoopStatus(loopStatus) {
|
||||
mpris2Model.currentPlayer.loopStatus = loopStatus;
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.loopStatus = loopStatus;
|
||||
|
||||
}
|
||||
|
||||
function raise() {
|
||||
mpris2Model.currentPlayer.Raise();
|
||||
if (resolvedPlayer)
|
||||
resolvedPlayer.Raise();
|
||||
|
||||
}
|
||||
|
||||
function formatTrackTime(s) {
|
||||
@@ -99,13 +172,11 @@ QtObject {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
onRowsInserted: root.modelRevision += 1
|
||||
onRowsRemoved: root.modelRevision += 1
|
||||
onModelReset: root.modelRevision += 1
|
||||
onDataChanged: root.modelRevision += 1
|
||||
onCurrentPlayerChanged: root.modelRevision += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,23 @@ MouseArea {
|
||||
readonly property bool hideNowPlayingArea: effectiveLabelVisibilityMode === "never"
|
||||
readonly property bool usesCustomBackground: configuredBackgroundStyle === "custom"
|
||||
readonly property int hideModeHorizontalPadding: 8
|
||||
readonly property int labelRailWidth: 100
|
||||
readonly property int contentInset: 8
|
||||
readonly property int availableContentWidth: Math.max(0, width - contentInset * 2)
|
||||
readonly property int separatorReservedWidth: nowPlayingLabelsVisible && !hideNowPlayingArea ? 1 + effectiveSeparatorGapLabel + effectiveSeparatorGapTrack : 0
|
||||
readonly property int maxLabelRailSize: Math.max(0, availableContentWidth - separatorReservedWidth)
|
||||
readonly property int labelRailSize: Math.max(0, Math.min(height - contentInset * 2, maxLabelRailSize))
|
||||
readonly property bool mediaControlsTargetShown: mediaControlsEnabled && controlsHoverActive && nowPlayingLabelsVisible && !hideNowPlayingArea
|
||||
readonly property int artworkControlsGap: configuredUseLabelArtwork && player.artUrl.length > 0 ? 2 : 0
|
||||
readonly property real labelShrunkScale: labelRailSize > 0 ? Math.max(0, (labelRailSize - buttonSize - artworkControlsGap) / labelRailSize) : 1
|
||||
property real labelContentScale: mediaControlsTargetShown ? labelShrunkScale : 1
|
||||
readonly property real controlsReveal: {
|
||||
const range = 1 - labelShrunkScale;
|
||||
if (range <= 0)
|
||||
return mediaControlsTargetShown ? 1 : 0;
|
||||
|
||||
return Math.max(0, Math.min(1, (1 - labelContentScale) / range));
|
||||
}
|
||||
readonly property real labelContentSize: labelRailSize * labelContentScale
|
||||
readonly property int effectiveImageBorderRadius: Math.max(0, configuredImageBorderRadius)
|
||||
readonly property string effectiveBackgroundColor: configuredBackgroundColor || "transparent"
|
||||
readonly property int effectiveBackgroundRadius: Math.max(0, configuredBackgroundRadius)
|
||||
@@ -77,9 +93,8 @@ MouseArea {
|
||||
onMediaControlsEnabledChanged: {
|
||||
if (!mediaControlsMouseArea.mediaControlsEnabled)
|
||||
hoverExitTimer.stop();
|
||||
|
||||
}
|
||||
Keys.onPressed: (event) => {
|
||||
Keys.onPressed: event => {
|
||||
if (!mediaControlsMouseArea.mediaControlsEnabled) {
|
||||
event.accepted = false;
|
||||
return;
|
||||
@@ -110,6 +125,13 @@ MouseArea {
|
||||
interval: 300
|
||||
}
|
||||
|
||||
Behavior on labelContentScale {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: mediaControlsMouseArea.usesCustomBackground
|
||||
@@ -119,75 +141,65 @@ MouseArea {
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: mediaControlsMouseArea.contentInset
|
||||
layoutDirection: mediaControlsMouseArea.labelsOnRight ? Qt.RightToLeft : Qt.LeftToRight
|
||||
|
||||
ColumnLayout {
|
||||
Item {
|
||||
id: leftColumn
|
||||
|
||||
visible: !mediaControlsMouseArea.hideNowPlayingArea
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: visible ? mediaControlsMouseArea.labelRailWidth : 0
|
||||
Layout.preferredWidth: visible ? mediaControlsMouseArea.labelRailWidth : 0
|
||||
Layout.maximumWidth: visible ? mediaControlsMouseArea.labelRailWidth : 0
|
||||
states: [
|
||||
State {
|
||||
name: "buttonsVisible"
|
||||
when: mediaControlsMouseArea.controlsHoverActive
|
||||
|
||||
PropertyChanges {
|
||||
target: nowPlayingLabels
|
||||
Layout.bottomMargin: 10
|
||||
}
|
||||
|
||||
},
|
||||
State {
|
||||
name: "buttonsHidden"
|
||||
when: !mediaControlsMouseArea.controlsHoverActive
|
||||
|
||||
PropertyChanges {
|
||||
target: nowPlayingLabels
|
||||
Layout.bottomMargin: 0
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
Layout.minimumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
|
||||
Layout.preferredWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
|
||||
Layout.maximumWidth: visible ? mediaControlsMouseArea.labelRailSize : 0
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
id: labelSlot
|
||||
|
||||
ColumnLayout {
|
||||
id: nowPlayingLabels
|
||||
readonly property bool showsArtwork: mediaControlsMouseArea.configuredUseLabelArtwork && player.artUrl.length > 0
|
||||
// Text is rasterized at whole pixel sizes, then lightly scaled between steps for smoother motion.
|
||||
readonly property real textTargetFontSize: Math.max(1, mediaControlsMouseArea.effectiveLabelFontSize * mediaControlsMouseArea.labelContentScale)
|
||||
readonly property int textRasterFontSize: Math.max(1, Math.round(textTargetFontSize))
|
||||
readonly property real textResidualScale: textTargetFontSize / textRasterFontSize
|
||||
|
||||
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
x: mediaControlsMouseArea.labelsOnRight ? 0 : leftColumn.width - width
|
||||
y: 0
|
||||
width: mediaControlsMouseArea.labelContentSize
|
||||
height: mediaControlsMouseArea.labelContentSize
|
||||
clip: true
|
||||
visible: mediaControlsMouseArea.nowPlayingLabelsVisible
|
||||
|
||||
LabelContent {
|
||||
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
|
||||
height: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
|
||||
scale: labelSlot.showsArtwork ? mediaControlsMouseArea.labelContentScale : labelSlot.textResidualScale
|
||||
transformOrigin: Item.TopLeft
|
||||
artworkSource: mediaControlsMouseArea.configuredUseLabelArtwork ? player.artUrl : ""
|
||||
labelLines: mediaControlsMouseArea.effectiveLabelLines
|
||||
fontFamily: mediaControlsMouseArea.configuredFontFamily
|
||||
fontPixelSize: mediaControlsMouseArea.effectiveLabelFontSize
|
||||
fontPixelSize: labelSlot.showsArtwork ? mediaControlsMouseArea.effectiveLabelFontSize : labelSlot.textRasterFontSize
|
||||
textColor: mediaControlsMouseArea.effectiveForegroundColor
|
||||
textShadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled
|
||||
verticalSpacing: mediaControlsMouseArea.effectiveLabelVerticalSpacing
|
||||
horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignLeft : Text.AlignRight
|
||||
borderRadius: mediaControlsMouseArea.effectiveImageBorderRadius
|
||||
artworkSize: mediaControlsMouseArea.labelRailWidth
|
||||
artworkSize: labelSlot.showsArtwork ? mediaControlsMouseArea.labelRailSize : mediaControlsMouseArea.labelContentSize / labelSlot.textResidualScale
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: mediaControls
|
||||
|
||||
Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight
|
||||
enabled: mediaControlsMouseArea.mediaControlsEnabled && mediaControlsMouseArea.controlsHoverActive
|
||||
opacity: mediaControlsMouseArea.mediaControlsEnabled && mediaControlsMouseArea.controlsHoverActive && mediaControlsMouseArea.nowPlayingLabelsVisible && !mediaControlsMouseArea.hideNowPlayingArea ? 1 : 0
|
||||
visible: mediaControlsMouseArea.mediaControlsEnabled && mediaControlsMouseArea.controlsHoverActive && mediaControlsMouseArea.nowPlayingLabelsVisible && !mediaControlsMouseArea.hideNowPlayingArea
|
||||
x: mediaControlsMouseArea.labelsOnRight ? 0 : leftColumn.width - width
|
||||
y: mediaControlsMouseArea.labelContentSize + mediaControlsMouseArea.artworkControlsGap
|
||||
width: implicitWidth
|
||||
height: mediaControlsMouseArea.buttonSize
|
||||
clip: true
|
||||
enabled: mediaControlsMouseArea.mediaControlsTargetShown
|
||||
opacity: mediaControlsMouseArea.controlsReveal
|
||||
visible: mediaControlsMouseArea.mediaControlsTargetShown || opacity > 0
|
||||
|
||||
QQC2.Button {
|
||||
Layout.preferredWidth: buttonSize
|
||||
@@ -203,7 +215,6 @@ MouseArea {
|
||||
source: "media-skip-backward"
|
||||
color: mediaControlsMouseArea.effectiveForegroundColor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
@@ -222,7 +233,6 @@ MouseArea {
|
||||
source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start"
|
||||
color: mediaControlsMouseArea.effectiveForegroundColor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
@@ -236,33 +246,8 @@ MouseArea {
|
||||
source: "media-skip-forward"
|
||||
color: mediaControlsMouseArea.effectiveForegroundColor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.InOutQuad
|
||||
properties: "Layout.bottomMargin"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -283,7 +268,6 @@ MouseArea {
|
||||
height: Math.round(parent.height * mediaControlsMouseArea.effectiveSeparatorHeight / 100)
|
||||
color: mediaControlsMouseArea.configuredHideSeparator ? "transparent" : mediaControlsMouseArea.effectiveForegroundColor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -320,9 +304,6 @@ MouseArea {
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignRight : Text.AlignLeft
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick.Dialogs as Dialogs
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kcmutils as KCM
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.private.mpris as Mpris
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: configRoot
|
||||
@@ -24,13 +25,16 @@ KCM.SimpleKCM {
|
||||
property alias cfg_foregroundColor: foregroundColorField.text
|
||||
property alias cfg_textShadowEnabled: textShadowCheckBox.checked
|
||||
property alias cfg_showMediaControls: showMediaControlsCheckBox.checked
|
||||
property alias cfg_sourcePriority: sourcePriorityField.text
|
||||
property alias cfg_trackTextVerticalSpacing: trackTextVerticalSpacingSpinBox.value
|
||||
property alias cfg_labelVerticalSpacing: labelVerticalSpacingSpinBox.value
|
||||
property alias cfg_separatorGapLabel: separatorGapLabelSpinBox.value
|
||||
property alias cfg_separatorGapTrack: separatorGapTrackSpinBox.value
|
||||
property alias cfg_separatorHeight: separatorHeightSpinBox.value
|
||||
property alias cfg_hideSeparator: hideSeparatorCheckBox.checked
|
||||
readonly property int containerRole: Qt.UserRole + 1
|
||||
readonly property var availableFonts: Qt.fontFamilies()
|
||||
property var detectedSources: []
|
||||
readonly property var labelVisibilityOptions: [{
|
||||
"text": i18n("Auto hide when idle"),
|
||||
"value": "auto"
|
||||
@@ -71,6 +75,28 @@ KCM.SimpleKCM {
|
||||
return options[resolvedIndex].value;
|
||||
}
|
||||
|
||||
function normalizeDesktopEntry(value) {
|
||||
return (value || "").toString().trim();
|
||||
}
|
||||
|
||||
function refreshDetectedSources() {
|
||||
const sources = [];
|
||||
const seen = {
|
||||
};
|
||||
const playerCount = detectedSourcesModel.rowCount();
|
||||
for (let row = 0; row < playerCount; ++row) {
|
||||
const player = detectedSourcesModel.data(detectedSourcesModel.index(row, 0), containerRole);
|
||||
const desktopEntry = normalizeDesktopEntry(player ? player.desktopEntry : "");
|
||||
const desktopEntryKey = desktopEntry.toLowerCase();
|
||||
if (!desktopEntry || seen[desktopEntryKey])
|
||||
continue;
|
||||
|
||||
seen[desktopEntryKey] = true;
|
||||
sources.push(desktopEntry);
|
||||
}
|
||||
detectedSources = sources;
|
||||
}
|
||||
|
||||
onCfg_fontFamilyChanged: {
|
||||
const index = availableFonts.indexOf(cfg_fontFamily);
|
||||
if (index >= 0 && fontFamilyComboBox.currentIndex !== index)
|
||||
@@ -191,6 +217,25 @@ KCM.SimpleKCM {
|
||||
text: i18n("Show media controls")
|
||||
}
|
||||
|
||||
QQC2.TextField {
|
||||
id: sourcePriorityField
|
||||
|
||||
Kirigami.FormData.label: i18n("Source priority:")
|
||||
Layout.fillWidth: true
|
||||
placeholderText: i18n("spotify, firefox")
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: detectedSourcesField
|
||||
|
||||
Kirigami.FormData.label: i18n("Current sources being detected:")
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.max(implicitHeight, Kirigami.Units.gridUnit * 2)
|
||||
textFormat: TextEdit.PlainText
|
||||
wrapMode: TextEdit.WrapAnywhere
|
||||
text: configRoot.detectedSources.length > 0 ? configRoot.detectedSources.join("\n") : i18n("No active MPRIS desktopEntry values detected")
|
||||
}
|
||||
|
||||
QQC2.SpinBox {
|
||||
id: separatorHeightSpinBox
|
||||
|
||||
@@ -333,6 +378,16 @@ KCM.SimpleKCM {
|
||||
onAccepted: configRoot.cfg_foregroundColor = selectedColor.toString()
|
||||
}
|
||||
|
||||
Mpris.Mpris2Model {
|
||||
id: detectedSourcesModel
|
||||
|
||||
onRowsInserted: configRoot.refreshDetectedSources()
|
||||
onRowsRemoved: configRoot.refreshDetectedSources()
|
||||
onModelReset: configRoot.refreshDetectedSources()
|
||||
onDataChanged: configRoot.refreshDetectedSources()
|
||||
Component.onCompleted: configRoot.refreshDetectedSources()
|
||||
}
|
||||
|
||||
component SectionHeader: Item {
|
||||
required property string title
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ PlasmoidItem {
|
||||
// qmllint disable missing-property
|
||||
property bool configuredShowMediaControls: plasmoid.configuration.showMediaControls
|
||||
// qmllint disable missing-property
|
||||
property string configuredSourcePriority: plasmoid.configuration.sourcePriority
|
||||
// qmllint disable missing-property
|
||||
property int configuredTrackTextVerticalSpacing: plasmoid.configuration.trackTextVerticalSpacing
|
||||
// qmllint disable missing-property
|
||||
property int configuredLabelVerticalSpacing: plasmoid.configuration.labelVerticalSpacing
|
||||
@@ -73,6 +75,8 @@ PlasmoidItem {
|
||||
|
||||
Player {
|
||||
id: player
|
||||
|
||||
sourcePriority: root.configuredSourcePriority
|
||||
}
|
||||
|
||||
fullRepresentation: Representation {
|
||||
|
||||
Reference in New Issue
Block a user