diff --git a/.gitignore b/.gitignore index 39a44e6..48e59ee 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ dist/ **/target/ **/test-plugins/ **/Charts/ +**/network-dev/ diff --git a/Helpers/Network.qml b/Helpers/Network.qml index 1bf124e..5d608c0 100644 --- a/Helpers/Network.qml +++ b/Helpers/Network.qml @@ -2,10 +2,19 @@ pragma Singleton import Quickshell import Quickshell.Networking +import QtQuick Singleton { id: root + readonly property list devices: Networking.devices.values + readonly property list networks: initialBuildNetworks() + property bool scanning: false + readonly property list wifiDevices: devices.filter(d => wifiDevice(d)) + readonly property bool wifiEnabled: Networking.wifiEnabled + + // Original code + readonly property list netDevice: Networking.devices.values readonly property string networkName: getNetworkName() readonly property list nicNames: networkInterfaceCardNames() @@ -42,4 +51,75 @@ Singleton { return nicList; } + // + + function initialBuildNetworks(): void { + const init = []; + for (const d of wifiDevices) { + d.scannerEnabled = true; + init.push(...d.networks.values); + d.scannerEnabled = false; + } + + networks = init; + } + + function isSecure(security): bool { + return !(security === WifiSecurityType.Open); + } + + function rebuildNetworks(): void { + if (!scanning) + return; + + const next = []; + for (const d of wifiDevices) { + next.push(...d.networks.values); + } + + networks = next; + } + + function rescanWifi(): void { + scanning = true; + setScan(true); + scanTimer.restart(); + } + + function setScan(value: bool): void { + for (const d of wifiDevices) { + d.scannerEnabled = value; + } + } + + function setWifi(value: bool): void { + Networking.wifiEnabled = value; + } + + function wifiDevice(dev): bool { + return dev.type === DeviceType.Wifi; + } + + Timer { + id: scanTimer + + interval: 5000 + repeat: false + + onTriggered: { + root.rebuildNetworks(); + root.setScan(false); + root.scanning = false; + } + } + + Timer { + interval: 500 + repeat: true + running: root.scanning + + onTriggered: { + root.rebuildNetworks(); + } + } } diff --git a/Modules/SysTray/Popouts/NetworkPopout.qml b/Modules/SysTray/Popouts/NetworkPopout.qml index 8e9b82e..63ce794 100644 --- a/Modules/SysTray/Popouts/NetworkPopout.qml +++ b/Modules/SysTray/Popouts/NetworkPopout.qml @@ -1,6 +1,8 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Layouts +import Quickshell import qs.Components import qs.Config import qs.Helpers @@ -12,40 +14,206 @@ CustomClippingRect { anchors.horizontalCenter: parent.horizontalCenter color: DynamicColors.tPalette.m3surfaceContainer - implicitHeight: 500 + 5 * 2 + implicitHeight: networkPopContent.height + networks.implicitHeight + networkPopContent.anchors.margins + networks.anchors.margins * 2 implicitWidth: 500 + 8 * 2 radius: (20 - Appearance.padding.small) * Appearance.rounding.scale - Item { + ColumnLayout { id: networkPopContent - anchors.fill: parent - anchors.margins: Appearance.padding.small + anchors.left: parent.left + anchors.margins: Appearance.padding.large + anchors.right: parent.right + anchors.top: parent.top CustomText { - anchors.left: parent.left - anchors.margins: Appearance.padding.large - anchors.top: parent.top + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.rightMargin: Appearance.padding.extraSmall font.pointSize: Appearance.font.size.large text: qsTr("Network") } - CustomText { - anchors.margins: Appearance.padding.large - anchors.right: parent.right - anchors.top: parent.top - font.pointSize: Appearance.font.size.large - text: Network.networkName + Toggle { + Layout.preferredHeight: visible ? implicitHeight : 0 + checked: Network.wifiEnabled + label: qsTr("WiFi enabled") + + toggle.onToggled: Network.setWifi(checked) } - StateLayer { - id: buttonScan + CustomText { + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.rightMargin: Appearance.padding.extraSmall + Layout.topMargin: visible ? Appearance.spacing.small : 0 + color: DynamicColors.palette.m3onSurfaceVariant + text: qsTr("%1 networks available").arg(Network.networks.length) // qmllint disable missing-property + } + } - onClicked: { - console.log("Network devices: " + Network.netDevice); - console.log("Network interface names: " + Network.nicNames); - console.log("Connected devices: " + Network.networkName); + ColumnLayout { + id: networks + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: Appearance.padding.large + anchors.right: parent.right + anchors.top: networkPopContent.bottom + + Repeater { + model: ScriptModel { + values: [...Network.networks] + } + + RowLayout { + id: networkItem + + required property var modelData + + Layout.fillWidth: true + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.rightMargin: Appearance.padding.extraSmall + opacity: 0 + scale: 0.7 + spacing: Appearance.spacing.small + + Behavior on opacity { + Anim { + type: Anim.DefaultEffects + } + } + Behavior on scale { + Anim { + } + } + + Component.onCompleted: { + opacity = 1; + scale = 1; + } + + MaterialIcon { + color: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant + text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, Network.isSecure(networkItem.modelData.security)) + } + + CustomText { + Layout.fillWidth: true + Layout.leftMargin: Appearance.spacing.extraSmall + Layout.rightMargin: Appearance.spacing.extraSmall + color: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface + elide: Text.ElideRight + text: networkItem.modelData.name + } + + CustomRect { + color: Qt.alpha(DynamicColors.palette.m3primary, networkItem.modelData.active ? 1 : 0) + implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.extraSmall + implicitWidth: implicitHeight + radius: Appearance.rounding.full + + // CircularIndicator { + // anchors.fill: parent + // running: networkItem.loading + // } + + StateLayer { + color: networkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface + + onClicked: { + console.log(Network.devices[1].scannerEnabled, Network.devices[2].scannerEnabled, Network.devices[3].scannerEnabled, Network.devices[4].scannerEnabled); + } + } + + MaterialIcon { + id: wirelessConnectIcon + + anchors.centerIn: parent + animate: true + color: networkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface + text: networkItem.modelData.active ? "link_off" : "link" + + // opacity: networkItem.loading ? 0 : 1 + + Behavior on opacity { + Anim { + type: Anim.DefaultEffects + } + } + } + } + } + } + + CustomRect { + Layout.fillWidth: true + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.spacing.small : 0 + color: DynamicColors.palette.m3primaryContainer + implicitHeight: rescanBtn.implicitHeight + Appearance.padding.small + radius: Appearance.rounding.full + + StateLayer { + color: DynamicColors.palette.m3onPrimaryContainer + enabled: !Network.scanning + + onClicked: Network.rescanWifi() + } + + RowLayout { + id: rescanBtn + + anchors.centerIn: parent + opacity: Network.scanning ? 0 : 1 + spacing: Appearance.spacing.small + + Behavior on opacity { + Anim { + type: Anim.DefaultEffects + } + } + + MaterialIcon { + id: scanIcon + + Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) + animate: true + color: DynamicColors.palette.m3onPrimaryContainer + text: "wifi_find" + } + + CustomText { + Layout.topMargin: -Math.round(scanIcon.fontInfo.pointSize * 0.0575) + color: DynamicColors.palette.m3onPrimaryContainer + text: qsTr("Rescan networks") + } + } + + CircularIndicator { + anchors.centerIn: parent + bgColor: "transparent" + implicitSize: parent.implicitHeight - Appearance.padding.large + running: Network.scanning + strokeWidth: Appearance.padding.extraSmall / 2 } } } + + component Toggle: RowLayout { + property alias checked: toggle.checked + required property string label + property alias toggle: toggle + + Layout.fillWidth: true + Layout.rightMargin: Appearance.padding.extraSmall + spacing: Appearance.spacing.small + + CustomText { + Layout.fillWidth: true + text: parent.label + } + + CustomSwitch { + id: toggle + } + } }