From fb650907a0b18fab4f996c2fdc110d2d091e4060 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 5 Aug 2025 16:19:58 +1000 Subject: internal: rename dcontent -> controlcenter --- config/Config.qml | 4 +- config/ControlCenterConfig.qml | 11 + config/DContentConfig.qml | 11 - modules/bar/popouts/Wrapper.qml | 6 +- modules/controlcenter/ControlCenter.qml | 61 +++ modules/controlcenter/NavRail.qml | 203 +++++++ modules/controlcenter/Panes.qml | 98 ++++ modules/controlcenter/Session.qml | 22 + modules/controlcenter/bluetooth/BtPane.qml | 121 +++++ modules/controlcenter/bluetooth/Details.qml | 663 +++++++++++++++++++++++ modules/controlcenter/bluetooth/DeviceList.qml | 363 +++++++++++++ modules/controlcenter/bluetooth/Settings.qml | 546 +++++++++++++++++++ modules/detachedcontent/DetachedContent.qml | 61 --- modules/detachedcontent/NavRail.qml | 203 ------- modules/detachedcontent/Panes.qml | 98 ---- modules/detachedcontent/Session.qml | 22 - modules/detachedcontent/bluetooth/BtPane.qml | 121 ----- modules/detachedcontent/bluetooth/Details.qml | 663 ----------------------- modules/detachedcontent/bluetooth/DeviceList.qml | 363 ------------- modules/detachedcontent/bluetooth/Settings.qml | 546 ------------------- 20 files changed, 2092 insertions(+), 2094 deletions(-) create mode 100644 config/ControlCenterConfig.qml delete mode 100644 config/DContentConfig.qml create mode 100644 modules/controlcenter/ControlCenter.qml create mode 100644 modules/controlcenter/NavRail.qml create mode 100644 modules/controlcenter/Panes.qml create mode 100644 modules/controlcenter/Session.qml create mode 100644 modules/controlcenter/bluetooth/BtPane.qml create mode 100644 modules/controlcenter/bluetooth/Details.qml create mode 100644 modules/controlcenter/bluetooth/DeviceList.qml create mode 100644 modules/controlcenter/bluetooth/Settings.qml delete mode 100644 modules/detachedcontent/DetachedContent.qml delete mode 100644 modules/detachedcontent/NavRail.qml delete mode 100644 modules/detachedcontent/Panes.qml delete mode 100644 modules/detachedcontent/Session.qml delete mode 100644 modules/detachedcontent/bluetooth/BtPane.qml delete mode 100644 modules/detachedcontent/bluetooth/Details.qml delete mode 100644 modules/detachedcontent/bluetooth/DeviceList.qml delete mode 100644 modules/detachedcontent/bluetooth/Settings.qml diff --git a/config/Config.qml b/config/Config.qml index ea7f3f4..a4a4a87 100644 --- a/config/Config.qml +++ b/config/Config.qml @@ -12,7 +12,7 @@ Singleton { property alias bar: adapter.bar property alias border: adapter.border property alias dashboard: adapter.dashboard - property alias dcontent: adapter.dcontent + property alias controlCenter: adapter.controlCenter property alias launcher: adapter.launcher property alias notifs: adapter.notifs property alias osd: adapter.osd @@ -36,7 +36,7 @@ Singleton { property BarConfig bar: BarConfig {} property BorderConfig border: BorderConfig {} property DashboardConfig dashboard: DashboardConfig {} - property DContentConfig dcontent: DContentConfig {} + property ControlCenterConfig controlCenter: ControlCenterConfig {} property LauncherConfig launcher: LauncherConfig {} property NotifsConfig notifs: NotifsConfig {} property OsdConfig osd: OsdConfig {} diff --git a/config/ControlCenterConfig.qml b/config/ControlCenterConfig.qml new file mode 100644 index 0000000..13afbd2 --- /dev/null +++ b/config/ControlCenterConfig.qml @@ -0,0 +1,11 @@ +import Quickshell.Io + +JsonObject { + property Sizes sizes: Sizes {} + + component Sizes: JsonObject { + property real heightMult: 0.7 + property real ratio: 16 / 9 + property real expandedNavWidth: 180 + } +} diff --git a/config/DContentConfig.qml b/config/DContentConfig.qml deleted file mode 100644 index 13afbd2..0000000 --- a/config/DContentConfig.qml +++ /dev/null @@ -1,11 +0,0 @@ -import Quickshell.Io - -JsonObject { - property Sizes sizes: Sizes {} - - component Sizes: JsonObject { - property real heightMult: 0.7 - property real ratio: 16 / 9 - property real expandedNavWidth: 180 - } -} diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml index 4ef7492..bb80ad0 100644 --- a/modules/bar/popouts/Wrapper.qml +++ b/modules/bar/popouts/Wrapper.qml @@ -3,7 +3,7 @@ pragma ComponentBehavior: Bound import qs.services import qs.config import qs.modules.windowinfo -import qs.modules.detachedcontent +import qs.modules.controlcenter import Quickshell import Quickshell.Wayland import Quickshell.Hyprland @@ -94,13 +94,11 @@ Item { } Comp { - id: detachedContent - shouldBeActive: root.detachedMode === "any" asynchronous: true anchors.centerIn: parent - sourceComponent: DetachedContent { + sourceComponent: ControlCenter { screen: root.screen active: root.queuedMode } diff --git a/modules/controlcenter/ControlCenter.qml b/modules/controlcenter/ControlCenter.qml new file mode 100644 index 0000000..2e7ce07 --- /dev/null +++ b/modules/controlcenter/ControlCenter.qml @@ -0,0 +1,61 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.components.controls +import qs.services +import qs.config +import Quickshell +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + required property ShellScreen screen + property alias active: session.active + readonly property Session session: Session { + id: session + } + + implicitWidth: implicitHeight * Config.controlCenter.sizes.ratio + implicitHeight: screen.height * Config.controlCenter.sizes.heightMult + + RowLayout { + anchors.fill: parent + + spacing: 0 + + StyledRect { + Layout.fillHeight: true + + topLeftRadius: Appearance.rounding.normal + bottomLeftRadius: Appearance.rounding.normal + implicitWidth: navRail.implicitWidth + color: Colours.palette.m3surfaceContainer + + CustomMouseArea { + anchors.fill: parent + + function onWheel(event: WheelEvent): void { + if (event.angleDelta.y < 0) + root.session.activeIndex = Math.min(root.session.activeIndex + 1, root.session.panes.length - 1); + else if (event.angleDelta.y > 0) + root.session.activeIndex = Math.max(root.session.activeIndex - 1, 0); + } + } + + NavRail { + id: navRail + + session: root.session + } + } + + Panes { + Layout.fillWidth: true + Layout.fillHeight: true + + session: root.session + } + } +} diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml new file mode 100644 index 0000000..a631465 --- /dev/null +++ b/modules/controlcenter/NavRail.qml @@ -0,0 +1,203 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + required property Session session + property bool expanded + + implicitWidth: layout.implicitWidth + Appearance.padding.large * 4 + implicitHeight: layout.implicitHeight + Appearance.padding.large * 2 + + ColumnLayout { + id: layout + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + states: State { + name: "expanded" + when: root.expanded + + PropertyChanges { + layout.spacing: Appearance.spacing.small + menuIcon.opacity: 0 + menuIconExpanded.opacity: 1 + menuIcon.rotation: 180 + menuIconExpanded.rotation: 0 + } + AnchorChanges { + target: menuIcon + anchors.horizontalCenter: undefined + } + AnchorChanges { + target: menuIconExpanded + anchors.horizontalCenter: undefined + } + } + + transitions: Transition { + Anim { + properties: "spacing,opacity,rotation" + } + } + + Item { + Layout.fillWidth: true + Layout.bottomMargin: Appearance.spacing.large * 2 + implicitHeight: Math.max(menuIcon.implicitHeight, menuIconExpanded.implicitHeight) + Appearance.padding.normal * 2 + + StateLayer { + radius: Appearance.rounding.small + + function onClicked(): void { + root.expanded = !root.expanded; + } + } + + MaterialIcon { + id: menuIcon + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: "menu" + font.pointSize: Appearance.font.size.large + } + + MaterialIcon { + id: menuIconExpanded + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: "menu_open" + font.pointSize: Appearance.font.size.large + opacity: 0 + rotation: -180 + } + } + + NavItem { + icon: "network_manage" + label: "network" + } + + NavItem { + icon: "settings_bluetooth" + label: "bluetooth" + } + + NavItem { + icon: "tune" + label: "audio" + } + } + + component NavItem: Item { + id: item + + required property string icon + required property string label + readonly property bool active: root.session.active === label + + implicitWidth: background.implicitWidth + implicitHeight: background.implicitHeight + smallLabel.implicitHeight + smallLabel.anchors.topMargin + + states: State { + name: "expanded" + when: root.expanded + + PropertyChanges { + expandedLabel.opacity: 1 + smallLabel.opacity: 0 + background.implicitWidth: Config.controlCenter.sizes.expandedNavWidth + background.implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + item.implicitHeight: background.implicitHeight + } + } + + transitions: Transition { + Anim { + property: "opacity" + duration: Appearance.anim.durations.small + } + + Anim { + properties: "implicitWidth,implicitHeight" + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + + StyledRect { + id: background + + radius: Appearance.rounding.full + color: item.active ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainer + + implicitWidth: icon.implicitWidth + icon.anchors.leftMargin * 2 + implicitHeight: icon.implicitHeight + Appearance.padding.small + + StateLayer { + color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + + function onClicked(): void { + root.session.active = item.label; + } + } + + MaterialIcon { + id: icon + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: Appearance.padding.large + + text: item.icon + color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.large + fill: item.active ? 1 : 0 + + Behavior on fill { + Anim {} + } + } + + StyledText { + id: expandedLabel + + anchors.left: icon.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: Appearance.spacing.normal + + opacity: 0 + text: item.label + color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface + font.capitalization: Font.Capitalize + } + + StyledText { + id: smallLabel + + anchors.horizontalCenter: icon.horizontalCenter + anchors.top: icon.bottom + anchors.topMargin: Appearance.spacing.small / 2 + + text: item.label + font.pointSize: Appearance.font.size.small + font.capitalization: Font.Capitalize + } + } + } + + component Anim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml new file mode 100644 index 0000000..d1ec581 --- /dev/null +++ b/modules/controlcenter/Panes.qml @@ -0,0 +1,98 @@ +pragma ComponentBehavior: Bound + +import "bluetooth" +import qs.components +import qs.components.effects +import qs.services +import qs.config +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +ClippingRectangle { + id: root + + required property Session session + + topRightRadius: Appearance.rounding.normal + bottomRightRadius: Appearance.rounding.normal + color: "transparent" + + ColumnLayout { + id: layout + + spacing: 0 + y: -root.session.activeIndex * root.height + + Pane { + 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 { + index: 1 + sourceComponent: BtPane { + session: root.session + } + } + + Pane { + index: 2 + sourceComponent: Item { + StyledText { + anchors.centerIn: parent + text: qsTr("Work in progress") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.extraLarge + font.weight: 500 + } + } + } + + Behavior on y { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + + InnerBorder { + leftThickness: 0 + } + + component Pane: Item { + id: pane + + required property int index + property alias sourceComponent: loader.sourceComponent + + implicitWidth: root.width + implicitHeight: root.height + + Loader { + id: loader + + anchors.fill: parent + clip: true + asynchronous: true + active: { + if (root.session.activeIndex === pane.index) + return true; + + const ly = -layout.y; + const ty = pane.index * root.height; + return ly + root.height > ty && ly < ty + root.height; + } + } + } +} diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml new file mode 100644 index 0000000..0c3f1b4 --- /dev/null +++ b/modules/controlcenter/Session.qml @@ -0,0 +1,22 @@ +import Quickshell.Bluetooth +import QtQuick + +QtObject { + readonly property list panes: ["network", "bluetooth", "audio"] + + 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 + property bool editingAdapterName + property bool fabMenuOpen + property bool editingDeviceName + } +} diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml new file mode 100644 index 0000000..c6dbbcc --- /dev/null +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -0,0 +1,121 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components.effects +import qs.components.containers +import qs.config +import Quickshell.Bluetooth +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + 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 { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Loader { + id: loader + + property BluetoothDevice pane: root.session.bt.active + + 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: pane ? details : settings + + Behavior on pane { + SequentialAnimation { + ParallelAnimation { + Anim { + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + PropertyAction {} + ParallelAnimation { + Anim { + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: settings + + StyledFlickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + Settings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } + } + } + + Component { + id: details + + Details { + session: root.session + } + } + } + + component Anim: NumberAnimation { + target: loader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } +} diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml new file mode 100644 index 0000000..f856002 --- /dev/null +++ b/modules/controlcenter/bluetooth/Details.qml @@ -0,0 +1,663 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import qs.utils +import Quickshell.Bluetooth +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + required property Session session + readonly property BluetoothDevice device: session.bt.active + + StyledFlickable { + anchors.fill: parent + + flickableDirection: Flickable.VerticalFlick + contentHeight: layout.height + + ColumnLayout { + id: layout + + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + animate: true + text: Icons.getBluetoothIcon(root.device.icon) + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: root.device?.name ?? "" + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Connection status") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Connection settings for this device") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.palette.m3surfaceContainer + + ColumnLayout { + id: deviceStatus + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.larger + + Toggle { + label: qsTr("Connected") + checked: root.device?.connected ?? false + toggle.onToggled: root.device.connected = checked + } + + Toggle { + label: qsTr("Paired") + checked: root.device?.paired ?? false + toggle.onToggled: { + if (root.device.paired) + root.device.forget(); + else + root.device.pair(); + } + } + + Toggle { + label: qsTr("Blocked") + checked: root.device?.blocked ?? false + toggle.onToggled: root.device.blocked = checked + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device properties") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Additional settings") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.palette.m3surfaceContainer + + ColumnLayout { + id: deviceProps + + 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.small + + Item { + id: renameDevice + + Layout.fillWidth: true + Layout.rightMargin: Appearance.spacing.small + + implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight + + states: State { + name: "editingDeviceName" + when: root.session.bt.editingDeviceName + + AnchorChanges { + target: deviceNameEdit + anchors.top: renameDevice.top + } + PropertyChanges { + renameDevice.implicitHeight: deviceNameEdit.implicitHeight + renameLabel.opacity: 0 + deviceNameEdit.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("Device name") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledTextField { + id: deviceNameEdit + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: renameLabel.bottom + anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal + + text: root.device?.name ?? "" + readOnly: !root.session.bt.editingDeviceName + onAccepted: { + root.session.bt.editingDeviceName = false; + root.device.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.editingDeviceName ? 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.editingDeviceName ? 1 : 0 + scale: root.session.bt.editingDeviceName ? 1 : 0.5 + + StateLayer { + color: Colours.palette.m3onSecondaryContainer + disabled: !root.session.bt.editingDeviceName + + function onClicked(): void { + root.session.bt.editingDeviceName = false; + deviceNameEdit.text = Qt.binding(() => root.device?.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.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 + color: root.session.bt.editingDeviceName ? Colours.palette.m3primary : "transparent" + + StateLayer { + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + + function onClicked(): void { + root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; + if (root.session.bt.editingDeviceName) + deviceNameEdit.forceActiveFocus(); + else + deviceNameEdit.accepted(); + } + } + + MaterialIcon { + id: editIcon + + anchors.centerIn: parent + animate: true + text: root.session.bt.editingDeviceName ? "check_circle" : "edit" + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + } + + Behavior on radius { + Anim {} + } + } + } + + Toggle { + label: qsTr("Trusted") + checked: root.device?.trusted ?? false + toggle.onToggled: root.device.trusted = checked + } + + Toggle { + label: qsTr("Wake allowed") + checked: root.device?.wakeAllowed ?? false + toggle.onToggled: root.device.wakeAllowed = checked + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device information") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Information about this device") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.palette.m3surfaceContainer + + ColumnLayout { + id: deviceInfo + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.small / 2 + + StyledText { + text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") + } + + RowLayout { + Layout.topMargin: Appearance.spacing.small / 2 + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.smaller + spacing: Appearance.spacing.small / 2 + + StyledRect { + Layout.fillHeight: true + implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } + + StyledRect { + Layout.fillWidth: true + Layout.fillHeight: true + radius: Appearance.rounding.full + color: Colours.palette.m3secondaryContainer + + StyledRect { + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: parent.height * 0.25 + + implicitWidth: height + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Dbus path") + } + + StyledText { + text: root.device?.dbusPath ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("MAC address") + } + + StyledText { + text: root.device?.address ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Bonded") + } + + StyledText { + text: root.device?.bonded ? qsTr("Yes") : qsTr("No") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("System name") + } + + StyledText { + text: root.device?.deviceName ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + } + } + } + } + + ColumnLayout { + anchors.right: fabRoot.right + anchors.bottom: fabRoot.top + anchors.bottomMargin: Appearance.padding.normal + + Repeater { + id: fabMenu + + model: ListModel { + ListElement { + name: "trust" + icon: "handshake" + } + ListElement { + name: "block" + icon: "block" + } + ListElement { + name: "pair" + icon: "missing_controller" + } + ListElement { + name: "connect" + icon: "bluetooth_connected" + } + } + + StyledClippingRect { + id: fabMenuItem + + required property var modelData + required property int index + + Layout.alignment: Qt.AlignRight + + implicitHeight: fabMenuItemInner.implicitHeight + Appearance.padding.larger * 2 + + radius: Appearance.rounding.full + color: Colours.palette.m3primaryContainer + + opacity: 0 + + states: State { + name: "visible" + when: root.session.bt.fabMenuOpen + + PropertyChanges { + fabMenuItem.implicitWidth: fabMenuItemInner.implicitWidth + Appearance.padding.large * 2 + fabMenuItem.opacity: 1 + fabMenuItemInner.opacity: 1 + } + } + + transitions: [ + Transition { + to: "visible" + + SequentialAnimation { + PauseAnimation { + duration: (fabMenu.count - 1 - fabMenuItem.index) * Appearance.anim.durations.small / 8 + } + ParallelAnimation { + Anim { + property: "implicitWidth" + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + Anim { + property: "opacity" + duration: Appearance.anim.durations.small + } + } + } + }, + Transition { + from: "visible" + + SequentialAnimation { + PauseAnimation { + duration: fabMenuItem.index * Appearance.anim.durations.small / 8 + } + ParallelAnimation { + Anim { + property: "implicitWidth" + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + Anim { + property: "opacity" + duration: Appearance.anim.durations.small + } + } + } + } + ] + + StateLayer { + function onClicked(): void { + root.session.bt.fabMenuOpen = false; + + const name = fabMenuItem.modelData.name; + if (fabMenuItem.modelData.name !== "pair") + root.device[`${name}ed`] = !root.device[`${name}ed`]; + else if (root.device.paired) + root.device.forget(); + else + root.device.pair(); + } + } + + RowLayout { + id: fabMenuItemInner + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + opacity: 0 + + MaterialIcon { + text: fabMenuItem.modelData.icon + color: Colours.palette.m3onPrimaryContainer + fill: 1 + } + + StyledText { + animate: true + text: (root.device && root.device[`${fabMenuItem.modelData.name}ed`] ? fabMenuItem.modelData.name === "connect" ? "dis" : "un" : "") + fabMenuItem.modelData.name + color: Colours.palette.m3onPrimaryContainer + font.capitalization: Font.Capitalize + Layout.preferredWidth: implicitWidth + + Behavior on Layout.preferredWidth { + Anim { + duration: Appearance.anim.durations.small + } + } + } + } + } + } + } + + Item { + id: fabRoot + + anchors.right: parent.right + anchors.bottom: parent.bottom + + implicitWidth: 64 + implicitHeight: 64 + + StyledRect { + id: fabBg + + anchors.right: parent.right + anchors.top: parent.top + + implicitWidth: 64 + implicitHeight: 64 + + radius: Appearance.rounding.normal + color: root.session.bt.fabMenuOpen ? Colours.palette.m3primary : Colours.palette.m3primaryContainer + + states: State { + name: "expanded" + when: root.session.bt.fabMenuOpen + + PropertyChanges { + fabBg.implicitWidth: 48 + fabBg.implicitHeight: 48 + fabBg.radius: 48 / 2 + fab.font.pointSize: Appearance.font.size.larger + } + } + + transitions: Transition { + Anim { + properties: "implicitWidth,implicitHeight" + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + Anim { + properties: "radius,font.pointSize" + } + } + + Elevation { + anchors.fill: parent + radius: parent.radius + z: -1 + level: fabState.containsMouse && !fabState.pressed ? 4 : 3 + } + + StateLayer { + id: fabState + + color: root.session.bt.fabMenuOpen ? Colours.palette.m3onPrimary : Colours.palette.m3onPrimaryContainer + + function onClicked(): void { + root.session.bt.fabMenuOpen = !root.session.bt.fabMenuOpen; + } + } + + MaterialIcon { + id: fab + + anchors.centerIn: parent + animate: true + text: root.session.bt.fabMenuOpen ? "close" : "settings" + color: root.session.bt.fabMenuOpen ? Colours.palette.m3onPrimary : Colours.palette.m3onPrimaryContainer + font.pointSize: Appearance.font.size.large + fill: 1 + } + } + } + + 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/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml new file mode 100644 index 0000000..d975e17 --- /dev/null +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -0,0 +1,363 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.containers +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 + + required property Session session + + anchors.fill: parent + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + ToggleButton { + toggled: Bluetooth.defaultAdapter?.enabled ?? false + icon: "power" + accent: "Tertiary" + + function onClicked(): void { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.enabled = !adapter.enabled; + } + } + + ToggleButton { + toggled: Bluetooth.defaultAdapter?.discoverable ?? false + icon: QsWindow.window.screen.height <= 1080 ? "group_search" : "" + label: QsWindow.window.screen.height <= 1080 ? "" : qsTr("Discoverable") + + function onClicked(): void { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.discoverable = !adapter.discoverable; + } + } + + ToggleButton { + toggled: Bluetooth.defaultAdapter?.pairable ?? false + icon: "missing_controller" + label: QsWindow.window.screen.height <= 960 ? "" : qsTr("Pairable") + + function onClicked(): void { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.pairable = !adapter.pairable; + } + } + + ToggleButton { + toggled: !root.session.bt.active + icon: "settings" + accent: "Primary" + + function onClicked(): void { + if (root.session.bt.active) + root.session.bt.active = null; + else { + root.session.bt.active = deviceModel.values[0] ?? null; + } + } + } + } + + RowLayout { + Layout.topMargin: Appearance.spacing.large + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + Layout.fillWidth: true + text: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length) + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + StyledText { + Layout.fillWidth: true + text: qsTr("All available bluetooth devices") + color: Colours.palette.m3outline + } + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2 + + radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 + color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer + + StateLayer { + color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer + + function onClicked(): void { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.discovering = !adapter.discovering; + } + } + + MaterialIcon { + id: scanIcon + + anchors.centerIn: parent + animate: true + text: "bluetooth_searching" + color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer + fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0 + } + + Behavior on radius { + Anim {} + } + } + } + + StyledListView { + model: ScriptModel { + id: deviceModel + values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired)) + } + + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + spacing: Appearance.spacing.small / 2 + + 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 + + color: root.session.bt.active === modelData ? Colours.palette.m3surfaceContainer : "transparent" + radius: Appearance.rounding.normal + + StateLayer { + id: stateLayer + + function onClicked(): void { + root.session.bt.active = device.modelData; + } + } + + RowLayout { + id: deviceInner + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.normal + 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 ToggleButton: StyledRect { + id: toggleBtn + + required property bool toggled + property string icon + property string label + property string accent: "Secondary" + + function onClicked(): void { + } + + Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.larger * 2 : toggled ? Appearance.padding.small * 2 : 0) + implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 + implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 + + radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 + color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] + + StateLayer { + id: toggleStateLayer + + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + + function onClicked(): void { + toggleBtn.onClicked(); + } + } + + RowLayout { + id: toggleBtnInner + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + MaterialIcon { + id: toggleBtnIcon + + visible: !!text + fill: toggleBtn.toggled ? 1 : 0 + text: toggleBtn.icon + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + font.pointSize: Appearance.font.size.large + + Behavior on fill { + Anim {} + } + } + + Loader { + asynchronous: true + active: !!toggleBtn.label + visible: active + + sourceComponent: StyledText { + text: toggleBtn.label + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + } + } + } + + Behavior on radius { + Anim {} + } + + Behavior on Layout.preferredWidth { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + } + + component Anim: NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } +} 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 + } +} diff --git a/modules/detachedcontent/DetachedContent.qml b/modules/detachedcontent/DetachedContent.qml deleted file mode 100644 index cf564cb..0000000 --- a/modules/detachedcontent/DetachedContent.qml +++ /dev/null @@ -1,61 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.components.controls -import qs.services -import qs.config -import Quickshell -import QtQuick -import QtQuick.Layouts - -Item { - id: root - - required property ShellScreen screen - property alias active: session.active - readonly property Session session: Session { - id: session - } - - implicitWidth: implicitHeight * Config.dcontent.sizes.ratio - implicitHeight: screen.height * Config.dcontent.sizes.heightMult - - RowLayout { - anchors.fill: parent - - spacing: 0 - - StyledRect { - Layout.fillHeight: true - - topLeftRadius: Appearance.rounding.normal - bottomLeftRadius: Appearance.rounding.normal - implicitWidth: navRail.implicitWidth - color: Colours.palette.m3surfaceContainer - - CustomMouseArea { - anchors.fill: parent - - function onWheel(event: WheelEvent): void { - if (event.angleDelta.y < 0) - root.session.activeIndex = Math.min(root.session.activeIndex + 1, root.session.panes.length - 1); - else if (event.angleDelta.y > 0) - root.session.activeIndex = Math.max(root.session.activeIndex - 1, 0); - } - } - - NavRail { - id: navRail - - session: root.session - } - } - - Panes { - Layout.fillWidth: true - Layout.fillHeight: true - - session: root.session - } - } -} diff --git a/modules/detachedcontent/NavRail.qml b/modules/detachedcontent/NavRail.qml deleted file mode 100644 index 826e8ca..0000000 --- a/modules/detachedcontent/NavRail.qml +++ /dev/null @@ -1,203 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -Item { - id: root - - required property Session session - property bool expanded - - implicitWidth: layout.implicitWidth + Appearance.padding.large * 4 - implicitHeight: layout.implicitHeight + Appearance.padding.large * 2 - - ColumnLayout { - id: layout - - anchors.centerIn: parent - spacing: Appearance.spacing.normal - - states: State { - name: "expanded" - when: root.expanded - - PropertyChanges { - layout.spacing: Appearance.spacing.small - menuIcon.opacity: 0 - menuIconExpanded.opacity: 1 - menuIcon.rotation: 180 - menuIconExpanded.rotation: 0 - } - AnchorChanges { - target: menuIcon - anchors.horizontalCenter: undefined - } - AnchorChanges { - target: menuIconExpanded - anchors.horizontalCenter: undefined - } - } - - transitions: Transition { - Anim { - properties: "spacing,opacity,rotation" - } - } - - Item { - Layout.fillWidth: true - Layout.bottomMargin: Appearance.spacing.large * 2 - implicitHeight: Math.max(menuIcon.implicitHeight, menuIconExpanded.implicitHeight) + Appearance.padding.normal * 2 - - StateLayer { - radius: Appearance.rounding.small - - function onClicked(): void { - root.expanded = !root.expanded; - } - } - - MaterialIcon { - id: menuIcon - - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - text: "menu" - font.pointSize: Appearance.font.size.large - } - - MaterialIcon { - id: menuIconExpanded - - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - text: "menu_open" - font.pointSize: Appearance.font.size.large - opacity: 0 - rotation: -180 - } - } - - NavItem { - icon: "network_manage" - label: "network" - } - - NavItem { - icon: "settings_bluetooth" - label: "bluetooth" - } - - NavItem { - icon: "tune" - label: "audio" - } - } - - component NavItem: Item { - id: item - - required property string icon - required property string label - readonly property bool active: root.session.active === label - - implicitWidth: background.implicitWidth - implicitHeight: background.implicitHeight + smallLabel.implicitHeight + smallLabel.anchors.topMargin - - states: State { - name: "expanded" - when: root.expanded - - PropertyChanges { - expandedLabel.opacity: 1 - smallLabel.opacity: 0 - background.implicitWidth: Config.dcontent.sizes.expandedNavWidth - background.implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - item.implicitHeight: background.implicitHeight - } - } - - transitions: Transition { - Anim { - property: "opacity" - duration: Appearance.anim.durations.small - } - - Anim { - properties: "implicitWidth,implicitHeight" - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - StyledRect { - id: background - - radius: Appearance.rounding.full - color: item.active ? Colours.palette.m3secondaryContainer : Colours.palette.m3surfaceContainer - - implicitWidth: icon.implicitWidth + icon.anchors.leftMargin * 2 - implicitHeight: icon.implicitHeight + Appearance.padding.small - - StateLayer { - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - - function onClicked(): void { - root.session.active = item.label; - } - } - - MaterialIcon { - id: icon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.padding.large - - text: item.icon - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.large - fill: item.active ? 1 : 0 - - Behavior on fill { - Anim {} - } - } - - StyledText { - id: expandedLabel - - anchors.left: icon.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.spacing.normal - - opacity: 0 - text: item.label - color: item.active ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface - font.capitalization: Font.Capitalize - } - - StyledText { - id: smallLabel - - anchors.horizontalCenter: icon.horizontalCenter - anchors.top: icon.bottom - anchors.topMargin: Appearance.spacing.small / 2 - - text: item.label - font.pointSize: Appearance.font.size.small - font.capitalization: Font.Capitalize - } - } - } - - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/modules/detachedcontent/Panes.qml b/modules/detachedcontent/Panes.qml deleted file mode 100644 index d1ec581..0000000 --- a/modules/detachedcontent/Panes.qml +++ /dev/null @@ -1,98 +0,0 @@ -pragma ComponentBehavior: Bound - -import "bluetooth" -import qs.components -import qs.components.effects -import qs.services -import qs.config -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts - -ClippingRectangle { - id: root - - required property Session session - - topRightRadius: Appearance.rounding.normal - bottomRightRadius: Appearance.rounding.normal - color: "transparent" - - ColumnLayout { - id: layout - - spacing: 0 - y: -root.session.activeIndex * root.height - - Pane { - 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 { - index: 1 - sourceComponent: BtPane { - session: root.session - } - } - - Pane { - index: 2 - sourceComponent: Item { - StyledText { - anchors.centerIn: parent - text: qsTr("Work in progress") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.extraLarge - font.weight: 500 - } - } - } - - Behavior on y { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - InnerBorder { - leftThickness: 0 - } - - component Pane: Item { - id: pane - - required property int index - property alias sourceComponent: loader.sourceComponent - - implicitWidth: root.width - implicitHeight: root.height - - Loader { - id: loader - - anchors.fill: parent - clip: true - asynchronous: true - active: { - if (root.session.activeIndex === pane.index) - return true; - - const ly = -layout.y; - const ty = pane.index * root.height; - return ly + root.height > ty && ly < ty + root.height; - } - } - } -} diff --git a/modules/detachedcontent/Session.qml b/modules/detachedcontent/Session.qml deleted file mode 100644 index 0c3f1b4..0000000 --- a/modules/detachedcontent/Session.qml +++ /dev/null @@ -1,22 +0,0 @@ -import Quickshell.Bluetooth -import QtQuick - -QtObject { - readonly property list panes: ["network", "bluetooth", "audio"] - - 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 - property bool editingAdapterName - property bool fabMenuOpen - property bool editingDeviceName - } -} diff --git a/modules/detachedcontent/bluetooth/BtPane.qml b/modules/detachedcontent/bluetooth/BtPane.qml deleted file mode 100644 index c6dbbcc..0000000 --- a/modules/detachedcontent/bluetooth/BtPane.qml +++ /dev/null @@ -1,121 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components.effects -import qs.components.containers -import qs.config -import Quickshell.Bluetooth -import QtQuick -import QtQuick.Layouts - -RowLayout { - id: root - - required property Session session - - anchors.fill: parent - - spacing: 0 - - Item { - 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 { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - - Loader { - id: loader - - property BluetoothDevice pane: root.session.bt.active - - 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: pane ? details : settings - - Behavior on pane { - SequentialAnimation { - ParallelAnimation { - Anim { - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - Anim { - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - } - PropertyAction {} - ParallelAnimation { - Anim { - property: "opacity" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - property: "scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - } - } - - InnerBorder { - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: settings - - StyledFlickable { - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height - - Settings { - id: settingsInner - - anchors.left: parent.left - anchors.right: parent.right - session: root.session - } - } - } - - Component { - id: details - - Details { - session: root.session - } - } - } - - component Anim: NumberAnimation { - target: loader - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } -} diff --git a/modules/detachedcontent/bluetooth/Details.qml b/modules/detachedcontent/bluetooth/Details.qml deleted file mode 100644 index f856002..0000000 --- a/modules/detachedcontent/bluetooth/Details.qml +++ /dev/null @@ -1,663 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.effects -import qs.components.containers -import qs.services -import qs.config -import qs.utils -import Quickshell.Bluetooth -import QtQuick -import QtQuick.Layouts - -Item { - id: root - - required property Session session - readonly property BluetoothDevice device: session.bt.active - - StyledFlickable { - anchors.fill: parent - - flickableDirection: Flickable.VerticalFlick - contentHeight: layout.height - - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.normal - - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - animate: true - text: Icons.getBluetoothIcon(root.device.icon) - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - animate: true - text: root.device?.name ?? "" - font.pointSize: Appearance.font.size.large - font.bold: true - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Connection status") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Connection settings for this device") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.palette.m3surfaceContainer - - ColumnLayout { - id: deviceStatus - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.larger - - Toggle { - label: qsTr("Connected") - checked: root.device?.connected ?? false - toggle.onToggled: root.device.connected = checked - } - - Toggle { - label: qsTr("Paired") - checked: root.device?.paired ?? false - toggle.onToggled: { - if (root.device.paired) - root.device.forget(); - else - root.device.pair(); - } - } - - Toggle { - label: qsTr("Blocked") - checked: root.device?.blocked ?? false - toggle.onToggled: root.device.blocked = checked - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device properties") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Additional settings") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.palette.m3surfaceContainer - - ColumnLayout { - id: deviceProps - - 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.small - - Item { - id: renameDevice - - Layout.fillWidth: true - Layout.rightMargin: Appearance.spacing.small - - implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight - - states: State { - name: "editingDeviceName" - when: root.session.bt.editingDeviceName - - AnchorChanges { - target: deviceNameEdit - anchors.top: renameDevice.top - } - PropertyChanges { - renameDevice.implicitHeight: deviceNameEdit.implicitHeight - renameLabel.opacity: 0 - deviceNameEdit.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("Device name") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledTextField { - id: deviceNameEdit - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: renameLabel.bottom - anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal - - text: root.device?.name ?? "" - readOnly: !root.session.bt.editingDeviceName - onAccepted: { - root.session.bt.editingDeviceName = false; - root.device.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.editingDeviceName ? 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.editingDeviceName ? 1 : 0 - scale: root.session.bt.editingDeviceName ? 1 : 0.5 - - StateLayer { - color: Colours.palette.m3onSecondaryContainer - disabled: !root.session.bt.editingDeviceName - - function onClicked(): void { - root.session.bt.editingDeviceName = false; - deviceNameEdit.text = Qt.binding(() => root.device?.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.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 - color: root.session.bt.editingDeviceName ? Colours.palette.m3primary : "transparent" - - StateLayer { - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - - function onClicked(): void { - root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; - if (root.session.bt.editingDeviceName) - deviceNameEdit.forceActiveFocus(); - else - deviceNameEdit.accepted(); - } - } - - MaterialIcon { - id: editIcon - - anchors.centerIn: parent - animate: true - text: root.session.bt.editingDeviceName ? "check_circle" : "edit" - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - } - - Behavior on radius { - Anim {} - } - } - } - - Toggle { - label: qsTr("Trusted") - checked: root.device?.trusted ?? false - toggle.onToggled: root.device.trusted = checked - } - - Toggle { - label: qsTr("Wake allowed") - checked: root.device?.wakeAllowed ?? false - toggle.onToggled: root.device.wakeAllowed = checked - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Information about this device") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.palette.m3surfaceContainer - - ColumnLayout { - id: deviceInfo - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.small / 2 - - StyledText { - text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") - } - - RowLayout { - Layout.topMargin: Appearance.spacing.small / 2 - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.smaller - spacing: Appearance.spacing.small / 2 - - StyledRect { - Layout.fillHeight: true - implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 - radius: Appearance.rounding.full - color: Colours.palette.m3primary - } - - StyledRect { - Layout.fillWidth: true - Layout.fillHeight: true - radius: Appearance.rounding.full - color: Colours.palette.m3secondaryContainer - - StyledRect { - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.margins: parent.height * 0.25 - - implicitWidth: height - radius: Appearance.rounding.full - color: Colours.palette.m3primary - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Dbus path") - } - - StyledText { - text: root.device?.dbusPath ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("MAC address") - } - - StyledText { - text: root.device?.address ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Bonded") - } - - StyledText { - text: root.device?.bonded ? qsTr("Yes") : qsTr("No") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("System name") - } - - StyledText { - text: root.device?.deviceName ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - } - } - } - } - - ColumnLayout { - anchors.right: fabRoot.right - anchors.bottom: fabRoot.top - anchors.bottomMargin: Appearance.padding.normal - - Repeater { - id: fabMenu - - model: ListModel { - ListElement { - name: "trust" - icon: "handshake" - } - ListElement { - name: "block" - icon: "block" - } - ListElement { - name: "pair" - icon: "missing_controller" - } - ListElement { - name: "connect" - icon: "bluetooth_connected" - } - } - - StyledClippingRect { - id: fabMenuItem - - required property var modelData - required property int index - - Layout.alignment: Qt.AlignRight - - implicitHeight: fabMenuItemInner.implicitHeight + Appearance.padding.larger * 2 - - radius: Appearance.rounding.full - color: Colours.palette.m3primaryContainer - - opacity: 0 - - states: State { - name: "visible" - when: root.session.bt.fabMenuOpen - - PropertyChanges { - fabMenuItem.implicitWidth: fabMenuItemInner.implicitWidth + Appearance.padding.large * 2 - fabMenuItem.opacity: 1 - fabMenuItemInner.opacity: 1 - } - } - - transitions: [ - Transition { - to: "visible" - - SequentialAnimation { - PauseAnimation { - duration: (fabMenu.count - 1 - fabMenuItem.index) * Appearance.anim.durations.small / 8 - } - ParallelAnimation { - Anim { - property: "implicitWidth" - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - Anim { - property: "opacity" - duration: Appearance.anim.durations.small - } - } - } - }, - Transition { - from: "visible" - - SequentialAnimation { - PauseAnimation { - duration: fabMenuItem.index * Appearance.anim.durations.small / 8 - } - ParallelAnimation { - Anim { - property: "implicitWidth" - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - Anim { - property: "opacity" - duration: Appearance.anim.durations.small - } - } - } - } - ] - - StateLayer { - function onClicked(): void { - root.session.bt.fabMenuOpen = false; - - const name = fabMenuItem.modelData.name; - if (fabMenuItem.modelData.name !== "pair") - root.device[`${name}ed`] = !root.device[`${name}ed`]; - else if (root.device.paired) - root.device.forget(); - else - root.device.pair(); - } - } - - RowLayout { - id: fabMenuItemInner - - anchors.centerIn: parent - spacing: Appearance.spacing.normal - opacity: 0 - - MaterialIcon { - text: fabMenuItem.modelData.icon - color: Colours.palette.m3onPrimaryContainer - fill: 1 - } - - StyledText { - animate: true - text: (root.device && root.device[`${fabMenuItem.modelData.name}ed`] ? fabMenuItem.modelData.name === "connect" ? "dis" : "un" : "") + fabMenuItem.modelData.name - color: Colours.palette.m3onPrimaryContainer - font.capitalization: Font.Capitalize - Layout.preferredWidth: implicitWidth - - Behavior on Layout.preferredWidth { - Anim { - duration: Appearance.anim.durations.small - } - } - } - } - } - } - } - - Item { - id: fabRoot - - anchors.right: parent.right - anchors.bottom: parent.bottom - - implicitWidth: 64 - implicitHeight: 64 - - StyledRect { - id: fabBg - - anchors.right: parent.right - anchors.top: parent.top - - implicitWidth: 64 - implicitHeight: 64 - - radius: Appearance.rounding.normal - color: root.session.bt.fabMenuOpen ? Colours.palette.m3primary : Colours.palette.m3primaryContainer - - states: State { - name: "expanded" - when: root.session.bt.fabMenuOpen - - PropertyChanges { - fabBg.implicitWidth: 48 - fabBg.implicitHeight: 48 - fabBg.radius: 48 / 2 - fab.font.pointSize: Appearance.font.size.larger - } - } - - transitions: Transition { - Anim { - properties: "implicitWidth,implicitHeight" - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - Anim { - properties: "radius,font.pointSize" - } - } - - Elevation { - anchors.fill: parent - radius: parent.radius - z: -1 - level: fabState.containsMouse && !fabState.pressed ? 4 : 3 - } - - StateLayer { - id: fabState - - color: root.session.bt.fabMenuOpen ? Colours.palette.m3onPrimary : Colours.palette.m3onPrimaryContainer - - function onClicked(): void { - root.session.bt.fabMenuOpen = !root.session.bt.fabMenuOpen; - } - } - - MaterialIcon { - id: fab - - anchors.centerIn: parent - animate: true - text: root.session.bt.fabMenuOpen ? "close" : "settings" - color: root.session.bt.fabMenuOpen ? Colours.palette.m3onPrimary : Colours.palette.m3onPrimaryContainer - font.pointSize: Appearance.font.size.large - fill: 1 - } - } - } - - 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/detachedcontent/bluetooth/DeviceList.qml b/modules/detachedcontent/bluetooth/DeviceList.qml deleted file mode 100644 index d975e17..0000000 --- a/modules/detachedcontent/bluetooth/DeviceList.qml +++ /dev/null @@ -1,363 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.containers -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 - - required property Session session - - anchors.fill: parent - spacing: Appearance.spacing.small - - RowLayout { - spacing: Appearance.spacing.smaller - - StyledText { - text: qsTr("Settings") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - ToggleButton { - toggled: Bluetooth.defaultAdapter?.enabled ?? false - icon: "power" - accent: "Tertiary" - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.enabled = !adapter.enabled; - } - } - - ToggleButton { - toggled: Bluetooth.defaultAdapter?.discoverable ?? false - icon: QsWindow.window.screen.height <= 1080 ? "group_search" : "" - label: QsWindow.window.screen.height <= 1080 ? "" : qsTr("Discoverable") - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.discoverable = !adapter.discoverable; - } - } - - ToggleButton { - toggled: Bluetooth.defaultAdapter?.pairable ?? false - icon: "missing_controller" - label: QsWindow.window.screen.height <= 960 ? "" : qsTr("Pairable") - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.pairable = !adapter.pairable; - } - } - - ToggleButton { - toggled: !root.session.bt.active - icon: "settings" - accent: "Primary" - - function onClicked(): void { - if (root.session.bt.active) - root.session.bt.active = null; - else { - root.session.bt.active = deviceModel.values[0] ?? null; - } - } - } - } - - RowLayout { - Layout.topMargin: Appearance.spacing.large - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small - - StyledText { - Layout.fillWidth: true - text: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length) - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - - StyledText { - Layout.fillWidth: true - text: qsTr("All available bluetooth devices") - color: Colours.palette.m3outline - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer - - StateLayer { - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.discovering = !adapter.discovering; - } - } - - MaterialIcon { - id: scanIcon - - anchors.centerIn: parent - animate: true - text: "bluetooth_searching" - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0 - } - - Behavior on radius { - Anim {} - } - } - } - - StyledListView { - model: ScriptModel { - id: deviceModel - values: [...Bluetooth.devices.values].sort((a, b) => (b.connected - a.connected) || (b.paired - a.paired)) - } - - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - spacing: Appearance.spacing.small / 2 - - 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 - - color: root.session.bt.active === modelData ? Colours.palette.m3surfaceContainer : "transparent" - radius: Appearance.rounding.normal - - StateLayer { - id: stateLayer - - function onClicked(): void { - root.session.bt.active = device.modelData; - } - } - - RowLayout { - id: deviceInner - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.normal - 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 ToggleButton: StyledRect { - id: toggleBtn - - required property bool toggled - property string icon - property string label - property string accent: "Secondary" - - function onClicked(): void { - } - - Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.larger * 2 : toggled ? Appearance.padding.small * 2 : 0) - implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 - implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 - color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] - - StateLayer { - id: toggleStateLayer - - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - - function onClicked(): void { - toggleBtn.onClicked(); - } - } - - RowLayout { - id: toggleBtnInner - - anchors.centerIn: parent - spacing: Appearance.spacing.normal - - MaterialIcon { - id: toggleBtnIcon - - visible: !!text - fill: toggleBtn.toggled ? 1 : 0 - text: toggleBtn.icon - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - font.pointSize: Appearance.font.size.large - - Behavior on fill { - Anim {} - } - } - - Loader { - asynchronous: true - active: !!toggleBtn.label - visible: active - - sourceComponent: StyledText { - text: toggleBtn.label - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - } - } - } - - Behavior on radius { - Anim {} - } - - Behavior on Layout.preferredWidth { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } - } - - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard - } -} diff --git a/modules/detachedcontent/bluetooth/Settings.qml b/modules/detachedcontent/bluetooth/Settings.qml deleted file mode 100644 index f298432..0000000 --- a/modules/detachedcontent/bluetooth/Settings.qml +++ /dev/null @@ -1,546 +0,0 @@ -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 - } -} -- cgit v1.2.3-freya