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.config
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import QtQuick
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ Item {
|
|||
anchors.topMargin: Appearance.spacing.smaller / 2
|
||||
|
||||
animate: true
|
||||
text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled"
|
||||
text: Bluetooth.defaultAdapter.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||
color: root.colour
|
||||
}
|
||||
|
||||
|
|
@ -55,16 +56,35 @@ Item {
|
|||
id: repeater
|
||||
|
||||
model: ScriptModel {
|
||||
values: Bluetooth.devices.filter(d => d.connected)
|
||||
values: Bluetooth.devices.values.filter(d => d.state !== BluetoothDeviceState.Disconnected)
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
required property Bluetooth.Device modelData
|
||||
id: device
|
||||
|
||||
required property BluetoothDevice modelData
|
||||
|
||||
animate: true
|
||||
text: Icons.getBluetoothIcon(modelData.icon)
|
||||
color: root.colour
|
||||
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
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
component Anim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,140 @@
|
|||
import qs.widgets
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
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
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Bluetooth %1").arg(Bluetooth.powered ? "enabled" : "disabled")
|
||||
text: qsTr("Bluetooth %1").arg(BluetoothAdapterState.toString(Bluetooth.defaultAdapter.state).toLowerCase())
|
||||
}
|
||||
|
||||
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 Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
|
@ -100,7 +101,7 @@ WrapperItem {
|
|||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
animate: true
|
||||
text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled"
|
||||
text: Bluetooth.defaultAdapter.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +112,7 @@ WrapperItem {
|
|||
|
||||
sourceComponent: StyledText {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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