diff --git a/Modules/Bar/Popouts/BluetoothPopout.qml b/Modules/Bar/Popouts/BluetoothPopout.qml index d82bee1..6680bb4 100644 --- a/Modules/Bar/Popouts/BluetoothPopout.qml +++ b/Modules/Bar/Popouts/BluetoothPopout.qml @@ -14,6 +14,8 @@ import qs.Helpers CustomClippingRect { id: root + readonly property list btConnected: Bluetooth.devices.values.filter(d => d.connected) + implicitHeight: layout.implicitHeight + layout.anchors.margins * 2 implicitWidth: 500 @@ -46,7 +48,7 @@ CustomClippingRect { Toggle { checked: Bluetooth.defaultAdapter?.discovering ?? false // qmllint disable unresolved-type - last: rows.sortedConnected.length === 0 + last: root.btConnected.length === 0 subtext: qsTr("Enable scanning for new devices") text: qsTr("Discover") @@ -60,11 +62,6 @@ CustomClippingRect { ListView { id: rows - readonly property var sortedConnected: { - const byName = (a, b) => a.name.localeCompare(b.name); - return [...Bluetooth.devices.values].filter(d => d.connected).sort(byName).slice(0, 5); - } - Layout.fillWidth: true Layout.preferredHeight: contentHeight interactive: false @@ -141,7 +138,7 @@ CustomClippingRect { component BluetoothItem: ConnectedRect { id: btItem - readonly property int connectedCount: rows.sortedConnected.length + readonly property int connectedCount: root.btConnected.length readonly property bool connecting: Network.connectingNetwork === modelData property int horizontalPadding: Tokens.padding.largeIncreased readonly property bool inConnectedGroup: index < connectedCount diff --git a/Modules/Bar/Popouts/NetworkPopout.qml b/Modules/Bar/Popouts/NetworkPopout.qml index b35b6f5..a005570 100644 --- a/Modules/Bar/Popouts/NetworkPopout.qml +++ b/Modules/Bar/Popouts/NetworkPopout.qml @@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell +import ZShell.Components import ZShell.Config import qs.Services import qs.Modules.Bar.Popouts.Network @@ -15,7 +16,7 @@ CustomClippingRect { required property var wrapper anchors.horizontalCenter: parent.horizontalCenter - implicitHeight: networkPopContent.height + networks.implicitHeight + networkPopContent.anchors.margins + networks.anchors.margins * 2 + implicitHeight: networkPopContent.height + networkPopContent.anchors.margins * 2 implicitWidth: 500 + 8 * 2 radius: (20 - Tokens.padding.small) * Tokens.rounding.scale @@ -42,99 +43,94 @@ CustomClippingRect { Layout.preferredHeight: visible ? implicitHeight : 0 checked: Network.wifiEnabled first: true - last: connected.count === 0 + last: Network.connectedNetworks.length === 0 subtext: qsTr("Toggle WiFi device state") text: checked ? qsTr("Enabled") : qsTr("Disabled") onToggled: Network.setWifi(checked) } - Repeater { - id: connected + ListView { + id: rows - visible: Network.connectedNetworks.length > 0 + Layout.fillWidth: true + Layout.preferredHeight: contentHeight + interactive: false + section.property: "connected" + spacing: Tokens.spacing.extraSmall / 2 + add: Transition { + Anim { + from: 0 + property: "opacity" + to: 1 + } + } + delegate: DelegateChooser { + role: "isDivider" + + DelegateChoice { + roleValue: true + + delegate: StatusDivider { + } + } + + DelegateChoice { + delegate: NetworkItem { + list: rows + width: rows.width + } + } + } + displaced: Transition { + Anim { + easing.type: Easing.OutCubic + property: "y" + } + } model: ScriptModel { - values: [...Network.connectedNetworks].sort((a, b) => { - return b.signalStrength - a.signalStrength; - }) + values: { + const byStrengthName = (a, b) => b.signalStrength - a.signalStrength || a.name.localeCompare(b.name); + const cNetworks = [...Network.connectedNetworks]; + const kNetworks = [...Network.knownNetworks]; + const uNetworks = [...Network.unknownNetworks]; + + return [...cNetworks.sort(byStrengthName), ...(kNetworks.length > 0 ? [ + { + isDivider: true, + type: "known" + }, + ...kNetworks.sort(byStrengthName)] : []), ...(uNetworks.length > 0 ? [ + { + isDivider: true, + type: "unknown" + }, + ...uNetworks.sort(byStrengthName)] : [])]; + } } - - NetworkItem { - first: false - known: true - repeater: connected + move: Transition { + Anim { + easing.type: Easing.OutCubic + property: "y" + } } - } - } - - ColumnLayout { - id: networks - - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.margins: Tokens.padding.large - anchors.right: parent.right - anchors.top: networkPopContent.bottom - spacing: Tokens.spacing.extraSmall / 2 - - Spacer { - repeater: known - visible: Network.connectedNetworks.length > 0 - } - - CustomText { - Layout.leftMargin: Tokens.padding.normal - Layout.preferredHeight: visible ? implicitHeight : 0 - color: Colors.palette.m3onSurfaceVariant - font.pointSize: Tokens.font.size.small - text: Network.knownNetworks.length > 1 ? qsTr("%1 known networks available").arg(Network.knownNetworks.length) : qsTr("1 known network available") // qmllint disable missing-property - visible: Network.knownNetworks.length > 0 - } - - Repeater { - id: known - - visible: Network.knownNetworks.length > 0 - - model: ScriptModel { - values: [...Network.knownNetworks].sort((a, b) => { - return b.signalStrength - a.signalStrength; - }) + remove: Transition { + Anim { + property: "opacity" + to: 0 + } } + section.delegate: Rectangle { + required property string section - NetworkItem { - known: true - repeater: known - } - } + color: "transparent" + height: childrenRect.height + width: ListView.view.width - Spacer { - repeater: unknown - visible: Network.knownNetworks.length > 0 - } - - CustomText { - Layout.leftMargin: Tokens.padding.normal - Layout.preferredHeight: visible ? implicitHeight : 0 - color: Colors.palette.m3onSurfaceVariant - font.pointSize: Tokens.font.size.small - text: Network.unknownNetworks.length > 1 ? qsTr("%1 networks available").arg(Network.unknownNetworks.length) : qsTr("1 network available") // qmllint disable missing-property - visible: Network.unknownNetworks.length > 0 - } - - Repeater { - id: unknown - - model: ScriptModel { - values: [...Network.unknownNetworks].sort((a, b) => { - return b.signalStrength - a.signalStrength; - }) - } - - NetworkItem { - known: false - repeater: unknown + CustomText { + text: parent.section + } } } } @@ -158,23 +154,31 @@ CustomClippingRect { component NetworkItem: ConnectedRect { id: networkItem + readonly property int connectedCount: Network.connectedNetworks.length readonly property bool connecting: Network.connectingNetwork === modelData property int horizontalPadding: Tokens.padding.largeIncreased + readonly property bool inConnectedGroup: index < connectedCount + // sectioning first / last item in list reminder + // if index === network.connectedNetworks.length - 1 + // if index === network.connectedNetworks.length - 1 + network.knownNetworks.length - 2 + // if index === network.connectedNetworks.length - 1 + network.knownNetworks.length + // list.model.values.length - 1 required property int index readonly property bool isSecure: Network.isSecure(modelData.security) - required property bool known + required property ListView list required property var modelData - required property Repeater repeater + property int verticalPadding: Tokens.padding.smaller Layout.fillWidth: true - Layout.preferredHeight: visible ? rowLayout.implicitHeight + Tokens.padding.smaller * 2 : 0 - first: index === 0 - last: index === (repeater.count - 1) + first: !inConnectedGroup && index === connectedCount + 1 + implicitHeight: rowLayout.implicitHeight + verticalPadding * 2 + last: inConnectedGroup ? index === connectedCount - 1 : index === list.count - 1 radius: Tokens.rounding.small RowLayout { id: rowLayout + Layout.preferredHeight: visible ? rowLayout.implicitHeight + Tokens.padding.smaller * 2 : 0 anchors.fill: parent anchors.leftMargin: networkItem.horizontalPadding anchors.rightMargin: networkItem.horizontalPadding @@ -201,7 +205,7 @@ CustomClippingRect { color: Colors.palette.m3outline elide: Text.ElideRight font.pointSize: Tokens.font.size.small - text: qsTr("%1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%") + text: networkItem.modelData.connected ? qsTr("Connected: %1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%") : qsTr("%1 strength").arg(Math.round(networkItem.modelData.signalStrength * 100) + "%") } } @@ -255,12 +259,28 @@ CustomClippingRect { } } } - component Spacer: Item { - id: spacer + component StatusDivider: CustomText { + required property var modelData - required property Repeater repeater + bottomPadding: Tokens.spacing.extraSmall + color: Colors.palette.m3onSurfaceVariant + font.pointSize: Tokens.font.size.smaller + text: { + if (modelData.type === "known") { + const count = Network.knownNetworks.filter(n => !n.connected).length; - Layout.preferredHeight: repeater.count > 0 ? Tokens.spacing.extraSmall : 0 + return qsTr("%1 known network%2").arg(count).arg(count === 1 ? "" : "s"); + } + + if (modelData.type === "unknown") { + const count = Network.unknownNetworks.filter(n => !n.connected).length; + + return qsTr("%1 network%2 available").arg(count).arg(count === 1 ? "" : "s"); + } + + return qsTr("object unknown"); + } + topPadding: Tokens.spacing.small } component Toggle: CustomSwitch { id: toggleItem