quickshell-toki-night/modules/dashboard/Mixer.qml

201 lines
6.4 KiB
QML

pragma ComponentBehavior: Bound
import qs.config
import qs.custom
import qs.services
import qs.util
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Pipewire
Item {
id: root
required property PersistentProperties uiState
required property int index
readonly property list<PwNode> nodes: Pipewire.nodes.values.filter(node => node.isSink && node.isStream)
width: Config.dashboard.mixerWidth
height: Config.dashboard.mixerHeight
PwObjectTracker {
objects: root.nodes
}
ColumnLayout {
id: layout
anchors.fill: parent
spacing: 7
CustomText {
Layout.topMargin: 6
text: qsTr("Master Volume")
color: Config.colors.secondary
}
CustomMouseArea {
Layout.fillWidth: true
implicitHeight: Config.osd.sliderWidth
function onWheel(event: WheelEvent) {
if (event.angleDelta.y > 0)
Audio.increaseVolume();
else if (event.angleDelta.y < 0)
Audio.decreaseVolume();
}
acceptedButtons: Qt.RightButton
onClicked: Audio.sink.audio.muted = !Audio.muted
CustomFilledSlider {
anchors.fill: parent
orientation: Qt.Horizontal
color: Audio.muted ? Config.colors.error : Config.colors.volume
icon: Icons.getVolumeIcon(value, Audio.muted)
value: Audio.volume
onMoved: Audio.setVolume(value)
Behavior on color {
CAnim {
duration: Config.anim.durations.small
}
}
}
}
CustomRect {
Layout.fillWidth: true
Layout.topMargin: 5
Layout.bottomMargin: 5
height: 1
color: Config.colors.inactive
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
CustomListView {
id: list
anchors.fill: parent
spacing: 12
model: ScriptModel {
values: [...root.nodes]
objectProp: "id"
}
CustomScrollBar.vertical: CustomScrollBar {
flickable: list
}
delegate: RowLayout {
id: entry
required property PwNode modelData
spacing: 6
width: root.width
IconImage {
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
visible: source != ""
implicitSize: slider.height * 1.4
source: {
const icon = entry.modelData.properties["application.icon-name"];
if (icon)
return Icons.getAppIcon(icon, "image-missing");
Icons.getAppIcon(entry.modelData.name, "image-missing")
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 6
CustomText {
Layout.fillWidth: true
elide: Text.ElideRight
text: {
// application.name -> description -> name
const app = entry.modelData.properties["application.name"]
?? (entry.modelData.description != "" ? entry.modelData.description : entry.modelData.name);
const media = entry.modelData.properties["media.name"];
return media != undefined ? `${app} 🞄 ${media}` : app;
}
color: Config.colors.secondary
}
CustomMouseArea {
id: slider
Layout.fillWidth: true
implicitHeight: Config.osd.sliderWidth
acceptedButtons: Qt.RightButton
onClicked: entry.modelData.audio.muted = !entry.modelData.audio.muted
CustomFilledSlider {
anchors.fill: parent
orientation: Qt.Horizontal
color: entry.modelData.audio.muted ? Config.colors.error : Config.colors.volume
icon: Icons.getVolumeIcon(value, entry.modelData.audio.muted)
value: entry.modelData.audio.volume
onMoved: {
if (entry.modelData.ready)
entry.modelData.audio.volume = Math.max(0, Math.min(1, value));
}
Behavior on color {
CAnim {
duration: Config.anim.durations.small
}
}
}
}
}
}
}
CustomText {
anchors.centerIn: parent
opacity: list.count === 0 ? 1 : 0
visible: opacity > 0
text: qsTr("No audio sources")
color: Config.colors.tertiary
font.pointSize: Config.font.size.normal
Behavior on opacity {
Anim {}
}
}
}
/* RowLayout { */
/* id: deviceSelectorRowLayout */
/* Layout.fillWidth: true */
/* uniformCellSizes: true */
/* AudioDeviceSelectorButton { */
/* Layout.fillWidth: true */
/* input: false */
/* onClicked: root.showDeviceSelectorDialog(input) */
/* } */
/* AudioDeviceSelectorButton { */
/* Layout.fillWidth: true */
/* input: true */
/* onClicked: root.showDeviceSelectorDialog(input) */
/* } */
/* } */
}
}