bluetooth: use qs bluetooth
This commit is contained in:
parent
f263df3ccd
commit
8784a015c2
4 changed files with 161 additions and 128 deletions
|
|
@ -3,6 +3,7 @@ import qs.services
|
||||||
import qs.utils
|
import qs.utils
|
||||||
import qs.config
|
import qs.config
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Bluetooth
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
|
|
@ -38,7 +39,7 @@ Item {
|
||||||
anchors.topMargin: Appearance.spacing.smaller / 2
|
anchors.topMargin: Appearance.spacing.smaller / 2
|
||||||
|
|
||||||
animate: true
|
animate: true
|
||||||
text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled"
|
text: Bluetooth.defaultAdapter.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||||
color: root.colour
|
color: root.colour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,16 +56,35 @@ Item {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: Bluetooth.devices.filter(d => d.connected)
|
values: Bluetooth.devices.values.filter(d => d.state !== BluetoothDeviceState.Disconnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialIcon {
|
MaterialIcon {
|
||||||
required property Bluetooth.Device modelData
|
id: device
|
||||||
|
|
||||||
|
required property BluetoothDevice modelData
|
||||||
|
|
||||||
animate: true
|
animate: true
|
||||||
text: Icons.getBluetoothIcon(modelData.icon)
|
text: Icons.getBluetoothIcon(modelData.icon)
|
||||||
color: root.colour
|
color: root.colour
|
||||||
fill: 1
|
fill: 1
|
||||||
|
|
||||||
|
SequentialAnimation on opacity {
|
||||||
|
running: device.modelData.state !== BluetoothDeviceState.Connected
|
||||||
|
alwaysRunToEnd: true
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
|
}
|
||||||
|
Anim {
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,19 +119,13 @@ Item {
|
||||||
fill: 1
|
fill: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
Anim {}
|
||||||
duration: Appearance.anim.durations.normal
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
component Anim: NumberAnimation {
|
||||||
NumberAnimation {
|
duration: Appearance.anim.durations.large
|
||||||
duration: Appearance.anim.durations.normal
|
easing.type: Easing.BezierSpline
|
||||||
easing.type: Easing.BezierSpline
|
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,140 @@
|
||||||
import qs.widgets
|
pragma ComponentBehavior: Bound
|
||||||
import qs.services
|
|
||||||
import qs.config
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Column {
|
import qs.widgets
|
||||||
|
import qs.config
|
||||||
|
import qs.utils
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Bluetooth
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: qsTr("Bluetooth %1").arg(Bluetooth.powered ? "enabled" : "disabled")
|
text: qsTr("Bluetooth %1").arg(BluetoothAdapterState.toString(Bluetooth.defaultAdapter.state).toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: Bluetooth.devices.some(d => d.connected) ? qsTr("Connected to: %1").arg(Bluetooth.devices.filter(d => d.connected).map(d => d.alias).join(", ")) : qsTr("No devices connected")
|
text: qsTr("%n connected device(s)", "", Bluetooth.devices.values.filter(d => d.connected).length)
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired))
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: device
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
readonly property bool loading: device.modelData.state === BluetoothDeviceState.Connecting || device.modelData.state === BluetoothDeviceState.Disconnecting
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
scale: 0.7
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
opacity = 1;
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
text: Icons.getBluetoothIcon(device.modelData.icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: device.modelData.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: connectBtn
|
||||||
|
|
||||||
|
implicitWidth: loadingIndicator.implicitWidth - Appearance.padding.small * 2
|
||||||
|
implicitHeight: loadingIndicator.implicitHeight - Appearance.padding.small * 2
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
id: loadingIndicator
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
implicitWidth: Appearance.font.size.large * 2 + Appearance.padding.small * 2
|
||||||
|
implicitHeight: Appearance.font.size.large * 2 + Appearance.padding.small * 2
|
||||||
|
|
||||||
|
background: null
|
||||||
|
running: opacity > 0
|
||||||
|
opacity: device.loading ? 1 : 0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
disabled: device.loading
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
device.modelData.connected = !device.modelData.connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
animate: true
|
||||||
|
text: device.modelData.connected ? "link_off" : "link"
|
||||||
|
|
||||||
|
font.pointSize: device.loading ? Appearance.font.size.normal : Appearance.font.size.larger
|
||||||
|
|
||||||
|
Behavior on font.pointSize {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
asynchronous: true
|
||||||
|
active: device.modelData.paired
|
||||||
|
sourceComponent: Item {
|
||||||
|
implicitWidth: connectBtn.implicitWidth
|
||||||
|
implicitHeight: connectBtn.implicitHeight
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
function onClicked(): void {
|
||||||
|
device.modelData.forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "delete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Anim: NumberAnimation {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import qs.config
|
||||||
import qs.utils
|
import qs.utils
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Bluetooth
|
||||||
import Quickshell.Services.UPower
|
import Quickshell.Services.UPower
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
@ -100,7 +101,7 @@ WrapperItem {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
animate: true
|
animate: true
|
||||||
text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled"
|
text: Bluetooth.defaultAdapter.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||||
font.pointSize: Appearance.font.size.large
|
font.pointSize: Appearance.font.size.large
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +112,7 @@ WrapperItem {
|
||||||
|
|
||||||
sourceComponent: StyledText {
|
sourceComponent: StyledText {
|
||||||
animate: true
|
animate: true
|
||||||
text: qsTr("%n device(s) connected", "", Bluetooth.devices.filter(d => d.connected).length)
|
text: qsTr("%n device(s) connected", "", Bluetooth.devices.values.filter(d => d.connected).length)
|
||||||
font.pointSize: Appearance.font.size.normal
|
font.pointSize: Appearance.font.size.normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool powered
|
|
||||||
property bool discovering
|
|
||||||
readonly property list<Device> devices: []
|
|
||||||
|
|
||||||
Process {
|
|
||||||
running: true
|
|
||||||
command: ["bluetoothctl"]
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: {
|
|
||||||
getInfo.running = true;
|
|
||||||
getDevices.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: getInfo
|
|
||||||
|
|
||||||
running: true
|
|
||||||
command: ["bluetoothctl", "show"]
|
|
||||||
environment: ({
|
|
||||||
LANG: "C",
|
|
||||||
LC_ALL: "C"
|
|
||||||
})
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
root.powered = text.includes("Powered: yes");
|
|
||||||
root.discovering = text.includes("Discovering: yes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: getDevices
|
|
||||||
|
|
||||||
running: true
|
|
||||||
command: ["fish", "-c", `
|
|
||||||
for a in (bluetoothctl devices)
|
|
||||||
if string match -q 'Device *' $a
|
|
||||||
bluetoothctl info $addr (string split ' ' $a)[2]
|
|
||||||
echo
|
|
||||||
end
|
|
||||||
end`]
|
|
||||||
environment: ({
|
|
||||||
LANG: "C",
|
|
||||||
LC_ALL: "C"
|
|
||||||
})
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const devices = text.trim().split("\n\n").map(d => ({
|
|
||||||
name: d.match(/Name: (.*)/)[1],
|
|
||||||
alias: d.match(/Alias: (.*)/)[1],
|
|
||||||
address: d.match(/Device ([0-9A-Z:]{17})/)[1],
|
|
||||||
icon: d.match(/Icon: (.*)/)[1],
|
|
||||||
connected: d.includes("Connected: yes"),
|
|
||||||
paired: d.includes("Paired: yes"),
|
|
||||||
trusted: d.includes("Trusted: yes")
|
|
||||||
}));
|
|
||||||
const rDevices = root.devices;
|
|
||||||
|
|
||||||
const destroyed = rDevices.filter(rd => !devices.find(d => d.address === rd.address));
|
|
||||||
for (const device of destroyed)
|
|
||||||
rDevices.splice(rDevices.indexOf(device), 1).forEach(d => d.destroy());
|
|
||||||
|
|
||||||
for (const device of devices) {
|
|
||||||
const match = rDevices.find(d => d.address === device.address);
|
|
||||||
if (match) {
|
|
||||||
match.lastIpcObject = device;
|
|
||||||
} else {
|
|
||||||
rDevices.push(deviceComp.createObject(root, {
|
|
||||||
lastIpcObject: device
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component Device: QtObject {
|
|
||||||
required property var lastIpcObject
|
|
||||||
readonly property string name: lastIpcObject.name
|
|
||||||
readonly property string alias: lastIpcObject.alias
|
|
||||||
readonly property string address: lastIpcObject.address
|
|
||||||
readonly property string icon: lastIpcObject.icon
|
|
||||||
readonly property bool connected: lastIpcObject.connected
|
|
||||||
readonly property bool paired: lastIpcObject.paired
|
|
||||||
readonly property bool trusted: lastIpcObject.trusted
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: deviceComp
|
|
||||||
|
|
||||||
Device {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue