summaryrefslogtreecommitdiff
path: root/modules/controlcenter/bluetooth/Settings.qml
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-08-05 16:19:58 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-08-05 16:19:58 +1000
commitfb650907a0b18fab4f996c2fdc110d2d091e4060 (patch)
treeab2ec5bb7f17fc36ea9ccc12bad451c43a1b7f9c /modules/controlcenter/bluetooth/Settings.qml
parentinternal: position slider handle correctly (diff)
downloadcaelestia-shell-fb650907a0b18fab4f996c2fdc110d2d091e4060.tar.gz
caelestia-shell-fb650907a0b18fab4f996c2fdc110d2d091e4060.tar.bz2
caelestia-shell-fb650907a0b18fab4f996c2fdc110d2d091e4060.zip
internal: rename dcontent -> controlcenter
Diffstat (limited to 'modules/controlcenter/bluetooth/Settings.qml')
-rw-r--r--modules/controlcenter/bluetooth/Settings.qml546
1 files changed, 546 insertions, 0 deletions
diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml
new file mode 100644
index 0000000..f298432
--- /dev/null
+++ b/modules/controlcenter/bluetooth/Settings.qml
@@ -0,0 +1,546 @@
+pragma ComponentBehavior: Bound
+
+import ".."
+import qs.components
+import qs.components.controls
+import qs.components.effects
+import qs.services
+import qs.config
+import Quickshell.Bluetooth
+import QtQuick
+import QtQuick.Layouts
+
+ColumnLayout {
+ id: root
+
+ required property Session session
+
+ 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
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Adapter status")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("General adapter settings")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: adapterStatus.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.palette.m3surfaceContainer
+
+ ColumnLayout {
+ id: adapterStatus
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
+
+ spacing: Appearance.spacing.larger
+
+ Toggle {
+ label: qsTr("Powered")
+ checked: Bluetooth.defaultAdapter?.enabled ?? false
+ toggle.onToggled: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.enabled = checked;
+ }
+ }
+
+ Toggle {
+ label: qsTr("Discoverable")
+ checked: Bluetooth.defaultAdapter?.discoverable ?? false
+ toggle.onToggled: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.discoverable = checked;
+ }
+ }
+
+ Toggle {
+ label: qsTr("Pairable")
+ checked: Bluetooth.defaultAdapter?.pairable ?? false
+ toggle.onToggled: {
+ const adapter = Bluetooth.defaultAdapter;
+ if (adapter)
+ adapter.pairable = checked;
+ }
+ }
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Adapter properties")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Per-adapter settings")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: adapterSettings.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.palette.m3surfaceContainer
+
+ ColumnLayout {
+ id: adapterSettings
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
+
+ spacing: Appearance.spacing.larger
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.normal
+
+ StyledText {
+ Layout.fillWidth: true
+ text: qsTr("Current adapter")
+ }
+
+ Item {
+ id: adapterPickerButton
+
+ property bool expanded
+
+ implicitWidth: adapterPicker.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: adapterPicker.implicitHeight + Appearance.padding.smaller * 2
+
+ StateLayer {
+ radius: Appearance.rounding.small
+
+ function onClicked(): void {
+ adapterPickerButton.expanded = !adapterPickerButton.expanded;
+ }
+ }
+
+ RowLayout {
+ id: adapterPicker
+
+ anchors.fill: parent
+ anchors.margins: Appearance.padding.normal
+ anchors.topMargin: Appearance.padding.smaller
+ anchors.bottomMargin: Appearance.padding.smaller
+ spacing: Appearance.spacing.normal
+
+ StyledText {
+ Layout.leftMargin: Appearance.padding.small
+ text: Bluetooth.defaultAdapter?.name ?? qsTr("None")
+ }
+
+ MaterialIcon {
+ text: "expand_more"
+ }
+ }
+
+ Elevation {
+ anchors.fill: adapterListBg
+ radius: adapterListBg.radius
+ opacity: adapterPickerButton.expanded ? 1 : 0
+ scale: adapterPickerButton.expanded ? 1 : 0.7
+ level: 2
+
+ Behavior on opacity {
+ Anim {}
+ }
+
+ Behavior on scale {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+ }
+
+ StyledClippingRect {
+ id: adapterListBg
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ implicitHeight: adapterPickerButton.expanded ? adapterList.implicitHeight : adapterPickerButton.implicitHeight
+
+ color: Colours.palette.m3secondaryContainer
+ radius: Appearance.rounding.small
+ opacity: adapterPickerButton.expanded ? 1 : 0
+ scale: adapterPickerButton.expanded ? 1 : 0.7
+
+ ColumnLayout {
+ id: adapterList
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+
+ spacing: 0
+
+ Repeater {
+ model: Bluetooth.adapters
+
+ Item {
+ id: adapter
+
+ required property BluetoothAdapter modelData
+
+ Layout.fillWidth: true
+ implicitHeight: adapterInner.implicitHeight + Appearance.padding.normal * 2
+
+ StateLayer {
+ disabled: !adapterPickerButton.expanded
+
+ function onClicked(): void {
+ adapterPickerButton.expanded = false;
+ root.session.bt.currentAdapter = adapter.modelData;
+ }
+ }
+
+ RowLayout {
+ id: adapterInner
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.normal
+ spacing: Appearance.spacing.normal
+
+ StyledText {
+ Layout.fillWidth: true
+ Layout.leftMargin: Appearance.padding.small
+ text: adapter.modelData.name
+ color: Colours.palette.m3onSecondaryContainer
+ }
+
+ MaterialIcon {
+ text: "check"
+ color: Colours.palette.m3onSecondaryContainer
+ visible: adapter.modelData === root.session.bt.currentAdapter
+ }
+ }
+ }
+ }
+ }
+
+ Behavior on opacity {
+ Anim {}
+ }
+
+ Behavior on scale {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+
+ Behavior on implicitHeight {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.normal
+
+ StyledText {
+ Layout.fillWidth: true
+ text: qsTr("Discoverable timeout")
+ }
+
+ CustomSpinBox {
+ min: 0
+ value: root.session.bt.currentAdapter.discoverableTimeout
+ onValueModified: value => root.session.bt.currentAdapter.discoverableTimeout = value
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ Item {
+ id: renameAdapter
+
+ Layout.fillWidth: true
+ Layout.rightMargin: Appearance.spacing.small
+
+ implicitHeight: renameLabel.implicitHeight + adapterNameEdit.implicitHeight
+
+ states: State {
+ name: "editingAdapterName"
+ when: root.session.bt.editingAdapterName
+
+ AnchorChanges {
+ target: adapterNameEdit
+ anchors.top: renameAdapter.top
+ }
+ PropertyChanges {
+ renameAdapter.implicitHeight: adapterNameEdit.implicitHeight
+ renameLabel.opacity: 0
+ adapterNameEdit.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"
+ }
+ }
+
+ StyledText {
+ id: renameLabel
+
+ anchors.left: parent.left
+
+ text: qsTr("Rename adapter (currently does not work)") // FIXME: remove disclaimer when fixed
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledTextField {
+ id: adapterNameEdit
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: renameLabel.bottom
+ anchors.leftMargin: root.session.bt.editingAdapterName ? 0 : -Appearance.padding.normal
+
+ 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
+ rightPadding: Appearance.padding.normal
+
+ background: StyledRect {
+ radius: Appearance.rounding.small
+ border.width: 2
+ border.color: Colours.palette.m3primary
+ opacity: root.session.bt.editingAdapterName ? 1 : 0
+
+ Behavior on border.color {
+ ColorAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+
+ Behavior on opacity {
+ Anim {}
+ }
+ }
+
+ Behavior on anchors.leftMargin {
+ Anim {}
+ }
+ }
+ }
+
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2
+
+ radius: Appearance.rounding.small
+ color: Colours.palette.m3secondaryContainer
+ opacity: root.session.bt.editingAdapterName ? 1 : 0
+ scale: root.session.bt.editingAdapterName ? 1 : 0.5
+
+ StateLayer {
+ color: Colours.palette.m3onSecondaryContainer
+ disabled: !root.session.bt.editingAdapterName
+
+ function onClicked(): void {
+ root.session.bt.editingAdapterName = false;
+ adapterNameEdit.text = Qt.binding(() => root.session.bt.currentAdapter.name);
+ }
+ }
+
+ MaterialIcon {
+ id: cancelEditIcon
+
+ anchors.centerIn: parent
+ animate: true
+ text: "cancel"
+ color: Colours.palette.m3onSecondaryContainer
+ }
+
+ Behavior on opacity {
+ Anim {}
+ }
+
+ 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
+
+ radius: root.session.bt.editingAdapterName ? Appearance.rounding.small : implicitHeight / 2
+ color: root.session.bt.editingAdapterName ? Colours.palette.m3primary : "transparent"
+
+ StateLayer {
+ color: root.session.bt.editingAdapterName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+
+ function onClicked(): void {
+ root.session.bt.editingAdapterName = !root.session.bt.editingAdapterName;
+ if (root.session.bt.editingAdapterName)
+ adapterNameEdit.forceActiveFocus();
+ else
+ adapterNameEdit.accepted();
+ }
+ }
+
+ MaterialIcon {
+ id: editIcon
+
+ anchors.centerIn: parent
+ animate: true
+ text: root.session.bt.editingAdapterName ? "check_circle" : "edit"
+ color: root.session.bt.editingAdapterName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
+ }
+
+ Behavior on radius {
+ Anim {}
+ }
+ }
+ }
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Adapter information")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Information about the default adapter")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: adapterInfo.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.palette.m3surfaceContainer
+
+ ColumnLayout {
+ id: adapterInfo
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
+
+ spacing: Appearance.spacing.small / 2
+
+ StyledText {
+ text: qsTr("Adapter state")
+ }
+
+ StyledText {
+ text: Bluetooth.defaultAdapter ? BluetoothAdapterState.toString(Bluetooth.defaultAdapter.state) : qsTr("Unknown")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Dbus path")
+ }
+
+ StyledText {
+ text: Bluetooth.defaultAdapter?.dbusPath ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Adapter id")
+ }
+
+ StyledText {
+ text: Bluetooth.defaultAdapter?.adapterId ?? ""
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+ }
+
+ component Toggle: RowLayout {
+ required property string label
+ property alias checked: toggle.checked
+ property alias toggle: toggle
+
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.normal
+
+ StyledText {
+ Layout.fillWidth: true
+ text: parent.label
+ }
+
+ StyledSwitch {
+ id: toggle
+ }
+ }
+
+ component Anim: NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+}