276 lines
8.4 KiB
QML
276 lines
8.4 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Services.Pipewire
|
|
import "root:/config"
|
|
import "root:/components"
|
|
import "root:/services"
|
|
|
|
// Output and input levels, the sink picker, and a per-app mixer.
|
|
Popout {
|
|
id: root
|
|
|
|
contentWidth: 360
|
|
contentHeight: surface.implicitContentHeight + Theme.padM * 2
|
|
|
|
PopoutSurface {
|
|
id: surface
|
|
anchors.fill: parent
|
|
tone: Theme.primary
|
|
|
|
readonly property real implicitContentHeight: header.height + column.implicitHeight + Theme.padM * 2
|
|
|
|
PopoutHeader {
|
|
id: header
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
title: "Audio"
|
|
icon: Audio.icon
|
|
accent: Theme.primary
|
|
subtitle: Audio.sinkName
|
|
}
|
|
|
|
Column {
|
|
id: column
|
|
anchors.top: header.bottom
|
|
anchors.topMargin: Theme.padM
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
spacing: Theme.padM
|
|
|
|
// ------------------------------------------------------- output
|
|
LevelRow {
|
|
width: parent.width
|
|
icon: Audio.icon
|
|
label: "Output"
|
|
readout: Audio.muted ? "MUTED" : Audio.volumePercent + "%"
|
|
value: Audio.volume
|
|
muted: Audio.muted
|
|
accent: Theme.primary
|
|
onMoved: v => Audio.setVolume(v)
|
|
onToggled: Audio.toggleMute()
|
|
}
|
|
|
|
// -------------------------------------------------------- input
|
|
LevelRow {
|
|
width: parent.width
|
|
icon: Audio.micIcon
|
|
label: "Input"
|
|
readout: Audio.micMuted ? "MUTED" : Audio.micPercent + "%"
|
|
value: Audio.micVolume
|
|
muted: Audio.micMuted
|
|
accent: Theme.blue
|
|
onMoved: v => Audio.setMicVolume(v)
|
|
onToggled: Audio.toggleMicMute()
|
|
}
|
|
|
|
SectionRule {
|
|
width: parent.width
|
|
text: "Outputs"
|
|
visible: Audio.sinks.length > 1
|
|
}
|
|
|
|
Repeater {
|
|
model: Audio.sinks.length > 1 ? Audio.sinks : []
|
|
|
|
PickRow {
|
|
required property var modelData
|
|
width: column.width
|
|
label: modelData.nickname || modelData.description || modelData.name
|
|
icon: ""
|
|
selected: Audio.sink === modelData
|
|
onClicked: Audio.setSink(modelData)
|
|
}
|
|
}
|
|
|
|
SectionRule {
|
|
width: parent.width
|
|
text: "Applications"
|
|
visible: Audio.streams.length > 0
|
|
}
|
|
|
|
Repeater {
|
|
model: Audio.streams
|
|
|
|
LevelRow {
|
|
required property var modelData
|
|
width: column.width
|
|
icon: ""
|
|
label: Audio.streamName(modelData)
|
|
readout: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : ""
|
|
value: modelData.audio?.volume ?? 0
|
|
muted: modelData.audio?.muted ?? false
|
|
accent: Theme.glow
|
|
onMoved: v => { if (modelData.audio) modelData.audio.volume = v; }
|
|
onToggled: { if (modelData.audio) modelData.audio.muted = !modelData.audio.muted; }
|
|
}
|
|
}
|
|
|
|
P5Button {
|
|
text: "Mixer"
|
|
icon: ""
|
|
onClicked: Actions.openAudioSettings()
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------- pieces
|
|
component LevelRow: Item {
|
|
id: levelRow
|
|
|
|
property string icon: ""
|
|
property string label: ""
|
|
property string readout: ""
|
|
property real value: 0
|
|
property bool muted: false
|
|
property color accent: Theme.primary
|
|
|
|
signal moved(real value)
|
|
signal toggled()
|
|
|
|
implicitHeight: 42
|
|
|
|
Row {
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
spacing: Theme.padS
|
|
|
|
Icon {
|
|
id: levelIcon
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: levelRow.icon
|
|
color: levelRow.muted ? Theme.accent : levelRow.accent
|
|
font.pixelSize: Theme.fsLarge
|
|
|
|
TapHandler { onTapped: levelRow.toggled() }
|
|
HoverHandler { cursorShape: Qt.PointingHandCursor }
|
|
}
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: levelRow.width - levelIcon.width - readoutText.width - Theme.padS * 3
|
|
text: levelRow.label
|
|
elide: Text.ElideRight
|
|
color: Theme.text
|
|
font.family: Theme.fontDisplay
|
|
font.pixelSize: Theme.fsSmall
|
|
font.weight: Font.DemiBold
|
|
font.italic: true
|
|
renderType: Text.NativeRendering
|
|
}
|
|
|
|
Text {
|
|
id: readoutText
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: levelRow.readout
|
|
color: levelRow.muted ? Theme.accent : Theme.subtext
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsMicro
|
|
font.weight: Font.Bold
|
|
renderType: Text.NativeRendering
|
|
}
|
|
}
|
|
|
|
P5Slider {
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
value: levelRow.value
|
|
accent: levelRow.accent
|
|
enabledControl: !levelRow.muted
|
|
onMoved: v => levelRow.moved(v)
|
|
}
|
|
}
|
|
|
|
component SectionRule: Item {
|
|
property string text: ""
|
|
implicitHeight: visible ? 18 : 0
|
|
|
|
Text {
|
|
id: sectionLabel
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: parent.text.toUpperCase()
|
|
color: Theme.muted
|
|
font.family: Theme.fontMono
|
|
font.pixelSize: Theme.fsMicro
|
|
font.letterSpacing: 2
|
|
renderType: Text.NativeRendering
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.left: sectionLabel.right
|
|
anchors.leftMargin: Theme.padS
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
height: 1
|
|
color: Theme.alpha(Theme.outline, 0.6)
|
|
}
|
|
}
|
|
|
|
component PickRow: Item {
|
|
id: pickRow
|
|
|
|
property string label: ""
|
|
property string icon: ""
|
|
property bool selected: false
|
|
|
|
signal clicked()
|
|
|
|
implicitHeight: 26
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: pickHover.hovered
|
|
? Theme.alpha(Theme.primary, 0.14)
|
|
: (pickRow.selected ? Theme.alpha(Theme.primary, 0.07) : "transparent")
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 3
|
|
height: pickRow.selected ? 18 : 0
|
|
color: Theme.accent
|
|
|
|
Behavior on height {
|
|
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutBack }
|
|
}
|
|
}
|
|
|
|
Row {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Theme.padM
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Theme.padS
|
|
|
|
Icon {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: pickRow.icon
|
|
color: pickRow.selected ? Theme.primary : Theme.muted
|
|
font.pixelSize: Theme.fsBody
|
|
}
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: pickRow.label
|
|
color: pickRow.selected ? Theme.text : Theme.subtext
|
|
font.family: Theme.fontDisplay
|
|
font.pixelSize: Theme.fsSmall
|
|
font.italic: true
|
|
renderType: Text.NativeRendering
|
|
}
|
|
}
|
|
|
|
HoverHandler {
|
|
id: pickHover
|
|
cursorShape: Qt.PointingHandCursor
|
|
}
|
|
|
|
TapHandler {
|
|
onTapped: pickRow.clicked()
|
|
}
|
|
}
|
|
}
|