From 681d1683f515cc8457428c7804abef45174d26d6 Mon Sep 17 00:00:00 2001 From: Micah Stanley Date: Fri, 25 Oct 2024 15:52:49 +0000 Subject: [PATCH] VolumeOSD: Improve design, and prevent touch events from being taken from outside the osd Fixes: https://invent.kde.org/plasma/plasma-mobile/-/issues/274 Any feedback on these changes would be much appreciated. ![Screenshot_20241024_093458-1](/uploads/7b4f89ace1a53c559737d1c05d591329/Screenshot_20241024_093458-1.png) ![Screenshot_20241023_070919](/uploads/c7b9a8de7c9bba2de01d734408e02b2b/Screenshot_20241023_070919.png) Change Log: - NanoShell FullScreenOverlay was changed to a LayerShellQt Window to keep it on the top layer and to prevent the popup from obsorbing all touch inputs. - New animations were added to the volume popup. - User can now change the volume by touching and dragging on the popup - The mute button on the popup was fixed - Mute buttons were added next to the volume sliders in the AudioApplet page - Volume icons now dynamically update to the volume level - Visual design adjustments --- CMakeLists.txt | 2 + components/mobileshell/CMakeLists.txt | 1 + .../mobileshell/qml/volumeosd/AudioApplet.qml | 38 +++ .../qml/volumeosd/ListItemBase.qml | 12 + .../mobileshell/qml/volumeosd/PopupCard.qml | 41 ++- .../qml/volumeosd/VolumeChangedPopup.qml | 270 ++++++++++++++++ .../mobileshell/qml/volumeosd/VolumeOSD.qml | 291 +++++++++--------- .../qml/volumeosd/VolumeOSDProvider.qml | 8 +- components/mobileshell/shellutil.cpp | 15 + components/mobileshell/shellutil.h | 6 + 10 files changed, 534 insertions(+), 150 deletions(-) create mode 100644 components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index c40606a1..0fb0de35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,8 @@ find_package(KWin ${PROJECT_DEP_VERSION} REQUIRED COMPONENTS kwin ) +find_package(LayerShellQt REQUIRED) + find_package(LibKWorkspace CONFIG REQUIRED) find_package(Libudev REQUIRED) diff --git a/components/mobileshell/CMakeLists.txt b/components/mobileshell/CMakeLists.txt index fb559749..44ac9716 100644 --- a/components/mobileshell/CMakeLists.txt +++ b/components/mobileshell/CMakeLists.txt @@ -52,6 +52,7 @@ target_link_libraries(mobileshellplugin Plasma::KWaylandClient KF6::Service KF6::Package + LayerShellQt::Interface ) ecm_finalize_qml_module(mobileshellplugin) diff --git a/components/mobileshell/qml/volumeosd/AudioApplet.qml b/components/mobileshell/qml/volumeosd/AudioApplet.qml index 20e1953e..be79a791 100644 --- a/components/mobileshell/qml/volumeosd/AudioApplet.qml +++ b/components/mobileshell/qml/volumeosd/AudioApplet.qml @@ -19,8 +19,14 @@ import org.kde.plasma.private.volume // capture presses on the audio applet so it doesn't close the overlay ColumnLayout { + id: audioApplet spacing: 0 + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + property real scale: 1.0 + PulseObjectFilterModel { id: paSinkFilterModel sortRoleName: "SortByDefault" @@ -50,6 +56,14 @@ ColumnLayout { PopupCard { Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Kirigami.Units.gridUnit + + transform: Scale { + origin.x: Math.round(implicitWidth / 2) + origin.y: Math.round(height / 2) + xScale: audioApplet.scale + yScale: audioApplet.scale + } + contentItem: ColumnLayout { anchors.rightMargin: Kirigami.Units.smallSpacing anchors.leftMargin: Kirigami.Units.smallSpacing @@ -80,6 +94,14 @@ ColumnLayout { PopupCard { Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Kirigami.Units.gridUnit + + transform: Scale { + origin.x: Math.round(implicitWidth / 2) + origin.y: Math.round(height / 2) + xScale: audioApplet.scale + yScale: audioApplet.scale + } + contentItem: ColumnLayout { anchors.rightMargin: Kirigami.Units.smallSpacing anchors.leftMargin: Kirigami.Units.smallSpacing @@ -111,6 +133,14 @@ ColumnLayout { visible: sourceInputView.model.count + sourceMediaInputView.model.count !== 0 Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Kirigami.Units.gridUnit + + transform: Scale { + origin.x: Math.round(implicitWidth / 2) + origin.y: Math.round(height / 2) + xScale: audioApplet.scale + yScale: audioApplet.scale + } + contentItem: ColumnLayout { anchors.rightMargin: Kirigami.Units.smallSpacing anchors.leftMargin: Kirigami.Units.smallSpacing @@ -164,6 +194,14 @@ ColumnLayout { visible: sourceOutputView.model.count !== 0 Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Kirigami.Units.gridUnit + + transform: Scale { + origin.x: Math.round(implicitWidth / 2) + origin.y: Math.round(height / 2) + xScale: audioApplet.scale + yScale: audioApplet.scale + } + contentItem: ColumnLayout { anchors.rightMargin: Kirigami.Units.smallSpacing anchors.leftMargin: Kirigami.Units.smallSpacing diff --git a/components/mobileshell/qml/volumeosd/ListItemBase.qml b/components/mobileshell/qml/volumeosd/ListItemBase.qml index 3d702142..8bb14924 100644 --- a/components/mobileshell/qml/volumeosd/ListItemBase.qml +++ b/components/mobileshell/qml/volumeosd/ListItemBase.qml @@ -128,6 +128,18 @@ Controls.ItemDelegate { Layout.fillWidth: true spacing: Kirigami.Units.smallSpacing + PlasmaComponents.ToolButton { + icon.name: Icon.name(Volume / PulseAudio.NormalVolume * 100.0, Muted) + text: Muted ? i18n("Unmute") : i18n("Mute") + display: Controls.AbstractButton.IconOnly + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium + onClicked: { + Muted = !Muted + } + } + // this slider was effectively copied from the source (linked at the top of the file) PlasmaComponents.Slider { id: slider diff --git a/components/mobileshell/qml/volumeosd/PopupCard.qml b/components/mobileshell/qml/volumeosd/PopupCard.qml index f29f079f..bd88df41 100644 --- a/components/mobileshell/qml/volumeosd/PopupCard.qml +++ b/components/mobileshell/qml/volumeosd/PopupCard.qml @@ -1,5 +1,6 @@ /* * SPDX-FileCopyrightText: 2021 Devin Lin + * SPDX-FileCopyrightText: 2024 Micah Stanley * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -9,6 +10,9 @@ import QtQuick.Controls as Controls import QtQuick.Layouts import QtQuick.Window +import QtQuick.Effects +import Qt5Compat.GraphicalEffects + import org.kde.kirigami 2.20 as Kirigami import org.kde.ksvg 1.0 as KSvg import org.kde.plasma.components 3.0 as PlasmaComponents @@ -16,11 +20,40 @@ import org.kde.plasma.components 3.0 as PlasmaComponents // capture presses on the audio applet so it doesn't close the overlay Controls.Control { id: content - implicitWidth: Math.min(Kirigami.Units.gridUnit * 20, parent.width - Kirigami.Units.gridUnit * 2) + implicitWidth: Math.min(Kirigami.Units.gridUnit * 20, Screen.width - Kirigami.Units.gridUnit * 2) padding: Kirigami.Units.smallSpacing * 2 - background: KSvg.FrameSvgItem { - imagePath: "widgets/background" - anchors.margins: -Kirigami.Units.smallSpacing * 2 + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + MultiEffect { anchors.fill: parent + source: simpleShadow + blurMax: 16 + shadowEnabled: true + shadowVerticalOffset: 1 + shadowOpacity: 0.85 + shadowColor: Qt.lighter(Kirigami.Theme.backgroundColor, 0.2) + } + + Rectangle { + id: simpleShadow + anchors.fill: parent + anchors.leftMargin: -1 + anchors.rightMargin: -1 + anchors.bottomMargin: -1 + + color: { + let darkerBackgroundColor = Qt.darker(Kirigami.Theme.backgroundColor, 1.3); + return Qt.rgba(darkerBackgroundColor.r, darkerBackgroundColor.g, darkerBackgroundColor.b, 0.5) + } + radius: Kirigami.Units.cornerRadius + } + + Rectangle { + anchors.fill: parent + color: Qt.lighter(Kirigami.Theme.backgroundColor, 1.5) + opacity: 0.85 + radius: Kirigami.Units.cornerRadius } } diff --git a/components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml b/components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml new file mode 100644 index 00000000..c6d4f336 --- /dev/null +++ b/components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml @@ -0,0 +1,270 @@ +/* + * SPDX-FileCopyrightText: 2024 Micah Stanley + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick +import QtQuick.Controls as Controls +import QtQuick.Layouts +import QtQuick.Window + +import org.kde.kirigami 2.20 as Kirigami +import org.kde.plasma.components 3.0 as PlasmaComponents +import org.kde.plasma.private.mobileshell as MobileShell +import org.kde.plasma.private.mobileshell.state as MobileShellState + +import org.kde.plasma.private.volume + +import org.kde.layershell 1.0 as LayerShell + + +Window { + id: window + + width: osd.width + 6 + height: cards.implicitHeight + 6 + + visible: false + + readonly property real offsetMargins: Math.max(cards.offset, 0) + + LayerShell.Window.scope: "overlay" + LayerShell.Window.anchors: LayerShell.Window.AnchorTop + LayerShell.Window.layer: LayerShell.Window.LayerOverlay + LayerShell.Window.exclusionZone: -1 + LayerShell.Window.margins.top: offsetMargins + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + color: "transparent" + + function showOverlay() { + if (cards.state == "closed") { + hideTimer.stop(); + window.open(); + } else if (!volumeSlider.isPressed) { + hideTimer.restart(); + } + } + + function open() { + cards.state = "open"; + // set window input transparency to accept touches + ShellUtil.setInputTransparent(window, false); + window.visible = true; + } + + function close() { + cards.state = "closed"; + // set window input transparency to allow touches to pass through while the closing animation is playing + ShellUtil.setInputTransparent(window, true); + } + + Timer { + id: hideTimer + interval: 2000 + running: false + onTriggered: { + window.close(); + } + } + + Component.onCompleted: { + window.close(); + visible = false; + } + + ColumnLayout { + id: cards + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + spacing: 0 + + readonly property real closedOffset: -(cards.implicitHeight + Kirigami.Units.smallSpacing) + readonly property real openOffset: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3 + property real offset: closedOffset + property real scale: 0.95 + + state: "closed" + + states: [ + State { + name: "open" + PropertyChanges { + target: cards; offset: openOffset + } + PropertyChanges { + target: cards; scale: 1.0 + } + }, + State { + name: "closed" + PropertyChanges { + target: cards; offset: closedOffset + } + PropertyChanges { + target: cards; scale: 0.95 + } + } + ] + + transitions: Transition { + SequentialAnimation { + ParallelAnimation { + PropertyAnimation { + properties: "offset"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + PropertyAnimation { + properties: "scale"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + } + ScriptAction { + script: { + if (cards.state == "open") { + hideTimer.restart(); + } else { + hideTimer.stop(); + window.visible = false; + } + } + } + } + } + + PopupCard { + id: osd + Layout.alignment: Qt.AlignHCenter + implicitWidth: Math.min(Kirigami.Units.gridUnit * 15, Screen.width - Kirigami.Units.gridUnit * 2) + + transform: [ + Translate { + y: cards.offset - window.offsetMargins + 1 + }, + Scale { + origin.x: Math.round(width / 2) + origin.y: Math.round(height / 2) + xScale: cards.scale + yScale: cards.scale + } + ] + + contentItem: RowLayout { + id: containerLayout + spacing: Kirigami.Units.smallSpacing + + anchors.leftMargin: Kirigami.Units.smallSpacing * 2 + anchors.rightMargin: Kirigami.Units.smallSpacing + + property int volumePercent: PreferredDevice.sink.volume / PulseAudio.NormalVolume * 100.0 + + PlasmaComponents.ToolButton { + icon.name: !PreferredDevice.sink || PreferredDevice.sink.muted ? "audio-volume-muted" : MobileShell.AudioInfo.icon + text: !PreferredDevice.sink || PreferredDevice.sink.muted ? i18n("Unmute") : i18n("Mute") + display: Controls.AbstractButton.IconOnly + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: Kirigami.Units.iconSizes.medium + Layout.preferredHeight: Kirigami.Units.iconSizes.medium + Layout.rightMargin: Kirigami.Units.smallSpacing + onClicked: { + hideTimer.restart(); + PreferredDevice.sink.muted = !PreferredDevice.sink.muted; + } + } + + PlasmaComponents.Slider { + id: volumeSlider + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: Kirigami.Units.smallSpacing * 2 + + property real volume: PreferredDevice.sink.volume + property bool muted: PreferredDevice.sink.muted + property bool ignoreValueChange: false + property bool isPressed: false + + from: PulseAudio.MinimalVolume + to: PulseAudio.NormalVolume + stepSize: to / (to / PulseAudio.NormalVolume * 100.0) + opacity: muted ? 0.5 : 1.0 + + Component.onCompleted: { + ignoreValueChange = false; + } + + onVolumeChanged: { + if (!window.visible) { + return; + } + var oldIgnoreValueChange = ignoreValueChange; + ignoreValueChange = true; + value = muted ? 0 : PreferredDevice.sink.volume; + ignoreValueChange = oldIgnoreValueChange; + if (volumeSlider.isPressed) { + return; + } + window.open(); + hideTimer.restart(); + } + + onMutedChanged: { + var oldIgnoreValueChange = ignoreValueChange; + ignoreValueChange = true; + value = muted ? 0 : PreferredDevice.sink.volume; + ignoreValueChange = oldIgnoreValueChange; + if (!window.visible || volumeSlider.isPressed) { + return; + } + window.open(); + hideTimer.restart(); + } + + onValueChanged: { + if (!ignoreValueChange) { + PreferredDevice.sink.muted = false; + PreferredDevice.sink.volume = value; + if (!volumeSlider.isPressed) { + updateTimer.restart(); + } + } + } + + onPressedChanged: { + volumeSlider.isPressed = pressed; + if (pressed) { + window.open(); + hideTimer.stop(); + } else { + // Make sure to sync the volume once the button was + // released. + // Otherwise it might be that the slider is at v10 + // whereas PA rejected the volume change and is + // still at v15 (e.g.). + hideTimer.restart(); + updateTimer.restart(); + } + } + + Timer { + id: updateTimer + interval: 200 + onTriggered: volumeSlider.value = PreferredDevice.sink.volume + } + } + + PlasmaComponents.ToolButton { + icon.name: window.showFullApplet ? "arrow-up" : "arrow-down" + text: i18n("configure audio streams") + display: Controls.AbstractButton.IconOnly + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: Kirigami.Units.iconSizes.medium + Layout.preferredHeight: Kirigami.Units.iconSizes.medium + onClicked: MobileShellState.ShellDBusClient.showVolumeOSD() + } + } + } + } +} diff --git a/components/mobileshell/qml/volumeosd/VolumeOSD.qml b/components/mobileshell/qml/volumeosd/VolumeOSD.qml index b2f3dccf..8f97d103 100644 --- a/components/mobileshell/qml/volumeosd/VolumeOSD.qml +++ b/components/mobileshell/qml/volumeosd/VolumeOSD.qml @@ -13,63 +13,130 @@ import QtQuick.Window import org.kde.kirigami 2.20 as Kirigami import org.kde.plasma.components 3.0 as PlasmaComponents -import org.kde.plasma.private.nanoshell 2.0 as NanoShell import org.kde.plasma.private.mobileshell as MobileShell import org.kde.plasma.private.mobileshell.state as MobileShellState import org.kde.plasma.private.volume -NanoShell.FullScreenOverlay { +import org.kde.layershell 1.0 as LayerShell + + +Window { id: window - // used by context menus opened in the applet to not autoclose the osd - property bool suppressActiveClose: false - - // whether the applet is showing all devices - property bool showFullApplet: false + width: Screen.width + height: Screen.height visible: false - color: showFullApplet ? Qt.rgba(0, 0, 0, 0.6) : "transparent" - Behavior on color { - ColorAnimation {} - } + LayerShell.Window.scope: "overlay" + LayerShell.Window.anchors: LayerShell.Window.AnchorTop + LayerShell.Window.layer: LayerShell.Window.LayerTop + LayerShell.Window.exclusionZone: -1 + + readonly property color backgroundColor: Qt.darker(Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95), 1.05) + + Kirigami.Theme.colorSet: Kirigami.Theme.View + Kirigami.Theme.inherit: false + + color: backgroundColor function showOverlay() { if (!window.visible) { - window.showFullApplet = false; - window.showFullScreen(); - hideTimer.restart(); - } else if (!window.showFullApplet) { // don't autohide applet when the full applet is showing - hideTimer.restart(); + window.open(); } } - onActiveChanged: { - if (!active && !suppressActiveClose) { - hideTimer.stop(); - hideTimer.triggered(); - } + function open() { + flickable.state = "open"; + // set window input transparency to accept touches + ShellUtil.setInputTransparent(window, false); + window.visible = true; } - Timer { - id: hideTimer - interval: 3000 - running: false - onTriggered: { - window.close(); - window.showFullApplet = false; - } + function close() { + flickable.state = "closed"; + // set window input transparency to allow touches to pass through while the closing animation is playing + ShellUtil.setInputTransparent(window, true); + } + + Component.onCompleted: { + window.close(); + visible = false; } Flickable { id: flickable anchors.fill: parent contentHeight: cards.implicitHeight - boundsBehavior: window.showFullApplet ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds + boundsBehavior: Flickable.DragAndOvershootBounds pressDelay: 50 + property real offset: -Kirigami.Units.gridUnit + property real scale: 0.95 + + state: "closed" + + states: [ + State { + name: "open" + PropertyChanges { + target: flickable; offset: 0 + } + PropertyChanges { + target: flickable; scale: 1.0 + } + PropertyChanges { + target: flickable; opacity: 1.0 + } + PropertyChanges { + target: window; color: backgroundColor + } + }, + State { + name: "closed" + PropertyChanges { + target: flickable; offset: -Kirigami.Units.gridUnit * 3 + } + PropertyChanges { + target: flickable; scale: 0.95 + } + PropertyChanges { + target: flickable; opacity: 0.0 + } + PropertyChanges { + target: window; color: "transparent" + } + } + ] + + transitions: Transition { + SequentialAnimation { + ParallelAnimation { + PropertyAnimation { + properties: "offset"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + PropertyAnimation { + properties: "scale"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + PropertyAnimation { + properties: "opacity"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + PropertyAnimation { + properties: "color"; easing.type: Easing.OutExpo; duration: Kirigami.Units.veryLongDuration + } + } + ScriptAction { + script: { + if (flickable.state == "closed") { + window.visible = false; + } + } + } + } + } + MouseArea { // capture taps behind cards to close anchors.left: parent.left @@ -77,8 +144,7 @@ NanoShell.FullScreenOverlay { width: parent.width height: Math.max(cards.implicitHeight, window.height) onReleased: { - hideTimer.stop(); - hideTimer.triggered(); + window.close(); } ColumnLayout { @@ -88,126 +154,63 @@ NanoShell.FullScreenOverlay { anchors.right: parent.right spacing: 0 - // osd card - PopupCard { - id: osd - Layout.topMargin: Kirigami.Units.gridUnit - Layout.alignment: Qt.AlignHCenter - - contentItem: RowLayout { - id: containerLayout - spacing: Kirigami.Units.smallSpacing - - anchors.leftMargin: Kirigami.Units.smallSpacing * 2 - anchors.rightMargin: Kirigami.Units.smallSpacing - - PlasmaComponents.ToolButton { - icon.name: !PreferredDevice.sink || PreferredDevice.sink.muted ? "audio-volume-muted" : "audio-volume-high" - text: !PreferredDevice.sink || PreferredDevice.sink.muted ? i18n("Unmute") : i18n("Mute") - display: Controls.AbstractButton.IconOnly - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: Kirigami.Units.iconSizes.medium - Layout.preferredHeight: Kirigami.Units.iconSizes.medium - Layout.rightMargin: Kirigami.Units.smallSpacing - onClicked: muteVolume() - } - - PlasmaComponents.ProgressBar { - id: volumeSlider - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: Kirigami.Units.smallSpacing * 2 - value: MobileShell.AudioInfo.volumeValue - from: 0 - to: MobileShell.AudioInfo.maxVolumePercent - Behavior on value { NumberAnimation { duration: Kirigami.Units.shortDuration } } - } - - // Get the width of a three-digit number so we can size the label - // to the maximum width to avoid the progress bar resizing itself - TextMetrics { - id: widestLabelSize - text: i18n("100%") - font: percentageLabel.font - } - - Kirigami.Heading { - id: percentageLabel - Layout.preferredWidth: widestLabelSize.width - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: Kirigami.Units.smallSpacing - level: 3 - text: i18nc("Percentage value", "%1%", MobileShell.AudioInfo.volumeValue) - - // Display a subtle visual indication that the volume might be - // dangerously high - // ------------------------------------------------ - // Keep this in sync with the copies in plasma-pa:ListItemBase.qml - // and plasma-pa:VolumeSlider.qml - color: { - if (MobileShell.AudioInfo.volumeValue <= 100) { - return Kirigami.Theme.textColor - } else if (MobileShell.AudioInfo.volumeValue > 100 && MobileShell.AudioInfo.volumeValue <= 125) { - return Kirigami.Theme.neutralTextColor - } else { - return Kirigami.Theme.negativeTextColor - } - } - } - - PlasmaComponents.ToolButton { - icon.name: "configure" - text: i18n("Open audio settings") - visible: opacity !== 0 - opacity: showFullApplet ? 1 : 0 - display: Controls.AbstractButton.IconOnly - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: Kirigami.Units.iconSizes.medium - Layout.preferredHeight: Kirigami.Units.iconSizes.medium - Layout.rightMargin: Kirigami.Units.smallSpacing - - Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } } - - onClicked: { - let coords = mapToItem(flickable, 0, 0); - MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_pulseaudio"); - } - } - - PlasmaComponents.ToolButton { - icon.name: window.showFullApplet ? "arrow-up" : "arrow-down" - text: i18n("Toggle showing audio streams") - display: Controls.AbstractButton.IconOnly - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: Kirigami.Units.iconSizes.medium - Layout.preferredHeight: Kirigami.Units.iconSizes.medium - onClicked: { - window.showFullApplet = !window.showFullApplet - // don't autohide applet when full applet is shown - if (window.showFullApplet) { - hideTimer.stop(); - } else { - hideTimer.restart(); - } - } - } - } + transform: Translate { + y: flickable.offset } - // other applet cards AudioApplet { id: applet - Layout.topMargin: Kirigami.Units.gridUnit + Layout.topMargin: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3 Layout.alignment: Qt.AlignHCenter Layout.preferredWidth: cards.width - opacity: window.showFullApplet ? 1 : 0 - visible: opacity !== 0 - transform: Translate { - y: window.showFullApplet ? 0 : -Kirigami.Units.gridUnit - Behavior on y { NumberAnimation {} } + scale: flickable.scale + } + + PopupCard { + id: settings + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: Kirigami.Units.gridUnit + + transform: Scale { + origin.x: Math.round(implicitWidth / 2) + origin.y: Math.round(height / 2) + xScale: flickable.scale + yScale: flickable.scale } - Behavior on opacity { NumberAnimation {} } + contentItem: RowLayout { + + PlasmaComponents.ToolButton { + property int addedPadding: Kirigami.Units.smallSpacing * 2 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.preferredWidth: parent.width - addedPadding * 2 + Layout.preferredHeight: Kirigami.Units.iconSizes.medium + Layout.margins: addedPadding + + contentItem: Item { + anchors.fill: parent + RowLayout { + spacing: Kirigami.Units.largeSpacing + anchors.centerIn: parent + Kirigami.Icon { + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium + Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium + source: "settings-configure" + } + PlasmaComponents.Label { + text: i18n("Open audio settings") + anchors.verticalCenter: parent.verticalCenter + } + } + } + + onClicked: { + MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_pulseaudio"); + window.close(); + } + } + } } } } diff --git a/components/mobileshell/qml/volumeosd/VolumeOSDProvider.qml b/components/mobileshell/qml/volumeosd/VolumeOSDProvider.qml index 8aa2ea30..1f77d3e0 100644 --- a/components/mobileshell/qml/volumeosd/VolumeOSDProvider.qml +++ b/components/mobileshell/qml/volumeosd/VolumeOSDProvider.qml @@ -20,7 +20,9 @@ QtObject { id: component function showVolumeOverlay() { - osd.showOverlay(); + if (!osd.visible) { + vcp.showOverlay(); + } } Component.onCompleted: { @@ -31,9 +33,11 @@ QtObject { target: MobileShellState.ShellDBusClient function onShowVolumeOSDRequested() { - component.showVolumeOverlay(); + osd.showOverlay(); + vcp.close(); } } property var osd: VolumeOSD {} + property var vcp: VolumeChangedPopup {} } diff --git a/components/mobileshell/shellutil.cpp b/components/mobileshell/shellutil.cpp index 13447b46..740d65f5 100644 --- a/components/mobileshell/shellutil.cpp +++ b/components/mobileshell/shellutil.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include #include #include @@ -87,3 +89,16 @@ void ShellUtil::launchApp(const QString &storageId) job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled)); job->start(); } + +void ShellUtil::setInputTransparent(QQuickWindow *window, bool transparent) { + if (window) { + Qt::WindowFlags flags = window->flags(); + if (transparent) { + flags |= Qt::WindowTransparentForInput; + } else { + flags &= ~Qt::WindowTransparentForInput; + } + window->setFlags(flags); + } +} + diff --git a/components/mobileshell/shellutil.h b/components/mobileshell/shellutil.h index a5d746e3..00c9766f 100644 --- a/components/mobileshell/shellutil.h +++ b/components/mobileshell/shellutil.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -65,6 +66,11 @@ public: */ Q_INVOKABLE bool isSystem24HourFormat(); + /** + * Set window input to be transparent. + */ + Q_INVOKABLE void setInputTransparent(QQuickWindow *window, bool transparent); + Q_SIGNALS: void isSystem24HourFormatChanged();