summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Uwe Broulik <[email protected]>2017-01-09 08:19:24 +0100
committerKai Uwe Broulik <[email protected]>2017-01-09 08:19:24 +0100
commit89e61df4e9364ab253875b8ebac50dfc9e8c87cb (patch)
tree3770626278909d257cd9b958bfe609bc69369f8b
parent3025aa6eb07ec22cfd362df2c47aba89464514b8 (diff)
[Task Manager] Indicate applications playing audio
This is similar to what most web browsers do nowadays, indicating when audio is being played with an option to mute it. When an application has an audio stream and it is actually playing something ("not corked"), after a delay of 2 seconds (to avoid flashing briefly) an audio icon is displayed which disappears again once audio stops playing. The context menu always offers the "Mute" option whenever there's an audio stream provided by the application, so you can still unmute even after playback has stopped. Also, the "muted" icon will be shown whilst the application is muted, regardless of whether sound is actually being played. An application is marked as "Muted" when all associated streams are muted, and toggling mute will (un)mute all of them. Since PulseAudio doesn't know windows, when an application has multiple windows, all of them will be flagged as playing audio; the same issue we already have with Unity Launcher API. Differential Revision: https://phabricator.kde.org/D3302
-rw-r--r--applets/taskmanager/package/contents/config/main.xml4
-rw-r--r--applets/taskmanager/package/contents/ui/ConfigGeneral.qml7
-rw-r--r--applets/taskmanager/package/contents/ui/ContextMenu.qml20
-rw-r--r--applets/taskmanager/package/contents/ui/Task.qml77
-rw-r--r--applets/taskmanager/package/contents/ui/ToolTipInstance.qml8
-rw-r--r--applets/taskmanager/package/contents/ui/main.qml6
6 files changed, 117 insertions, 5 deletions
diff --git a/applets/taskmanager/package/contents/config/main.xml b/applets/taskmanager/package/contents/config/main.xml
index 5fece6d..0e104bd 100644
--- a/applets/taskmanager/package/contents/config/main.xml
+++ b/applets/taskmanager/package/contents/config/main.xml
@@ -89,6 +89,10 @@
<label>Whether to show progress and status information on task buttons.</label>
<default>true</default>
</entry>
+ <entry name="indicateAudioStreams" type="Bool">
+ <label>Whether to indicate applications that are playing audio including an option to mute them.</label>
+ <default>true</default>
+ </entry>
</group>
</kcfg>
diff --git a/applets/taskmanager/package/contents/ui/ConfigGeneral.qml b/applets/taskmanager/package/contents/ui/ConfigGeneral.qml
index de12afc..efa960a 100644
--- a/applets/taskmanager/package/contents/ui/ConfigGeneral.qml
+++ b/applets/taskmanager/package/contents/ui/ConfigGeneral.qml
@@ -34,6 +34,7 @@ Item {
property alias cfg_wheelEnabled: wheelEnabled.checked
property alias cfg_highlightWindows: highlightWindows.checked
property alias cfg_smartLaunchersEnabled: smartLaunchers.checked
+ property alias cfg_indicateAudioStreams: indicateAudioStreams.checked
property alias cfg_maxStripes: maxStripes.value
property alias cfg_groupingStrategy: groupingStrategy.currentIndex
property alias cfg_middleClickAction: middleClickAction.currentIndex
@@ -107,6 +108,12 @@ Item {
text: i18n("Show progress and status information in task buttons")
}
+ CheckBox {
+ id: indicateAudioStreams
+ Layout.fillWidth: true
+ text: i18n("Denote applications that play audio")
+ }
+
RowLayout {
Label {
text: i18n("On middle-click:")
diff --git a/applets/taskmanager/package/contents/ui/ContextMenu.qml b/applets/taskmanager/package/contents/ui/ContextMenu.qml
index b86ced9..a09a429 100644
--- a/applets/taskmanager/package/contents/ui/ContextMenu.qml
+++ b/applets/taskmanager/package/contents/ui/ContextMenu.qml
@@ -188,6 +188,26 @@ PlasmaComponents.ContextMenu {
}
}
}
+
+ // We allow mute/unmute whenever an application has a stream, regardless of whether it
+ // is actually playing sound.
+ // This way you can unmute, e.g. a telephony app, even after the conversation has ended,
+ // so you still have it ringing later on.
+ if (menu.visualParent.hasAudioStream) {
+ var muteItem = menu.newMenuItem(menu);
+ muteItem.checkable = true;
+ muteItem.checked = Qt.binding(function() {
+ return menu.visualParent && menu.visualParent.muted;
+ });
+ muteItem.clicked.connect(function() {
+ menu.visualParent.toggleMuted();
+ });
+ muteItem.text = i18n("Mute");
+ muteItem.icon = "audio-volume-muted";
+ menu.addMenuItem(muteItem, virtualDesktopsMenuItem);
+
+ menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem);
+ }
}
PlasmaComponents.MenuItem {
diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml
index 03fd3e0..4975f9a 100644
--- a/applets/taskmanager/package/contents/ui/Task.qml
+++ b/applets/taskmanager/package/contents/ui/Task.qml
@@ -41,6 +41,7 @@ MouseArea {
readonly property var m: model
+ readonly property int pid: model.AppPid
property int itemIndex: index
property bool inPopup: false
property bool isWindow: model.IsWindow === true
@@ -55,6 +56,16 @@ MouseArea {
readonly property bool smartLauncherEnabled: plasmoid.configuration.smartLaunchersEnabled && !inPopup && model.IsStartup !== true
property QtObject smartLauncherItem: null
+ property Item audioStreamOverlay
+ property var audioStreams: []
+ readonly property bool hasAudioStream: plasmoid.configuration.indicateAudioStreams && audioStreams.length > 0
+ readonly property bool playingAudio: hasAudioStream && audioStreams.some(function (item) {
+ return !item.corked
+ })
+ readonly property bool muted: hasAudioStream && audioStreams.every(function (item) {
+ return item.muted
+ })
+
readonly property bool highlighted: (inPopup && activeFocus) || (!inPopup && containsMouse)
function hideToolTipTemporarily() {
@@ -63,6 +74,8 @@ MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MidButton
+ onPidChanged: updateAudioStreams()
+
onIsWindowChanged: {
if (isWindow) {
taskInitComponent.createObject(task);
@@ -167,6 +180,12 @@ MouseArea {
}
}
+ onHasAudioStreamChanged: {
+ if (hasAudioStream) {
+ audioStreamIconLoader.active = true
+ }
+ }
+
Keys.onReturnPressed: TaskTools.activateTask(modelIndex(), model, event.modifiers, task)
Keys.onEnterPressed: Keys.onReturnPressed(event);
@@ -175,6 +194,35 @@ MouseArea {
: tasksModel.makeModelIndex(index));
}
+ function updateAudioStreams() {
+ if (!pid) {
+ task.audioStreams = [];
+ return;
+ }
+
+ var pa = pulseAudio.item;
+ if (!pa) {
+ task.audioStreams = [];
+ return;
+ }
+
+ task.audioStreams = pa.streamsForPid(pid);
+ }
+
+ function toggleMuted() {
+ if (muted) {
+ task.audioStreams.forEach(function (item) { item.unmute(); });
+ } else {
+ task.audioStreams.forEach(function (item) { item.mute(); });
+ }
+ }
+
+ Connections {
+ target: pulseAudio.item
+ ignoreUnknownSignals: true // Plasma-PA might not be available
+ onStreamsChanged: task.updateAudioStreams()
+ }
+
Component {
id: taskInitComponent
@@ -300,9 +348,7 @@ MouseArea {
topMargin: adjustMargin(false, parent.height, taskFrame.margins.top)
}
- width: (label.visible ? height
- : parent.width - adjustMargin(true, parent.width, taskFrame.margins.left)
- - adjustMargin(true, parent.width, taskFrame.margins.right))
+ width: height
height: (parent.height - adjustMargin(false, parent.height, taskFrame.margins.top)
- adjustMargin(false, parent.height, taskFrame.margins.bottom))
@@ -351,7 +397,7 @@ MouseArea {
// the text label margin, which derives from the icon width.
State {
name: "standalone"
- when: !label.visible
+ when: !label.visible && !audioStreamIconLoader.shown
AnchorChanges {
target: iconBox
@@ -362,6 +408,8 @@ MouseArea {
PropertyChanges {
target: iconBox
anchors.leftMargin: 0
+ width: parent.width - adjustMargin(true, task.width, taskFrame.margins.left)
+ - adjustMargin(true, task.width, taskFrame.margins.right)
}
}
]
@@ -380,6 +428,23 @@ MouseArea {
}
}
+ Loader {
+ id: audioStreamIconLoader
+
+ readonly property bool shown: item && item.visible
+
+ source: "AudioStream.qml"
+ width: Math.min(units.iconSizes.medium, iconBox.width)
+ height: Math.min(units.iconSizes.medium, iconBox.height)
+
+ anchors {
+ right: parent.right
+ rightMargin: iconBox.adjustMargin(true, parent.width, taskFrame.margins.right)
+ top: parent.top
+ topMargin: iconBox.adjustMargin(false, parent.height, taskFrame.margins.top)
+ }
+ }
+
PlasmaComponents.Label {
id: label
@@ -390,7 +455,7 @@ MouseArea {
fill: parent
leftMargin: taskFrame.margins.left + iconBox.width + units.smallSpacing
topMargin: taskFrame.margins.top
- rightMargin: taskFrame.margins.right
+ rightMargin: taskFrame.margins.right + (audioStreamIconLoader.shown ? (audioStreamIconLoader.width + units.smallSpacing) : 0)
bottomMargin: taskFrame.margins.bottom
}
@@ -458,5 +523,7 @@ MouseArea {
if (!inPopup && model.IsWindow !== true) {
taskInitComponent.createObject(task);
}
+
+ updateAudioStreams()
}
}
diff --git a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
index 0496e0f..d6f3f20 100644
--- a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
+++ b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
@@ -460,6 +460,14 @@ Column {
}
}
+ if (parentTask.playingAudio) {
+ if (parentTask.muted) {
+ subTextEntries.push(i18nc("The application this window belongs to is muted", "Currently Muted"));
+ } else {
+ subTextEntries.push(i18nc("The application this window belongs to is playing audio", "Currently Playing Audio"));
+ }
+ }
+
return subTextEntries.join("\n");
}
}
diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml
index 85b3563..0df4f38 100644
--- a/applets/taskmanager/package/contents/ui/main.qml
+++ b/applets/taskmanager/package/contents/ui/main.qml
@@ -233,6 +233,12 @@ Item {
}
}
+ Loader {
+ id: pulseAudio
+ source: "PulseAudio.qml"
+ active: plasmoid.configuration.indicateAudioStreams
+ }
+
Timer {
id: iconGeometryTimer