dcontent: impl bt settings

This commit is contained in:
2 * r + 2 * t 2025-07-26 23:11:28 +10:00
parent be6b19ad21
commit db1e2ea3bf
9 changed files with 491 additions and 12 deletions

View file

@ -38,7 +38,9 @@ ClippingRectangle {
Pane {
index: 1
sourceComponent: BtPane {}
sourceComponent: BtPane {
session: root.session
}
}
Behavior on y {

View file

@ -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
}
}

View file

@ -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
}
}
}
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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;

107
widgets/CustomSpinBox.qml Normal file
View file

@ -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();
}
}
}

View file

@ -13,6 +13,8 @@ TextField {
font.family: Appearance.font.family.sans
font.pointSize: Appearance.font.size.smaller
background: null
cursorDelegate: StyledRect {
id: cursor