diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-07-26 23:11:28 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-07-26 23:11:28 +1000 |
| commit | db1e2ea3bf7dd1f5ac1960ca4c9dc17757649460 (patch) | |
| tree | 49db4ec68518e475f6e24f96373b28dd85e40e19 | |
| parent | dcontent: add bt pane (diff) | |
| download | caelestia-shell-db1e2ea3bf7dd1f5ac1960ca4c9dc17757649460.tar.gz caelestia-shell-db1e2ea3bf7dd1f5ac1960ca4c9dc17757649460.tar.bz2 caelestia-shell-db1e2ea3bf7dd1f5ac1960ca4c9dc17757649460.zip | |
dcontent: impl bt settings
| -rw-r--r-- | modules/detachedcontent/Panes.qml | 4 | ||||
| -rw-r--r-- | modules/detachedcontent/Session.qml | 8 | ||||
| -rw-r--r-- | modules/detachedcontent/bluetooth/BtPane.qml | 42 | ||||
| -rw-r--r-- | modules/detachedcontent/bluetooth/Details.qml | 9 | ||||
| -rw-r--r-- | modules/detachedcontent/bluetooth/DeviceList.qml | 20 | ||||
| -rw-r--r-- | modules/detachedcontent/bluetooth/Settings.qml | 310 | ||||
| -rw-r--r-- | modules/launcher/Content.qml | 1 | ||||
| -rw-r--r-- | widgets/CustomSpinBox.qml | 107 | ||||
| -rw-r--r-- | widgets/StyledTextField.qml | 2 |
9 files changed, 491 insertions, 12 deletions
diff --git a/modules/detachedcontent/Panes.qml b/modules/detachedcontent/Panes.qml index b9f53ae9..912382c 100644 --- a/modules/detachedcontent/Panes.qml +++ b/modules/detachedcontent/Panes.qml @@ -38,7 +38,9 @@ ClippingRectangle { Pane { index: 1 - sourceComponent: BtPane {} + sourceComponent: BtPane { + session: root.session + } } Behavior on y { diff --git a/modules/detachedcontent/Session.qml b/modules/detachedcontent/Session.qml index f86528c..0565fd0 100644 --- a/modules/detachedcontent/Session.qml +++ b/modules/detachedcontent/Session.qml @@ -1,3 +1,4 @@ +import Quickshell.Bluetooth import QtQuick QtObject { @@ -6,6 +7,13 @@ QtObject { property string active property int activeIndex + readonly property Bt bt: Bt {} + onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] + + component Bt: QtObject { + property BluetoothDevice active + property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter + } } diff --git a/modules/detachedcontent/bluetooth/BtPane.qml b/modules/detachedcontent/bluetooth/BtPane.qml index 750166b..e72b1ab 100644 --- a/modules/detachedcontent/bluetooth/BtPane.qml +++ b/modules/detachedcontent/bluetooth/BtPane.qml @@ -1,23 +1,30 @@ pragma ComponentBehavior: Bound +import ".." import qs.widgets import qs.config import QtQuick import QtQuick.Layouts RowLayout { + id: root + + required property Session session + anchors.fill: parent spacing: 0 Item { - Layout.preferredWidth: Math.floor(parent.width / 3) + Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.fillHeight: true DeviceList { 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 { @@ -30,13 +37,40 @@ RowLayout { Layout.fillWidth: true Layout.fillHeight: true - Details { - anchors.margins: Appearance.padding.normal - anchors.leftMargin: Appearance.padding.normal / 2 + Loader { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 + Appearance.padding.normal / 2 + + asynchronous: true + sourceComponent: root.session.bt.active ? details : settings } InnerBorder { leftThickness: Appearance.padding.normal / 2 } + + Component { + id: settings + + Settings { + anchors.margins: Appearance.padding.normal + anchors.leftMargin: Appearance.padding.normal / 2 + + session: root.session + } + } + + Component { + id: details + + Details { + anchors.margins: Appearance.padding.normal + anchors.leftMargin: Appearance.padding.normal / 2 + + session: root.session + } + } } } diff --git a/modules/detachedcontent/bluetooth/Details.qml b/modules/detachedcontent/bluetooth/Details.qml index b879195..b3f7e2a 100644 --- a/modules/detachedcontent/bluetooth/Details.qml +++ b/modules/detachedcontent/bluetooth/Details.qml @@ -1,3 +1,4 @@ +import ".." import qs.widgets import qs.services import qs.config @@ -5,4 +6,10 @@ import Quickshell import Quickshell.Bluetooth import QtQuick.Layouts -ColumnLayout {} +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal +} diff --git a/modules/detachedcontent/bluetooth/DeviceList.qml b/modules/detachedcontent/bluetooth/DeviceList.qml index 8f77cfd..91fca23 100644 --- a/modules/detachedcontent/bluetooth/DeviceList.qml +++ b/modules/detachedcontent/bluetooth/DeviceList.qml @@ -1,3 +1,6 @@ +pragma ComponentBehavior: Bound + +import ".." import qs.widgets import qs.services import qs.config @@ -11,6 +14,8 @@ import QtQuick.Controls ColumnLayout { id: root + required property Session session + anchors.fill: parent spacing: Appearance.spacing.small @@ -32,7 +37,11 @@ ColumnLayout { radius: Appearance.rounding.normal color: Colours.palette.m3surfaceContainer - StateLayer {} + StateLayer { + function onClicked(): void { + root.session.bt.active = null; + } + } StyledText { id: settingsText @@ -65,7 +74,7 @@ ColumnLayout { ScrollBar.vertical: StyledScrollBar {} - delegate: StyledRect { + delegate: Item { id: device required property BluetoothDevice modelData @@ -79,7 +88,10 @@ ColumnLayout { StateLayer { id: stateLayer + radius: Appearance.rounding.small + function onClicked(): void { + root.session.bt.active = device.modelData; } } @@ -88,8 +100,6 @@ ColumnLayout { anchors.fill: parent anchors.margins: Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large spacing: Appearance.spacing.normal @@ -97,7 +107,7 @@ ColumnLayout { implicitWidth: implicitHeight implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - radius: Appearance.rounding.full + radius: Appearance.rounding.normal color: device.connected ? Colours.palette.m3primaryContainer : device.modelData.bonded ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainerHigh StyledRect { diff --git a/modules/detachedcontent/bluetooth/Settings.qml b/modules/detachedcontent/bluetooth/Settings.qml new file mode 100644 index 0000000..808cd7c --- /dev/null +++ b/modules/detachedcontent/bluetooth/Settings.qml @@ -0,0 +1,310 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.widgets +import qs.services +import qs.config +import Quickshell.Bluetooth +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects + +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; + } + } + } + } + + 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.normal * 2 + + StateLayer { + radius: Appearance.rounding.small + + function onClicked(): void { + adapterPickerButton.expanded = !adapterPickerButton.expanded; + } + } + + RowLayout { + id: adapterPicker + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.normal + + StyledText { + Layout.leftMargin: Appearance.padding.small + text: Bluetooth.defaultAdapter?.name ?? qsTr("None") + } + + MaterialIcon { + text: "expand_more" + } + } + + RectangularShadow { + anchors.fill: adapterListBg + radius: adapterListBg.radius + color: Qt.alpha(Colours.palette.m3shadow, 0.7) + opacity: adapterPickerButton.expanded ? 1 : 0 + scale: adapterPickerButton.expanded ? 1 : 0.7 + blur: 5 + spread: 0 + + 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 { + enabled: 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: root.session.bt.currentAdapter.discoverableTimeout = value + } + } + } + } + + Item { + Layout.fillHeight: true + } + + 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 + } +} diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml index c8ad3a9..e887bf9 100644 --- a/modules/launcher/Content.qml +++ b/modules/launcher/Content.qml @@ -76,7 +76,6 @@ Item { bottomPadding: Appearance.padding.larger placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix) - background: null onAccepted: { const currentItem = list.currentList?.currentItem; diff --git a/widgets/CustomSpinBox.qml b/widgets/CustomSpinBox.qml new file mode 100644 index 0000000..5021bd5 --- /dev/null +++ b/widgets/CustomSpinBox.qml @@ -0,0 +1,107 @@ +pragma ComponentBehavior: Bound + +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + property int value + property real max: NaN + property real min: NaN + property alias repeatRate: timer.interval + + signal valueModified(value: int) + + spacing: Appearance.spacing.small + + StyledTextField { + inputMethodHints: Qt.ImhFormattedNumbersOnly + text: root.value + onAccepted: root.valueModified(text) + + padding: Appearance.padding.small + leftPadding: Appearance.padding.normal + rightPadding: Appearance.padding.normal + + background: StyledRect { + implicitWidth: 100 + radius: Appearance.rounding.small + color: Colours.palette.m3surfaceContainerHigh + } + } + + StyledRect { + radius: Appearance.rounding.small + color: Colours.palette.m3primary + + implicitWidth: implicitHeight + implicitHeight: upIcon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + id: upState + + color: Colours.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + + function onClicked(): void { + root.valueModified(Math.min(root.max, root.value + 1)); + } + } + + MaterialIcon { + id: upIcon + + anchors.centerIn: parent + text: "keyboard_arrow_up" + color: Colours.palette.m3onPrimary + } + } + + StyledRect { + radius: Appearance.rounding.small + color: Colours.palette.m3primary + + implicitWidth: implicitHeight + implicitHeight: downIcon.implicitHeight + Appearance.padding.small * 2 + + StateLayer { + id: downState + + color: Colours.palette.m3onPrimary + + onPressAndHold: timer.start() + onReleased: timer.stop() + + function onClicked(): void { + root.valueModified(Math.max(root.min, root.value - 1)); + } + } + + MaterialIcon { + id: downIcon + + anchors.centerIn: parent + text: "keyboard_arrow_down" + color: Colours.palette.m3onPrimary + } + } + + Timer { + id: timer + + interval: 100 + repeat: true + triggeredOnStart: true + onTriggered: { + if (upState.pressed) + upState.onClicked(); + else if (downState.pressed) + downState.onClicked(); + } + } +} diff --git a/widgets/StyledTextField.qml b/widgets/StyledTextField.qml index d36ae02..ecde872 100644 --- a/widgets/StyledTextField.qml +++ b/widgets/StyledTextField.qml @@ -13,6 +13,8 @@ TextField { font.family: Appearance.font.family.sans font.pointSize: Appearance.font.size.smaller + background: null + cursorDelegate: StyledRect { id: cursor |