summaryrefslogtreecommitdiff
path: root/modules/controlcenter/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'modules/controlcenter/bluetooth')
-rw-r--r--modules/controlcenter/bluetooth/BtPane.qml137
-rw-r--r--modules/controlcenter/bluetooth/Details.qml654
-rw-r--r--modules/controlcenter/bluetooth/DeviceList.qml307
-rw-r--r--modules/controlcenter/bluetooth/Settings.qml32
4 files changed, 498 insertions, 632 deletions
diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml
index 96dc002..a987e75 100644
--- a/modules/controlcenter/bluetooth/BtPane.qml
+++ b/modules/controlcenter/bluetooth/BtPane.qml
@@ -1,134 +1,73 @@
pragma ComponentBehavior: Bound
import ".."
-import qs.components.effects
+import "../components"
+import "."
+import qs.components
+import qs.components.controls
import qs.components.containers
import qs.config
import Quickshell.Widgets
import Quickshell.Bluetooth
import QtQuick
-import QtQuick.Layouts
-RowLayout {
+SplitPaneWithDetails {
id: root
required property Session session
anchors.fill: parent
- spacing: 0
-
- Item {
- Layout.preferredWidth: Math.floor(parent.width * 0.4)
- Layout.minimumWidth: 420
- Layout.fillHeight: true
-
- DeviceList {
- anchors.fill: parent
- anchors.margins: Appearance.padding.large + Appearance.padding.normal
- anchors.leftMargin: Appearance.padding.large
- anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2
-
- session: root.session
- }
-
- InnerBorder {
- leftThickness: 0
- rightThickness: Appearance.padding.normal / 2
- }
+ activeItem: session.bt.active
+ paneIdGenerator: function(item) {
+ return item ? (item.address || "") : "";
}
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- ClippingRectangle {
- anchors.fill: parent
- anchors.margins: Appearance.padding.normal
- anchors.leftMargin: 0
- anchors.rightMargin: Appearance.padding.normal / 2
-
- radius: rightBorder.innerRadius
- color: "transparent"
+ leftContent: Component {
+ StyledFlickable {
+ id: leftFlickable
- Loader {
- id: loader
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: deviceList.height
- property BluetoothDevice pane: root.session.bt.active
-
- anchors.fill: parent
- anchors.margins: Appearance.padding.large * 2
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: leftFlickable
+ }
- asynchronous: true
- sourceComponent: pane ? details : settings
+ DeviceList {
+ id: deviceList
- Behavior on pane {
- SequentialAnimation {
- ParallelAnimation {
- Anim {
- property: "opacity"
- to: 0
- easing.bezierCurve: Appearance.anim.curves.standardAccel
- }
- Anim {
- property: "scale"
- to: 0.8
- easing.bezierCurve: Appearance.anim.curves.standardAccel
- }
- }
- PropertyAction {}
- ParallelAnimation {
- Anim {
- property: "opacity"
- to: 1
- easing.bezierCurve: Appearance.anim.curves.standardDecel
- }
- Anim {
- property: "scale"
- to: 1
- easing.bezierCurve: Appearance.anim.curves.standardDecel
- }
- }
- }
- }
+ anchors.left: parent.left
+ anchors.right: parent.right
+ session: root.session
}
}
+ }
- InnerBorder {
- id: rightBorder
-
- leftThickness: Appearance.padding.normal / 2
+ rightDetailsComponent: Component {
+ Details {
+ session: root.session
}
+ }
- Component {
- id: settings
-
- StyledFlickable {
- flickableDirection: Flickable.VerticalFlick
- contentHeight: settingsInner.height
-
- Settings {
- id: settingsInner
+ rightSettingsComponent: Component {
+ StyledFlickable {
+ id: settingsFlickable
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: settingsInner.height
- anchors.left: parent.left
- anchors.right: parent.right
- session: root.session
- }
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: settingsFlickable
}
- }
- Component {
- id: details
+ Settings {
+ id: settingsInner
- Details {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
session: root.session
}
}
}
-
- component Anim: NumberAnimation {
- target: loader
- duration: Appearance.anim.durations.normal / 2
- easing.type: Easing.BezierSpline
- }
}
diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml
index 104f673..5299045 100644
--- a/modules/controlcenter/bluetooth/Details.qml
+++ b/modules/controlcenter/bluetooth/Details.qml
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import ".."
+import "../components"
import qs.components
import qs.components.controls
import qs.components.effects
@@ -12,409 +13,430 @@ import Quickshell.Bluetooth
import QtQuick
import QtQuick.Layouts
-Item {
+StyledFlickable {
id: root
required property Session session
readonly property BluetoothDevice device: session.bt.active
- StyledFlickable {
- anchors.fill: parent
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: detailsWrapper.height
- flickableDirection: Flickable.VerticalFlick
- contentHeight: layout.height
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: root
+ }
+
+ Item {
+ id: detailsWrapper
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ implicitHeight: details.implicitHeight
- ColumnLayout {
- id: layout
+ DeviceDetails {
+ id: details
anchors.left: parent.left
anchors.right: parent.right
- spacing: Appearance.spacing.normal
+ anchors.top: parent.top
- MaterialIcon {
- Layout.alignment: Qt.AlignHCenter
- animate: true
- text: Icons.getBluetoothIcon(root.device.icon)
- font.pointSize: Appearance.font.size.extraLarge * 3
- font.bold: true
- }
+ session: root.session
+ device: root.device
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- animate: true
- text: root.device?.name ?? ""
- font.pointSize: Appearance.font.size.large
- font.bold: true
+ headerComponent: Component {
+ SettingsHeader {
+ icon: Icons.getBluetoothIcon(root.device?.icon ?? "")
+ title: root.device?.name ?? ""
+ }
}
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Connection status")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ sections: [
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- StyledText {
- text: qsTr("Connection settings for this device")
- color: Colours.palette.m3outline
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Connection status")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Connection settings for this device")
+ color: Colours.palette.m3outline
+ }
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- ColumnLayout {
- id: deviceStatus
+ ColumnLayout {
+ id: deviceStatus
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- spacing: Appearance.spacing.larger
+ spacing: Appearance.spacing.larger
- Toggle {
- label: qsTr("Connected")
- checked: root.device?.connected ?? false
- toggle.onToggled: root.device.connected = checked
- }
+ Toggle {
+ label: qsTr("Connected")
+ checked: root.device?.connected ?? false
+ toggle.onToggled: root.device.connected = checked
+ }
+
+ Toggle {
+ label: qsTr("Paired")
+ checked: root.device?.paired ?? false
+ toggle.onToggled: {
+ if (root.device.paired)
+ root.device.forget();
+ else
+ root.device.pair();
+ }
+ }
- Toggle {
- label: qsTr("Paired")
- checked: root.device?.paired ?? false
- toggle.onToggled: {
- if (root.device.paired)
- root.device.forget();
- else
- root.device.pair();
+ Toggle {
+ label: qsTr("Blocked")
+ checked: root.device?.blocked ?? false
+ toggle.onToggled: root.device.blocked = checked
+ }
+ }
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- Toggle {
- label: qsTr("Blocked")
- checked: root.device?.blocked ?? false
- toggle.onToggled: root.device.blocked = checked
- }
- }
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Device properties")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Device properties")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ StyledText {
+ text: qsTr("Additional settings")
+ color: Colours.palette.m3outline
+ }
- StyledText {
- text: qsTr("Additional settings")
- color: Colours.palette.m3outline
- }
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ ColumnLayout {
+ id: deviceProps
- ColumnLayout {
- id: deviceProps
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ spacing: Appearance.spacing.larger
- spacing: Appearance.spacing.larger
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
- RowLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.small
+ Item {
+ id: renameDevice
- Item {
- id: renameDevice
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.spacing.small
- Layout.fillWidth: true
- Layout.rightMargin: Appearance.spacing.small
+ implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight
- implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight
+ states: State {
+ name: "editingDeviceName"
+ when: root.session.bt.editingDeviceName
- states: State {
- name: "editingDeviceName"
- when: root.session.bt.editingDeviceName
+ AnchorChanges {
+ target: deviceNameEdit
+ anchors.top: renameDevice.top
+ }
+ PropertyChanges {
+ renameDevice.implicitHeight: deviceNameEdit.implicitHeight
+ renameLabel.opacity: 0
+ deviceNameEdit.padding: Appearance.padding.normal
+ }
+ }
- AnchorChanges {
- target: deviceNameEdit
- anchors.top: renameDevice.top
- }
- PropertyChanges {
- renameDevice.implicitHeight: deviceNameEdit.implicitHeight
- renameLabel.opacity: 0
- deviceNameEdit.padding: Appearance.padding.normal
- }
- }
+ transitions: Transition {
+ AnchorAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ Anim {
+ properties: "implicitHeight,opacity,padding"
+ }
+ }
- transitions: Transition {
- AnchorAnimation {
- duration: Appearance.anim.durations.normal
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Appearance.anim.curves.standard
- }
- Anim {
- properties: "implicitHeight,opacity,padding"
- }
- }
+ StyledText {
+ id: renameLabel
- StyledText {
- id: renameLabel
+ anchors.left: parent.left
- anchors.left: parent.left
+ text: qsTr("Device name")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- text: qsTr("Device name")
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledTextField {
+ id: deviceNameEdit
- StyledTextField {
- id: deviceNameEdit
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: renameLabel.bottom
+ anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: renameLabel.bottom
- anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal
+ text: root.device?.name ?? ""
+ readOnly: !root.session.bt.editingDeviceName
+ onAccepted: {
+ root.session.bt.editingDeviceName = false;
+ root.device.name = text;
+ }
- text: root.device?.name ?? ""
- readOnly: !root.session.bt.editingDeviceName
- onAccepted: {
- root.session.bt.editingDeviceName = false;
- root.device.name = text;
- }
+ leftPadding: Appearance.padding.normal
+ rightPadding: Appearance.padding.normal
- leftPadding: Appearance.padding.normal
- rightPadding: Appearance.padding.normal
+ background: StyledRect {
+ radius: Appearance.rounding.small
+ border.width: 2
+ border.color: Colours.palette.m3primary
+ opacity: root.session.bt.editingDeviceName ? 1 : 0
- background: StyledRect {
- radius: Appearance.rounding.small
- border.width: 2
- border.color: Colours.palette.m3primary
- opacity: root.session.bt.editingDeviceName ? 1 : 0
+ Behavior on border.color {
+ CAnim {}
+ }
- Behavior on border.color {
- CAnim {}
- }
+ Behavior on opacity {
+ Anim {}
+ }
+ }
- Behavior on opacity {
- Anim {}
+ Behavior on anchors.leftMargin {
+ Anim {}
+ }
+ }
}
- }
- Behavior on anchors.leftMargin {
- Anim {}
- }
- }
- }
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2
+ radius: Appearance.rounding.small
+ color: Colours.palette.m3secondaryContainer
+ opacity: root.session.bt.editingDeviceName ? 1 : 0
+ scale: root.session.bt.editingDeviceName ? 1 : 0.5
- radius: Appearance.rounding.small
- color: Colours.palette.m3secondaryContainer
- opacity: root.session.bt.editingDeviceName ? 1 : 0
- scale: root.session.bt.editingDeviceName ? 1 : 0.5
+ StateLayer {
+ color: Colours.palette.m3onSecondaryContainer
+ disabled: !root.session.bt.editingDeviceName
- StateLayer {
- color: Colours.palette.m3onSecondaryContainer
- disabled: !root.session.bt.editingDeviceName
+ function onClicked(): void {
+ root.session.bt.editingDeviceName = false;
+ deviceNameEdit.text = Qt.binding(() => root.device?.name ?? "");
+ }
+ }
- function onClicked(): void {
- root.session.bt.editingDeviceName = false;
- deviceNameEdit.text = Qt.binding(() => root.device?.name ?? "");
- }
- }
+ MaterialIcon {
+ id: cancelEditIcon
- MaterialIcon {
- id: cancelEditIcon
+ anchors.centerIn: parent
+ animate: true
+ text: "cancel"
+ color: Colours.palette.m3onSecondaryContainer
+ }
- anchors.centerIn: parent
- animate: true
- text: "cancel"
- color: Colours.palette.m3onSecondaryContainer
- }
+ Behavior on opacity {
+ Anim {}
+ }
- Behavior on opacity {
- Anim {}
- }
+ Behavior on scale {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+ }
- Behavior on scale {
- Anim {
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- }
- }
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2
+ radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
+ color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0)
- radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
- color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0)
+ StateLayer {
+ color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
- StateLayer {
- color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+ function onClicked(): void {
+ root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName;
+ if (root.session.bt.editingDeviceName)
+ deviceNameEdit.forceActiveFocus();
+ else
+ deviceNameEdit.accepted();
+ }
+ }
- function onClicked(): void {
- root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName;
- if (root.session.bt.editingDeviceName)
- deviceNameEdit.forceActiveFocus();
- else
- deviceNameEdit.accepted();
- }
- }
+ MaterialIcon {
+ id: editIcon
- MaterialIcon {
- id: editIcon
+ anchors.centerIn: parent
+ animate: true
+ text: root.session.bt.editingDeviceName ? "check_circle" : "edit"
+ color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+ }
- anchors.centerIn: parent
- animate: true
- text: root.session.bt.editingDeviceName ? "check_circle" : "edit"
- color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
- }
+ Behavior on radius {
+ Anim {}
+ }
+ }
+ }
- Behavior on radius {
- Anim {}
+ Toggle {
+ label: qsTr("Trusted")
+ checked: root.device?.trusted ?? false
+ toggle.onToggled: root.device.trusted = checked
+ }
+
+ Toggle {
+ label: qsTr("Wake allowed")
+ checked: root.device?.wakeAllowed ?? false
+ toggle.onToggled: root.device.wakeAllowed = checked
+ }
}
}
}
+ },
+ Component {
+ ColumnLayout {
+ spacing: Appearance.spacing.normal
- Toggle {
- label: qsTr("Trusted")
- checked: root.device?.trusted ?? false
- toggle.onToggled: root.device.trusted = checked
- }
-
- Toggle {
- label: qsTr("Wake allowed")
- checked: root.device?.wakeAllowed ?? false
- toggle.onToggled: root.device.wakeAllowed = checked
- }
- }
- }
-
- StyledText {
- Layout.topMargin: Appearance.spacing.large
- text: qsTr("Device information")
- font.pointSize: Appearance.font.size.larger
- font.weight: 500
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Device information")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
- StyledText {
- text: qsTr("Information about this device")
- color: Colours.palette.m3outline
- }
+ StyledText {
+ text: qsTr("Information about this device")
+ color: Colours.palette.m3outline
+ }
- StyledRect {
- Layout.fillWidth: true
- implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2
- radius: Appearance.rounding.normal
- color: Colours.tPalette.m3surfaceContainer
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
- ColumnLayout {
- id: deviceInfo
+ ColumnLayout {
+ id: deviceInfo
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.verticalCenter: parent.verticalCenter
- anchors.margins: Appearance.padding.large
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
- spacing: Appearance.spacing.small / 2
+ spacing: Appearance.spacing.small / 2
- StyledText {
- text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable")
- }
+ StyledText {
+ text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable")
+ }
- RowLayout {
- Layout.topMargin: Appearance.spacing.small / 2
- Layout.fillWidth: true
- Layout.preferredHeight: Appearance.padding.smaller
- spacing: Appearance.spacing.small / 2
+ RowLayout {
+ Layout.topMargin: Appearance.spacing.small / 2
+ Layout.fillWidth: true
+ Layout.preferredHeight: Appearance.padding.smaller
+ spacing: Appearance.spacing.small / 2
- StyledRect {
- Layout.fillHeight: true
- implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0
- radius: Appearance.rounding.full
- color: Colours.palette.m3primary
- }
+ StyledRect {
+ Layout.fillHeight: true
+ implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3primary
+ }
- StyledRect {
- Layout.fillWidth: true
- Layout.fillHeight: true
- radius: Appearance.rounding.full
- color: Colours.palette.m3secondaryContainer
+ StyledRect {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3secondaryContainer
- StyledRect {
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.margins: parent.height * 0.25
+ StyledRect {
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.margins: parent.height * 0.25
- implicitWidth: height
- radius: Appearance.rounding.full
- color: Colours.palette.m3primary
- }
- }
- }
+ implicitWidth: height
+ radius: Appearance.rounding.full
+ color: Colours.palette.m3primary
+ }
+ }
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("Dbus path")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Dbus path")
+ }
- StyledText {
- text: root.device?.dbusPath ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.dbusPath ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("MAC address")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("MAC address")
+ }
- StyledText {
- text: root.device?.address ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.address ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("Bonded")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Bonded")
+ }
- StyledText {
- text: root.device?.bonded ? qsTr("Yes") : qsTr("No")
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
- }
+ StyledText {
+ text: root.device?.bonded ? qsTr("Yes") : qsTr("No")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
- StyledText {
- Layout.topMargin: Appearance.spacing.normal
- text: qsTr("System name")
- }
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("System name")
+ }
- StyledText {
- text: root.device?.deviceName ?? ""
- color: Colours.palette.m3outline
- font.pointSize: Appearance.font.size.small
+ StyledText {
+ text: root.device?.deviceName ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+ }
}
}
- }
+ ]
}
}
@@ -562,11 +584,11 @@ Item {
Item {
id: fabRoot
- anchors.right: parent.right
- anchors.bottom: parent.bottom
-
- implicitWidth: 64
- implicitHeight: 64
+ x: root.contentX + root.width - width
+ y: root.contentY + root.height - height
+ width: 64
+ height: 64
+ z: 10000
StyledRect {
id: fabBg
diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml
index 3831e4a..b978a2d 100644
--- a/modules/controlcenter/bluetooth/DeviceList.qml
+++ b/modules/controlcenter/bluetooth/DeviceList.qml
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import ".."
+import "../components"
import qs.components
import qs.components.controls
import qs.components.containers
@@ -12,172 +13,142 @@ import Quickshell.Bluetooth
import QtQuick
import QtQuick.Layouts
-ColumnLayout {
+DeviceList {
id: root
required property Session session
readonly property bool smallDiscoverable: width <= 540
readonly property bool smallPairable: width <= 480
- spacing: Appearance.spacing.small
+ title: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length)
+ description: qsTr("All available bluetooth devices")
+ activeItem: session.bt.active
- RowLayout {
- spacing: Appearance.spacing.smaller
+ model: ScriptModel {
+ id: deviceModel
- StyledText {
- text: qsTr("Settings")
- font.pointSize: Appearance.font.size.large
- font.weight: 500
- }
-
- Item {
- Layout.fillWidth: true
- }
+ values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired))
+ }
- ToggleButton {
- toggled: Bluetooth.defaultAdapter?.enabled ?? false
- icon: "power"
- accent: "Tertiary"
+ headerComponent: Component {
+ RowLayout {
+ spacing: Appearance.spacing.smaller
- function onClicked(): void {
- const adapter = Bluetooth.defaultAdapter;
- if (adapter)
- adapter.enabled = !adapter.enabled;
+ StyledText {
+ text: qsTr("Bluetooth")
+ font.pointSize: Appearance.font.size.large
+ font.weight: 500
}
- }
-
- ToggleButton {
- toggled: Bluetooth.defaultAdapter?.discoverable ?? false
- icon: root.smallDiscoverable ? "group_search" : ""
- label: root.smallDiscoverable ? "" : qsTr("Discoverable")
- function onClicked(): void {
- const adapter = Bluetooth.defaultAdapter;
- if (adapter)
- adapter.discoverable = !adapter.discoverable;
+ Item {
+ Layout.fillWidth: true
}
- }
- ToggleButton {
- toggled: Bluetooth.defaultAdapter?.pairable ?? false
- icon: "missing_controller"
- label: root.smallPairable ? "" : qsTr("Pairable")
+ ToggleButton {
+ toggled: Bluetooth.defaultAdapter?.enabled ?? false
+ icon: "power"
+ accent: "Tertiary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Toggle Bluetooth")
- function onClicked(): void {
- const adapter = Bluetooth.defaultAdapter;
- if (adapter)
- adapter.pairable = !adapter.pairable;
+ onClicked: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.enabled = !adapter.enabled;
+ }
}
- }
- ToggleButton {
- toggled: !root.session.bt.active
- icon: "settings"
- accent: "Primary"
+ ToggleButton {
+ toggled: Bluetooth.defaultAdapter?.discoverable ?? false
+ icon: root.smallDiscoverable ? "group_search" : ""
+ label: root.smallDiscoverable ? "" : qsTr("Discoverable")
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Make discoverable")
- function onClicked(): void {
- if (root.session.bt.active)
- root.session.bt.active = null;
- else {
- root.session.bt.active = deviceModel.values[0] ?? null;
+ onClicked: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.discoverable = !adapter.discoverable;
}
}
- }
- }
- RowLayout {
- Layout.topMargin: Appearance.spacing.large
- Layout.fillWidth: true
- spacing: Appearance.spacing.normal
+ ToggleButton {
+ toggled: Bluetooth.defaultAdapter?.pairable ?? false
+ icon: "missing_controller"
+ label: root.smallPairable ? "" : qsTr("Pairable")
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Make pairable")
- ColumnLayout {
- Layout.fillWidth: true
- spacing: Appearance.spacing.small
-
- StyledText {
- Layout.fillWidth: true
- text: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length)
- font.pointSize: Appearance.font.size.large
- font.weight: 500
- }
-
- StyledText {
- Layout.fillWidth: true
- text: qsTr("All available bluetooth devices")
- color: Colours.palette.m3outline
+ onClicked: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.pairable = !adapter.pairable;
+ }
}
- }
-
- StyledRect {
- implicitWidth: implicitHeight
- implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2
- radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale)
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer
+ ToggleButton {
+ toggled: Bluetooth.defaultAdapter?.discovering ?? false
+ icon: "bluetooth_searching"
+ accent: "Secondary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Scan for devices")
- StateLayer {
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
-
- function onClicked(): void {
+ onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.discovering = !adapter.discovering;
}
}
- MaterialIcon {
- id: scanIcon
-
- anchors.centerIn: parent
- animate: true
- text: "bluetooth_searching"
- color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer
- fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0
- }
+ ToggleButton {
+ toggled: !root.session.bt.active
+ icon: "settings"
+ accent: "Primary"
+ iconSize: Appearance.font.size.normal
+ horizontalPadding: Appearance.padding.normal
+ verticalPadding: Appearance.padding.smaller
+ tooltip: qsTr("Bluetooth settings")
- Behavior on radius {
- Anim {}
+ onClicked: {
+ if (root.session.bt.active)
+ root.session.bt.active = null;
+ else {
+ root.session.bt.active = root.model.values[0] ?? null;
+ }
+ }
}
}
}
- StyledListView {
- id: view
-
- model: ScriptModel {
- id: deviceModel
-
- values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired))
- }
-
- Layout.fillWidth: true
- Layout.fillHeight: true
- clip: true
- spacing: Appearance.spacing.small / 2
-
- StyledScrollBar.vertical: StyledScrollBar {
- flickable: view
- }
- delegate: StyledRect {
+ delegate: Component {
+ StyledRect {
id: device
required property BluetoothDevice modelData
- readonly property bool loading: modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting
- readonly property bool connected: modelData.state === BluetoothDeviceState.Connected
+ readonly property bool loading: modelData && (modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting)
+ readonly property bool connected: modelData && modelData.state === BluetoothDeviceState.Connected
- anchors.left: parent.left
- anchors.right: parent.right
+ width: ListView.view ? ListView.view.width : undefined
implicitHeight: deviceInner.implicitHeight + Appearance.padding.normal * 2
- color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.bt.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
+ color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
radius: Appearance.rounding.normal
StateLayer {
id: stateLayer
function onClicked(): void {
- root.session.bt.active = device.modelData;
+ if (device.modelData)
+ root.session.bt.active = device.modelData;
}
}
@@ -194,20 +165,20 @@ ColumnLayout {
implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
radius: Appearance.rounding.normal
- color: device.connected ? Colours.palette.m3primaryContainer : device.modelData.bonded ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainerHigh
+ color: device.connected ? Colours.palette.m3primaryContainer : (device.modelData && device.modelData.bonded) ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainerHigh
StyledRect {
anchors.fill: parent
radius: parent.radius
- color: Qt.alpha(device.connected ? Colours.palette.m3onPrimaryContainer : device.modelData.bonded ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface, stateLayer.pressed ? 0.1 : stateLayer.containsMouse ? 0.08 : 0)
+ color: Qt.alpha(device.connected ? Colours.palette.m3onPrimaryContainer : (device.modelData && device.modelData.bonded) ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface, stateLayer.pressed ? 0.1 : stateLayer.containsMouse ? 0.08 : 0)
}
MaterialIcon {
id: icon
anchors.centerIn: parent
- text: Icons.getBluetoothIcon(device.modelData.icon)
- color: device.connected ? Colours.palette.m3onPrimaryContainer : device.modelData.bonded ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
+ text: Icons.getBluetoothIcon(device.modelData ? device.modelData.icon : "")
+ color: device.connected ? Colours.palette.m3onPrimaryContainer : (device.modelData && device.modelData.bonded) ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
font.pointSize: Appearance.font.size.large
fill: device.connected ? 1 : 0
@@ -224,13 +195,13 @@ ColumnLayout {
StyledText {
Layout.fillWidth: true
- text: device.modelData.name
+ text: device.modelData ? device.modelData.name : qsTr("Unknown")
elide: Text.ElideRight
}
StyledText {
Layout.fillWidth: true
- text: device.modelData.address + (device.connected ? qsTr(" (Connected)") : device.modelData.bonded ? qsTr(" (Paired)") : "")
+ text: (device.modelData ? device.modelData.address : "") + (device.connected ? qsTr(" (Connected)") : (device.modelData && device.modelData.bonded) ? qsTr(" (Paired)") : "")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
elide: Text.ElideRight
@@ -256,7 +227,18 @@ ColumnLayout {
disabled: device.loading
function onClicked(): void {
- device.modelData.connected = !device.modelData.connected;
+ if (device.loading)
+ return;
+
+ if (device.connected) {
+ device.modelData.connected = false;
+ } else {
+ if (device.modelData.bonded) {
+ device.modelData.connected = true;
+ } else {
+ device.modelData.pair();
+ }
+ }
}
}
@@ -265,7 +247,7 @@ ColumnLayout {
anchors.centerIn: parent
animate: true
- text: device.modelData.connected ? "link_off" : "link"
+ text: device.connected ? "link_off" : "link"
color: device.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
opacity: device.loading ? 0 : 1
@@ -279,78 +261,7 @@ ColumnLayout {
}
}
- component ToggleButton: StyledRect {
- id: toggleBtn
-
- required property bool toggled
- property string icon
- property string label
- property string accent: "Secondary"
-
- function onClicked(): void {
- }
-
- Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0)
- implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2
- implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2
-
- radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale)
- color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`]
-
- StateLayer {
- id: toggleStateLayer
-
- color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
-
- function onClicked(): void {
- toggleBtn.onClicked();
- }
- }
-
- RowLayout {
- id: toggleBtnInner
-
- anchors.centerIn: parent
- spacing: Appearance.spacing.normal
-
- MaterialIcon {
- id: toggleBtnIcon
-
- visible: !!text
- fill: toggleBtn.toggled ? 1 : 0
- text: toggleBtn.icon
- color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
- font.pointSize: Appearance.font.size.large
-
- Behavior on fill {
- Anim {}
- }
- }
-
- Loader {
- asynchronous: true
- active: !!toggleBtn.label
- visible: active
-
- sourceComponent: StyledText {
- text: toggleBtn.label
- color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
- }
- }
- }
-
- Behavior on radius {
- Anim {
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- }
-
- Behavior on Layout.preferredWidth {
- Anim {
- duration: Appearance.anim.durations.expressiveFastSpatial
- easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
- }
- }
+ onItemSelected: function(item) {
+ session.bt.active = item;
}
}
diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml
index fb493ff..c547240 100644
--- a/modules/controlcenter/bluetooth/Settings.qml
+++ b/modules/controlcenter/bluetooth/Settings.qml
@@ -1,6 +1,7 @@
pragma ComponentBehavior: Bound
import ".."
+import "../components"
import qs.components
import qs.components.controls
import qs.components.effects
@@ -17,18 +18,9 @@ ColumnLayout {
spacing: Appearance.spacing.normal
- MaterialIcon {
- Layout.alignment: Qt.AlignHCenter
- text: "bluetooth"
- font.pointSize: Appearance.font.size.extraLarge * 3
- font.bold: true
- }
-
- StyledText {
- Layout.alignment: Qt.AlignHCenter
- text: qsTr("Bluetooth settings")
- font.pointSize: Appearance.font.size.large
- font.bold: true
+ SettingsHeader {
+ icon: "bluetooth"
+ title: qsTr("Bluetooth Settings")
}
StyledText {
@@ -284,8 +276,12 @@ ColumnLayout {
CustomSpinBox {
min: 0
- value: root.session.bt.currentAdapter.discoverableTimeout
- onValueModified: value => root.session.bt.currentAdapter.discoverableTimeout = value
+ value: root.session.bt.currentAdapter?.discoverableTimeout ?? 0
+ onValueModified: value => {
+ if (root.session.bt.currentAdapter) {
+ root.session.bt.currentAdapter.discoverableTimeout = value;
+ }
+ }
}
}
@@ -332,7 +328,7 @@ ColumnLayout {
anchors.left: parent.left
- text: qsTr("Rename adapter (currently does not work)") // FIXME: remove disclaimer when fixed
+ text: qsTr("Rename adapter (currently does not work)")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
@@ -345,12 +341,10 @@ ColumnLayout {
anchors.top: renameLabel.bottom
anchors.leftMargin: root.session.bt.editingAdapterName ? 0 : -Appearance.padding.normal
- text: root.session.bt.currentAdapter.name
+ text: root.session.bt.currentAdapter?.name ?? ""
readOnly: !root.session.bt.editingAdapterName
onAccepted: {
root.session.bt.editingAdapterName = false;
- // Doesn't work for now, will be added to QS later
- // root.session.bt.currentAdapter.name = text;
}
leftPadding: Appearance.padding.normal
@@ -392,7 +386,7 @@ ColumnLayout {
function onClicked(): void {
root.session.bt.editingAdapterName = false;
- adapterNameEdit.text = Qt.binding(() => root.session.bt.currentAdapter.name);
+ adapterNameEdit.text = Qt.binding(() => root.session.bt.currentAdapter?.name ?? "");
}
}