Files
dms-gamebar/widgets/AppMixer.qml
T

158 lines
5.0 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Services.Pipewire
import qs.Common
import qs.Services
import qs.Widgets
import "../components"
DankFlickable {
id: mixer
readonly property var streams: Pipewire.nodes.values.filter(node => node.audio && node.isSink && node.isStream)
readonly property int streamCount: streams.length
function appName(node) {
if (!node)
return "";
const props = node.properties || {};
return props["application.name"] || AudioService.displayName(node) || "Unknown";
}
function mediaName(node) {
const props = (node && node.properties) ? node.properties : {};
return props["media.name"] || "";
}
function appIconValue(node) {
const props = (node && node.properties) ? node.properties : {};
const candidates = [props["application.icon-name"], props["application.process.binary"], props["application.name"]];
for (const candidate of candidates) {
if (!candidate)
continue;
const entry = DesktopEntries.heuristicLookup(String(candidate));
if (entry && entry.icon)
return entry.icon;
}
return "material:graphic_eq";
}
clip: true
contentHeight: streamColumn.height
contentWidth: width
Column {
id: streamColumn
width: mixer.width
spacing: Theme.spacingS
Repeater {
model: ScriptModel {
values: mixer.streams
}
delegate: Rectangle {
id: streamItem
required property var modelData
width: streamColumn.width
height: 86
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceLight, Theme.popupTransparency)
border.color: Theme.outlineLight
border.width: Theme.layerOutlineWidth
AppIconRenderer {
id: streamIcon
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.top: parent.top
anchors.topMargin: Theme.spacingM
width: Theme.iconSize
height: Theme.iconSize
iconValue: mixer.appIconValue(streamItem.modelData)
iconSize: Theme.iconSize
fallbackText: mixer.appName(streamItem.modelData).charAt(0).toUpperCase()
}
Column {
anchors.left: streamIcon.right
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.top: parent.top
anchors.topMargin: Theme.spacingM
spacing: 1
StyledText {
width: parent.width
text: mixer.appName(streamItem.modelData)
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
elide: Text.ElideRight
}
StyledText {
width: parent.width
text: mixer.mediaName(streamItem.modelData)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
visible: text !== ""
}
}
VolumeRow {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingM
anchors.bottomMargin: Theme.spacingXS
node: streamItem.modelData
maxPercent: 100
activeIcon: "volume_up"
mutedIcon: "volume_off"
}
PwObjectTracker {
objects: [streamItem.modelData]
}
}
}
Item {
width: streamColumn.width
height: 120
visible: mixer.streamCount === 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
name: "music_off"
size: Theme.iconSizeLarge
color: Theme.surfaceVariantText
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
text: "No apps are playing audio"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}
}