import QtQuick import QtQuick.Controls import Quickshell import Quickshell.Io import Quickshell.Widgets import qs.Common import qs.Services import qs.Widgets import qs.Modules.ControlCenter.Network Item { id: networkTab property var wifiPasswordModalRef: wifiPasswordModal property var networkInfoModalRef: networkInfoModal property var sortedWifiNetworks: { if (!NetworkService.wifiAvailable || !NetworkService.wifiEnabled) { return [] } var allNetworks = NetworkService.wifiNetworks var savedNetworks = NetworkService.savedWifiNetworks var currentSSID = NetworkService.currentWifiSSID var signalStrength = NetworkService.wifiSignalStrength var refreshTrigger = forceRefresh // Force recalculation var networks = [...allNetworks] networks.forEach(function (network) { network.connected = (network.ssid === currentSSID) network.saved = savedNetworks.some(function (saved) { return saved.ssid === network.ssid }) if (network.connected && signalStrength) { network.signalStrength = signalStrength } }) networks.sort(function (a, b) { if (a.connected && !b.connected) return -1 if (!a.connected && b.connected) return 1 return b.signal - a.signal }) return networks } property int forceRefresh: 0 Connections { target: NetworkService function onNetworksUpdated() { forceRefresh++ } } Component.onCompleted: { NetworkService.addRef() NetworkService.autoRefreshEnabled = true if (NetworkService.wifiEnabled) NetworkService.scanWifi() wifiMonitorTimer.start() } Component.onDestruction: { NetworkService.removeRef() NetworkService.autoRefreshEnabled = false } Row { anchors.fill: parent spacing: Theme.spacingM Column { width: (parent.width - Theme.spacingM) / 2 height: parent.height spacing: Theme.spacingS Flickable { width: parent.width height: parent.height - 30 clip: true contentWidth: width contentHeight: wifiContent.height boundsBehavior: Flickable.DragAndOvershootBounds // Qt 6.9+ scrolling: flickDeceleration/maximumFlickVelocity only affect touch now flickDeceleration: 1500 maximumFlickVelocity: 2000 // Custom wheel handler for Qt 6.9+ responsive mouse wheel scrolling WheelHandler { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad onWheel: event => { let delta = event.pixelDelta.y !== 0 ? event.pixelDelta.y * 1.8 : event.angleDelta.y / 120 * 60 let newY = parent.contentY - delta newY = Math.max( 0, Math.min(parent.contentHeight - parent.height, newY)) parent.contentY = newY event.accepted = true } } Column { id: wifiContent width: parent.width spacing: Theme.spacingM WiFiCard { refreshTimer: refreshTimer } } ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } } } Column { width: (parent.width - Theme.spacingM) / 2 height: parent.height spacing: Theme.spacingS Flickable { width: parent.width height: parent.height - 30 clip: true contentWidth: width contentHeight: ethernetContent.height boundsBehavior: Flickable.StopAtBounds // Qt 6.9+ scrolling: flickDeceleration/maximumFlickVelocity only affect touch now flickDeceleration: 1500 maximumFlickVelocity: 2000 // Custom wheel handler for Qt 6.9+ responsive mouse wheel scrolling WheelHandler { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad onWheel: event => { let delta = event.pixelDelta.y !== 0 ? event.pixelDelta.y * 1.8 : event.angleDelta.y / 120 * 60 let newY = parent.contentY - delta newY = Math.max( 0, Math.min(parent.contentHeight - parent.height, newY)) parent.contentY = newY event.accepted = true } } Column { id: ethernetContent width: parent.width spacing: Theme.spacingM EthernetCard {} } ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } } } } Rectangle { anchors.top: parent.top anchors.topMargin: 100 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom color: "transparent" visible: !NetworkService.wifiEnabled Column { anchors.centerIn: parent spacing: Theme.spacingM DankIcon { anchors.horizontalCenter: parent.horizontalCenter name: "wifi_off" size: 48 color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3) } StyledText { anchors.horizontalCenter: parent.horizontalCenter text: "WiFi is turned off" font.pixelSize: Theme.fontSizeLarge color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) font.weight: Font.Medium } StyledText { anchors.horizontalCenter: parent.horizontalCenter text: "Turn on WiFi to see networks" font.pixelSize: Theme.fontSizeMedium color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4) } } } WiFiNetworksList { wifiContextMenuWindow: wifiContextMenuWindow sortedWifiNetworks: networkTab.sortedWifiNetworks wifiPasswordModalRef: networkTab.wifiPasswordModalRef } Timer { id: refreshTimer interval: 2000 running: visible && refreshTimer.triggered property bool triggered: false onTriggered: { NetworkService.refreshNetworkStatus() if (NetworkService.wifiEnabled && !NetworkService.isScanning) { NetworkService.scanWifi() } triggered = false } } Connections { target: NetworkService function onWifiEnabledChanged() { if (NetworkService.wifiEnabled && visible) { wifiScanDelayTimer.start() wifiMonitorTimer.start() } else { NetworkService.currentWifiSSID = "" NetworkService.wifiSignalStrength = "excellent" NetworkService.wifiNetworks = [] NetworkService.savedWifiNetworks = [] NetworkService.connectionStatus = "" NetworkService.connectingSSID = "" NetworkService.isScanning = false NetworkService.refreshNetworkStatus() wifiMonitorTimer.stop() } } } Timer { id: wifiScanDelayTimer interval: 1500 running: false repeat: false onTriggered: { if (NetworkService.wifiEnabled && visible) { if (!NetworkService.isScanning) { NetworkService.scanWifi() } else { wifiRetryTimer.start() } } } } Timer { id: wifiRetryTimer interval: 2000 running: false repeat: false onTriggered: { if (NetworkService.wifiEnabled && visible && NetworkService.wifiNetworks.length === 0) { if (!NetworkService.isScanning) { NetworkService.scanWifi() } } } } Timer { id: wifiMonitorTimer interval: 8000 // Check every 8 seconds running: false repeat: true onTriggered: { if (!visible || !NetworkService.wifiEnabled) { running = false return } var shouldScan = false var reason = "" if (NetworkService.networkStatus !== "wifi") { shouldScan = true reason = "not connected to WiFi" } else if (NetworkService.wifiNetworks.length === 0) { shouldScan = true reason = "no networks cached" } if (shouldScan && !NetworkService.isScanning) { NetworkService.scanWifi() } } } onVisibleChanged: { if (visible && NetworkService.wifiEnabled) { wifiMonitorTimer.start() } else { wifiMonitorTimer.stop() } } WiFiContextMenu { id: wifiContextMenuWindow parentItem: networkTab wifiPasswordModalRef: networkTab.wifiPasswordModalRef networkInfoModalRef: networkTab.networkInfoModalRef } MouseArea { anchors.fill: parent visible: wifiContextMenuWindow.visible onClicked: { wifiContextMenuWindow.hide() } MouseArea { x: wifiContextMenuWindow.x y: wifiContextMenuWindow.y width: wifiContextMenuWindow.width height: wifiContextMenuWindow.height onClicked: { } } } }