mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
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.   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
This commit is contained in:
parent
8d1bb7642d
commit
681d1683f5
10 changed files with 534 additions and 150 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ target_link_libraries(mobileshellplugin
|
|||
Plasma::KWaylandClient
|
||||
KF6::Service
|
||||
KF6::Package
|
||||
LayerShellQt::Interface
|
||||
)
|
||||
|
||||
ecm_finalize_qml_module(mobileshellplugin)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
||||
* SPDX-FileCopyrightText: 2024 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
270
components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml
Normal file
270
components/mobileshell/qml/volumeosd/VolumeChangedPopup.qml
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Micah Stanley <stanleymicah@proton.me>
|
||||
*
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#include <KNotification>
|
||||
#include <KNotificationJobUiDelegate>
|
||||
|
||||
#include <QQuickWindow>
|
||||
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
#include <qqmlregistration.h>
|
||||
|
||||
#include <KConfigWatcher>
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue