diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml index d9b11eb..0f73b03 100644 --- a/package/contents/config/main.xml +++ b/package/contents/config/main.xml @@ -13,5 +13,33 @@ Noto Sans + + + auto + + + + left + + + + custom + + + + transparent + + + + 0 + + + + white + + + + false + diff --git a/package/contents/ui/Representation.qml b/package/contents/ui/Representation.qml index efba567..14ba7ee 100644 --- a/package/contents/ui/Representation.qml +++ b/package/contents/ui/Representation.qml @@ -7,7 +7,35 @@ import org.kde.plasma.components as PlasmaComponents MouseArea { id: mediaControlsMouseArea + property string configuredFontFamily: "Noto Sans" + property string configuredLabelVisibilityMode: "auto" + property string configuredLabelPlacement: "left" + property string configuredBackgroundStyle: "custom" + property string configuredBackgroundColor: "transparent" + property int configuredBackgroundRadius: 0 + property string configuredForegroundColor: "white" + property bool configuredTextShadowEnabled: false readonly property double buttonSize: 16 + readonly property string effectiveLabelVisibilityMode: configuredLabelVisibilityMode === "always" || configuredLabelVisibilityMode === "never" ? configuredLabelVisibilityMode : "auto" + readonly property bool labelsOnRight: configuredLabelPlacement === "right" + 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 separatorPadding: 4 + readonly property string effectiveBackgroundColor: configuredBackgroundColor || "transparent" + readonly property int effectiveBackgroundRadius: Math.max(0, configuredBackgroundRadius) + readonly property string effectiveForegroundColor: configuredForegroundColor || "white" + readonly property bool hasTrackInfo: player.ready && (((player.title || "").trim().length > 0) || ((player.artists || "").trim().length > 0)) + readonly property bool nowPlayingLabelsVisible: { + if (effectiveLabelVisibilityMode === "always") + return true; + + if (effectiveLabelVisibilityMode === "never") + return false; + + return hasTrackInfo; + } focus: true hoverEnabled: true @@ -25,13 +53,25 @@ MouseArea { } } + Rectangle { + anchors.fill: parent + visible: mediaControlsMouseArea.usesCustomBackground + color: mediaControlsMouseArea.effectiveBackgroundColor + radius: mediaControlsMouseArea.effectiveBackgroundRadius + } + RowLayout { anchors.fill: parent + layoutDirection: mediaControlsMouseArea.labelsOnRight ? Qt.RightToLeft : Qt.LeftToRight ColumnLayout { 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" @@ -63,29 +103,34 @@ MouseArea { ColumnLayout { id: nowPlayingLabels - Layout.alignment: Qt.AlignRight + Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight spacing: 0 + visible: mediaControlsMouseArea.nowPlayingLabelsVisible - PlasmaComponents.Label { - Layout.alignment: Qt.AlignRight + ShadowedLabel { + Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight + shadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled text: "NOW" lineHeight: 0.8 font.pixelSize: 16 font.bold: true - // qmllint disable missing-property - font.family: plasmoid.configuration.fontFamily + font.family: mediaControlsMouseArea.configuredFontFamily + color: mediaControlsMouseArea.effectiveForegroundColor + horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignLeft : Text.AlignRight } - PlasmaComponents.Label { + ShadowedLabel { id: nowPlayingLabel2 - Layout.alignment: Qt.AlignRight + Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight + shadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled text: "PLAYING" lineHeight: 0.8 font.bold: true font.pixelSize: 16 - // qmllint disable missing-property - font.family: plasmoid.configuration.fontFamily + font.family: mediaControlsMouseArea.configuredFontFamily + color: mediaControlsMouseArea.effectiveForegroundColor + horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignLeft : Text.AlignRight } } @@ -93,9 +138,9 @@ MouseArea { RowLayout { id: mediaControls - Layout.alignment: Qt.AlignHCenter - opacity: mediaControlsMouseArea.containsMouse ? 1 : 0 - visible: opacity > 0 + Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignLeft : Qt.AlignRight + opacity: mediaControlsMouseArea.containsMouse && mediaControlsMouseArea.nowPlayingLabelsVisible && !mediaControlsMouseArea.hideNowPlayingArea ? 1 : 0 + visible: opacity > 0 && mediaControlsMouseArea.nowPlayingLabelsVisible && !mediaControlsMouseArea.hideNowPlayingArea QQC2.Button { Layout.preferredWidth: buttonSize @@ -109,6 +154,7 @@ MouseArea { contentItem: Kirigami.Icon { source: "media-skip-backward" + color: mediaControlsMouseArea.effectiveForegroundColor } } @@ -127,6 +173,7 @@ MouseArea { contentItem: Kirigami.Icon { source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start" + color: mediaControlsMouseArea.effectiveForegroundColor } } @@ -140,6 +187,7 @@ MouseArea { contentItem: Kirigami.Icon { source: "media-skip-forward" + color: mediaControlsMouseArea.effectiveForegroundColor } } @@ -173,39 +221,95 @@ MouseArea { Rectangle { id: separator + visible: mediaControlsMouseArea.nowPlayingLabelsVisible && !mediaControlsMouseArea.hideNowPlayingArea Layout.fillHeight: true - width: 1 + Layout.leftMargin: visible ? mediaControlsMouseArea.separatorPadding : 0 + Layout.rightMargin: visible ? mediaControlsMouseArea.separatorPadding : 0 + Layout.topMargin: visible ? mediaControlsMouseArea.separatorPadding : 0 + Layout.bottomMargin: visible ? mediaControlsMouseArea.separatorPadding : 0 + Layout.minimumWidth: visible ? 1 : 0 + Layout.preferredWidth: visible ? 1 : 0 + Layout.maximumWidth: visible ? 1 : 0 + color: mediaControlsMouseArea.effectiveForegroundColor } ColumnLayout { id: infoColumn Layout.fillWidth: true + Layout.minimumWidth: 0 + Layout.leftMargin: mediaControlsMouseArea.hideNowPlayingArea ? mediaControlsMouseArea.hideModeHorizontalPadding : 0 + Layout.rightMargin: mediaControlsMouseArea.hideNowPlayingArea ? mediaControlsMouseArea.hideModeHorizontalPadding : 0 - PlasmaComponents.Label { + ShadowedLabel { Layout.fillWidth: true + shadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled text: player.title font.pixelSize: 28 font.bold: true - // qmllint disable missing-property - font.family: plasmoid.configuration.fontFamily + font.family: mediaControlsMouseArea.configuredFontFamily + color: mediaControlsMouseArea.effectiveForegroundColor lineHeight: 0.8 elide: Text.ElideRight + horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignRight : Text.AlignLeft } - PlasmaComponents.Label { + ShadowedLabel { Layout.maximumWidth: 300 Layout.fillWidth: true + Layout.alignment: mediaControlsMouseArea.labelsOnRight ? Qt.AlignRight : Qt.AlignLeft + shadowEnabled: mediaControlsMouseArea.configuredTextShadowEnabled text: player.artists font.pixelSize: 26 - // qmllint disable missing-property - font.family: plasmoid.configuration.fontFamily + font.family: mediaControlsMouseArea.configuredFontFamily + color: mediaControlsMouseArea.effectiveForegroundColor lineHeight: 0.8 elide: Text.ElideRight + horizontalAlignment: mediaControlsMouseArea.labelsOnRight ? Text.AlignRight : Text.AlignLeft } } } + component ShadowedLabel: Item { + id: shadowedLabel + + property alias text: foregroundLabel.text + property alias font: foregroundLabel.font + property alias lineHeight: foregroundLabel.lineHeight + property alias elide: foregroundLabel.elide + property alias horizontalAlignment: foregroundLabel.horizontalAlignment + property alias verticalAlignment: foregroundLabel.verticalAlignment + property alias color: foregroundLabel.color + property bool shadowEnabled: false + + implicitWidth: foregroundLabel.implicitWidth + (shadowEnabled ? 1 : 0) + implicitHeight: foregroundLabel.implicitHeight + (shadowEnabled ? 1 : 0) + + PlasmaComponents.Label { + id: shadowLabel + + visible: shadowedLabel.shadowEnabled + x: 1 + y: 1 + width: foregroundLabel.width + height: foregroundLabel.height + text: foregroundLabel.text + font: foregroundLabel.font + lineHeight: foregroundLabel.lineHeight + elide: foregroundLabel.elide + horizontalAlignment: foregroundLabel.horizontalAlignment + verticalAlignment: foregroundLabel.verticalAlignment + color: "#99000000" + } + + PlasmaComponents.Label { + id: foregroundLabel + + anchors.fill: parent + } + + } + } diff --git a/package/contents/ui/configGeneral.qml b/package/contents/ui/configGeneral.qml index fa61994..64ff334 100644 --- a/package/contents/ui/configGeneral.qml +++ b/package/contents/ui/configGeneral.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls as QQC2 +import QtQuick.Dialogs as Dialogs import QtQuick.Layouts import org.kde.kcmutils as KCM import org.kde.kirigami as Kirigami @@ -9,13 +10,77 @@ KCM.SimpleKCM { property alias cfg_opacity: opacitySpinBox.value property string cfg_fontFamily + property string cfg_labelVisibilityMode + property string cfg_labelPlacement + property string cfg_backgroundStyle + property alias cfg_backgroundColor: backgroundColorField.text + property alias cfg_backgroundRadius: backgroundRadiusSpinBox.value + property alias cfg_foregroundColor: foregroundColorField.text + property alias cfg_textShadowEnabled: textShadowCheckBox.checked readonly property var availableFonts: Qt.fontFamilies() + readonly property var labelVisibilityOptions: [{ + "text": i18n("Auto hide when idle"), + "value": "auto" + }, { + "text": i18n("Always show"), + "value": "always" + }, { + "text": i18n("Always hide"), + "value": "never" + }] + readonly property var labelPlacementOptions: [{ + "text": i18n("Left"), + "value": "left" + }, { + "text": i18n("Right"), + "value": "right" + }] + readonly property var backgroundStyleOptions: [{ + "text": i18n("Default"), + "value": "custom" + }, { + "text": i18n("Solid background"), + "value": "default" + }] + + function syncComboByValue(comboBox, options, configuredValue, fallbackValue) { + const requestedValue = configuredValue || fallbackValue; + const requestedIndex = options.findIndex((option) => { + return option.value === requestedValue; + }); + const fallbackIndex = options.findIndex((option) => { + return option.value === fallbackValue; + }); + const resolvedIndex = requestedIndex >= 0 ? requestedIndex : (fallbackIndex >= 0 ? fallbackIndex : 0); + if (comboBox.currentIndex !== resolvedIndex) + comboBox.currentIndex = resolvedIndex; + + return options[resolvedIndex].value; + } onCfg_fontFamilyChanged: { const index = availableFonts.indexOf(cfg_fontFamily); if (index >= 0 && fontFamilyComboBox.currentIndex !== index) fontFamilyComboBox.currentIndex = index; + } + onCfg_labelVisibilityModeChanged: { + const resolvedValue = syncComboByValue(labelVisibilityModeComboBox, labelVisibilityOptions, cfg_labelVisibilityMode, "auto"); + if (cfg_labelVisibilityMode !== resolvedValue) + cfg_labelVisibilityMode = resolvedValue; + + } + onCfg_labelPlacementChanged: { + const resolvedValue = syncComboByValue(labelPlacementComboBox, labelPlacementOptions, cfg_labelPlacement, "left"); + if (cfg_labelPlacement !== resolvedValue) + cfg_labelPlacement = resolvedValue; + + } + onCfg_backgroundStyleChanged: { + const resolvedValue = syncComboByValue(backgroundStyleComboBox, backgroundStyleOptions, cfg_backgroundStyle, "custom"); + if (cfg_backgroundStyle !== resolvedValue) + cfg_backgroundStyle = resolvedValue; + } Kirigami.FormLayout { @@ -35,6 +100,109 @@ KCM.SimpleKCM { onActivated: configRoot.cfg_fontFamily = currentText } + QQC2.ComboBox { + id: labelVisibilityModeComboBox + + Kirigami.FormData.label: i18n("Now Playing labels:") + model: configRoot.labelVisibilityOptions + textRole: "text" + valueRole: "value" + onActivated: configRoot.cfg_labelVisibilityMode = currentValue + } + + QQC2.ComboBox { + id: labelPlacementComboBox + + Kirigami.FormData.label: i18n("Label placement:") + model: configRoot.labelPlacementOptions + textRole: "text" + valueRole: "value" + onActivated: configRoot.cfg_labelPlacement = currentValue + } + + QQC2.ComboBox { + id: backgroundStyleComboBox + + Kirigami.FormData.label: i18n("Background style:") + model: configRoot.backgroundStyleOptions + textRole: "text" + valueRole: "value" + onActivated: configRoot.cfg_backgroundStyle = currentValue + } + + RowLayout { + Kirigami.FormData.label: i18n("Background color:") + enabled: configRoot.cfg_backgroundStyle === "custom" + + QQC2.TextField { + id: backgroundColorField + + Layout.fillWidth: true + placeholderText: i18n("transparent or #AARRGGBB") + } + + QQC2.Button { + text: i18n("Pick...") + onClicked: { + backgroundColorDialog.selectedColor = backgroundColorField.text || "transparent"; + backgroundColorDialog.open(); + } + } + + } + + RowLayout { + Kirigami.FormData.label: i18n("Foreground color:") + + QQC2.TextField { + id: foregroundColorField + + Layout.fillWidth: true + placeholderText: i18n("white or #RRGGBB") + } + + QQC2.Button { + text: i18n("Pick...") + onClicked: { + foregroundColorDialog.selectedColor = foregroundColorField.text || "white"; + foregroundColorDialog.open(); + } + } + + } + + QQC2.SpinBox { + id: backgroundRadiusSpinBox + + Kirigami.FormData.label: i18n("Background radius:") + enabled: configRoot.cfg_backgroundStyle === "custom" + from: 0 + to: 100 + } + + QQC2.CheckBox { + id: textShadowCheckBox + + Kirigami.FormData.label: i18n("Text shadow:") + text: i18n("Enable") + } + + } + + Dialogs.ColorDialog { + id: backgroundColorDialog + + title: i18n("Select background color") + options: Dialogs.ColorDialog.ShowAlphaChannel + onAccepted: configRoot.cfg_backgroundColor = selectedColor.toString() + } + + Dialogs.ColorDialog { + id: foregroundColorDialog + + title: i18n("Select foreground color") + options: Dialogs.ColorDialog.ShowAlphaChannel + onAccepted: configRoot.cfg_foregroundColor = selectedColor.toString() } } diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml index 1868611..0bc2a18 100644 --- a/package/contents/ui/main.qml +++ b/package/contents/ui/main.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import org.kde.kirigami as Kirigami +import org.kde.plasma.core as PlasmaCore import org.kde.plasma.plasmoid PlasmoidItem { @@ -10,12 +11,37 @@ PlasmoidItem { // "qmllint" sees `plasmoid` as QObject here, but Plasma injects `configuration` at runtime. // qmllint disable missing-property property int configuredOpacity: plasmoid.configuration.opacity + // qmllint disable missing-property + property string configuredFontFamily: plasmoid.configuration.fontFamily + // qmllint disable missing-property + property string configuredLabelVisibilityMode: plasmoid.configuration.labelVisibilityMode + // qmllint disable missing-property + property string configuredLabelPlacement: plasmoid.configuration.labelPlacement + // qmllint disable missing-property + property string configuredBackgroundStyle: plasmoid.configuration.backgroundStyle + // qmllint disable missing-property + property string configuredBackgroundColor: plasmoid.configuration.backgroundColor + // qmllint disable missing-property + property int configuredBackgroundRadius: plasmoid.configuration.backgroundRadius + // qmllint disable missing-property + property string configuredForegroundColor: plasmoid.configuration.foregroundColor + // qmllint disable missing-property + property bool configuredTextShadowEnabled: plasmoid.configuration.textShadowEnabled + readonly property int resolvedBackgroundHints: { + if (configuredBackgroundStyle === "default") + return PlasmaCore.Types.DefaultBackground | PlasmaCore.Types.ConfigurableBackground; + + if (configuredBackgroundStyle === "none") + return PlasmaCore.Types.NoBackground; + + return PlasmaCore.Types.NoBackground; + } width: Kirigami.Units.gridUnit * 25 height: Kirigami.Units.gridUnit * 5 Layout.minimumWidth: Kirigami.Units.gridUnit * 25 Layout.minimumHeight: Kirigami.Units.gridUnit * 5 - Plasmoid.backgroundHints: "NoBackground" + Plasmoid.backgroundHints: resolvedBackgroundHints opacity: configuredOpacity / 100 Player { @@ -27,11 +53,29 @@ PlasmoidItem { Layout.minimumHeight: Kirigami.Units.gridUnit * 5 Layout.preferredWidth: root.width Layout.preferredHeight: root.height + configuredFontFamily: root.configuredFontFamily + configuredLabelVisibilityMode: root.configuredLabelVisibilityMode + configuredLabelPlacement: root.configuredLabelPlacement + configuredBackgroundStyle: root.configuredBackgroundStyle + configuredBackgroundColor: root.configuredBackgroundColor + configuredBackgroundRadius: root.configuredBackgroundRadius + configuredForegroundColor: root.configuredForegroundColor + configuredTextShadowEnabled: root.configuredTextShadowEnabled } compactRepresentation: Representation { Layout.minimumWidth: Kirigami.Units.gridUnit * 25 Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + Layout.preferredWidth: root.width + Layout.preferredHeight: root.height + configuredFontFamily: root.configuredFontFamily + configuredLabelVisibilityMode: root.configuredLabelVisibilityMode + configuredLabelPlacement: root.configuredLabelPlacement + configuredBackgroundStyle: root.configuredBackgroundStyle + configuredBackgroundColor: root.configuredBackgroundColor + configuredBackgroundRadius: root.configuredBackgroundRadius + configuredForegroundColor: root.configuredForegroundColor + configuredTextShadowEnabled: root.configuredTextShadowEnabled } }