update quickshell conf
This commit is contained in:
@ -1,4 +1,3 @@
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
@ -9,13 +8,12 @@ Item {
|
||||
implicitWidth: chip.implicitWidth + 10
|
||||
implicitHeight: 24
|
||||
|
||||
// Track all PipeWire nodes so their properties are populated.
|
||||
// Without this, nodes in Pipewire.nodes are "unbound" and have null audio/type.
|
||||
signal clickedLeft
|
||||
|
||||
PwObjectTracker {
|
||||
objects: Pipewire.nodes.values
|
||||
}
|
||||
|
||||
// ── helpers ────────────────────────────────────────────────────────
|
||||
function safeVolume(node) {
|
||||
if (!node || !node.ready || !node.audio) return 0
|
||||
const v = node.audio.volume
|
||||
@ -26,13 +24,12 @@ Item {
|
||||
node.audio.volume = Math.max(0, Math.min(1, v))
|
||||
}
|
||||
function volIcon(vol, muted) {
|
||||
if (muted) return "\uf6a9" // fa-volume-mute
|
||||
if (vol > 0.6) return "\uf028" // fa-volume-up
|
||||
if (vol > 0.2) return "\uf027" // fa-volume-down
|
||||
return "\uf026" // fa-volume-off
|
||||
if (muted) return "\uf6a9"
|
||||
if (vol > 0.6) return "\uf028"
|
||||
if (vol > 0.2) return "\uf027"
|
||||
return "\uf026"
|
||||
}
|
||||
|
||||
// ── bar chip (default sink) ─────────────────────────────────────────
|
||||
readonly property var defaultSink: Pipewire.defaultAudioSink
|
||||
readonly property real defaultVolume: safeVolume(defaultSink)
|
||||
readonly property bool defaultMuted: defaultSink?.audio?.muted ?? false
|
||||
@ -66,7 +63,7 @@ Item {
|
||||
if (root.defaultSink?.ready && root.defaultSink?.audio)
|
||||
root.defaultSink.audio.muted = !root.defaultSink.audio.muted
|
||||
} else {
|
||||
mixerPopup.visible = !mixerPopup.visible
|
||||
root.clickedLeft()
|
||||
}
|
||||
}
|
||||
onWheel: wheel => {
|
||||
@ -74,149 +71,4 @@ Item {
|
||||
root.setVolume(root.defaultSink, root.defaultVolume + wheel.angleDelta.y / 120 * 0.05)
|
||||
}
|
||||
}
|
||||
|
||||
// ── mixer popup ─────────────────────────────────────────────────────
|
||||
PopupWindow {
|
||||
id: mixerPopup
|
||||
visible: false
|
||||
anchor.item: root
|
||||
anchor.edges: Edges.Bottom
|
||||
anchor.gravity: Edges.Bottom
|
||||
|
||||
implicitWidth: mixerBg.implicitWidth
|
||||
implicitHeight: mixerBg.implicitHeight
|
||||
|
||||
Rectangle {
|
||||
id: mixerBg
|
||||
anchors.fill: parent
|
||||
color: Theme.bg
|
||||
border.color: Theme.border
|
||||
border.width: Theme.borderWidth
|
||||
radius: Theme.radius
|
||||
implicitWidth: 300
|
||||
implicitHeight: mixerCol.implicitHeight + 24
|
||||
|
||||
ColumnLayout {
|
||||
id: mixerCol
|
||||
anchors { fill: parent; margins: 12 }
|
||||
spacing: 4
|
||||
|
||||
// ── Output devices ──────────────────────────────────────
|
||||
Text {
|
||||
text: "Output Devices"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes
|
||||
delegate: NodeRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
node: modelData
|
||||
visible: modelData.isSink && !modelData.isStream && modelData.ready
|
||||
}
|
||||
}
|
||||
|
||||
// ── Application streams ────────────────────────────────
|
||||
Text {
|
||||
text: "Applications"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes
|
||||
delegate: NodeRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
node: modelData
|
||||
// exclude monitor sources; include only audio output streams
|
||||
visible: modelData.isStream && !modelData.isSink && modelData.ready
|
||||
&& !(modelData.description ?? "").toLowerCase().includes("monitor")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── per-node row component ──────────────────────────────────────────
|
||||
component NodeRow: RowLayout {
|
||||
id: row
|
||||
required property var node
|
||||
spacing: 8
|
||||
implicitHeight: 28
|
||||
|
||||
// mute toggle
|
||||
Text {
|
||||
id: nodeIcon
|
||||
text: root.volIcon(root.safeVolume(row.node), row.node.audio?.muted ?? false)
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 13
|
||||
color: (row.node.audio?.muted ?? false) ? Theme.textDim : Theme.text
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (row.node.ready && row.node.audio)
|
||||
row.node.audio.muted = !row.node.audio.muted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name
|
||||
Text {
|
||||
text: row.node.description || row.node.nickname || row.node.name || "?"
|
||||
font.pixelSize: 11
|
||||
color: Theme.textDim
|
||||
Layout.preferredWidth: 90
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// horizontal slider
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 16
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width
|
||||
height: 3
|
||||
radius: 2
|
||||
color: Theme.progressTrack
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * Math.min(1, root.safeVolume(row.node))
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: Theme.accent
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors { fill: parent; topMargin: -4; bottomMargin: -4 }
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
function seek(mx) {
|
||||
root.setVolume(row.node, mx / width)
|
||||
}
|
||||
onClicked: seek(mouseX)
|
||||
onPositionChanged: if (pressed) seek(mouseX)
|
||||
}
|
||||
}
|
||||
|
||||
// percentage
|
||||
Text {
|
||||
text: Math.round(root.safeVolume(row.node) * 100) + "%"
|
||||
font.pixelSize: 10
|
||||
color: Theme.textDim
|
||||
Layout.preferredWidth: 30
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user