Files
dotfiles/.config/quickshell/caelestia/modules/controlcenter/bluetooth/Settings.qml

533 lines
19 KiB
QML

pragma ComponentBehavior: Bound
import ".."
import "../components"
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
SettingsHeader {
icon: "bluetooth"
title: qsTr("Bluetooth Settings")
}
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.tPalette.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.tPalette.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 ?? 0
onValueModified: value => {
if (root.session.bt.currentAdapter) {
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)")
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;
}
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 {
CAnim {}
}
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 * Math.min(1, Appearance.rounding.scale)
color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingAdapterName ? 1 : 0)
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.tPalette.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
cLayer: 2
}
}
}