From 0d153c407d5e262888f111792d3045910e960007 Mon Sep 17 00:00:00 2001 From: purian23 Date: Sat, 19 Jul 2025 20:52:01 -0400 Subject: [PATCH] Combine network tab interface --- Modules/ControlCenter/NetworkTab.qml | 1194 +++++++++++++++----------- 1 file changed, 672 insertions(+), 522 deletions(-) diff --git a/Modules/ControlCenter/NetworkTab.qml b/Modules/ControlCenter/NetworkTab.qml index 61cb0e7f..af4fa182 100644 --- a/Modules/ControlCenter/NetworkTab.qml +++ b/Modules/ControlCenter/NetworkTab.qml @@ -9,491 +9,205 @@ import qs.Widgets import "../../Widgets" Item { - // Default to WiFi when nothing is connected - id: networkTab - property int networkSubTab: { - // Default to WiFi tab if WiFi is connected, otherwise Ethernet - if (NetworkService.networkStatus === "wifi") - return 1; - else if (NetworkService.networkStatus === "ethernet") - return 0; - else - return 1; + // Auto-enable WiFi auto-refresh when network tab is visible + Component.onCompleted: { + WifiService.autoRefreshEnabled = true; + if (NetworkService.wifiEnabled) + WifiService.scanWifi(); } - Column { + // Two-column layout for WiFi and Ethernet (WiFi on left, Ethernet on right) + Row { anchors.fill: parent spacing: Theme.spacingM - // Network sub-tabs - DankTabBar { - width: parent.width - currentIndex: networkTab.networkSubTab - model: [ - { - "icon": "lan", - "text": "Ethernet" - }, - { - "icon": NetworkService.wifiEnabled ? "wifi" : "wifi_off", - "text": "Wi-Fi" - } - ] - onTabClicked: function(index) { - networkTab.networkSubTab = index; - if (index === 0) { - WifiService.autoRefreshEnabled = false; - } else { - WifiService.autoRefreshEnabled = true; - if (NetworkService.wifiEnabled) - WifiService.scanWifi(); - } - } - } - - // Ethernet Tab Content - Flickable { - width: parent.width - height: parent.height - 48 - visible: networkTab.networkSubTab === 0 - clip: true - contentWidth: width - contentHeight: ethernetContent.height - boundsBehavior: Flickable.StopAtBounds - flickDeceleration: 8000 - maximumFlickVelocity: 15000 - - Column { - id: ethernetContent + // WiFi Column (left side) + Column { + width: (parent.width - Theme.spacingM) / 2 + height: parent.height + spacing: Theme.spacingS + // WiFi Header + Text { + text: "Wi-Fi" + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText + font.weight: Font.Medium width: parent.width - spacing: Theme.spacingL + } + + // 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 - // Ethernet status card - Rectangle { width: parent.width - height: 70 - radius: Theme.cornerRadius - color: 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 - visible: true + spacing: Theme.spacingM - Row { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingL - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: "lan" - size: Theme.iconSizeLarge - 4 - color: networkTab.networkStatus === "ethernet" ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } + // Current WiFi connection status card + Rectangle { + width: parent.width + height: 80 + radius: Theme.cornerRadius + color: 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 { - spacing: 4 + anchors.left: parent.left + anchors.leftMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter - - Text { - text: networkTab.networkStatus === "ethernet" ? (networkTab.ethernetInterface || "Ethernet") : "Ethernet" - font.pixelSize: Theme.fontSizeMedium - color: networkTab.networkStatus === "ethernet" ? Theme.primary : Theme.surfaceText - font.weight: Font.Medium - } - - 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) - } - - } - - // Force Ethernet preference button - Rectangle { - width: 150 - height: 30 - color: networkTab.networkStatus === "ethernet" ? Theme.primary : Theme.surface - border.color: Theme.primary - border.width: 1 - radius: 6 - anchors.verticalCenter: parent.verticalCenter - z: 10 - opacity: NetworkService.changingPreference ? 0.6 : 1 - visible: NetworkService.networkStatus !== "ethernet" && NetworkService.wifiAvailable && NetworkService.wifiEnabled + anchors.right: wifiToggle.left + anchors.rightMargin: Theme.spacingM + spacing: Theme.spacingS Row { - anchors.centerIn: parent - spacing: Theme.spacingXS + spacing: Theme.spacingM DankIcon { - id: ethernetPreferenceIcon - - name: NetworkService.changingPreference ? "sync" : "" - size: Theme.fontSizeSmall - color: networkTab.networkStatus === "ethernet" ? Theme.background : Theme.primary - visible: NetworkService.changingPreference - anchors.verticalCenter: parent.verticalCenter - rotation: NetworkService.changingPreference ? ethernetPreferenceIcon.rotation : 0 - - RotationAnimation { - target: ethernetPreferenceIcon - property: "rotation" - running: NetworkService.changingPreference - from: 0 - to: 360 - duration: 1000 - loops: Animation.Infinite + name: { + if (!NetworkService.wifiEnabled) { + return "wifi_off"; + } else if (NetworkService.networkStatus === "wifi") { + return WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi"; + } else { + return "wifi"; + } } - + size: Theme.iconSize + color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter } Text { - text: NetworkService.changingPreference ? "Switching..." : (networkTab.networkStatus === "ethernet" ? "" : "Prefer over WiFi") - font.pixelSize: Theme.fontSizeSmall - color: networkTab.networkStatus === "ethernet" ? Theme.background : Theme.primary - anchors.verticalCenter: parent.verticalCenter - font.weight: Font.Medium - } - - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - propagateComposedEvents: false - enabled: !NetworkService.changingPreference - onClicked: { - console.log("*** ETHERNET PREFERENCE BUTTON CLICKED ***"); - if (networkTab.networkStatus !== "ethernet") { - console.log("Setting preference to ethernet"); - NetworkService.setNetworkPreference("ethernet"); - } else { - console.log("Setting preference to auto"); - NetworkService.setNetworkPreference("auto"); + 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 } } - Behavior on opacity { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - - } - - } - - } - - // Ethernet control button - Rectangle { - width: parent.width - height: 50 - radius: Theme.cornerRadius - color: ethernetControlArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) - - Row { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingM - anchors.verticalCenter: parent.verticalCenter - spacing: Theme.spacingM - - DankIcon { - name: networkTab.ethernetConnected ? "link_off" : "link" - size: Theme.iconSize - color: networkTab.ethernetConnected ? Theme.error : Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: networkTab.ethernetConnected ? "Disconnect Ethernet" : "Connect Ethernet" - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - font.weight: Font.Medium - anchors.verticalCenter: parent.verticalCenter - } - - } - - MouseArea { - id: ethernetControlArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - NetworkService.toggleNetworkConnection("ethernet"); - } - } - - } - - } - - ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AsNeeded - } - - } - - // WiFi Tab Content - Flickable { - width: parent.width - height: parent.height - 48 - visible: networkTab.networkSubTab === 1 - clip: true - contentWidth: width - contentHeight: wifiContent.height - boundsBehavior: Flickable.StopAtBounds - flickDeceleration: 8000 - maximumFlickVelocity: 15000 - - Column { - id: wifiContent - - width: parent.width - spacing: Theme.spacingM - - - // Current WiFi connection (if connected) - Rectangle { - width: parent.width - height: 60 - radius: Theme.cornerRadius - color: 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 - - // WiFi icon - DankIcon { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingL - anchors.verticalCenter: parent.verticalCenter - name: { - if (!NetworkService.wifiEnabled) { - return "wifi_off"; - } else if (NetworkService.networkStatus === "wifi") { - return WifiService.wifiSignalStrength === "excellent" ? "wifi" : WifiService.wifiSignalStrength === "good" ? "wifi_2_bar" : WifiService.wifiSignalStrength === "fair" ? "wifi_1_bar" : WifiService.wifiSignalStrength === "poor" ? "wifi_calling_3" : "wifi"; - } else { - return "wifi"; - } - } - size: Theme.iconSize - color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText - } - - // WiFi info text - Column { - anchors.left: parent.left - anchors.leftMargin: Theme.spacingL + Theme.iconSize + Theme.spacingM - anchors.right: parent.right - anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM - anchors.verticalCenter: parent.verticalCenter - spacing: 4 - - 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 - } - - Text { - text: { - if (!NetworkService.wifiEnabled) { - return "Turn on WiFi to see available 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) - } - } - - // WiFi toggle switch - DankToggle { - checked: NetworkService.wifiEnabled - enabled: true - toggling: NetworkService.wifiToggling - anchors.right: parent.right - anchors.rightMargin: Theme.spacingL - anchors.verticalCenter: parent.verticalCenter - onClicked: { - NetworkService.toggleWifiRadio(); - refreshTimer.triggered = true; - } - } - - // Force WiFi preference button - Rectangle { - width: 150 - height: 30 - color: networkTab.networkStatus === "wifi" ? Theme.primary : Theme.surface - border.color: Theme.primary - border.width: 1 - radius: 6 - anchors.right: parent.right - anchors.rightMargin: Theme.spacingL + 48 + Theme.spacingM - anchors.verticalCenter: parent.verticalCenter - opacity: NetworkService.changingPreference ? 0.6 : 1 - visible: NetworkService.networkStatus !== "wifi" && NetworkService.ethernetConnected && NetworkService.wifiEnabled - - Row { - anchors.centerIn: parent - spacing: Theme.spacingXS - - DankIcon { - id: wifiPreferenceIcon - - name: NetworkService.changingPreference ? "sync" : "" - size: Theme.fontSizeSmall - color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary - visible: NetworkService.changingPreference - anchors.verticalCenter: parent.verticalCenter - rotation: NetworkService.changingPreference ? wifiPreferenceIcon.rotation : 0 - - RotationAnimation { - target: wifiPreferenceIcon - property: "rotation" - running: NetworkService.changingPreference - from: 0 - to: 360 - duration: 1000 - loops: Animation.Infinite - } - - } - Text { - text: NetworkService.changingPreference ? "Switching..." : "Prefer over Ethernet" - font.pixelSize: Theme.fontSizeSmall - color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary - anchors.verticalCenter: parent.verticalCenter - font.weight: Font.Medium - } - - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - propagateComposedEvents: false - enabled: !NetworkService.changingPreference - onClicked: { - console.log("Force WiFi preference clicked"); - if (NetworkService.networkStatus !== "wifi") - NetworkService.setNetworkPreference("wifi"); - else - NetworkService.setNetworkPreference("auto"); - } - } - - Behavior on opacity { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - - } - - } - - // Available WiFi Networks - Column { - width: parent.width - spacing: Theme.spacingS - visible: NetworkService.wifiEnabled - - Row { - width: parent.width - - Text { - text: "Available Networks" - font.pixelSize: Theme.fontSizeLarge - color: Theme.surfaceText - font.weight: Font.Medium - } - - Item { - width: parent.width - 200 - height: 1 - } - - Rectangle { - width: 32 - height: 32 - radius: 16 - color: refreshArea.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: refreshIcon - - anchors.centerIn: parent - name: WifiService.isScanning ? "sync" : "refresh" - size: Theme.iconSize - 4 - color: Theme.surfaceText - rotation: WifiService.isScanning ? refreshIcon.rotation : 0 - - RotationAnimation { - target: refreshIcon - property: "rotation" - running: WifiService.isScanning - from: 0 - to: 360 - duration: 1000 - loops: Animation.Infinite + 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 + } - Behavior on rotation { - RotationAnimation { - duration: 200 - easing.type: Easing.OutQuad + // Force WiFi preference button + Rectangle { + width: 120 + height: 26 + color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surface + border.color: Theme.primary + border.width: 1 + radius: 6 + opacity: NetworkService.changingNetworkPreference ? 0.6 : 1 + visible: NetworkService.networkStatus === "ethernet" && NetworkService.wifiEnabled + + Row { + anchors.centerIn: parent + spacing: Theme.spacingXS + + DankIcon { + id: wifiPreferenceIcon + + name: NetworkService.changingNetworkPreference ? "sync" : "" + size: Theme.fontSizeSmall - 2 + color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary + visible: NetworkService.changingNetworkPreference || false + anchors.verticalCenter: parent.verticalCenter + rotation: NetworkService.changingNetworkPreference ? wifiPreferenceIcon.rotation : 0 + + RotationAnimation { + target: wifiPreferenceIcon + property: "rotation" + running: NetworkService.changingNetworkPreference || false + from: 0 + to: 360 + duration: 1000 + loops: Animation.Infinite + } } + Text { + text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over Ethernet" + font.pixelSize: Theme.fontSizeSmall - 1 + color: NetworkService.networkStatus === "wifi" ? Theme.background : Theme.primary + anchors.verticalCenter: parent.verticalCenter + font.weight: Font.Medium + } } - } - - MouseArea { - id: refreshArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - enabled: !WifiService.isScanning - onClicked: { - if (NetworkService.wifiEnabled) - WifiService.scanWifi(); + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + propagateComposedEvents: false + enabled: !NetworkService.changingNetworkPreference + onClicked: { + console.log("Force WiFi preference clicked"); + if (NetworkService.networkStatus !== "wifi") + NetworkService.setNetworkPreference("wifi"); + else + NetworkService.setNetworkPreference("auto"); + } + } + Behavior on opacity { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } } } - } + // 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: { + NetworkService.toggleWifiRadio(); + refreshTimer.triggered = true; + } + } } // Connection status indicator @@ -532,26 +246,20 @@ Item { name: { if (WifiService.connectionStatus === "connecting") return "sync"; - if (WifiService.connectionStatus === "failed") return "error"; - if (WifiService.connectionStatus === "connected") return "check_circle"; - return ""; } size: Theme.iconSize - 6 color: { if (WifiService.connectionStatus === "connecting") return Theme.warning; - if (WifiService.connectionStatus === "failed") return Theme.error; - if (WifiService.connectionStatus === "connected") return Theme.success; - return Theme.surfaceText; } anchors.verticalCenter: parent.verticalCenter @@ -572,40 +280,32 @@ Item { duration: 200 easing.type: Easing.OutQuad } - } - } Text { text: { if (WifiService.connectionStatus === "connecting") return "Connecting to " + WifiService.connectingSSID; - if (WifiService.connectionStatus === "failed") return "Failed to connect to " + WifiService.connectingSSID; - if (WifiService.connectionStatus === "connected") return "Connected to " + WifiService.connectingSSID; - return ""; } - font.pixelSize: Theme.fontSizeMedium + font.pixelSize: Theme.fontSizeSmall color: { if (WifiService.connectionStatus === "connecting") return Theme.warning; - if (WifiService.connectionStatus === "failed") return Theme.error; - if (WifiService.connectionStatus === "connected") return Theme.success; - return Theme.surfaceText; } anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight } - } Behavior on color { @@ -613,18 +313,85 @@ Item { duration: Theme.shortDuration easing.type: Theme.standardEasing } - } - } - // WiFi networks list (only show if WiFi is available and enabled) + // Available Networks Section with refresh button + Row { + width: parent.width + spacing: Theme.spacingS + visible: NetworkService.wifiEnabled && (NetworkService.ethernetConnected && NetworkService.networkStatus !== "ethernet") + + 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 + Rectangle { + width: 28 + height: 28 + radius: 14 + color: refreshArea.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: refreshIcon + anchors.centerIn: parent + name: "refresh" + size: Theme.iconSize - 6 + color: refreshArea.containsMouse ? Theme.primary : Theme.surfaceText + rotation: WifiService.isScanning ? refreshIcon.rotation : 0 + + RotationAnimation { + target: refreshIcon + 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: refreshArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (!WifiService.isScanning) { + console.log("Manual WiFi scan triggered"); + // Immediate visual feedback + refreshIcon.rotation += 30; + WifiService.scanWifi(); + } + } + } + } + } + + // WiFi networks list (only show if WiFi is available and enabled and spanning is not needed) Repeater { - model: NetworkService.wifiAvailable && NetworkService.wifiEnabled ? WifiService.wifiNetworks : [] + model: (NetworkService.wifiAvailable && NetworkService.wifiEnabled && NetworkService.ethernetConnected && NetworkService.networkStatus !== "ethernet") ? WifiService.wifiNetworks : [] Rectangle { width: parent.width - height: 42 + height: 38 radius: Theme.cornerRadiusSmall color: networkArea.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" @@ -632,7 +399,7 @@ Item { Item { anchors.fill: parent - anchors.margins: Theme.spacingS + anchors.margins: Theme.spacingXS // Signal strength icon DankIcon { @@ -641,23 +408,23 @@ Item { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter name: modelData.signalStrength === "excellent" ? "wifi" : modelData.signalStrength === "good" ? "wifi_2_bar" : modelData.signalStrength === "fair" ? "wifi_1_bar" : modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi" - size: Theme.iconSize + size: Theme.iconSize - 2 color: modelData.connected ? Theme.primary : Theme.surfaceText } // Network info Column { anchors.left: signalIcon.right - anchors.leftMargin: Theme.spacingS + anchors.leftMargin: Theme.spacingXS anchors.right: rightIcons.left - anchors.rightMargin: Theme.spacingS + anchors.rightMargin: Theme.spacingXS anchors.verticalCenter: parent.verticalCenter spacing: 2 Text { width: parent.width text: modelData.ssid - font.pixelSize: Theme.fontSizeMedium + font.pixelSize: Theme.fontSizeSmall color: modelData.connected ? Theme.primary : Theme.surfaceText font.weight: modelData.connected ? Font.Medium : Font.Normal elide: Text.ElideRight @@ -668,17 +435,14 @@ Item { text: { if (modelData.connected) return "Connected"; - if (modelData.saved) return "Saved" + (modelData.secured ? " • Secured" : " • Open"); - return modelData.secured ? "Secured" : "Open"; } - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall - 1 color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) elide: Text.ElideRight } - } // Right side icons @@ -692,7 +456,7 @@ Item { // Lock icon (if secured) DankIcon { name: "lock" - size: Theme.iconSize - 6 + 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 @@ -700,16 +464,16 @@ Item { // Forget button (for saved networks) Rectangle { - width: 28 - height: 28 - radius: 14 + width: 24 + height: 24 + radius: 12 color: forgetArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent" visible: modelData.saved || modelData.connected DankIcon { anchors.centerIn: parent name: "delete" - size: Theme.iconSize - 6 + size: Theme.iconSize - 8 color: forgetArea.containsMouse ? Theme.error : Theme.surfaceText } @@ -723,16 +487,11 @@ Item { WifiService.forgetWifiNetwork(modelData.ssid); } } - } - } - } MouseArea { - // Already connected, do nothing or show info - id: networkArea anchors.fill: parent @@ -740,7 +499,7 @@ Item { cursorShape: Qt.PointingHandCursor onClicked: { if (modelData.connected) - return ; + return; if (modelData.saved) { // Saved network, connect directly @@ -756,58 +515,450 @@ Item { } } } + } + } + // WiFi disabled message + Column { + width: parent.width + spacing: Theme.spacingM + visible: !NetworkService.wifiEnabled + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + anchors.horizontalCenter: parent.horizontalCenter + name: "wifi_off" + size: 36 + 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.fontSizeMedium + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "Turn on WiFi to see networks" + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4) + } + } } - // WiFi disabled message - Column { - width: parent.width - spacing: Theme.spacingM - visible: !NetworkService.wifiEnabled - anchors.horizontalCenter: parent.horizontalCenter - - 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) - } - - Text { - anchors.horizontalCenter: parent.horizontalCenter - text: "Turn on WiFi to see available networks" - font.pixelSize: Theme.fontSizeMedium - color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4) - } - + 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 + Text { + text: "Ethernet" + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText + font.weight: Font.Medium + width: parent.width } + // 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 { + width: parent.width + height: 80 + radius: Theme.cornerRadius + color: 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 + } + } + + // 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"); + } + } + } + + // Ethernet preference button (moved below the status card) + Rectangle { + width: parent.width + height: 30 + color: NetworkService.networkStatus === "ethernet" ? Theme.primary : Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.5) + border.color: Theme.primary + border.width: 1 + radius: 6 + opacity: NetworkService.changingNetworkPreference ? 0.6 : 1 + visible: NetworkService.ethernetConnected && NetworkService.networkStatus !== "ethernet" && NetworkService.wifiAvailable && NetworkService.wifiEnabled + + Row { + anchors.centerIn: parent + spacing: Theme.spacingXS + + DankIcon { + id: ethernetPreferenceIcon + + name: NetworkService.changingNetworkPreference ? "sync" : "" + size: Theme.fontSizeSmall - 2 + color: NetworkService.networkStatus === "ethernet" ? Theme.background : Theme.primary + visible: NetworkService.changingNetworkPreference || false + anchors.verticalCenter: parent.verticalCenter + rotation: NetworkService.changingNetworkPreference ? ethernetPreferenceIcon.rotation : 0 + + RotationAnimation { + target: ethernetPreferenceIcon + property: "rotation" + running: NetworkService.changingNetworkPreference || false + from: 0 + to: 360 + duration: 1000 + loops: Animation.Infinite + } + } + + Text { + text: NetworkService.changingNetworkPreference ? "Switching..." : "Prefer over WiFi" + font.pixelSize: Theme.fontSizeMedium + color: NetworkService.networkStatus === "ethernet" ? Theme.background : Theme.primary + anchors.verticalCenter: parent.verticalCenter + font.weight: Font.Medium + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + propagateComposedEvents: false + enabled: !NetworkService.changingNetworkPreference + onClicked: { + console.log("*** ETHERNET PREFERENCE BUTTON CLICKED ***"); + if (NetworkService.networkStatus !== "ethernet") { + console.log("Setting preference to ethernet"); + NetworkService.setNetworkPreference("ethernet"); + } else { + console.log("Setting preference to auto"); + NetworkService.setNetworkPreference("auto"); + } + } + } + + Behavior on opacity { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } + } + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + } + } + } + } + + // WiFi networks spanning across both columns when Ethernet preference button is hidden + Column { + anchors.top: parent.top + anchors.topMargin: 120 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + visible: NetworkService.wifiEnabled && !(NetworkService.ethernetConnected && NetworkService.networkStatus !== "ethernet") + 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) { + console.log("Manual WiFi scan triggered (spanning)"); + // 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 ? WifiService.wifiNetworks : [] + + 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 + + // Signal strength icon + DankIcon { + id: signalIcon2 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + name: modelData.signalStrength === "excellent" ? "wifi" : modelData.signalStrength === "good" ? "wifi_2_bar" : modelData.signalStrength === "fair" ? "wifi_1_bar" : modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi" + 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 (modelData.saved) + return "Saved" + (modelData.secured ? " • Secured" : " • Open"); + return modelData.secured ? "Secured" : "Open"; + } + font.pixelSize: Theme.fontSizeSmall - 1 + color: 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 + } + + // Forget button (for saved networks) + Rectangle { + width: 24 + height: 24 + radius: 12 + color: forgetArea2.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent" + visible: modelData.saved || modelData.connected + + DankIcon { + anchors.centerIn: parent + name: "delete" + size: Theme.iconSize - 8 + color: forgetArea2.containsMouse ? Theme.error : Theme.surfaceText + } + + MouseArea { + id: forgetArea2 + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + WifiService.forgetWifiNetwork(modelData.ssid); + } + } + } + } + } + + MouseArea { + id: networkArea2 + anchors.fill: parent + 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: networkTab.visible && refreshTimer.triggered + running: visible && refreshTimer.triggered property bool triggered: false onTriggered: { NetworkService.refreshNetworkStatus(); @@ -822,11 +973,10 @@ Item { Connections { target: NetworkService function onWifiEnabledChanged() { - if (NetworkService.wifiEnabled && networkTab.visible) { + if (NetworkService.wifiEnabled && visible) { // When WiFi is enabled, scan and update info (only if tab is visible) WifiService.scanWifi(); } } } - -} +} \ No newline at end of file