Improve Bluetooth panel connected devices
This commit is contained in:
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Ui
|
||||
import qs.Commons
|
||||
import "Model.js" as Model
|
||||
@@ -19,6 +20,9 @@ Panel {
|
||||
|
||||
readonly property var adapter: Bluetooth.defaultAdapter
|
||||
readonly property var devices: Bluetooth.devices ? Bluetooth.devices.values : []
|
||||
readonly property var pipewireNodes: Pipewire.nodes ? Pipewire.nodes.values : []
|
||||
property var pendingAudioOutputDevice: null
|
||||
property int pendingAudioOutputAttempts: 0
|
||||
|
||||
function deviceLabel(device) {
|
||||
return Model.deviceLabel(device)
|
||||
@@ -112,6 +116,64 @@ Panel {
|
||||
return Model.sectionDevices(deviceGroups, section)
|
||||
}
|
||||
|
||||
function audioSinks() {
|
||||
var sinks = []
|
||||
for (var i = 0; i < pipewireNodes.length; i++) {
|
||||
var node = pipewireNodes[i]
|
||||
if (node && node.isSink && !node.isStream) sinks.push(node)
|
||||
}
|
||||
return sinks
|
||||
}
|
||||
|
||||
function bluetoothAudioSink(device) {
|
||||
var sinks = audioSinks()
|
||||
for (var i = 0; i < sinks.length; i++) {
|
||||
if (Model.bluetoothSinkMatchesDevice(sinks[i], device)) return sinks[i]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function setDefaultAudioSink(sink) {
|
||||
if (!sink) return
|
||||
Pipewire.preferredDefaultAudioSink = sink
|
||||
if (root.bar && root.bar.omarchyPath && sink.id !== undefined && sink.name) {
|
||||
Quickshell.execDetached([
|
||||
root.bar.omarchyPath + "/bin/omarchy-audio-output-set-default",
|
||||
String(sink.id),
|
||||
String(sink.name)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleAudioOutputSwitch(device) {
|
||||
pendingAudioOutputDevice = {
|
||||
address: device && device.address ? device.address : "",
|
||||
name: device && device.name ? device.name : "",
|
||||
deviceName: device && device.deviceName ? device.deviceName : ""
|
||||
}
|
||||
pendingAudioOutputAttempts = 0
|
||||
audioSwitchTimer.restart()
|
||||
}
|
||||
|
||||
function switchPendingAudioOutput() {
|
||||
if (!pendingAudioOutputDevice) return
|
||||
|
||||
var sink = bluetoothAudioSink(pendingAudioOutputDevice)
|
||||
if (sink) {
|
||||
setDefaultAudioSink(sink)
|
||||
pendingAudioOutputDevice = null
|
||||
audioSwitchTimer.stop()
|
||||
return
|
||||
}
|
||||
|
||||
pendingAudioOutputAttempts += 1
|
||||
if (pendingAudioOutputAttempts >= 8) {
|
||||
pendingAudioOutputDevice = null
|
||||
return
|
||||
}
|
||||
audioSwitchTimer.restart()
|
||||
}
|
||||
|
||||
function deviceAt(section, index) {
|
||||
var list = devicesForSection(section)
|
||||
return index >= 0 && index < list.length ? list[index] : null
|
||||
@@ -179,9 +241,11 @@ Panel {
|
||||
}
|
||||
}
|
||||
|
||||
if ((action === "connecting" && found && found.connected)
|
||||
var finishedConnecting = action === "connecting" && found && found.connected
|
||||
if (finishedConnecting
|
||||
|| (action === "disconnecting" && found && !found.connected)
|
||||
|| (action === "forgetting" && (!found || (!found.paired && !found.bonded && !found.trusted)))) {
|
||||
if (finishedConnecting) scheduleAudioOutputSwitch(found)
|
||||
delete next[address]
|
||||
changed = true
|
||||
}
|
||||
@@ -360,6 +424,13 @@ Panel {
|
||||
onTriggered: root.pendingActions = ({})
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: audioSwitchTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: root.switchPendingAudioOutput()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: phraseTimer
|
||||
interval: 2800
|
||||
@@ -512,6 +583,39 @@ Panel {
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
Column {
|
||||
id: connectedList
|
||||
visible: root.connectedDevices.length > 0
|
||||
width: parent.width
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "CONNECTED"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.connectedDevices
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: connectedList.width
|
||||
dev: modelData
|
||||
rowIndex: index
|
||||
sectionName: "connected"
|
||||
isDiscovered: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PanelSeparator {
|
||||
visible: root.connectedDevices.length > 0
|
||||
&& (root.knownDevices.length > 0
|
||||
|| (root.adapter && root.adapter.discovering && root.discoveredDevices.length > 0))
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: deviceFlick
|
||||
width: parent.width
|
||||
@@ -528,33 +632,7 @@ Panel {
|
||||
width: parent.width
|
||||
spacing: Style.space(10)
|
||||
|
||||
// Connected devices.
|
||||
PanelSectionHeader {
|
||||
visible: root.connectedDevices.length > 0
|
||||
text: "CONNECTED"
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.connectedDevices
|
||||
DeviceRow {
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: deviceList.width
|
||||
dev: modelData
|
||||
rowIndex: index
|
||||
sectionName: "connected"
|
||||
isDiscovered: false
|
||||
}
|
||||
}
|
||||
|
||||
// Remembered devices.
|
||||
PanelSeparator {
|
||||
visible: root.connectedDevices.length > 0 && root.knownDevices.length > 0
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
PanelSectionHeader {
|
||||
visible: root.knownDevices.length > 0
|
||||
text: "PAIRED"
|
||||
@@ -578,7 +656,7 @@ Panel {
|
||||
// Discovered (unpaired) devices, only shown while scanning.
|
||||
PanelSeparator {
|
||||
visible: root.adapter && root.adapter.discovering && root.discoveredDevices.length > 0
|
||||
&& (root.connectedDevices.length > 0 || root.knownDevices.length > 0)
|
||||
&& root.knownDevices.length > 0
|
||||
foreground: root.bar.foreground
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user