add Bluetooth to quickshell bar
This commit is contained in:
187
roles/quickshell/files/bar/BluetoothContent.qml
Normal file
187
roles/quickshell/files/bar/BluetoothContent.qml
Normal file
@ -0,0 +1,187 @@
|
||||
import Quickshell.Bluetooth
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import "../components"
|
||||
|
||||
// Bluetooth panel – a plain Item so it can live inside an expanding section border.
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: 300
|
||||
implicitHeight: btCol.implicitHeight + 24
|
||||
|
||||
// Driven by the popup's open state — only scan for nearby devices while visible.
|
||||
property bool scanning: false
|
||||
|
||||
readonly property var adapter: Bluetooth.defaultAdapter
|
||||
readonly property bool powered: adapter?.enabled ?? false
|
||||
|
||||
// Run device discovery only while the popup is open and the adapter is on.
|
||||
Binding {
|
||||
target: root.adapter
|
||||
property: "discovering"
|
||||
value: root.scanning && root.powered
|
||||
when: root.adapter !== null
|
||||
}
|
||||
|
||||
// \uf293 = FA bluetooth-b, \uf294 = FA bluetooth
|
||||
function devIcon(dev) {
|
||||
return dev.connected ? "\uf293" : "\uf294"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: btCol
|
||||
anchors { fill: parent; margins: 12 }
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 2
|
||||
|
||||
Text {
|
||||
text: "Bluetooth"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Text {
|
||||
text: root.powered ? "On" : "Off"
|
||||
font.pixelSize: 10
|
||||
color: root.powered ? Theme.accent : Theme.textDim
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: if (root.adapter) root.adapter.enabled = !root.adapter.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: !root.powered
|
||||
text: "Adapter disabled"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 11
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// ── Paired devices ────────────────────────────────────────────
|
||||
Text {
|
||||
visible: root.powered
|
||||
text: "Paired"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 2
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Bluetooth.devices
|
||||
delegate: DeviceRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
device: modelData
|
||||
visible: root.powered && modelData.bonded
|
||||
}
|
||||
}
|
||||
|
||||
// ── Available (discoverable) devices ──────────────────────────
|
||||
RowLayout {
|
||||
visible: root.powered
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 2
|
||||
|
||||
Text {
|
||||
text: "Available"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Text {
|
||||
visible: root.adapter?.discovering ?? false
|
||||
text: "scanning…"
|
||||
color: Theme.textDim
|
||||
font.pixelSize: 10
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Bluetooth.devices
|
||||
delegate: DeviceRow {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
device: modelData
|
||||
// Only named devices — hides the swarm of random-address BLE beacons.
|
||||
visible: root.powered && !modelData.bonded
|
||||
&& (modelData.deviceName ?? "").length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component DeviceRow: RowLayout {
|
||||
id: drow
|
||||
required property var device
|
||||
spacing: 8
|
||||
implicitHeight: 28
|
||||
|
||||
// Once a freshly-paired device bonds, trust it (so it can reconnect
|
||||
// without an agent) and bring up the connection automatically.
|
||||
Connections {
|
||||
target: drow.device
|
||||
function onBondedChanged() {
|
||||
if (!drow.device.bonded) return
|
||||
if (!drow.device.trusted) drow.device.trusted = true
|
||||
if (!drow.device.connected) drow.device.connect()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.devIcon(drow.device)
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: 13
|
||||
color: drow.device.connected ? Theme.accent : Theme.textDim
|
||||
}
|
||||
|
||||
Text {
|
||||
text: drow.device.name || drow.device.address || "?"
|
||||
font.pixelSize: 11
|
||||
color: drow.device.connected ? Theme.text : Theme.textDim
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: drow.device.batteryAvailable
|
||||
text: Math.round(drow.device.battery * 100) + "%"
|
||||
font.pixelSize: 10
|
||||
color: Theme.textDim
|
||||
Layout.preferredWidth: 30
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: drow.device.pairing ? "pairing…"
|
||||
: drow.device.state === BluetoothDeviceState.Connecting ? "…"
|
||||
: drow.device.state === BluetoothDeviceState.Disconnecting ? "…"
|
||||
: !drow.device.bonded ? "Pair"
|
||||
: drow.device.connected ? "Disconnect"
|
||||
: "Connect"
|
||||
font.pixelSize: 10
|
||||
color: drow.device.connected ? Theme.text : Theme.textDim
|
||||
Layout.preferredWidth: 70
|
||||
horizontalAlignment: Text.AlignRight
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const d = drow.device
|
||||
if (d.pairing) d.cancelPair()
|
||||
else if (!d.bonded) d.pair()
|
||||
else if (d.connected) d.disconnect()
|
||||
else d.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user