dcontent: add bt pane

This commit is contained in:
2 * r + 2 * t 2025-07-26 19:41:56 +10:00
parent 978e038a26
commit be6b19ad21
6 changed files with 323 additions and 72 deletions

View file

@ -67,7 +67,7 @@ ColumnLayout {
id: device
required property BluetoothDevice modelData
readonly property bool loading: device.modelData.state === BluetoothDeviceState.Connecting || device.modelData.state === BluetoothDeviceState.Disconnecting
readonly property bool loading: modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting
Layout.fillWidth: true
Layout.rightMargin: Appearance.padding.small

View file

@ -1,12 +1,12 @@
pragma ComponentBehavior: Bound
import "bluetooth"
import qs.widgets
import qs.services
import qs.config
import Quickshell.Widgets
import QtQuick
import QtQuick.Layouts
import QtQuick.Effects
ClippingRectangle {
id: root
@ -24,23 +24,21 @@ ClippingRectangle {
y: -root.session.activeIndex * root.height
Pane {
StyledText {
anchors.centerIn: parent
text: qsTr("Work in progress")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.extraLarge
font.weight: 500
index: 0
sourceComponent: Item {
StyledText {
anchors.centerIn: parent
text: qsTr("Work in progress")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.extraLarge
font.weight: 500
}
}
}
Pane {
StyledText {
anchors.centerIn: parent
text: qsTr("Work in progress")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.extraLarge
font.weight: 500
}
index: 1
sourceComponent: BtPane {}
}
Behavior on y {
@ -52,71 +50,31 @@ ClippingRectangle {
}
}
StyledRect {
anchors.fill: parent
color: Colours.palette.m3surfaceContainer
layer.enabled: true
layer.effect: MultiEffect {
maskSource: mask
maskEnabled: true
maskInverted: true
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
InnerBorder {
leftThickness: 0
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
anchors.margins: Appearance.padding.normal
anchors.leftMargin: 0
radius: Appearance.rounding.small
}
}
component Pane: Loader {
component Pane: Item {
id: pane
default property Item child
required property int index
property alias sourceComponent: loader.sourceComponent
asynchronous: true
active: {
const ly = -layout.y;
const ty = layout.children.indexOf(this) * root.height;
return ly + root.height > ty && ly < ty + root.height;
}
implicitWidth: root.width
implicitHeight: root.height
sourceComponent: Item {
implicitWidth: root.width
implicitHeight: root.height
Loader {
id: loader
Item {
anchors.fill: parent
anchors.margins: Appearance.padding.normal
anchors.leftMargin: 0
anchors.fill: parent
asynchronous: true
active: {
if (root.session.activeIndex === pane.index)
return true;
children: [pane.child]
}
StyledRect {
anchors.fill: parent
color: Colours.palette.m3surfaceContainer
layer.enabled: true
layer.effect: MultiEffect {
maskSource: mask
maskEnabled: true
maskInverted: true
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
const ly = -layout.y;
const ty = pane.index * root.height;
return ly + root.height > ty && ly < ty + root.height;
}
}
}

View file

@ -0,0 +1,42 @@
pragma ComponentBehavior: Bound
import qs.widgets
import qs.config
import QtQuick
import QtQuick.Layouts
RowLayout {
anchors.fill: parent
spacing: 0
Item {
Layout.preferredWidth: Math.floor(parent.width / 3)
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
}
InnerBorder {
leftThickness: 0
rightThickness: Appearance.padding.normal / 2
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Details {
anchors.margins: Appearance.padding.normal
anchors.leftMargin: Appearance.padding.normal / 2
}
InnerBorder {
leftThickness: Appearance.padding.normal / 2
}
}
}

View file

@ -0,0 +1,8 @@
import qs.widgets
import qs.services
import qs.config
import Quickshell
import Quickshell.Bluetooth
import QtQuick.Layouts
ColumnLayout {}

View file

@ -0,0 +1,200 @@
import qs.widgets
import qs.services
import qs.config
import qs.utils
import Quickshell
import Quickshell.Bluetooth
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
ColumnLayout {
id: root
anchors.fill: parent
spacing: Appearance.spacing.small
StyledText {
text: qsTr("Settings")
font.pointSize: Appearance.font.size.large
font.weight: 500
}
StyledText {
text: qsTr("General bluetooth settings")
color: Colours.palette.m3outline
}
StyledRect {
Layout.fillWidth: true
implicitHeight: settingsText.implicitHeight + Appearance.padding.normal * 2
radius: Appearance.rounding.normal
color: Colours.palette.m3surfaceContainer
StateLayer {}
StyledText {
id: settingsText
anchors.centerIn: parent
text: qsTr("Bluetooth settings")
}
}
StyledText {
Layout.topMargin: Appearance.spacing.large
text: qsTr("Devices")
font.pointSize: Appearance.font.size.large
font.weight: 500
}
StyledText {
text: qsTr("All available bluetooth devices")
color: Colours.palette.m3outline
}
StyledListView {
model: ScriptModel {
values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired))
}
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ScrollBar.vertical: StyledScrollBar {}
delegate: 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
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: deviceInner.implicitHeight + Appearance.padding.normal * 2
StateLayer {
id: stateLayer
function onClicked(): void {
}
}
RowLayout {
id: deviceInner
anchors.fill: parent
anchors.margins: Appearance.padding.normal
anchors.leftMargin: Appearance.padding.large
anchors.rightMargin: Appearance.padding.large
spacing: Appearance.spacing.normal
StyledRect {
implicitWidth: implicitHeight
implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
radius: Appearance.rounding.full
color: device.connected ? Colours.palette.m3primaryContainer : device.modelData.bonded ? Colours.palette.m3secondaryContainer : Colours.palette.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)
}
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
font.pointSize: Appearance.font.size.large
fill: device.connected ? 1 : 0
Behavior on fill {
Anim {}
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
StyledText {
Layout.fillWidth: true
text: device.modelData.name
elide: Text.ElideRight
}
StyledText {
Layout.fillWidth: true
text: device.modelData.address + (device.connected ? qsTr(" (Connected)") : device.modelData.bonded ? qsTr(" (Paired)") : "")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
elide: Text.ElideRight
}
}
StyledRect {
id: connectBtn
implicitWidth: implicitHeight
implicitHeight: connectIcon.implicitHeight + Appearance.padding.small * 2
radius: Appearance.rounding.full
color: device.connected ? Colours.palette.m3primaryContainer : "transparent"
StyledBusyIndicator {
anchors.centerIn: parent
implicitWidth: implicitHeight
implicitHeight: connectIcon.implicitHeight
running: opacity > 0
opacity: device.loading ? 1 : 0
Behavior on opacity {
Anim {}
}
}
StateLayer {
color: device.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
disabled: device.loading
function onClicked(): void {
device.modelData.connected = !device.modelData.connected;
}
}
MaterialIcon {
id: connectIcon
anchors.centerIn: parent
animate: true
text: device.modelData.connected ? "link_off" : "link"
color: device.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
opacity: device.loading ? 0 : 1
Behavior on opacity {
Anim {}
}
}
}
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}

43
widgets/InnerBorder.qml Normal file
View file

@ -0,0 +1,43 @@
pragma ComponentBehavior: Bound
import qs.services
import qs.config
import QtQuick
import QtQuick.Effects
StyledRect {
property alias innerRadius: maskInner.radius
property alias thickness: maskInner.anchors.margins
property alias leftThickness: maskInner.anchors.leftMargin
property alias topThickness: maskInner.anchors.topMargin
property alias rightThickness: maskInner.anchors.rightMargin
property alias bottomThickness: maskInner.anchors.bottomMargin
anchors.fill: parent
color: Colours.palette.m3surfaceContainer
layer.enabled: true
layer.effect: MultiEffect {
maskSource: mask
maskEnabled: true
maskInverted: true
maskThresholdMin: 0.5
maskSpreadAtMin: 1
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
id: maskInner
anchors.fill: parent
anchors.margins: Appearance.padding.normal
radius: Appearance.rounding.small
}
}
}