nosignal-shell/modules/bar/popouts/Bluetooth.qml
2025-07-16 23:49:45 +10:00

140 lines
4 KiB
QML

pragma ComponentBehavior: Bound
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(BluetoothAdapterState.toString(Bluetooth.defaultAdapter.state).toLowerCase())
}
StyledText {
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
}
}