import QtQuick import QtQuick.Controls import Quickshell import Quickshell.Io import Quickshell.Widgets import qs.Common import qs.Services import qs.Widgets import "../../Widgets" Item { id: networkTab // Helper function for consistent WiFi signal icons function getWiFiSignalIcon(signalStrength) { switch (signalStrength) { case "excellent": return "wifi"; case "good": return "wifi_2_bar"; case "fair": return "wifi_1_bar"; case "poor": return "signal_wifi_0_bar"; default: return "wifi"; } } // Properly sorted WiFi networks with connected networks first property var sortedWifiNetworks: { if (!NetworkService.wifiAvailable || !NetworkService.wifiEnabled) { return []; } // Explicitly reference both arrays to ensure reactivity var allNetworks = WifiService.wifiNetworks; var savedNetworks = WifiService.savedWifiNetworks; var currentSSID = WifiService.currentWifiSSID; var signalStrength = WifiService.wifiSignalStrength; var refreshTrigger = forceRefresh; // Force recalculation var networks = [...allNetworks]; // Update connected status, saved status and signal strength based on current state networks.forEach(function(network) { network.connected = (network.ssid === currentSSID); // Update saved status based on savedWifiNetworks network.saved = savedNetworks.some(function(saved) { return saved.ssid === network.ssid; }); // Use current connection's signal strength for connected network if (network.connected && signalStrength) { network.signalStrength = signalStrength; } }); // Sort: connected networks first, then by signal strength networks.sort(function(a, b) { // Connected networks always come first if (a.connected && !b.connected) return -1; if (!a.connected && b.connected) return 1; // If both connected or both not connected, sort by signal strength return b.signal - a.signal; }); return networks; } // Force refresh of sortedWifiNetworks when networks are updated property int forceRefresh: 0 Connections { target: WifiService function onNetworksUpdated() { forceRefresh++; } } // Auto-enable WiFi auto-refresh when network tab is visible Component.onCompleted: { WifiService.autoRefreshEnabled = true; if (NetworkService.wifiEnabled) WifiService.scanWifi(); // Start smart monitoring wifiMonitorTimer.start(); } // Two-column layout for WiFi and Ethernet (WiFi on left, Ethernet on right) Row { anchors.fill: parent spacing: Theme.spacingM // WiFi Column (left side) Column { width: (parent.width - Theme.spacingM) / 2 height: parent.height spacing: Theme.spacingS // WiFi Content in Flickable Flickable { width: parent.width height: parent.height - 30 clip: true contentWidth: width contentHeight: wifiContent.height boundsBehavior: Flickable.DragAndOvershootBounds flickDeceleration: 8000 maximumFlickVelocity: 15000 Column { id: wifiContent width: parent.width spacing: Theme.spacingM // Current WiFi connection status card Rectangle { id: wifiCard width: parent.width height: 80 radius: Theme.cornerRadius color: { if (wifiPreferenceArea.containsMouse && NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "wifi") return Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.8); return Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5); } border.color: NetworkService.networkStatus === "wifi" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: NetworkService.networkStatus === "wifi" ? 2 : 1 visible: NetworkService.wifiAvailable Column { anchors.left: parent.left anchors.leftMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter anchors.right: wifiToggle.left anchors.rightMargin: Theme.spacingM spacing: Theme.spacingS Row { spacing: Theme.spacingM DankIcon { name: { if (!NetworkService.wifiEnabled) { return "wifi_off"; } else if (WifiService.currentWifiSSID !== "") { return getWiFiSignalIcon(WifiService.wifiSignalStrength); } else { return "wifi"; } } size: Theme.iconSize color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: { if (!NetworkService.wifiEnabled) { return "WiFi is off"; } else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) { return WifiService.currentWifiSSID || "Connected"; } else { return "Not Connected"; } } font.pixelSize: Theme.fontSizeMedium color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight } } Text { text: { if (!NetworkService.wifiEnabled) { return "Turn on WiFi to see networks"; } else if (NetworkService.wifiEnabled && WifiService.currentWifiSSID) { return NetworkService.wifiIP || "Connected"; } else { return "Select a network below"; } } font.pixelSize: Theme.fontSizeSmall color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) leftPadding: Theme.iconSize + Theme.spacingM elide: Text.ElideRight } } // Loading spinner for preference changes DankIcon { id: wifiLoadingSpinner name: "refresh" size: Theme.iconSize - 4 color: Theme.primary anchors.right: wifiToggle.left anchors.rightMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter visible: NetworkService.changingPreference && NetworkService.targetPreference === "wifi" z: 10 RotationAnimation { target: wifiLoadingSpinner property: "rotation" running: wifiLoadingSpinner.visible from: 0 to: 360 duration: 1000 loops: Animation.Infinite } } // WiFi toggle switch DankToggle { id: wifiToggle checked: NetworkService.wifiEnabled enabled: true toggling: NetworkService.wifiToggling anchors.right: parent.right anchors.rightMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter onClicked: { if (NetworkService.wifiEnabled) { // When turning WiFi off, clear all cached WiFi data WifiService.currentWifiSSID = ""; WifiService.wifiSignalStrength = "excellent"; WifiService.wifiNetworks = []; WifiService.savedWifiNetworks = []; WifiService.connectionStatus = ""; WifiService.connectingSSID = ""; WifiService.isScanning = false; NetworkService.refreshNetworkStatus(); } NetworkService.toggleWifiRadio(); refreshTimer.triggered = true; } } // MouseArea for network preference (excluding toggle area) MouseArea { id: wifiPreferenceArea anchors.fill: parent anchors.rightMargin: 60 // Exclude toggle area hoverEnabled: true cursorShape: (NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "wifi") ? Qt.PointingHandCursor : Qt.ArrowCursor enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "wifi" && !NetworkService.changingNetworkPreference onClicked: { if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) { console.log("WiFi card clicked for preference"); if (NetworkService.networkStatus !== "wifi") NetworkService.setNetworkPreference("wifi"); else NetworkService.setNetworkPreference("auto"); } } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } } } // Ethernet Column (right side) Column { width: (parent.width - Theme.spacingM) / 2 height: parent.height spacing: Theme.spacingS // Ethernet Header removed // Ethernet Content in Flickable Flickable { width: parent.width height: parent.height - 30 clip: true contentWidth: width contentHeight: ethernetContent.height boundsBehavior: Flickable.StopAtBounds flickDeceleration: 8000 maximumFlickVelocity: 15000 Column { id: ethernetContent width: parent.width spacing: Theme.spacingM // Ethernet connection status card (matching WiFi height) Rectangle { id: ethernetCard width: parent.width height: 80 radius: Theme.cornerRadius color: { if (ethernetPreferenceArea.containsMouse && NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "ethernet") return Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.8); return Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5); } border.color: NetworkService.networkStatus === "ethernet" ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: NetworkService.networkStatus === "ethernet" ? 2 : 1 Column { anchors.left: parent.left anchors.leftMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter anchors.right: ethernetToggle.left anchors.rightMargin: Theme.spacingM spacing: Theme.spacingS Row { spacing: Theme.spacingM DankIcon { name: "lan" size: Theme.iconSize color: NetworkService.networkStatus === "ethernet" ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: "Ethernet" font.pixelSize: Theme.fontSizeMedium color: NetworkService.networkStatus === "ethernet" ? Theme.primary : Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter elide: Text.ElideRight } } Text { text: NetworkService.ethernetConnected ? (NetworkService.ethernetIP || "Connected") : "Disconnected" font.pixelSize: Theme.fontSizeSmall color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) leftPadding: Theme.iconSize + Theme.spacingM elide: Text.ElideRight } } // Loading spinner for preference changes DankIcon { id: ethernetLoadingSpinner name: "refresh" size: Theme.iconSize - 4 color: Theme.primary anchors.right: ethernetToggle.left anchors.rightMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter visible: NetworkService.changingPreference && NetworkService.targetPreference === "ethernet" z: 10 RotationAnimation { target: ethernetLoadingSpinner property: "rotation" running: ethernetLoadingSpinner.visible from: 0 to: 360 duration: 1000 loops: Animation.Infinite } } // Ethernet toggle switch (matching WiFi style) DankToggle { id: ethernetToggle checked: NetworkService.ethernetConnected enabled: true anchors.right: parent.right anchors.rightMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter onClicked: { NetworkService.toggleNetworkConnection("ethernet"); } } // MouseArea for network preference (excluding toggle area) MouseArea { id: ethernetPreferenceArea anchors.fill: parent anchors.rightMargin: 60 // Exclude toggle area hoverEnabled: true cursorShape: (NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "ethernet") ? Qt.PointingHandCursor : Qt.ArrowCursor enabled: NetworkService.ethernetConnected && NetworkService.wifiEnabled && NetworkService.networkStatus !== "ethernet" && !NetworkService.changingNetworkPreference onClicked: { if (NetworkService.ethernetConnected && NetworkService.wifiEnabled) { console.log("Ethernet card clicked for preference"); if (NetworkService.networkStatus !== "ethernet") NetworkService.setNetworkPreference("ethernet"); else NetworkService.setNetworkPreference("auto"); } } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } } } } // WiFi disabled message spanning across both columns 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) } Text { 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 } Text { 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) } } } // WiFi networks spanning across both columns when Ethernet preference button is hidden Column { anchors.top: parent.top anchors.topMargin: 100 anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom visible: NetworkService.wifiEnabled spacing: Theme.spacingS // Available Networks Section with refresh button (spanning version) Row { width: parent.width spacing: Theme.spacingS Text { text: "Available Networks" font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } Item { width: parent.width - 170 height: 1 } // WiFi refresh button (spanning version) Rectangle { width: 28 height: 28 radius: 14 color: refreshAreaSpan.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : WifiService.isScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent" DankIcon { id: refreshIconSpan anchors.centerIn: parent name: "refresh" size: Theme.iconSize - 6 color: refreshAreaSpan.containsMouse ? Theme.primary : Theme.surfaceText rotation: WifiService.isScanning ? refreshIconSpan.rotation : 0 RotationAnimation { target: refreshIconSpan property: "rotation" running: WifiService.isScanning from: 0 to: 360 duration: 1000 loops: Animation.Infinite } Behavior on rotation { RotationAnimation { duration: 200 easing.type: Easing.OutQuad } } } MouseArea { id: refreshAreaSpan anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { if (!WifiService.isScanning) { // Immediate visual feedback refreshIconSpan.rotation += 30; WifiService.scanWifi(); } } } } } // Scrollable networks container Flickable { width: parent.width height: parent.height - 40 clip: true contentWidth: width contentHeight: spanningNetworksColumn.height boundsBehavior: Flickable.DragAndOvershootBounds flickDeceleration: 8000 maximumFlickVelocity: 15000 Column { id: spanningNetworksColumn width: parent.width spacing: Theme.spacingXS Repeater { model: NetworkService.wifiAvailable && NetworkService.wifiEnabled ? sortedWifiNetworks : [] Rectangle { width: parent.width height: 38 radius: Theme.cornerRadiusSmall color: networkArea2.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : modelData.connected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" border.color: modelData.connected ? Theme.primary : "transparent" border.width: modelData.connected ? 1 : 0 Item { anchors.fill: parent anchors.margins: Theme.spacingXS anchors.rightMargin: Theme.spacingM // Extra right margin for scrollbar // Signal strength icon DankIcon { id: signalIcon2 anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter name: getWiFiSignalIcon(modelData.signalStrength) size: Theme.iconSize - 2 color: modelData.connected ? Theme.primary : Theme.surfaceText } // Network info Column { anchors.left: signalIcon2.right anchors.leftMargin: Theme.spacingXS anchors.right: rightIcons2.left anchors.rightMargin: Theme.spacingXS anchors.verticalCenter: parent.verticalCenter spacing: 2 Text { width: parent.width text: modelData.ssid font.pixelSize: Theme.fontSizeSmall color: modelData.connected ? Theme.primary : Theme.surfaceText font.weight: modelData.connected ? Font.Medium : Font.Normal elide: Text.ElideRight } Text { width: parent.width text: { if (modelData.connected) return "Connected"; if (WifiService.connectionStatus === "connecting" && WifiService.connectingSSID === modelData.ssid) return "Connecting..."; if (WifiService.connectionStatus === "invalid_password" && WifiService.connectingSSID === modelData.ssid) return "Invalid password"; if (modelData.saved) return "Saved" + (modelData.secured ? " • Secured" : " • Open"); return modelData.secured ? "Secured" : "Open"; } font.pixelSize: Theme.fontSizeSmall - 1 color: { if (WifiService.connectionStatus === "connecting" && WifiService.connectingSSID === modelData.ssid) return Theme.primary; if (WifiService.connectionStatus === "invalid_password" && WifiService.connectingSSID === modelData.ssid) return Theme.error; return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7); } elide: Text.ElideRight } } // Right side icons Row { id: rightIcons2 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS // Lock icon (if secured) DankIcon { name: "lock" size: Theme.iconSize - 8 color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) visible: modelData.secured anchors.verticalCenter: parent.verticalCenter } // Context menu button Rectangle { id: wifiMenuButton width: 24 height: 24 radius: 12 color: wifiMenuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent" DankIcon { name: "more_vert" size: Theme.iconSize - 8 color: Theme.surfaceText opacity: 0.6 anchors.centerIn: parent } MouseArea { id: wifiMenuButtonArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { wifiContextMenuWindow.networkData = modelData; let localPos = wifiMenuButtonArea.mapToItem(networkTab, wifiMenuButtonArea.width / 2, wifiMenuButtonArea.height); wifiContextMenuWindow.show(localPos.x, localPos.y); } } Behavior on color { ColorAnimation { duration: Theme.shortDuration } } } } } MouseArea { id: networkArea2 anchors.fill: parent anchors.rightMargin: 32 // Exclude menu button area hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { if (modelData.connected) return; if (modelData.saved) { // Saved network, connect directly WifiService.connectToWifi(modelData.ssid); } else if (modelData.secured) { // Secured network, need password - use root dialog wifiPasswordDialog.wifiPasswordSSID = modelData.ssid; wifiPasswordDialog.wifiPasswordInput = ""; wifiPasswordDialog.wifiPasswordDialogVisible = true; } else { // Open network, connect directly WifiService.connectToWifi(modelData.ssid); } } } } } } ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } } } // Timer for refreshing network status after WiFi toggle Timer { id: refreshTimer interval: 2000 running: visible && refreshTimer.triggered property bool triggered: false onTriggered: { NetworkService.refreshNetworkStatus(); if (NetworkService.wifiEnabled && !WifiService.isScanning) { WifiService.scanWifi(); } triggered = false; } } // Auto-refresh when WiFi state changes Connections { target: NetworkService function onWifiEnabledChanged() { if (NetworkService.wifiEnabled && visible) { // When WiFi is enabled, scan and update info (only if tab is visible) // Add a small delay to ensure WiFi service is ready wifiScanDelayTimer.start(); // Start monitoring when WiFi comes back on wifiMonitorTimer.start(); } else { // When WiFi is disabled, clear all cached WiFi data WifiService.currentWifiSSID = ""; WifiService.wifiSignalStrength = "excellent"; WifiService.wifiNetworks = []; WifiService.savedWifiNetworks = []; WifiService.connectionStatus = ""; WifiService.connectingSSID = ""; WifiService.isScanning = false; NetworkService.refreshNetworkStatus(); // Stop monitoring when WiFi is off wifiMonitorTimer.stop(); } } } // Delayed WiFi scan timer to ensure service is ready Timer { id: wifiScanDelayTimer interval: 1500 running: false repeat: false onTriggered: { if (NetworkService.wifiEnabled && visible) { if (!WifiService.isScanning) { WifiService.scanWifi(); } else { // If still scanning, try again in a bit wifiRetryTimer.start(); } } } } // Retry timer for when WiFi is still scanning Timer { id: wifiRetryTimer interval: 2000 running: false repeat: false onTriggered: { if (NetworkService.wifiEnabled && visible && WifiService.wifiNetworks.length === 0) { if (!WifiService.isScanning) { WifiService.scanWifi(); } } } } // Smart WiFi monitoring - only runs when tab visible and conditions met Timer { id: wifiMonitorTimer interval: 8000 // Check every 8 seconds running: false repeat: true onTriggered: { if (!visible || !NetworkService.wifiEnabled) { // Stop monitoring when not needed running = false; return; } // Monitor connection changes and refresh networks when disconnected var shouldScan = false; var reason = ""; // Always scan if not connected to WiFi if (NetworkService.networkStatus !== "wifi") { shouldScan = true; reason = "not connected to WiFi"; } // Also scan occasionally even when connected to keep networks fresh else if (WifiService.wifiNetworks.length === 0) { shouldScan = true; reason = "no networks cached"; } if (shouldScan && !WifiService.isScanning) { WifiService.scanWifi(); } } } // Monitor tab visibility to start/stop smart monitoring onVisibleChanged: { if (visible && NetworkService.wifiEnabled) { wifiMonitorTimer.start(); } else { wifiMonitorTimer.stop(); } } // WiFi Context Menu Window Rectangle { id: wifiContextMenuWindow property var networkData: null property bool menuVisible: false function show(x, y) { const menuWidth = 160; const menuHeight = wifiMenuColumn.implicitHeight + Theme.spacingS * 2; let finalX = x - menuWidth / 2; let finalY = y; finalX = Math.max(0, Math.min(finalX, networkTab.width - menuWidth)); finalY = Math.max(0, Math.min(finalY, networkTab.height - menuHeight)); wifiContextMenuWindow.x = finalX; wifiContextMenuWindow.y = finalY; wifiContextMenuWindow.visible = true; wifiContextMenuWindow.menuVisible = true; } function hide() { wifiContextMenuWindow.menuVisible = false; Qt.callLater(() => { wifiContextMenuWindow.visible = false; }); } visible: false width: 160 height: wifiMenuColumn.implicitHeight + Theme.spacingS * 2 radius: Theme.cornerRadiusLarge color: Theme.popupBackground() border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 z: 1000 opacity: menuVisible ? 1 : 0 scale: menuVisible ? 1 : 0.85 // Drop shadow Rectangle { anchors.fill: parent anchors.topMargin: 4 anchors.leftMargin: 2 anchors.rightMargin: -2 anchors.bottomMargin: -4 radius: parent.radius color: Qt.rgba(0, 0, 0, 0.15) z: parent.z - 1 } Column { id: wifiMenuColumn anchors.fill: parent anchors.margins: Theme.spacingS spacing: 1 // Connect/Disconnect option Rectangle { width: parent.width height: 32 radius: Theme.cornerRadiusSmall color: connectWifiArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingS DankIcon { name: wifiContextMenuWindow.networkData && wifiContextMenuWindow.networkData.connected ? "wifi_off" : "wifi" size: Theme.iconSize - 2 color: Theme.surfaceText opacity: 0.7 anchors.verticalCenter: parent.verticalCenter } Text { text: wifiContextMenuWindow.networkData && wifiContextMenuWindow.networkData.connected ? "Disconnect" : "Connect" font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceText font.weight: Font.Normal anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: connectWifiArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { if (wifiContextMenuWindow.networkData) { if (wifiContextMenuWindow.networkData.connected) { // Disconnect from current network WifiService.disconnectWifi(); } else { // Connect to selected network if (wifiContextMenuWindow.networkData.saved) { WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid); } else if (wifiContextMenuWindow.networkData.secured) { // Show password dialog for secured networks wifiPasswordDialog.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid; wifiPasswordDialog.wifiPasswordInput = ""; wifiPasswordDialog.wifiPasswordDialogVisible = true; } else { WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid); } } } wifiContextMenuWindow.hide(); } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } // Separator Rectangle { width: parent.width - Theme.spacingS * 2 height: 5 anchors.horizontalCenter: parent.horizontalCenter color: "transparent" Rectangle { anchors.centerIn: parent width: parent.width height: 1 color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) } } // Forget Network option (only for saved networks) Rectangle { width: parent.width height: 32 radius: Theme.cornerRadiusSmall color: forgetWifiArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent" visible: wifiContextMenuWindow.networkData && (wifiContextMenuWindow.networkData.saved || wifiContextMenuWindow.networkData.connected) Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingS DankIcon { name: "delete" size: Theme.iconSize - 2 color: forgetWifiArea.containsMouse ? Theme.error : Theme.surfaceText opacity: 0.7 anchors.verticalCenter: parent.verticalCenter } Text { text: "Forget Network" font.pixelSize: Theme.fontSizeSmall color: forgetWifiArea.containsMouse ? Theme.error : Theme.surfaceText font.weight: Font.Normal anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: forgetWifiArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { if (wifiContextMenuWindow.networkData) { WifiService.forgetWifiNetwork(wifiContextMenuWindow.networkData.ssid); } wifiContextMenuWindow.hide(); } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } // Network Info option Rectangle { width: parent.width height: 32 radius: Theme.cornerRadiusSmall color: infoWifiArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" Row { anchors.left: parent.left anchors.leftMargin: Theme.spacingS anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingS DankIcon { name: "info" size: Theme.iconSize - 2 color: Theme.surfaceText opacity: 0.7 anchors.verticalCenter: parent.verticalCenter } Text { text: "Network Info" font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceText font.weight: Font.Normal anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: infoWifiArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { if (wifiContextMenuWindow.networkData) { networkInfoDialog.showNetworkInfo(wifiContextMenuWindow.networkData.ssid, wifiContextMenuWindow.networkData); } wifiContextMenuWindow.hide(); } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } Behavior on opacity { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on scale { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } } // Background MouseArea to close the context menu MouseArea { anchors.fill: parent visible: wifiContextMenuWindow.visible onClicked: { wifiContextMenuWindow.hide(); } MouseArea { x: wifiContextMenuWindow.x y: wifiContextMenuWindow.y width: wifiContextMenuWindow.width height: wifiContextMenuWindow.height onClicked: { // Prevent clicks on menu from closing it } } } }