From fc223237f0938c7904360d8c2674368bffa53373 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 21:58:14 -0500 Subject: controlcenter: wireless panel rewrite --- .../network/WirelessPasswordDialog.qml | 299 +++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 modules/controlcenter/network/WirelessPasswordDialog.qml (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml new file mode 100644 index 0000000..778fb4f --- /dev/null +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -0,0 +1,299 @@ +pragma ComponentBehavior: Bound + +import ".." +import "." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + required property Session session + + readonly property var network: { + // Prefer pendingNetwork, then active network + if (session.network.pendingNetwork) { + return session.network.pendingNetwork; + } + if (session.network.active) { + return session.network.active; + } + return null; + } + + visible: session.network.showPasswordDialog + enabled: visible + focus: visible + + Keys.onEscapePressed: { + closeDialog(); + } + + Rectangle { + anchors.fill: parent + color: Qt.rgba(0, 0, 0, 0.5) + opacity: root.visible ? 1 : 0 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + MouseArea { + anchors.fill: parent + onClicked: closeDialog(); + } + } + + StyledRect { + id: dialog + + anchors.centerIn: parent + + implicitWidth: 400 + implicitHeight: content.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surface + opacity: root.visible ? 1 : 0 + scale: root.visible ? 1 : 0.9 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Behavior on scale { + NumberAnimation { duration: 200 } + } + + Keys.onEscapePressed: closeDialog(); + + ColumnLayout { + id: content + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "lock" + font.pointSize: Appearance.font.size.extraLarge * 2 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Enter password") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: root.network ? qsTr("Network: %1").arg(root.network.ssid) : "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + id: statusText + + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Appearance.spacing.small + visible: Network.connectionStatus.length > 0 || connectButton.connecting + text: { + if (Network.connectionStatus.length > 0) { + return Network.connectionStatus; + } else if (connectButton.connecting) { + return qsTr("Connecting..."); + } + return ""; + } + color: { + const status = Network.connectionStatus; + if (status.includes("Error") || status.includes("error") || status.includes("failed")) { + return Colours.palette.m3error; + } else if (status.includes("successful") || status.includes("Connected") || status.includes("success")) { + return Colours.palette.m3primary; + } + return Colours.palette.m3onSurfaceVariant; + } + font.pointSize: Appearance.font.size.small + font.weight: (Network.connectionStatus.includes("Error") || Network.connectionStatus.includes("error")) ? 500 : 400 + wrapMode: Text.WordWrap + Layout.maximumWidth: parent.width - Appearance.padding.large * 2 + } + + Item { + Layout.topMargin: Appearance.spacing.large + Layout.fillWidth: true + implicitHeight: passwordField.implicitHeight + Appearance.padding.normal * 2 + + StyledRect { + anchors.fill: parent + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + border.width: passwordField.activeFocus ? 2 : 1 + border.color: passwordField.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline + + Behavior on border.color { + CAnim {} + } + } + + StyledTextField { + id: passwordField + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + echoMode: TextField.Password + placeholderText: qsTr("Password") + + Connections { + target: root + function onVisibleChanged(): void { + if (root.visible) { + passwordField.forceActiveFocus(); + passwordField.text = ""; + Network.clearConnectionStatus(); + } + } + } + + Keys.onReturnPressed: { + if (connectButton.enabled) { + connectButton.clicked(); + } + } + Keys.onEnterPressed: { + if (connectButton.enabled) { + connectButton.clicked(); + } + } + } + } + + RowLayout { + Layout.topMargin: Appearance.spacing.normal + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + SimpleButton { + id: cancelButton + + Layout.fillWidth: true + color: Colours.palette.m3secondaryContainer + onColor: Colours.palette.m3onSecondaryContainer + text: qsTr("Cancel") + + onClicked: closeDialog(); + } + + SimpleButton { + id: connectButton + + Layout.fillWidth: true + color: Colours.palette.m3primary + onColor: Colours.palette.m3onPrimary + text: qsTr("Connect") + enabled: passwordField.text.length > 0 && !connecting + + property bool connecting: false + + onClicked: { + if (!root.network || connecting) { + return; + } + + const password = passwordField.text; + if (!password || password.length === 0) { + return; + } + + // Set connecting state + connecting = true; + enabled = false; + text = qsTr("Connecting..."); + Network.clearConnectionStatus(); + + // Connect to network + Network.connectToNetwork( + root.network.ssid, + password, + root.network.bssid || "", + null + ); + + // Start monitoring connection + connectionMonitor.start(); + } + } + } + } + } + + Timer { + id: connectionMonitor + interval: 1000 + repeat: true + triggeredOnStart: false + + onTriggered: { + // Check if we're connected to the target network + if (root.network && Network.active && Network.active.ssid === root.network.ssid) { + // Successfully connected + stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + closeDialog(); + } else if (connectButton.connecting) { + // Still connecting, check status + const status = Network.connectionStatus; + if (status.includes("Error") || status.includes("error") || status.includes("failed")) { + // Connection failed + stop(); + connectButton.connecting = false; + connectButton.enabled = true; + connectButton.text = qsTr("Connect"); + } + } else { + // Not connecting anymore + stop(); + } + } + } + + Connections { + target: Network + function onActiveChanged() { + if (root.visible && root.network && Network.active && Network.active.ssid === root.network.ssid) { + // Connected successfully + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + closeDialog(); + } + } + } + + function closeDialog(): void { + session.network.showPasswordDialog = false; + passwordField.text = ""; + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + connectionMonitor.stop(); + Network.clearConnectionStatus(); + } +} + -- cgit v1.2.3-freya From b62a22b13d6d0c9704b58cf9d79ee89d4b156a74 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 22:09:51 -0500 Subject: controlcenter: wireless panel refactoring --- .../network/WirelessConnectionHelper.qml | 46 -------------- modules/controlcenter/network/WirelessDetails.qml | 71 ++++++++++++++++------ modules/controlcenter/network/WirelessList.qml | 46 ++++++++++++-- .../network/WirelessPasswordDialog.qml | 56 +++++++++-------- 4 files changed, 123 insertions(+), 96 deletions(-) delete mode 100644 modules/controlcenter/network/WirelessConnectionHelper.qml (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessConnectionHelper.qml b/modules/controlcenter/network/WirelessConnectionHelper.qml deleted file mode 100644 index d63b359..0000000 --- a/modules/controlcenter/network/WirelessConnectionHelper.qml +++ /dev/null @@ -1,46 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.services -import QtQuick - -QtObject { - id: root - - required property Session session - - function connectToNetwork(network: var): void { - if (!network) { - return; - } - - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== network.ssid) { - Network.disconnectFromNetwork(); - Qt.callLater(() => { - performConnect(network); - }); - } else { - performConnect(network); - } - } - - function performConnect(network: var): void { - if (network.isSecure) { - // Try connecting without password first (in case it's saved) - Network.connectToNetworkWithPasswordCheck( - network.ssid, - network.isSecure, - () => { - // Callback: connection failed, show password dialog - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = network; - }, - network.bssid - ); - } else { - Network.connectToNetwork(network.ssid, "", network.bssid, null); - } - } -} - diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index e858fbc..418c463 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -16,33 +16,28 @@ Item { required property Session session readonly property var network: session.network.active - - readonly property var connectionHelper: WirelessConnectionHelper { - session: root.session - } Component.onCompleted: { - if (network && network.active) { - Network.updateWirelessDeviceDetails(); - } + updateDeviceDetails(); } onNetworkChanged: { - if (network && network.active) { - Network.updateWirelessDeviceDetails(); - } else { - Network.wirelessDeviceDetails = null; - } + updateDeviceDetails(); } Connections { target: Network function onActiveChanged() { - if (root.network && root.network.active && Network.active && Network.active.ssid === root.network.ssid) { - Network.updateWirelessDeviceDetails(); - } else if (!root.network || !root.network.active) { - Network.wirelessDeviceDetails = null; - } + updateDeviceDetails(); + } + } + + function updateDeviceDetails(): void { + // Only update details if the selected network is currently active + if (network && Network.active && Network.active.ssid === network.ssid) { + Network.updateWirelessDeviceDetails(); + } else { + Network.wirelessDeviceDetails = null; } } @@ -75,7 +70,7 @@ Item { checked: root.network?.active ?? false toggle.onToggled: { if (checked) { - root.connectionHelper.connectToNetwork(root.network); + handleConnect(); } else { Network.disconnectFromNetwork(); } @@ -154,5 +149,45 @@ Item { } } + + function handleConnect(): void { + // If already connected to a different network, disconnect first + if (Network.active && Network.active.ssid !== root.network.ssid) { + Network.disconnectFromNetwork(); + Qt.callLater(() => { + connectToNetwork(); + }); + } else { + connectToNetwork(); + } + } + + function connectToNetwork(): void { + if (root.network.isSecure) { + // Check if we have a saved connection profile for this network + const hasSavedProfile = Network.savedConnections.includes(root.network.ssid); + + if (hasSavedProfile) { + // Try connecting with saved password - don't show dialog if it fails + // The saved password should work, but if connection fails for other reasons, + // we'll let the user try manually later + Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null); + } else { + // No saved profile, try connecting without password first + Network.connectToNetworkWithPasswordCheck( + root.network.ssid, + root.network.isSecure, + () => { + // Callback: connection failed, show password dialog + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = root.network; + }, + root.network.bssid + ); + } + } else { + Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null); + } + } } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 1a4ba00..c64c4be 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -15,10 +15,6 @@ ColumnLayout { required property Session session - readonly property var connectionHelper: WirelessConnectionHelper { - session: root.session - } - spacing: Appearance.spacing.small RowLayout { @@ -185,7 +181,7 @@ ColumnLayout { if (modelData.active) { Network.disconnectFromNetwork(); } else { - root.connectionHelper.connectToNetwork(modelData); + handleConnect(modelData); } } } @@ -203,5 +199,45 @@ ColumnLayout { implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 } } + + function handleConnect(network): void { + // If already connected to a different network, disconnect first + if (Network.active && Network.active.ssid !== network.ssid) { + Network.disconnectFromNetwork(); + Qt.callLater(() => { + connectToNetwork(network); + }); + } else { + connectToNetwork(network); + } + } + + function connectToNetwork(network): void { + if (network.isSecure) { + // Check if we have a saved connection profile for this network + const hasSavedProfile = Network.savedConnections.includes(network.ssid); + + if (hasSavedProfile) { + // Try connecting with saved password - don't show dialog if it fails + // The saved password should work, but if connection fails for other reasons, + // we'll let the user try manually later + Network.connectToNetwork(network.ssid, "", network.bssid, null); + } else { + // No saved profile, try connecting without password first + Network.connectToNetworkWithPasswordCheck( + network.ssid, + network.isSecure, + () => { + // Callback: connection failed, show password dialog + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = network; + }, + network.bssid + ); + } + } else { + Network.connectToNetwork(network.ssid, "", network.bssid, null); + } + } } diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 778fb4f..df8a8cf 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -243,6 +243,32 @@ Item { } } + function checkConnectionStatus(): void { + if (!root.visible || !connectButton.connecting) { + return; + } + + // Check if we're connected to the target network + if (root.network && Network.active && Network.active.ssid === root.network.ssid) { + // Successfully connected + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + closeDialog(); + return; + } + + // Check for connection errors + const status = Network.connectionStatus; + if (status.includes("Error") || status.includes("error") || status.includes("failed")) { + // Connection failed + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.enabled = true; + connectButton.text = qsTr("Connect"); + } + } + Timer { id: connectionMonitor interval: 1000 @@ -250,39 +276,15 @@ Item { triggeredOnStart: false onTriggered: { - // Check if we're connected to the target network - if (root.network && Network.active && Network.active.ssid === root.network.ssid) { - // Successfully connected - stop(); - connectButton.connecting = false; - connectButton.text = qsTr("Connect"); - closeDialog(); - } else if (connectButton.connecting) { - // Still connecting, check status - const status = Network.connectionStatus; - if (status.includes("Error") || status.includes("error") || status.includes("failed")) { - // Connection failed - stop(); - connectButton.connecting = false; - connectButton.enabled = true; - connectButton.text = qsTr("Connect"); - } - } else { - // Not connecting anymore - stop(); - } + checkConnectionStatus(); } } Connections { target: Network function onActiveChanged() { - if (root.visible && root.network && Network.active && Network.active.ssid === root.network.ssid) { - // Connected successfully - connectionMonitor.stop(); - connectButton.connecting = false; - connectButton.text = qsTr("Connect"); - closeDialog(); + if (root.visible) { + checkConnectionStatus(); } } } -- cgit v1.2.3-freya From 6ae1313b6b61c965ccc5f2d9d61458d7a5ed21b8 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 22:31:11 -0500 Subject: controlcenter: wireless panel refactoring --- modules/controlcenter/network/WirelessDetails.qml | 24 +- modules/controlcenter/network/WirelessList.qml | 18 +- .../network/WirelessPasswordDialog.qml | 53 +++-- services/Network.qml | 257 ++++++++++++++++++--- 4 files changed, 300 insertions(+), 52 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 418c463..3e48b55 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -19,10 +19,22 @@ Item { Component.onCompleted: { updateDeviceDetails(); + checkSavedProfile(); } onNetworkChanged: { updateDeviceDetails(); + checkSavedProfile(); + } + + function checkSavedProfile(): void { + // Refresh saved connections list to ensure it's up to date + // This ensures the "Forget Network" button visibility is accurate + if (network && network.ssid) { + // Always refresh to ensure we have the latest saved connections + // This is important when networks are selected or changed + Network.listConnectionsProc.running = true; + } } Connections { @@ -80,7 +92,13 @@ Item { SimpleButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal - visible: root.network && root.network.ssid && Network.savedConnections.includes(root.network.ssid) + visible: { + if (!root.network || !root.network.ssid) { + return false; + } + // Check if profile exists - this will update reactively when savedConnectionSsids changes + return Network.hasSavedProfile(root.network.ssid); + } color: Colours.palette.m3errorContainer onColor: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") @@ -164,8 +182,8 @@ Item { function connectToNetwork(): void { if (root.network.isSecure) { - // Check if we have a saved connection profile for this network - const hasSavedProfile = Network.savedConnections.includes(root.network.ssid); + // Check if we have a saved connection profile for this network (by SSID) + const hasSavedProfile = Network.hasSavedProfile(root.network.ssid); if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index c64c4be..aabfc4b 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -117,6 +117,10 @@ ColumnLayout { StateLayer { function onClicked(): void { root.session.network.active = modelData; + // Check if we need to refresh saved connections when selecting a network + if (modelData && modelData.ssid) { + root.checkSavedProfileForNetwork(modelData.ssid); + } } } @@ -200,6 +204,16 @@ ColumnLayout { } } + function checkSavedProfileForNetwork(ssid: string): void { + // Refresh saved connections list to ensure it's up to date + // This ensures accurate profile detection when selecting networks + if (ssid && ssid.length > 0) { + // Always refresh to ensure we have the latest saved connections + // This is important when a network is selected from the list + Network.listConnectionsProc.running = true; + } + } + function handleConnect(network): void { // If already connected to a different network, disconnect first if (Network.active && Network.active.ssid !== network.ssid) { @@ -214,8 +228,8 @@ ColumnLayout { function connectToNetwork(network): void { if (network.isSecure) { - // Check if we have a saved connection profile for this network - const hasSavedProfile = Network.savedConnections.includes(network.ssid); + // Check if we have a saved connection profile for this network (by SSID) + const hasSavedProfile = Network.hasSavedProfile(network.ssid); if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index df8a8cf..5bcf33c 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -248,24 +248,47 @@ Item { return; } - // Check if we're connected to the target network - if (root.network && Network.active && Network.active.ssid === root.network.ssid) { - // Successfully connected - connectionMonitor.stop(); - connectButton.connecting = false; - connectButton.text = qsTr("Connect"); - closeDialog(); + // Check connection status message for success indicators + const status = Network.connectionStatus; + const statusLower = status.toLowerCase(); + + // Check for success indicators in status message + const hasSuccessIndicator = statusLower.includes("connection activated") || + statusLower.includes("successfully") || + statusLower.includes("connected successfully") || + (statusLower.includes("connected") && !statusLower.includes("error") && !statusLower.includes("failed")); + + // Check if we're connected to the target network (case-insensitive SSID comparison) + const isConnected = root.network && Network.active && Network.active.ssid && + Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + + if (isConnected || hasSuccessIndicator) { + // Successfully connected - give it a moment for network list to update + Qt.callLater(() => { + // Double-check connection is still active + if (root.visible && Network.active && Network.active.ssid) { + const stillConnected = Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + if (stillConnected || hasSuccessIndicator) { + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + closeDialog(); + } + } + }, 500); return; } - // Check for connection errors - const status = Network.connectionStatus; - if (status.includes("Error") || status.includes("error") || status.includes("failed")) { - // Connection failed - connectionMonitor.stop(); - connectButton.connecting = false; - connectButton.enabled = true; - connectButton.text = qsTr("Connect"); + // Check for connection errors (but not warnings about duplicate names) + if (status.includes("Error") || (status.includes("error") && !status.includes("Warning"))) { + // Only treat as error if it's not just a warning about duplicate names + if (!status.includes("another connection with the name") && !status.includes("Reference the connection by its uuid")) { + // Connection failed + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.enabled = true; + connectButton.text = qsTr("Connect"); + } } } diff --git a/services/Network.qml b/services/Network.qml index ea5c3e7..0b936b8 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -90,19 +90,26 @@ Singleton { if (hasBssid) { // Use BSSID when password is provided - ensure BSSID is uppercase const bssidUpper = bssid.toUpperCase(); - // Create connection profile with all required properties for BSSID + password - // First remove any existing connection with this name - cmd = ["nmcli", "connection", "add", - "type", "wifi", - "con-name", ssid, - "ifname", "*", - "ssid", ssid, - "802-11-wireless.bssid", bssidUpper, - "802-11-wireless-security.key-mgmt", "wpa-psk", - "802-11-wireless-security.psk", password]; - root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper)); - root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid)); - root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt")); + + // Check if a connection with this SSID already exists + const existingConnection = root.savedConnections.find(conn => + conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim() + ); + + if (existingConnection) { + // Connection already exists - delete it first, then create new one with updated password + root.addDebugInfo(qsTr("Connection '%1' already exists, deleting it first...").arg(existingConnection)); + deleteConnectionProc.exec(["nmcli", "connection", "delete", existingConnection]); + // Wait a moment for deletion to complete, then create new connection + Qt.callLater(() => { + createConnectionWithPassword(ssid, bssidUpper, password); + }, 300); + return; + } else { + // No existing connection, create new one + createConnectionWithPassword(ssid, bssidUpper, password); + return; + } } else { // Fallback to SSID if BSSID not available - use device wifi connect cmd = ["nmcli", "device", "wifi", "connect", ssid, "password", password]; @@ -164,6 +171,29 @@ Singleton { root.addDebugInfo(qsTr("No callback provided - not starting connection check timer")); } } + + function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string): void { + // Create connection profile with all required properties for BSSID + password + const cmd = ["nmcli", "connection", "add", + "type", "wifi", + "con-name", ssid, + "ifname", "*", + "ssid", ssid, + "802-11-wireless.bssid", bssidUpper, + "802-11-wireless-security.key-mgmt", "wpa-psk", + "802-11-wireless-security.psk", password]; + + root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper)); + root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid)); + root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt")); + + // Set command and start process + connectProc.command = cmd; + + Qt.callLater(() => { + connectProc.running = true; + }); + } function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void { root.addDebugInfo(qsTr("=== connectToNetworkWithPasswordCheck ===")); @@ -224,23 +254,150 @@ Singleton { } property list savedConnections: [] + property list savedConnectionSsids: [] + property var wifiConnectionQueue: [] + property int currentSsidQueryIndex: 0 Process { id: listConnectionsProc - command: ["nmcli", "-t", "-f", "NAME", "connection", "show"] + command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] + environment: ({ + LANG: "C.UTF-8", + LC_ALL: "C.UTF-8" + }) + onExited: { + if (exitCode === 0) { + parseConnectionList(stdout.text); + } + } + stdout: StdioCollector { + onStreamFinished: { + parseConnectionList(text); + } + } + } + + function parseConnectionList(output: string): void { + const lines = output.trim().split("\n").filter(line => line.length > 0); + const wifiConnections = []; + const connections = []; + + // First pass: identify WiFi connections + for (const line of lines) { + const parts = line.split(":"); + if (parts.length >= 2) { + const name = parts[0]; + const type = parts[1]; + connections.push(name); + + if (type === "802-11-wireless") { + wifiConnections.push(name); + } + } + } + + root.savedConnections = connections; + + // Second pass: get SSIDs for WiFi connections + if (wifiConnections.length > 0) { + root.wifiConnectionQueue = wifiConnections; + root.currentSsidQueryIndex = 0; + root.savedConnectionSsids = []; + // Start querying SSIDs one by one + queryNextSsid(); + } else { + root.savedConnectionSsids = []; + root.wifiConnectionQueue = []; + } + } + + Process { + id: getSsidProc + + environment: ({ + LANG: "C.UTF-8", + LC_ALL: "C.UTF-8" + }) onExited: { if (exitCode === 0) { - // Parse connection names from output - const connections = stdout.text.trim().split("\n").filter(name => name.length > 0); - root.savedConnections = connections; + processSsidOutput(stdout.text); + } else { + // Move to next connection even if this one failed + queryNextSsid(); } } stdout: StdioCollector { onStreamFinished: { - const connections = text.trim().split("\n").filter(name => name.length > 0); - root.savedConnections = connections; + processSsidOutput(text); + } + } + } + + function processSsidOutput(output: string): void { + // Parse "802-11-wireless.ssid:SSID_NAME" format + const lines = output.trim().split("\n"); + for (const line of lines) { + if (line.startsWith("802-11-wireless.ssid:")) { + const ssid = line.substring("802-11-wireless.ssid:".length).trim(); + if (ssid && ssid.length > 0) { + // Add to list if not already present (case-insensitive) + const ssidLower = ssid.toLowerCase(); + if (!root.savedConnectionSsids.some(s => s && s.toLowerCase() === ssidLower)) { + // Create new array to trigger QML property change notification + const newList = root.savedConnectionSsids.slice(); + newList.push(ssid); + root.savedConnectionSsids = newList; + } + } + } + } + + // Query next connection + queryNextSsid(); + } + + function queryNextSsid(): void { + if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) { + const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex]; + root.currentSsidQueryIndex++; + getSsidProc.command = ["nmcli", "-t", "-f", "802-11-wireless.ssid", "connection", "show", connectionName]; + getSsidProc.running = true; + } else { + // All SSIDs retrieved + root.wifiConnectionQueue = []; + root.currentSsidQueryIndex = 0; + } + } + + function hasSavedProfile(ssid: string): bool { + if (!ssid || ssid.length === 0) { + return false; + } + const ssidLower = ssid.toLowerCase().trim(); + + // If currently connected to this network, it definitely has a saved profile + if (root.active && root.active.ssid) { + const activeSsidLower = root.active.ssid.toLowerCase().trim(); + if (activeSsidLower === ssidLower) { + return true; } } + + // Check if SSID is in saved connections (case-insensitive comparison) + const hasSsid = root.savedConnectionSsids.some(savedSsid => + savedSsid && savedSsid.toLowerCase().trim() === ssidLower + ); + + if (hasSsid) { + return true; + } + + // Fallback: also check if connection name matches SSID (some connections use SSID as name) + const hasConnectionName = root.savedConnections.some(connName => + connName && connName.toLowerCase().trim() === ssidLower + ); + + return hasConnectionName; } function getWifiStatus(): void { @@ -445,22 +602,58 @@ Singleton { && connectProc.command[1] === "connection" && connectProc.command[2] === "add"; - if (wasConnectionAdd && exitCode === 0 && root.pendingConnection) { - // Connection profile was created successfully, now activate it + if (wasConnectionAdd && root.pendingConnection) { const ssid = root.pendingConnection.ssid; - root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid)); - root.setConnectionStatus(qsTr("Activating connection...")); - // Update saved connections list since we just created one - listConnectionsProc.running = true; + // Check for duplicate connection warning in stderr text + const stderrText = connectProc.stderr ? connectProc.stderr.text : ""; + const hasDuplicateWarning = stderrText && ( + stderrText.includes("another connection with the name") || + stderrText.includes("Reference the connection by its uuid") + ); - // Activate the connection we just created - connectProc.command = ["nmcli", "connection", "up", ssid]; - Qt.callLater(() => { - connectProc.running = true; - }); - // Don't start timers yet - wait for activation to complete - return; + // Even with duplicate warning (or if connection already exists), we should try to activate it + // Also try if exit code is non-zero but small (might be a warning, not a real error) + if (exitCode === 0 || hasDuplicateWarning || (exitCode > 0 && exitCode < 10)) { + if (hasDuplicateWarning) { + root.addDebugInfo(qsTr("Connection with name '%1' already exists (warning), will try to activate it").arg(ssid)); + root.setConnectionStatus(qsTr("Activating existing connection...")); + } else { + root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid)); + root.setConnectionStatus(qsTr("Activating connection...")); + } + + // Update saved connections list + listConnectionsProc.running = true; + + // Try to activate the connection by SSID (connection name) + connectProc.command = ["nmcli", "connection", "up", ssid]; + Qt.callLater(() => { + connectProc.running = true; + }); + // Don't start timers yet - wait for activation to complete + return; + } else { + // Connection add failed - try using device wifi connect as fallback + root.addDebugInfo(qsTr("Connection add failed (exit code %1), trying device wifi connect as fallback").arg(exitCode)); + // Extract password from the command if available + let password = ""; + if (connectProc.command) { + const pskIndex = connectProc.command.findIndex(arg => arg === "802-11-wireless-security.psk"); + if (pskIndex >= 0 && pskIndex + 1 < connectProc.command.length) { + password = connectProc.command[pskIndex + 1]; + } + } + + if (password && password.length > 0) { + root.addDebugInfo(qsTr("Using device wifi connect with password as fallback")); + connectProc.command = ["nmcli", "device", "wifi", "connect", ssid, "password", password]; + Qt.callLater(() => { + connectProc.running = true; + }); + return; + } + } } // Refresh network list after connection attempt -- cgit v1.2.3-freya From e9113a1710af3dd031c3fa413059d70aed18c4e5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:12:34 -0500 Subject: cleanup: trailing whitespace --- modules/controlcenter/audio/AudioPane.qml | 4 +--- modules/controlcenter/ethernet/EthernetDetails.qml | 26 +--------------------- modules/controlcenter/ethernet/EthernetList.qml | 23 +------------------ modules/controlcenter/ethernet/EthernetPane.qml | 10 +-------- .../controlcenter/ethernet/EthernetSettings.qml | 23 +------------------ modules/controlcenter/network/SimpleButton.qml | 3 +-- modules/controlcenter/network/WirelessDetails.qml | 3 +-- modules/controlcenter/network/WirelessList.qml | 3 +-- modules/controlcenter/network/WirelessPane.qml | 3 +-- .../network/WirelessPasswordDialog.qml | 3 +-- modules/controlcenter/network/WirelessSettings.qml | 3 +-- 11 files changed, 11 insertions(+), 93 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 6c478e5..502134a 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -345,6 +345,4 @@ RowLayout { leftThickness: Appearance.padding.normal / 2 } } -} - - +} \ No newline at end of file diff --git a/modules/controlcenter/ethernet/EthernetDetails.qml b/modules/controlcenter/ethernet/EthernetDetails.qml index a49eb4f..d9a004b 100644 --- a/modules/controlcenter/ethernet/EthernetDetails.qml +++ b/modules/controlcenter/ethernet/EthernetDetails.qml @@ -110,28 +110,4 @@ Item { } } -} - - - - - - - - - - - - - - - - - - - - - - - - +} \ No newline at end of file diff --git a/modules/controlcenter/ethernet/EthernetList.qml b/modules/controlcenter/ethernet/EthernetList.qml index 8b04c09..b8c485b 100644 --- a/modules/controlcenter/ethernet/EthernetList.qml +++ b/modules/controlcenter/ethernet/EthernetList.qml @@ -166,25 +166,4 @@ ColumnLayout { implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 } } -} - - - - - - - - - - - - - - - - - - - - - +} \ No newline at end of file diff --git a/modules/controlcenter/ethernet/EthernetPane.qml b/modules/controlcenter/ethernet/EthernetPane.qml index fc3e1c0..b3ff317 100644 --- a/modules/controlcenter/ethernet/EthernetPane.qml +++ b/modules/controlcenter/ethernet/EthernetPane.qml @@ -145,12 +145,4 @@ RowLayout { duration: Appearance.anim.durations.normal / 2 easing.type: Easing.BezierSpline } -} - - - - - - - - +} \ No newline at end of file diff --git a/modules/controlcenter/ethernet/EthernetSettings.qml b/modules/controlcenter/ethernet/EthernetSettings.qml index 33b1449..68a1f61 100644 --- a/modules/controlcenter/ethernet/EthernetSettings.qml +++ b/modules/controlcenter/ethernet/EthernetSettings.qml @@ -81,25 +81,4 @@ ColumnLayout { } } } -} - - - - - - - - - - - - - - - - - - - - - +} \ No newline at end of file diff --git a/modules/controlcenter/network/SimpleButton.qml b/modules/controlcenter/network/SimpleButton.qml index 00e5a4e..7d85e4f 100644 --- a/modules/controlcenter/network/SimpleButton.qml +++ b/modules/controlcenter/network/SimpleButton.qml @@ -49,5 +49,4 @@ StyledRect { } signal clicked -} - +} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 3e48b55..7039720 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -207,5 +207,4 @@ Item { Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } } -} - +} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index aabfc4b..f861db4 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -253,5 +253,4 @@ ColumnLayout { Network.connectToNetwork(network.ssid, "", network.bssid, null); } } -} - +} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessPane.qml b/modules/controlcenter/network/WirelessPane.qml index a23b6e8..9d48729 100644 --- a/modules/controlcenter/network/WirelessPane.qml +++ b/modules/controlcenter/network/WirelessPane.qml @@ -151,5 +151,4 @@ RowLayout { duration: Appearance.anim.durations.normal / 2 easing.type: Easing.BezierSpline } -} - +} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 5bcf33c..2b33b43 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -320,5 +320,4 @@ Item { connectionMonitor.stop(); Network.clearConnectionStatus(); } -} - +} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessSettings.qml b/modules/controlcenter/network/WirelessSettings.qml index 7890099..073b09c 100644 --- a/modules/controlcenter/network/WirelessSettings.qml +++ b/modules/controlcenter/network/WirelessSettings.qml @@ -78,5 +78,4 @@ ColumnLayout { value: Network.active ? qsTr("%1 MHz").arg(Network.active.frequency) : qsTr("N/A") } } -} - +} \ No newline at end of file -- cgit v1.2.3-freya From 1da9c68be8f336a671f9514cf5feaaf5998da981 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:41:14 -0500 Subject: cleanup: trailing whitespace removeal (entire project) --- components/controls/CollapsibleSection.qml | 2 +- .../controlcenter/appearance/AppearancePane.qml | 50 +++---- modules/controlcenter/launcher/LauncherPane.qml | 10 +- modules/controlcenter/network/WirelessDetails.qml | 6 +- modules/controlcenter/network/WirelessList.qml | 2 +- .../network/WirelessPasswordDialog.qml | 10 +- modules/controlcenter/taskbar/TaskbarPane.qml | 8 +- modules/drawers/Interactions.qml | 22 +-- services/Network.qml | 154 ++++++++++----------- services/VPN.qml | 6 +- utils/Icons.qml | 4 +- 11 files changed, 137 insertions(+), 137 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 945386c..cb6e62a 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -12,7 +12,7 @@ ColumnLayout { required property string title property string description: "" property bool expanded: false - + signal toggleRequested spacing: Appearance.spacing.small / 2 diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 68e2e2d..fc338f9 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -310,20 +310,20 @@ RowLayout { StateLayer { function onClicked(): void { const variant = modelData.variant; - + // Optimistic update - set immediately Schemes.currentVariant = variant; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -410,20 +410,20 @@ RowLayout { const name = modelData.name; const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - + // Optimistic update - set immediately Schemes.currentScheme = schemeKey; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -1053,7 +1053,7 @@ RowLayout { columns: Math.max(1, Math.floor(parent.width / 200)) rowSpacing: Appearance.spacing.normal columnSpacing: Appearance.spacing.normal - + // Center the grid content Layout.maximumWidth: { const cols = columns; @@ -1100,16 +1100,16 @@ RowLayout { path: modelData.path anchors.fill: parent - + // Ensure sourceSize is always set to valid dimensions sourceSize: Qt.size( Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height)) ) - + // Show when ready, hide if fallback is showing opacity: status === Image.Ready && !fallbackImage.visible ? 1 : 0 - + Behavior on opacity { NumberAnimation { duration: 200 @@ -1129,11 +1129,11 @@ RowLayout { Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height)) ) - + // Show if caching image hasn't loaded after a delay visible: opacity > 0 opacity: 0 - + Timer { id: fallbackTimer interval: 500 @@ -1144,7 +1144,7 @@ RowLayout { } } } - + // Also check status changes onStatusChanged: { if (status === Image.Ready && cachingImage.status !== Image.Ready) { @@ -1155,7 +1155,7 @@ RowLayout { }); } } - + Behavior on opacity { NumberAnimation { duration: 200 @@ -1182,26 +1182,26 @@ RowLayout { anchors.right: parent.right anchors.bottom: parent.bottom height: filenameText.implicitHeight + Appearance.padding.normal * 2 - + // Match the parent's rounded corners at the bottom radius: Appearance.rounding.normal - + gradient: Gradient { GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0) } GradientStop { position: 0.3; color: Qt.rgba(0, 0, 0, 0.3) } GradientStop { position: 0.7; color: Qt.rgba(0, 0, 0, 0.75) } GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.85) } } - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } @@ -1228,20 +1228,20 @@ RowLayout { color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" elide: Text.ElideMiddle maximumLineCount: 1 - + // Text shadow for better readability style: Text.Outline styleColor: Qt.rgba(0, 0, 0, 0.6) - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 9b2570a..dd00877 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -53,7 +53,7 @@ RowLayout { try { const config = JSON.parse(configFile.text()); const appId = root.selectedApp.id || root.selectedApp.entry?.id; - + if (config.launcher && config.launcher.hiddenApps) { root.hideFromLauncherChecked = config.launcher.hiddenApps.includes(appId); } else { @@ -72,12 +72,12 @@ RowLayout { try { const config = JSON.parse(configFile.text()); const appId = root.selectedApp.id || root.selectedApp.entry?.id; - + if (!config.launcher) config.launcher = {}; if (!config.launcher.hiddenApps) config.launcher.hiddenApps = []; - + const hiddenApps = config.launcher.hiddenApps; - + if (isHidden) { // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { @@ -90,7 +90,7 @@ RowLayout { hiddenApps.splice(index, 1); } } - + const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); } catch (e) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7039720..d5abc9d 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -26,7 +26,7 @@ Item { updateDeviceDetails(); checkSavedProfile(); } - + function checkSavedProfile(): void { // Refresh saved connections list to ensure it's up to date // This ensures the "Forget Network" button visibility is accurate @@ -102,7 +102,7 @@ Item { color: Colours.palette.m3errorContainer onColor: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") - + onClicked: { if (root.network && root.network.ssid) { // Disconnect first if connected @@ -184,7 +184,7 @@ Item { if (root.network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(root.network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index f861db4..ca6947a 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -230,7 +230,7 @@ ColumnLayout { if (network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 2b33b43..8a71fa8 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -15,7 +15,7 @@ Item { id: root required property Session session - + readonly property var network: { // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { @@ -105,7 +105,7 @@ Item { StyledText { id: statusText - + Layout.alignment: Qt.AlignHCenter Layout.topMargin: Appearance.spacing.small visible: Network.connectionStatus.length > 0 || connectButton.connecting @@ -251,15 +251,15 @@ Item { // Check connection status message for success indicators const status = Network.connectionStatus; const statusLower = status.toLowerCase(); - + // Check for success indicators in status message - const hasSuccessIndicator = statusLower.includes("connection activated") || + const hasSuccessIndicator = statusLower.includes("connection activated") || statusLower.includes("successfully") || statusLower.includes("connected successfully") || (statusLower.includes("connected") && !statusLower.includes("error") && !statusLower.includes("failed")); // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Network.active && Network.active.ssid && + const isConnected = root.network && Network.active && Network.active.ssid && Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected || hasSuccessIndicator) { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 2bb50d8..cf52fd3 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -120,13 +120,13 @@ RowLayout { if (!configFile.loaded) { return; } - + try { const config = JSON.parse(configFile.text()); - + // Ensure bar object exists if (!config.bar) config.bar = {}; - + // Update clock setting if (!config.bar.clock) config.bar.clock = {}; config.bar.clock.showIcon = clockShowIconSwitch.checked; @@ -163,7 +163,7 @@ RowLayout { // Update entries from the model (same approach as clock - use provided value if available) if (!config.bar.entries) config.bar.entries = []; config.bar.entries = []; - + for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 10190a4..2d0c115 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -204,18 +204,18 @@ CustomMouseArea { const panelWidth = panels.notifications.width || panels.notifications.implicitWidth || Config.notifs.sizes.width; const panelX = bar.implicitWidth + panels.notifications.x; const isPanelCollapsed = panelHeight < 10; // Consider collapsed if height is very small - + let showNotifications = inTopPanel(panels.notifications, x, y); - + // Only use fallback corner detection when panel is collapsed if (!showNotifications && isPanelCollapsed) { // Use panel's actual width and position for fallback, with some padding const cornerPadding = Config.border.rounding || 20; - showNotifications = x >= panelX - cornerPadding && - x <= panelX + panelWidth + cornerPadding && + showNotifications = x >= panelX - cornerPadding && + x <= panelX + panelWidth + cornerPadding && y < Config.border.thickness + cornerPadding; } - + // Check if mouse is over the clear all button area // Button is positioned to the left of the notification panel if (!showNotifications && panels.notifications.height > 0 && panels.clearAllButton && panels.clearAllButton.visible) { @@ -223,17 +223,17 @@ CustomMouseArea { const buttonY = Config.border.thickness + panels.clearAllButton.y; const buttonWidth = panels.clearAllButton.width; const buttonHeight = panels.clearAllButton.height; - - const inButtonArea = x >= buttonX && - x <= buttonX + buttonWidth && - y >= buttonY && + + const inButtonArea = x >= buttonX && + x <= buttonX + buttonWidth && + y >= buttonY && y <= buttonY + buttonHeight; - + if (inButtonArea) { showNotifications = true; } } - + // Show or hide notification panel based on hover if (panels.notifications.content) { if (showNotifications) { diff --git a/services/Network.qml b/services/Network.qml index 0b936b8..7732a1c 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -30,17 +30,17 @@ Singleton { property var wirelessDeviceDetails: null property string connectionStatus: "" property string connectionDebug: "" - + function clearConnectionStatus(): void { connectionStatus = ""; // Don't clear debug - keep it for reference // connectionDebug = ""; } - + function setConnectionStatus(status: string): void { connectionStatus = status; } - + function addDebugInfo(info: string): void { const timestamp = new Date().toLocaleTimeString(); const newInfo = "[" + timestamp + "] " + info; @@ -79,23 +79,23 @@ Singleton { // When no password, use SSID (will use saved password if available) const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0; let cmd = []; - + // Set up pending connection tracking if callback provided if (callback) { root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback }; } - + if (password && password.length > 0) { // When password is provided, try BSSID first if available, otherwise use SSID if (hasBssid) { // Use BSSID when password is provided - ensure BSSID is uppercase const bssidUpper = bssid.toUpperCase(); - + // Check if a connection with this SSID already exists - const existingConnection = root.savedConnections.find(conn => + const existingConnection = root.savedConnections.find(conn => conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim() ); - + if (existingConnection) { // Connection already exists - delete it first, then create new one with updated password root.addDebugInfo(qsTr("Connection '%1' already exists, deleting it first...").arg(existingConnection)); @@ -122,7 +122,7 @@ Singleton { root.setConnectionStatus(qsTr("Connecting to %1 (using saved password)...").arg(ssid)); root.addDebugInfo(qsTr("Using saved password for: %1").arg(ssid)); } - + // Show the exact command being executed const cmdStr = cmd.join(" "); root.addDebugInfo(qsTr("=== COMMAND TO EXECUTE ===")); @@ -130,17 +130,17 @@ Singleton { root.addDebugInfo(qsTr("Command array: [%1]").arg(cmd.map((arg, i) => `"${arg}"`).join(", "))); root.addDebugInfo(qsTr("Command array length: %1").arg(cmd.length)); root.addDebugInfo(qsTr("===========================")); - + // Set command and start process root.addDebugInfo(qsTr("Setting command property...")); connectProc.command = cmd; const setCmdStr = connectProc.command ? connectProc.command.join(" ") : "null"; root.addDebugInfo(qsTr("Command property set, value: %1").arg(setCmdStr)); root.addDebugInfo(qsTr("Command property verified: %1").arg(setCmdStr === cmdStr ? "Match" : "MISMATCH")); - + // If we're creating a connection profile, we need to activate it after creation const isConnectionAdd = cmd.length > 0 && cmd[0] === "nmcli" && cmd[1] === "connection" && cmd[2] === "add"; - + // Wait a moment before starting to ensure command is set Qt.callLater(() => { root.addDebugInfo(qsTr("=== STARTING PROCESS ===")); @@ -150,7 +150,7 @@ Singleton { connectProc.running = true; root.addDebugInfo(qsTr("Process running set to: %1").arg(connectProc.running)); root.addDebugInfo(qsTr("========================")); - + // Check if process actually started after a short delay Qt.callLater(() => { root.addDebugInfo(qsTr("Process status check (100ms later):")); @@ -162,7 +162,7 @@ Singleton { } }, 100); }); - + // Start connection check timer if we have a callback if (callback) { root.addDebugInfo(qsTr("Starting connection check timer (4 second interval)")); @@ -171,25 +171,25 @@ Singleton { root.addDebugInfo(qsTr("No callback provided - not starting connection check timer")); } } - + function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string): void { // Create connection profile with all required properties for BSSID + password - const cmd = ["nmcli", "connection", "add", - "type", "wifi", + const cmd = ["nmcli", "connection", "add", + "type", "wifi", "con-name", ssid, "ifname", "*", "ssid", ssid, "802-11-wireless.bssid", bssidUpper, "802-11-wireless-security.key-mgmt", "wpa-psk", "802-11-wireless-security.psk", password]; - + root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper)); root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid)); root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt")); - + // Set command and start process connectProc.command = cmd; - + Qt.callLater(() => { connectProc.running = true; }); @@ -198,7 +198,7 @@ Singleton { function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void { root.addDebugInfo(qsTr("=== connectToNetworkWithPasswordCheck ===")); root.addDebugInfo(qsTr("SSID: %1, isSecure: %2").arg(ssid).arg(isSecure)); - + // For secure networks, try connecting without password first // If connection succeeds (saved password exists), we're done // If it fails with password error, callback will be called to show password dialog @@ -228,7 +228,7 @@ Singleton { disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]); } } - + function forgetNetwork(ssid: string): void { // Delete the connection profile for this network // This will remove the saved password and connection settings @@ -240,7 +240,7 @@ Singleton { }, 500); } } - + function hasConnectionProfile(ssid: string): bool { // Check if a connection profile exists for this SSID // This is synchronous check - returns true if connection exists @@ -252,12 +252,12 @@ Singleton { // The actual check will be done asynchronously return false; } - + property list savedConnections: [] property list savedConnectionSsids: [] property var wifiConnectionQueue: [] property int currentSsidQueryIndex: 0 - + Process { id: listConnectionsProc command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] @@ -276,12 +276,12 @@ Singleton { } } } - + function parseConnectionList(output: string): void { const lines = output.trim().split("\n").filter(line => line.length > 0); const wifiConnections = []; const connections = []; - + // First pass: identify WiFi connections for (const line of lines) { const parts = line.split(":"); @@ -289,15 +289,15 @@ Singleton { const name = parts[0]; const type = parts[1]; connections.push(name); - + if (type === "802-11-wireless") { wifiConnections.push(name); } } } - + root.savedConnections = connections; - + // Second pass: get SSIDs for WiFi connections if (wifiConnections.length > 0) { root.wifiConnectionQueue = wifiConnections; @@ -310,10 +310,10 @@ Singleton { root.wifiConnectionQueue = []; } } - + Process { id: getSsidProc - + environment: ({ LANG: "C.UTF-8", LC_ALL: "C.UTF-8" @@ -332,7 +332,7 @@ Singleton { } } } - + function processSsidOutput(output: string): void { // Parse "802-11-wireless.ssid:SSID_NAME" format const lines = output.trim().split("\n"); @@ -351,11 +351,11 @@ Singleton { } } } - + // Query next connection queryNextSsid(); } - + function queryNextSsid(): void { if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) { const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex]; @@ -368,13 +368,13 @@ Singleton { root.currentSsidQueryIndex = 0; } } - + function hasSavedProfile(ssid: string): bool { if (!ssid || ssid.length === 0) { return false; } const ssidLower = ssid.toLowerCase().trim(); - + // If currently connected to this network, it definitely has a saved profile if (root.active && root.active.ssid) { const activeSsidLower = root.active.ssid.toLowerCase().trim(); @@ -382,21 +382,21 @@ Singleton { return true; } } - + // Check if SSID is in saved connections (case-insensitive comparison) - const hasSsid = root.savedConnectionSsids.some(savedSsid => + const hasSsid = root.savedConnectionSsids.some(savedSsid => savedSsid && savedSsid.toLowerCase().trim() === ssidLower ); - + if (hasSsid) { return true; } - + // Fallback: also check if connection name matches SSID (some connections use SSID as name) - const hasConnectionName = root.savedConnections.some(connName => + const hasConnectionName = root.savedConnections.some(connName => connName && connName.toLowerCase().trim() === ssidLower ); - + return hasConnectionName; } @@ -442,7 +442,7 @@ Singleton { if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) { return ""; } - + const mask = (0xffffffff << (32 - cidrNum)) >>> 0; const octets = [ (mask >>> 24) & 0xff, @@ -450,7 +450,7 @@ Singleton { (mask >>> 8) & 0xff, mask & 0xff ]; - + return octets.join("."); } @@ -510,7 +510,7 @@ Singleton { root.addDebugInfo(qsTr(" Pending SSID: %1").arg(root.pendingConnection.ssid)); root.addDebugInfo(qsTr(" Active SSID: %1").arg(root.active ? root.active.ssid : "None")); root.addDebugInfo(qsTr(" Connected: %1").arg(connected)); - + if (!connected && root.pendingConnection.callback) { // Connection didn't succeed after multiple checks, show password dialog root.addDebugInfo(qsTr("Connection failed - calling password dialog callback")); @@ -543,19 +543,19 @@ Singleton { repeat: true triggeredOnStart: false property int checkCount: 0 - + onRunningChanged: { if (running) { root.addDebugInfo(qsTr("Immediate check timer started (checks every 500ms)")); } } - + onTriggered: { if (root.pendingConnection) { checkCount++; const connected = root.active && root.active.ssid === root.pendingConnection.ssid; root.addDebugInfo(qsTr("Immediate check #%1: Connected=%2").arg(checkCount).arg(connected)); - + if (connected) { // Connection succeeded, stop timers and clear pending root.addDebugInfo(qsTr("Connection succeeded on check #%1!").arg(checkCount)); @@ -586,32 +586,32 @@ Singleton { onRunningChanged: { root.addDebugInfo(qsTr("Process running changed to: %1").arg(running)); } - + onStarted: { root.addDebugInfo(qsTr("Process started successfully")); } - + onExited: { root.addDebugInfo(qsTr("=== PROCESS EXITED ===")); root.addDebugInfo(qsTr("Exit code: %1").arg(exitCode)); root.addDebugInfo(qsTr("(Exit code 0 = success, non-zero = error)")); - + // Check if this was a "connection add" command - if so, we need to activate it - const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 - && connectProc.command[0] === "nmcli" - && connectProc.command[1] === "connection" + const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 + && connectProc.command[0] === "nmcli" + && connectProc.command[1] === "connection" && connectProc.command[2] === "add"; - + if (wasConnectionAdd && root.pendingConnection) { const ssid = root.pendingConnection.ssid; - + // Check for duplicate connection warning in stderr text const stderrText = connectProc.stderr ? connectProc.stderr.text : ""; const hasDuplicateWarning = stderrText && ( stderrText.includes("another connection with the name") || stderrText.includes("Reference the connection by its uuid") ); - + // Even with duplicate warning (or if connection already exists), we should try to activate it // Also try if exit code is non-zero but small (might be a warning, not a real error) if (exitCode === 0 || hasDuplicateWarning || (exitCode > 0 && exitCode < 10)) { @@ -622,10 +622,10 @@ Singleton { root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid)); root.setConnectionStatus(qsTr("Activating connection...")); } - + // Update saved connections list listConnectionsProc.running = true; - + // Try to activate the connection by SSID (connection name) connectProc.command = ["nmcli", "connection", "up", ssid]; Qt.callLater(() => { @@ -644,7 +644,7 @@ Singleton { password = connectProc.command[pskIndex + 1]; } } - + if (password && password.length > 0) { root.addDebugInfo(qsTr("Using device wifi connect with password as fallback")); connectProc.command = ["nmcli", "device", "wifi", "connect", ssid, "password", password]; @@ -655,10 +655,10 @@ Singleton { } } } - + // Refresh network list after connection attempt getNetworks.running = true; - + // Check if connection succeeded after a short delay (network list needs to update) if (root.pendingConnection) { if (exitCode === 0) { @@ -704,10 +704,10 @@ Singleton { root.addDebugInfo(qsTr("STDERR: %1").arg(line)); } } - + // Check for specific errors that indicate password is needed // Be careful not to match success messages - const needsPassword = (error.includes("Secrets were required") || + const needsPassword = (error.includes("Secrets were required") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || (error.includes("password") && !error.includes("Connection activated")) || @@ -715,7 +715,7 @@ Singleton { (error.includes("802.11") && !error.includes("Connection activated"))) && !error.includes("Connection activated") && !error.includes("successfully"); - + if (needsPassword && root.pendingConnection && root.pendingConnection.callback) { // Connection failed because password is needed - show dialog immediately connectionCheckTimer.stop(); @@ -784,7 +784,7 @@ Singleton { Process { id: deleteConnectionProc - + // Delete connection profile - refresh network list and saved connections after deletion onExited: { // Refresh network list and saved connections after deletion @@ -924,12 +924,12 @@ Singleton { onStreamFinished: { const output = text.trim(); root.ethernetDebugInfo = "Output received in onStreamFinished! Length: " + output.length + ", First 100 chars: " + output.substring(0, 100); - + if (!output || output.length === 0) { root.ethernetDebugInfo = "No output received (empty)"; return; } - + root.processEthernetOutput(output); } } @@ -942,7 +942,7 @@ Singleton { const lines = output.split("\n"); root.ethernetDebugInfo = "Processing " + lines.length + " lines"; - + const allDevices = lines.map(d => { const dev = d.replace(rep, PLACEHOLDER).split(":"); return { @@ -952,9 +952,9 @@ Singleton { connection: dev[3]?.replace(rep2, ":") ?? "" }; }); - + root.ethernetDebugInfo = "All devices: " + allDevices.length + ", Types: " + allDevices.map(d => d.type).join(", "); - + const ethernetOnly = allDevices.filter(d => d.type === "ethernet"); root.ethernetDebugInfo = "Ethernet devices found: " + ethernetOnly.length; @@ -975,7 +975,7 @@ Singleton { speed: "" }; }); - + root.ethernetDebugInfo = "Ethernet devices processed: " + ethernetDevices.length + ", First device: " + (ethernetDevices[0]?.interface || "none"); // Update the list - replace the entire array to ensure QML detects the change @@ -984,13 +984,13 @@ Singleton { for (let i = 0; i < ethernetDevices.length; i++) { newDevices.push(ethernetDevices[i]); } - + // Replace the entire list root.ethernetDevices = newDevices; - + // Force QML to detect the change by updating a property root.ethernetDeviceCount = ethernetDevices.length; - + // Force QML to re-evaluate the list by accessing it Qt.callLater(() => { const count = root.ethernetDevices.length; @@ -1130,7 +1130,7 @@ const line = lines[i]; // Find the connected wifi interface from device status const lines = output.split("\n"); let wifiInterface = ""; - + for (let i = 0; i < lines.length; i++) { const line = lines[i]; const parts = line.split(/\s+/); diff --git a/services/VPN.qml b/services/VPN.qml index 10e5e7e..412bda4 100644 --- a/services/VPN.qml +++ b/services/VPN.qml @@ -21,7 +21,7 @@ Singleton { const name = providerName; const iface = interfaceName; const defaults = getBuiltinDefaults(name, iface); - + if (isCustomProvider) { const custom = providerInput; return { @@ -31,7 +31,7 @@ Singleton { displayName: custom.displayName || defaults.displayName }; } - + return defaults; } @@ -62,7 +62,7 @@ Singleton { displayName: "Tailscale" } }; - + return builtins[name] || { connectCmd: [name, "up"], disconnectCmd: [name, "down"], diff --git a/utils/Icons.qml b/utils/Icons.qml index e946c4f..389eca3 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -194,13 +194,13 @@ Singleton { function getSpecialWsIcon(name: string): string { name = name.toLowerCase().slice("special:".length); - + for (const iconConfig of Config.bar.workspaces.specialWorkspaceIcons) { if (iconConfig.name === name) { return iconConfig.icon; } } - + if (name === "special") return "star"; if (name === "communication") -- cgit v1.2.3-freya From 7799ec67fda75d8b06f59c491ef119b15d9daf3d Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 19:52:24 -0500 Subject: nmcli: migrated all of wireless controlcenter --- modules/controlcenter/network/WirelessDetails.qml | 61 +++++++++----------- modules/controlcenter/network/WirelessList.qml | 55 +++++++++--------- .../network/WirelessPasswordDialog.qml | 66 ++++++++-------------- modules/controlcenter/network/WirelessSettings.qml | 12 ++-- 4 files changed, 84 insertions(+), 110 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index d5abc9d..4f11589 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -28,28 +28,23 @@ Item { } function checkSavedProfile(): void { - // Refresh saved connections list to ensure it's up to date - // This ensures the "Forget Network" button visibility is accurate if (network && network.ssid) { - // Always refresh to ensure we have the latest saved connections - // This is important when networks are selected or changed - Network.listConnectionsProc.running = true; + Nmcli.loadSavedConnections(() => {}); } } Connections { - target: Network + target: Nmcli function onActiveChanged() { updateDeviceDetails(); } } function updateDeviceDetails(): void { - // Only update details if the selected network is currently active - if (network && Network.active && Network.active.ssid === network.ssid) { - Network.updateWirelessDeviceDetails(); + if (network && Nmcli.active && Nmcli.active.ssid === network.ssid) { + Nmcli.getWirelessDeviceDetails("", () => {}); } else { - Network.wirelessDeviceDetails = null; + Nmcli.wirelessDeviceDetails = null; } } @@ -84,7 +79,7 @@ Item { if (checked) { handleConnect(); } else { - Network.disconnectFromNetwork(); + Nmcli.disconnectFromNetwork(); } } } @@ -96,8 +91,7 @@ Item { if (!root.network || !root.network.ssid) { return false; } - // Check if profile exists - this will update reactively when savedConnectionSsids changes - return Network.hasSavedProfile(root.network.ssid); + return Nmcli.hasSavedProfile(root.network.ssid); } color: Colours.palette.m3errorContainer onColor: Colours.palette.m3onErrorContainer @@ -105,12 +99,10 @@ Item { onClicked: { if (root.network && root.network.ssid) { - // Disconnect first if connected if (root.network.active) { - Network.disconnectFromNetwork(); + Nmcli.disconnectFromNetwork(); } - // Delete the connection profile - Network.forgetNetwork(root.network.ssid); + Nmcli.forgetNetwork(root.network.ssid, () => {}); } } } @@ -161,7 +153,7 @@ Item { SectionContainer { ConnectionInfoSection { - deviceDetails: Network.wirelessDeviceDetails + deviceDetails: Nmcli.wirelessDeviceDetails } } @@ -169,9 +161,8 @@ Item { } function handleConnect(): void { - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== root.network.ssid) { - Network.disconnectFromNetwork(); + if (Nmcli.active && Nmcli.active.ssid !== root.network.ssid) { + Nmcli.disconnectFromNetwork(); Qt.callLater(() => { connectToNetwork(); }); @@ -182,29 +173,31 @@ Item { function connectToNetwork(): void { if (root.network.isSecure) { - // Check if we have a saved connection profile for this network (by SSID) - const hasSavedProfile = Network.hasSavedProfile(root.network.ssid); + const hasSavedProfile = Nmcli.hasSavedProfile(root.network.ssid); if (hasSavedProfile) { - // Try connecting with saved password - don't show dialog if it fails - // The saved password should work, but if connection fails for other reasons, - // we'll let the user try manually later - Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null); + Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } else { - // No saved profile, try connecting without password first - Network.connectToNetworkWithPasswordCheck( + Nmcli.connectToNetworkWithPasswordCheck( root.network.ssid, root.network.isSecure, - () => { - // Callback: connection failed, show password dialog - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = root.network; + (result) => { + if (result.needsPassword) { + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; + } + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = root.network; + } }, root.network.bssid ); } } else { - Network.connectToNetwork(root.network.ssid, "", root.network.bssid, null); + Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } } } \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index ca6947a..f4e76b4 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -31,22 +31,22 @@ ColumnLayout { } ToggleButton { - toggled: Network.wifiEnabled + toggled: Nmcli.wifiEnabled icon: "wifi" accent: "Tertiary" onClicked: { - Network.toggleWifi(); + Nmcli.toggleWifi(null); } } ToggleButton { - toggled: Network.scanning + toggled: Nmcli.scanning icon: "wifi_find" accent: "Secondary" onClicked: { - Network.rescanWifi(); + Nmcli.rescanWifi(); } } @@ -70,13 +70,13 @@ ColumnLayout { spacing: Appearance.spacing.small StyledText { - text: qsTr("Networks (%1)").arg(Network.networks.length) + text: qsTr("Networks (%1)").arg(Nmcli.networks.length) font.pointSize: Appearance.font.size.large font.weight: 500 } StyledText { - visible: Network.scanning + visible: Nmcli.scanning text: qsTr("Scanning...") color: Colours.palette.m3primary font.pointSize: Appearance.font.size.small @@ -94,7 +94,7 @@ ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true - model: Network.networks + model: Nmcli.networks spacing: Appearance.spacing.small / 2 clip: true @@ -183,7 +183,7 @@ ColumnLayout { StateLayer { function onClicked(): void { if (modelData.active) { - Network.disconnectFromNetwork(); + Nmcli.disconnectFromNetwork(); } else { handleConnect(modelData); } @@ -205,19 +205,14 @@ ColumnLayout { } function checkSavedProfileForNetwork(ssid: string): void { - // Refresh saved connections list to ensure it's up to date - // This ensures accurate profile detection when selecting networks if (ssid && ssid.length > 0) { - // Always refresh to ensure we have the latest saved connections - // This is important when a network is selected from the list - Network.listConnectionsProc.running = true; + Nmcli.loadSavedConnections(() => {}); } } function handleConnect(network): void { - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== network.ssid) { - Network.disconnectFromNetwork(); + if (Nmcli.active && Nmcli.active.ssid !== network.ssid) { + Nmcli.disconnectFromNetwork(); Qt.callLater(() => { connectToNetwork(network); }); @@ -228,29 +223,31 @@ ColumnLayout { function connectToNetwork(network): void { if (network.isSecure) { - // Check if we have a saved connection profile for this network (by SSID) - const hasSavedProfile = Network.hasSavedProfile(network.ssid); + const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid); if (hasSavedProfile) { - // Try connecting with saved password - don't show dialog if it fails - // The saved password should work, but if connection fails for other reasons, - // we'll let the user try manually later - Network.connectToNetwork(network.ssid, "", network.bssid, null); + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); } else { - // No saved profile, try connecting without password first - Network.connectToNetworkWithPasswordCheck( + Nmcli.connectToNetworkWithPasswordCheck( network.ssid, network.isSecure, - () => { - // Callback: connection failed, show password dialog - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = network; + (result) => { + if (result.needsPassword) { + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; + } + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = network; + } }, network.bssid ); } } else { - Network.connectToNetwork(network.ssid, "", network.bssid, null); + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); } } } \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 8a71fa8..e416016 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -108,26 +108,16 @@ Item { Layout.alignment: Qt.AlignHCenter Layout.topMargin: Appearance.spacing.small - visible: Network.connectionStatus.length > 0 || connectButton.connecting + visible: connectButton.connecting text: { - if (Network.connectionStatus.length > 0) { - return Network.connectionStatus; - } else if (connectButton.connecting) { + if (connectButton.connecting) { return qsTr("Connecting..."); } return ""; } - color: { - const status = Network.connectionStatus; - if (status.includes("Error") || status.includes("error") || status.includes("failed")) { - return Colours.palette.m3error; - } else if (status.includes("successful") || status.includes("Connected") || status.includes("success")) { - return Colours.palette.m3primary; - } - return Colours.palette.m3onSurfaceVariant; - } + color: Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small - font.weight: (Network.connectionStatus.includes("Error") || Network.connectionStatus.includes("error")) ? 500 : 400 + font.weight: 400 wrapMode: Text.WordWrap Layout.maximumWidth: parent.width - Appearance.padding.large * 2 } @@ -166,7 +156,6 @@ Item { if (root.visible) { passwordField.forceActiveFocus(); passwordField.text = ""; - Network.clearConnectionStatus(); } } } @@ -225,10 +214,9 @@ Item { connecting = true; enabled = false; text = qsTr("Connecting..."); - Network.clearConnectionStatus(); // Connect to network - Network.connectToNetwork( + Nmcli.connectToNetwork( root.network.ssid, password, root.network.bssid || "", @@ -248,27 +236,17 @@ Item { return; } - // Check connection status message for success indicators - const status = Network.connectionStatus; - const statusLower = status.toLowerCase(); - - // Check for success indicators in status message - const hasSuccessIndicator = statusLower.includes("connection activated") || - statusLower.includes("successfully") || - statusLower.includes("connected successfully") || - (statusLower.includes("connected") && !statusLower.includes("error") && !statusLower.includes("failed")); - // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Network.active && Network.active.ssid && - Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && + Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); - if (isConnected || hasSuccessIndicator) { + if (isConnected) { // Successfully connected - give it a moment for network list to update Qt.callLater(() => { // Double-check connection is still active - if (root.visible && Network.active && Network.active.ssid) { - const stillConnected = Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); - if (stillConnected || hasSuccessIndicator) { + if (root.visible && Nmcli.active && Nmcli.active.ssid) { + const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + if (stillConnected) { connectionMonitor.stop(); connectButton.connecting = false; connectButton.text = qsTr("Connect"); @@ -279,11 +257,10 @@ Item { return; } - // Check for connection errors (but not warnings about duplicate names) - if (status.includes("Error") || (status.includes("error") && !status.includes("Warning"))) { - // Only treat as error if it's not just a warning about duplicate names - if (!status.includes("another connection with the name") && !status.includes("Reference the connection by its uuid")) { - // Connection failed + // Check for connection failures - if pending connection was cleared but we're not connected + if (Nmcli.pendingConnection === null && connectButton.connecting) { + // Wait a bit more before giving up (allow time for connection to establish) + if (connectionMonitor.repeatCount > 10) { connectionMonitor.stop(); connectButton.connecting = false; connectButton.enabled = true; @@ -297,14 +274,22 @@ Item { interval: 1000 repeat: true triggeredOnStart: false + property int repeatCount: 0 onTriggered: { + repeatCount++; checkConnectionStatus(); } + + onRunningChanged: { + if (!running) { + repeatCount = 0; + } + } } Connections { - target: Network + target: Nmcli function onActiveChanged() { if (root.visible) { checkConnectionStatus(); @@ -318,6 +303,5 @@ Item { connectButton.connecting = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); - Network.clearConnectionStatus(); } -} \ No newline at end of file +} diff --git a/modules/controlcenter/network/WirelessSettings.qml b/modules/controlcenter/network/WirelessSettings.qml index 073b09c..0eb1578 100644 --- a/modules/controlcenter/network/WirelessSettings.qml +++ b/modules/controlcenter/network/WirelessSettings.qml @@ -39,9 +39,9 @@ ColumnLayout { SectionContainer { ToggleRow { label: qsTr("WiFi enabled") - checked: Network.wifiEnabled + checked: Nmcli.wifiEnabled toggle.onToggled: { - Network.enableWifi(checked); + Nmcli.enableWifi(checked); } } } @@ -57,25 +57,25 @@ ColumnLayout { PropertyRow { label: qsTr("Connected network") - value: Network.active ? Network.active.ssid : qsTr("Not connected") + value: Nmcli.active ? Nmcli.active.ssid : qsTr("Not connected") } PropertyRow { showTopMargin: true label: qsTr("Signal strength") - value: Network.active ? qsTr("%1%").arg(Network.active.strength) : qsTr("N/A") + value: Nmcli.active ? qsTr("%1%").arg(Nmcli.active.strength) : qsTr("N/A") } PropertyRow { showTopMargin: true label: qsTr("Security") - value: Network.active ? (Network.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") + value: Nmcli.active ? (Nmcli.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") } PropertyRow { showTopMargin: true label: qsTr("Frequency") - value: Network.active ? qsTr("%1 MHz").arg(Network.active.frequency) : qsTr("N/A") + value: Nmcli.active ? qsTr("%1 MHz").arg(Nmcli.active.frequency) : qsTr("N/A") } } } \ No newline at end of file -- cgit v1.2.3-freya From 144d04b08b132c9ee662fbde79be7f31e18b3d5b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 22:42:46 -0500 Subject: controlcenter: password dialog matches lockscreen --- components/controls/StyledTextField.qml | 2 +- .../network/WirelessPasswordDialog.qml | 216 +++++++++++++++++---- 2 files changed, 180 insertions(+), 38 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/components/controls/StyledTextField.qml b/components/controls/StyledTextField.qml index 4db87e9..60bcff2 100644 --- a/components/controls/StyledTextField.qml +++ b/components/controls/StyledTextField.qml @@ -13,7 +13,7 @@ TextField { placeholderTextColor: Colours.palette.m3outline font.family: Appearance.font.family.sans font.pointSize: Appearance.font.size.smaller - renderType: TextField.NativeRendering + renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering cursorVisible: !readOnly background: null diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index e416016..1ea39c9 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -8,6 +8,7 @@ import qs.components.effects import qs.components.containers import qs.services import qs.config +import Quickshell import QtQuick import QtQuick.Layouts @@ -27,7 +28,8 @@ Item { return null; } - visible: session.network.showPasswordDialog + property bool isClosing: false + visible: session.network.showPasswordDialog && !isClosing enabled: visible focus: visible @@ -38,10 +40,14 @@ Item { Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.5) - opacity: root.visible ? 1 : 0 + opacity: root.visible && !root.isClosing ? 1 : 0 Behavior on opacity { - NumberAnimation { duration: 200 } + Anim { + property: "opacity" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } MouseArea { @@ -60,15 +66,23 @@ Item { radius: Appearance.rounding.normal color: Colours.tPalette.m3surface - opacity: root.visible ? 1 : 0 - scale: root.visible ? 1 : 0.9 + opacity: root.visible && !root.isClosing ? 1 : 0 + scale: root.visible && !root.isClosing ? 1 : 0.9 Behavior on opacity { - NumberAnimation { duration: 200 } + Anim { + property: "opacity" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } Behavior on scale { - NumberAnimation { duration: 200 } + Anim { + property: "scale" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } Keys.onEscapePressed: closeDialog(); @@ -123,53 +137,151 @@ Item { } Item { + id: passwordContainer Layout.topMargin: Appearance.spacing.large Layout.fillWidth: true - implicitHeight: passwordField.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: Math.max(48, charList.implicitHeight + Appearance.padding.normal * 2) + + focus: true + Keys.onPressed: event => { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + if (connectButton.enabled) { + connectButton.clicked(); + } + } else if (event.key === Qt.Key_Backspace) { + if (event.modifiers & Qt.ControlModifier) { + passwordBuffer = ""; + } else { + passwordBuffer = passwordBuffer.slice(0, -1); + } + } else if (event.text && event.text.length > 0) { + passwordBuffer += event.text; + } + } + + property string passwordBuffer: "" + + Connections { + target: root + function onVisibleChanged(): void { + if (root.visible) { + passwordContainer.forceActiveFocus(); + passwordContainer.passwordBuffer = ""; + } + } + } StyledRect { anchors.fill: parent radius: Appearance.rounding.normal color: Colours.tPalette.m3surfaceContainer - border.width: passwordField.activeFocus ? 2 : 1 - border.color: passwordField.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline + border.width: passwordContainer.activeFocus ? 2 : 1 + border.color: passwordContainer.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline Behavior on border.color { CAnim {} } } - StyledTextField { - id: passwordField + StateLayer { + hoverEnabled: false + cursorShape: Qt.IBeamCursor + + function onClicked(): void { + passwordContainer.forceActiveFocus(); + } + } + + StyledText { + id: placeholder + anchors.centerIn: parent + text: qsTr("Password") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + font.family: Appearance.font.family.mono + opacity: passwordContainer.passwordBuffer ? 0 : 1 + + Behavior on opacity { + Anim {} + } + } + + ListView { + id: charList + + readonly property int fullWidth: count * (implicitHeight + spacing) - spacing + + anchors.centerIn: parent + implicitWidth: fullWidth + implicitHeight: Appearance.font.size.normal + + orientation: Qt.Horizontal + spacing: Appearance.spacing.small / 2 + interactive: false + + model: ScriptModel { + values: passwordContainer.passwordBuffer.split("") + } + + delegate: StyledRect { + id: ch - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + implicitWidth: implicitHeight + implicitHeight: charList.implicitHeight - echoMode: TextField.Password - placeholderText: qsTr("Password") + color: Colours.palette.m3onSurface + radius: Appearance.rounding.small / 2 + + opacity: 0 + scale: 0 + Component.onCompleted: { + opacity = 1; + scale = 1; + } + ListView.onRemove: removeAnim.start() - Connections { - target: root - function onVisibleChanged(): void { - if (root.visible) { - passwordField.forceActiveFocus(); - passwordField.text = ""; + SequentialAnimation { + id: removeAnim + + PropertyAction { + target: ch + property: "ListView.delayRemove" + value: true + } + ParallelAnimation { + Anim { + target: ch + property: "opacity" + to: 0 + } + Anim { + target: ch + property: "scale" + to: 0.5 + } + } + PropertyAction { + target: ch + property: "ListView.delayRemove" + value: false } } - } - Keys.onReturnPressed: { - if (connectButton.enabled) { - connectButton.clicked(); + Behavior on opacity { + Anim {} } - } - Keys.onEnterPressed: { - if (connectButton.enabled) { - connectButton.clicked(); + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } } } + + Behavior on implicitWidth { + Anim {} + } } } @@ -196,7 +308,7 @@ Item { color: Colours.palette.m3primary onColor: Colours.palette.m3onPrimary text: qsTr("Connect") - enabled: passwordField.text.length > 0 && !connecting + enabled: passwordContainer.passwordBuffer.length > 0 && !connecting property bool connecting: false @@ -205,7 +317,7 @@ Item { return; } - const password = passwordField.text; + const password = passwordContainer.passwordBuffer; if (!password || password.length === 0) { return; } @@ -220,7 +332,19 @@ Item { root.network.ssid, password, root.network.bssid || "", - null + (result) => { + if (result && result.success) { + // Connection successful, monitor will handle the rest + } else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + connectionMonitor.stop(); + connecting = false; + enabled = true; + text = qsTr("Connect"); + } else { + // Connection failed, monitor will handle timeout + } + } ); // Start monitoring connection @@ -295,13 +419,31 @@ Item { checkConnectionStatus(); } } + function onConnectionFailed(ssid: string) { + if (root.visible && root.network && root.network.ssid === ssid && connectButton.connecting) { + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.enabled = true; + connectButton.text = qsTr("Connect"); + } + } } function closeDialog(): void { - session.network.showPasswordDialog = false; - passwordField.text = ""; + if (isClosing) { + return; + } + + isClosing = true; + passwordContainer.passwordBuffer = ""; connectButton.connecting = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); + + // Wait for fade-out animation to complete before actually hiding + Qt.callLater(() => { + session.network.showPasswordDialog = false; + isClosing = false; + }, Appearance.anim.durations.normal); } } -- cgit v1.2.3-freya From a004902bc6ff931342d7bc18ef4b448205f833d2 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 08:17:21 -0500 Subject: controlcenter: correcting cancel/connect sizing issues --- modules/controlcenter/network/WirelessPasswordDialog.qml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 1ea39c9..d1ce066 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -294,6 +294,7 @@ Item { id: cancelButton Layout.fillWidth: true + Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 color: Colours.palette.m3secondaryContainer onColor: Colours.palette.m3onSecondaryContainer text: qsTr("Cancel") @@ -305,6 +306,7 @@ Item { id: connectButton Layout.fillWidth: true + Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 color: Colours.palette.m3primary onColor: Colours.palette.m3onPrimary text: qsTr("Connect") -- cgit v1.2.3-freya From f472be184a012031d490a3eeaf924d1c54b628a2 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 16:21:22 -0500 Subject: controlcenter: fade-out animation on password input dialog close --- .../network/WirelessPasswordDialog.qml | 51 ++++++++++++---------- 1 file changed, 27 insertions(+), 24 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index d1ce066..31372e0 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -29,9 +29,9 @@ Item { } property bool isClosing: false - visible: session.network.showPasswordDialog && !isClosing - enabled: visible - focus: visible + visible: session.network.showPasswordDialog || isClosing + enabled: session.network.showPasswordDialog && !isClosing + focus: enabled Keys.onEscapePressed: { closeDialog(); @@ -40,14 +40,10 @@ Item { Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.5) - opacity: root.visible && !root.isClosing ? 1 : 0 + opacity: root.session.network.showPasswordDialog && !root.isClosing ? 1 : 0 Behavior on opacity { - Anim { - property: "opacity" - duration: Appearance.anim.durations.normal - easing.bezierCurve: Appearance.anim.curves.standardDecel - } + Anim {} } MouseArea { @@ -66,22 +62,35 @@ Item { radius: Appearance.rounding.normal color: Colours.tPalette.m3surface - opacity: root.visible && !root.isClosing ? 1 : 0 - scale: root.visible && !root.isClosing ? 1 : 0.9 + opacity: root.session.network.showPasswordDialog && !root.isClosing ? 1 : 0 + scale: root.session.network.showPasswordDialog && !root.isClosing ? 1 : 0.7 Behavior on opacity { - Anim { - property: "opacity" - duration: Appearance.anim.durations.normal - easing.bezierCurve: Appearance.anim.curves.standardDecel - } + Anim {} } Behavior on scale { + Anim {} + } + + ParallelAnimation { + running: root.isClosing + onFinished: { + if (root.isClosing) { + root.session.network.showPasswordDialog = false; + root.isClosing = false; + } + } + Anim { + target: dialog + property: "opacity" + to: 0 + } + Anim { + target: dialog property: "scale" - duration: Appearance.anim.durations.normal - easing.bezierCurve: Appearance.anim.curves.standardDecel + to: 0.7 } } @@ -441,11 +450,5 @@ Item { connectButton.connecting = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); - - // Wait for fade-out animation to complete before actually hiding - Qt.callLater(() => { - session.network.showPasswordDialog = false; - isClosing = false; - }, Appearance.anim.durations.normal); } } -- cgit v1.2.3-freya From 9a08d6666eeef12608a834bdd54147d5075c10cd Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 18:00:24 -0500 Subject: controlcenter: auto-focus reliably for wireless password input --- .../controlcenter/network/WirelessPasswordDialog.qml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 31372e0..d0c023f 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -170,12 +170,27 @@ Item { property string passwordBuffer: "" + Connections { + target: root.session.network + function onShowPasswordDialogChanged(): void { + if (root.session.network.showPasswordDialog) { + // Use callLater to ensure focus happens after dialog is fully rendered + Qt.callLater(() => { + passwordContainer.forceActiveFocus(); + passwordContainer.passwordBuffer = ""; + }); + } + } + } + Connections { target: root function onVisibleChanged(): void { if (root.visible) { - passwordContainer.forceActiveFocus(); - passwordContainer.passwordBuffer = ""; + // Use callLater to ensure focus happens after dialog is fully rendered + Qt.callLater(() => { + passwordContainer.forceActiveFocus(); + }); } } } -- cgit v1.2.3-freya From 6a92f985d8739ef2c397714f79e18b74f48fb705 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:15:02 +1100 Subject: internal: use existing button controls Instead of SimpleButton --- components/controls/IconTextButton.qml | 9 +- modules/bar/popouts/Audio.qml | 11 +- modules/bar/popouts/Bluetooth.qml | 11 +- modules/bar/popouts/Network.qml | 15 +- modules/bar/popouts/WirelessPasswordPopout.qml | 165 +++++++-------------- modules/controlcenter/network/SimpleButton.qml | 50 ------- modules/controlcenter/network/WirelessDetails.qml | 44 +++--- .../network/WirelessPasswordDialog.qml | 54 +++---- utils/Icons.qml | 1 + 9 files changed, 120 insertions(+), 240 deletions(-) delete mode 100644 modules/controlcenter/network/SimpleButton.qml (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/components/controls/IconTextButton.qml b/components/controls/IconTextButton.qml index 78e7c5b..0badd7a 100644 --- a/components/controls/IconTextButton.qml +++ b/components/controls/IconTextButton.qml @@ -2,6 +2,7 @@ import ".." import qs.services import qs.config import QtQuick +import QtQuick.Layouts StyledRect { id: root @@ -53,7 +54,7 @@ StyledRect { } } - Row { + RowLayout { id: row anchors.centerIn: parent @@ -62,7 +63,8 @@ StyledRect { MaterialIcon { id: iconLabel - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter + Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour fill: root.internalChecked ? 1 : 0 @@ -74,7 +76,8 @@ StyledRect { StyledText { id: label - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter + Layout.topMargin: -Math.round(iconLabel.fontInfo.pointSize * 0.0575) color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour } } diff --git a/modules/bar/popouts/Audio.qml b/modules/bar/popouts/Audio.qml index 40c09d3..58b29ba 100644 --- a/modules/bar/popouts/Audio.qml +++ b/modules/bar/popouts/Audio.qml @@ -105,17 +105,16 @@ Item { } } - SimpleButton { + IconTextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal - color: Colours.palette.m3primaryContainer - onColor: Colours.palette.m3onPrimaryContainer + inactiveColour: Colours.palette.m3primaryContainer + inactiveOnColour: Colours.palette.m3onPrimaryContainer + verticalPadding: Appearance.padding.small text: qsTr("Open settings") icon: "settings" - onClicked: { - root.wrapper.detach("audio"); - } + onClicked: root.wrapper.detach("audio") } } } diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml index 63621c2..4674905 100644 --- a/modules/bar/popouts/Bluetooth.qml +++ b/modules/bar/popouts/Bluetooth.qml @@ -165,17 +165,16 @@ ColumnLayout { } } - SimpleButton { + IconTextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal - color: Colours.palette.m3primaryContainer - onColor: Colours.palette.m3onPrimaryContainer + inactiveColour: Colours.palette.m3primaryContainer + inactiveOnColour: Colours.palette.m3onPrimaryContainer + verticalPadding: Appearance.padding.small text: qsTr("Open settings") icon: "settings" - onClicked: { - root.wrapper.detach("bluetooth"); - } + onClicked: root.wrapper.detach("bluetooth") } component Toggle: RowLayout { diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml index 93ff867..b9f66c4 100644 --- a/modules/bar/popouts/Network.qml +++ b/modules/bar/popouts/Network.qml @@ -111,10 +111,8 @@ ColumnLayout { } StyledRect { - id: connectBtn - implicitWidth: implicitHeight - implicitHeight: connectIcon.implicitHeight + Appearance.padding.small + implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.small radius: Appearance.rounding.full color: Qt.alpha(Colours.palette.m3primary, networkItem.modelData.active ? 1 : 0) @@ -136,7 +134,7 @@ ColumnLayout { // Check if network is secure if (networkItem.modelData.isSecure) { // Try to connect first - will show password dialog if password is needed - Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, (result) => { + Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, result => { if (result && result.needsPassword) { // Password is required - show password dialog root.passwordNetwork = networkItem.modelData; @@ -159,7 +157,7 @@ ColumnLayout { } MaterialIcon { - id: connectIcon + id: wirelessConnectIcon anchors.centerIn: parent animate: true @@ -205,12 +203,14 @@ ColumnLayout { MaterialIcon { id: scanIcon + Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) animate: true text: "wifi_find" color: Colours.palette.m3onPrimaryContainer } StyledText { + Layout.topMargin: -Math.round(scanIcon.fontInfo.pointSize * 0.0575) text: qsTr("Rescan networks") color: Colours.palette.m3onPrimaryContainer } @@ -303,8 +303,6 @@ ColumnLayout { } StyledRect { - id: connectBtn - implicitWidth: implicitHeight implicitHeight: connectIcon.implicitHeight + Appearance.padding.small @@ -354,8 +352,7 @@ ColumnLayout { if (Nmcli.active && root.connectingToSsid === Nmcli.active.ssid) { root.connectingToSsid = ""; // Close password dialog if we successfully connected - if (root.showPasswordDialog && root.passwordNetwork && - Nmcli.active.ssid === root.passwordNetwork.ssid) { + if (root.showPasswordDialog && root.passwordNetwork && Nmcli.active.ssid === root.passwordNetwork.ssid) { root.showPasswordDialog = false; root.passwordNetwork = null; if (root.wrapper.currentName === "wirelesspassword") { diff --git a/modules/bar/popouts/WirelessPasswordPopout.qml b/modules/bar/popouts/WirelessPasswordPopout.qml index 2fd063f..aa7f40f 100644 --- a/modules/bar/popouts/WirelessPasswordPopout.qml +++ b/modules/bar/popouts/WirelessPasswordPopout.qml @@ -41,14 +41,14 @@ ColumnLayout { } spacing: Appearance.spacing.normal - + implicitWidth: 400 implicitHeight: content.implicitHeight + Appearance.padding.large * 2 visible: shouldBeVisible || isClosing enabled: shouldBeVisible && !isClosing focus: enabled - + Component.onCompleted: { if (shouldBeVisible) { Qt.callLater(() => { @@ -57,7 +57,7 @@ ColumnLayout { }, 150); } } - + onShouldBeVisibleChanged: { if (shouldBeVisible) { Qt.callLater(() => { @@ -67,9 +67,7 @@ ColumnLayout { } } - Keys.onEscapePressed: { - closeDialog(); - } + Keys.onEscapePressed: closeDialog() StyledRect { Layout.fillWidth: true @@ -109,7 +107,7 @@ ColumnLayout { } } - Keys.onEscapePressed: closeDialog(); + Keys.onEscapePressed: root.closeDialog() ColumnLayout { id: content @@ -149,7 +147,7 @@ ColumnLayout { color: Colours.palette.m3outline font.pointSize: Appearance.font.size.small } - + Timer { interval: 50 running: root.shouldBeVisible && (!root.network || !root.network.ssid) @@ -217,12 +215,12 @@ ColumnLayout { if (!activeFocus) { forceActiveFocus(); } - + // Clear error when user starts typing if (connectButton.hasError && event.text && event.text.length > 0) { connectButton.hasError = false; } - + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { if (connectButton.enabled) { connectButton.clicked(); @@ -253,7 +251,7 @@ ColumnLayout { } } } - + Component.onCompleted: { if (root.shouldBeVisible) { Qt.callLater(() => { @@ -265,9 +263,7 @@ ColumnLayout { StyledRect { anchors.fill: parent radius: Appearance.rounding.normal - color: passwordContainer.activeFocus - ? Qt.lighter(Colours.tPalette.m3surfaceContainer, 1.05) - : Colours.tPalette.m3surfaceContainer + color: passwordContainer.activeFocus ? Qt.lighter(Colours.tPalette.m3surfaceContainer, 1.05) : Colours.tPalette.m3surfaceContainer border.width: passwordContainer.activeFocus || connectButton.hasError ? 4 : (root.shouldBeVisible ? 1 : 0) border.color: { if (connectButton.hasError) { @@ -401,31 +397,31 @@ ColumnLayout { Layout.fillWidth: true spacing: Appearance.spacing.normal - SimpleButton { + TextButton { id: cancelButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3secondaryContainer - onColor: Colours.palette.m3onSecondaryContainer + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer text: qsTr("Cancel") - onClicked: closeDialog(); + onClicked: root.closeDialog() } - SimpleButton { + TextButton { id: connectButton + property bool connecting: false + property bool hasError: false + Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3primary - onColor: Colours.palette.m3onPrimary + inactiveColour: Colours.palette.m3primary + inactiveOnColour: Colours.palette.m3onPrimary text: qsTr("Connect") enabled: passwordContainer.passwordBuffer.length > 0 && !connecting - property bool connecting: false - property bool hasError: false - onClicked: { if (!root.network || connecting) { return; @@ -445,40 +441,35 @@ ColumnLayout { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork( - root.network.ssid, - password, - root.network.bssid || "", - (result) => { - if (result && result.success) { - // Connection successful, monitor will handle the rest - } else if (result && result.needsPassword) { - // Shouldn't happen since we provided password - connectionMonitor.stop(); - connecting = false; - hasError = true; - enabled = true; - text = qsTr("Connect"); - passwordContainer.passwordBuffer = ""; - // Delete the failed connection - if (root.network && root.network.ssid) { - Nmcli.forgetNetwork(root.network.ssid, () => {}); - } - } else { - // Connection failed immediately - show error - connectionMonitor.stop(); - connecting = false; - hasError = true; - enabled = true; - text = qsTr("Connect"); - passwordContainer.passwordBuffer = ""; - // Delete the failed connection - if (root.network && root.network.ssid) { - Nmcli.forgetNetwork(root.network.ssid, () => {}); - } + Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + if (result && result.success) + // Connection successful, monitor will handle the rest + {} else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + connectionMonitor.stop(); + connecting = false; + hasError = true; + enabled = true; + text = qsTr("Connect"); + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + if (root.network && root.network.ssid) { + Nmcli.forgetNetwork(root.network.ssid); + } + } else { + // Connection failed immediately - show error + connectionMonitor.stop(); + connecting = false; + hasError = true; + enabled = true; + text = qsTr("Connect"); + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + if (root.network && root.network.ssid) { + Nmcli.forgetNetwork(root.network.ssid); } } - ); + }); // Start monitoring connection connectionMonitor.start(); @@ -494,8 +485,7 @@ ColumnLayout { } // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && - Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected) { // Successfully connected - give it a moment for network list to update @@ -530,7 +520,7 @@ ColumnLayout { passwordContainer.passwordBuffer = ""; // Delete the failed connection if (root.network && root.network.ssid) { - Nmcli.forgetNetwork(root.network.ssid, () => {}); + Nmcli.forgetNetwork(root.network.ssid); } } } @@ -545,7 +535,7 @@ ColumnLayout { onTriggered: { repeatCount++; - checkConnectionStatus(); + root.checkConnectionStatus(); } onRunningChanged: { @@ -559,7 +549,7 @@ ColumnLayout { target: Nmcli function onActiveChanged() { if (root.shouldBeVisible) { - checkConnectionStatus(); + root.checkConnectionStatus(); } } function onConnectionFailed(ssid: string) { @@ -571,7 +561,7 @@ ColumnLayout { connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; // Delete the failed connection - Nmcli.forgetNetwork(ssid, () => {}); + Nmcli.forgetNetwork(ssid); } } } @@ -580,64 +570,17 @@ ColumnLayout { if (isClosing) { return; } - + isClosing = true; passwordContainer.passwordBuffer = ""; connectButton.connecting = false; connectButton.hasError = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); - + // Return to network popout if (root.wrapper.currentName === "wirelesspassword") { root.wrapper.currentName = "network"; } } - - component SimpleButton: StyledRect { - id: button - - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - property string icon: "" - - implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: rowLayout.implicitHeight + Appearance.padding.small - radius: Appearance.rounding.small - - StateLayer { - id: stateLayer - color: parent.onColor - function onClicked(): void { - if (parent.enabled !== false) { - parent.clicked(); - } - } - } - - RowLayout { - id: rowLayout - anchors.centerIn: parent - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconItem - visible: button.icon.length > 0 - text: button.icon - color: button.onColor - font.pointSize: Appearance.font.size.large - } - - StyledText { - id: label - Layout.leftMargin: button.icon.length > 0 ? Appearance.padding.smaller : 0 - color: parent.parent.onColor - } - } - - signal clicked - } } - diff --git a/modules/controlcenter/network/SimpleButton.qml b/modules/controlcenter/network/SimpleButton.qml deleted file mode 100644 index a73f751..0000000 --- a/modules/controlcenter/network/SimpleButton.qml +++ /dev/null @@ -1,50 +0,0 @@ -import qs.components -import qs.components.controls -import qs.components.effects -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - property string icon: "" - - implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: rowLayout.implicitHeight + Appearance.padding.small * 2 - radius: Appearance.rounding.normal - - StateLayer { - id: stateLayer - color: parent.onColor - function onClicked(): void { - if (parent.enabled !== false) { - parent.clicked(); - } - } - } - - RowLayout { - id: rowLayout - anchors.centerIn: parent - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconItem - visible: root.icon.length > 0 - text: root.icon - color: root.onColor - } - - StyledText { - id: label - color: parent.parent.onColor - } - } - - signal clicked -} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 5b4f541..651f1fb 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -69,7 +69,7 @@ Item { if (!Nmcli.wirelessDeviceDetails || Nmcli.wirelessDeviceDetails === null) { // Network is active but we don't have details yet, fetch them Nmcli.getWirelessDeviceDetails("", () => { - // After fetching, check if we got details - if not, timer will try again + // After fetching, check if we got details - if not, timer will try again }); } else { // We have details, can stop the timer @@ -89,7 +89,7 @@ Item { if (network && network.ssid) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive) { - Nmcli.getWirelessDeviceDetails("", () => {}); + Nmcli.getWirelessDeviceDetails(""); } else { Nmcli.wirelessDeviceDetails = null; } @@ -134,14 +134,14 @@ Item { checked: root.network?.active ?? false toggle.onToggled: { if (checked) { - handleConnect(); + root.handleConnect(); } else { Nmcli.disconnectFromNetwork(); } } } - SimpleButton { + TextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 @@ -151,8 +151,8 @@ Item { } return Nmcli.hasSavedProfile(root.network.ssid); } - color: Colours.palette.m3errorContainer - onColor: Colours.palette.m3onErrorContainer + inactiveColour: Colours.palette.m3errorContainer + inactiveOnColour: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") onClicked: { @@ -160,7 +160,7 @@ Item { if (root.network.active) { Nmcli.disconnectFromNetwork(); } - Nmcli.forgetNetwork(root.network.ssid, () => {}); + Nmcli.forgetNetwork(root.network.ssid); } } } @@ -214,7 +214,6 @@ Item { deviceDetails: Nmcli.wirelessDeviceDetails } } - } } @@ -236,26 +235,21 @@ Item { if (hasSavedProfile) { Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } else { - Nmcli.connectToNetworkWithPasswordCheck( - root.network.ssid, - root.network.isSecure, - (result) => { - if (result.needsPassword) { - if (Nmcli.pendingConnection) { - Nmcli.connectionCheckTimer.stop(); - Nmcli.immediateCheckTimer.stop(); - Nmcli.immediateCheckTimer.checkCount = 0; - Nmcli.pendingConnection = null; - } - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = root.network; + Nmcli.connectToNetworkWithPasswordCheck(root.network.ssid, root.network.isSecure, result => { + if (result.needsPassword) { + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; } - }, - root.network.bssid - ); + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = root.network; + } + }, root.network.bssid); } } else { Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } } -} \ No newline at end of file +} diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index d0c023f..f3381b7 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -48,7 +48,7 @@ Item { MouseArea { anchors.fill: parent - onClicked: closeDialog(); + onClicked: closeDialog() } } @@ -94,7 +94,7 @@ Item { } } - Keys.onEscapePressed: closeDialog(); + Keys.onEscapePressed: closeDialog() ColumnLayout { id: content @@ -314,25 +314,25 @@ Item { Layout.fillWidth: true spacing: Appearance.spacing.normal - SimpleButton { + TextButton { id: cancelButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3secondaryContainer - onColor: Colours.palette.m3onSecondaryContainer + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer text: qsTr("Cancel") - onClicked: closeDialog(); + onClicked: root.closeDialog() } - SimpleButton { + TextButton { id: connectButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3primary - onColor: Colours.palette.m3onPrimary + inactiveColour: Colours.palette.m3primary + inactiveOnColour: Colours.palette.m3onPrimary text: qsTr("Connect") enabled: passwordContainer.passwordBuffer.length > 0 && !connecting @@ -354,24 +354,19 @@ Item { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork( - root.network.ssid, - password, - root.network.bssid || "", - (result) => { - if (result && result.success) { - // Connection successful, monitor will handle the rest - } else if (result && result.needsPassword) { - // Shouldn't happen since we provided password - connectionMonitor.stop(); - connecting = false; - enabled = true; - text = qsTr("Connect"); - } else { - // Connection failed, monitor will handle timeout - } - } - ); + Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + if (result && result.success) + // Connection successful, monitor will handle the rest + {} else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + connectionMonitor.stop(); + connecting = false; + enabled = true; + text = qsTr("Connect"); + } else + // Connection failed, monitor will handle timeout + {} + }); // Start monitoring connection connectionMonitor.start(); @@ -387,8 +382,7 @@ Item { } // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && - Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected) { // Successfully connected - give it a moment for network list to update @@ -459,7 +453,7 @@ Item { if (isClosing) { return; } - + isClosing = true; passwordContainer.passwordBuffer = ""; connectButton.connecting = false; diff --git a/utils/Icons.qml b/utils/Icons.qml index 389eca3..b3fe8ab 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -3,6 +3,7 @@ pragma Singleton import qs.config import Quickshell import Quickshell.Services.Notifications +import QtQuick Singleton { id: root -- cgit v1.2.3-freya From 05b0660627586dc7624380e82b818b53004771f5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 01:31:53 -0500 Subject: controlcenter: password input errors/wrong pass --- modules/bar/popouts/WirelessPasswordPopout.qml | 85 ++- modules/controlcenter/network/NetworkingPane.qml | 691 +++++++++++---------- .../network/WirelessPasswordDialog.qml | 115 +++- services/Nmcli.qml | 10 +- 4 files changed, 502 insertions(+), 399 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/bar/popouts/WirelessPasswordPopout.qml b/modules/bar/popouts/WirelessPasswordPopout.qml index aa7f40f..59a15b9 100644 --- a/modules/bar/popouts/WirelessPasswordPopout.qml +++ b/modules/bar/popouts/WirelessPasswordPopout.qml @@ -32,14 +32,22 @@ ColumnLayout { } } // Force focus to password container when popout becomes active - Qt.callLater(() => { - passwordContainer.forceActiveFocus(); - }, 100); - }, 100); + // Use Timer for actual delay to ensure dialog is fully rendered + focusTimer.start(); + }); } } } + Timer { + id: focusTimer + interval: 150 + onTriggered: { + root.forceActiveFocus(); + passwordContainer.forceActiveFocus(); + } + } + spacing: Appearance.spacing.normal implicitWidth: 400 @@ -51,19 +59,15 @@ ColumnLayout { Component.onCompleted: { if (shouldBeVisible) { - Qt.callLater(() => { - root.forceActiveFocus(); - passwordContainer.forceActiveFocus(); - }, 150); + // Use Timer for actual delay to ensure dialog is fully rendered + focusTimer.start(); } } onShouldBeVisibleChanged: { if (shouldBeVisible) { - Qt.callLater(() => { - root.forceActiveFocus(); - passwordContainer.forceActiveFocus(); - }, 150); + // Use Timer for actual delay to ensure dialog is fully rendered + focusTimer.start(); } } @@ -243,20 +247,26 @@ ColumnLayout { target: root function onShouldBeVisibleChanged(): void { if (root.shouldBeVisible) { - Qt.callLater(() => { - passwordContainer.forceActiveFocus(); - }, 50); + // Use Timer for actual delay to ensure focus works correctly + passwordFocusTimer.start(); passwordContainer.passwordBuffer = ""; connectButton.hasError = false; } } } + Timer { + id: passwordFocusTimer + interval: 50 + onTriggered: { + passwordContainer.forceActiveFocus(); + } + } + Component.onCompleted: { if (root.shouldBeVisible) { - Qt.callLater(() => { - passwordContainer.forceActiveFocus(); - }, 100); + // Use Timer for actual delay to ensure focus works correctly + passwordFocusTimer.start(); } } @@ -489,22 +499,8 @@ ColumnLayout { if (isConnected) { // Successfully connected - give it a moment for network list to update - Qt.callLater(() => { - // Double-check connection is still active - if (root.shouldBeVisible && Nmcli.active && Nmcli.active.ssid) { - const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); - if (stillConnected) { - connectionMonitor.stop(); - connectButton.connecting = false; - connectButton.text = qsTr("Connect"); - // Return to network popout on successful connection - if (root.wrapper.currentName === "wirelesspassword") { - root.wrapper.currentName = "network"; - } - closeDialog(); - } - } - }, 500); + // Use Timer for actual delay + connectionSuccessTimer.start(); return; } @@ -545,6 +541,27 @@ ColumnLayout { } } + Timer { + id: connectionSuccessTimer + interval: 500 + onTriggered: { + // Double-check connection is still active + if (root.shouldBeVisible && Nmcli.active && Nmcli.active.ssid) { + const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + if (stillConnected) { + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + // Return to network popout on successful connection + if (root.wrapper.currentName === "wirelesspassword") { + root.wrapper.currentName = "network"; + } + closeDialog(); + } + } + } + } + Connections { target: Nmcli function onActiveChanged() { diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 56ab7f1..74e0034 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -15,500 +15,507 @@ import Quickshell.Widgets import QtQuick import QtQuick.Layouts -RowLayout { +Item { id: root required property Session session anchors.fill: parent - spacing: 0 + RowLayout { + id: contentLayout - Item { - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true + anchors.fill: parent + spacing: 0 - // Left pane - networking list with collapsible sections - StyledFlickable { - id: leftFlickable + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - flickableDirection: Flickable.VerticalFlick - contentHeight: leftContent.height - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: leftFlickable - } + // Left pane - networking list with collapsible sections + StyledFlickable { + id: leftFlickable - ColumnLayout { - id: leftContent + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height + clip: true - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.normal + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } - // Settings header above the collapsible sections - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.smaller + ColumnLayout { + id: leftContent - StyledText { - text: qsTr("Settings") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal - Item { + // Settings header above the collapsible sections + RowLayout { Layout.fillWidth: true - } + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } - ToggleButton { - toggled: Nmcli.wifiEnabled - icon: "wifi" - accent: "Tertiary" + Item { + Layout.fillWidth: true + } - onClicked: { - Nmcli.toggleWifi(null); + ToggleButton { + toggled: Nmcli.wifiEnabled + icon: "wifi" + accent: "Tertiary" + + onClicked: { + Nmcli.toggleWifi(null); + } } - } - ToggleButton { - toggled: Nmcli.scanning - icon: "wifi_find" - accent: "Secondary" + ToggleButton { + toggled: Nmcli.scanning + icon: "wifi_find" + accent: "Secondary" - onClicked: { - Nmcli.rescanWifi(); + onClicked: { + Nmcli.rescanWifi(); + } } - } - ToggleButton { - toggled: !root.session.ethernet.active && !root.session.network.active - icon: "settings" - accent: "Primary" - - onClicked: { - if (root.session.ethernet.active || root.session.network.active) { - root.session.ethernet.active = null; - root.session.network.active = null; - } else { - // Toggle to show settings - prefer ethernet if available, otherwise wireless - if (Nmcli.ethernetDevices.length > 0) { - root.session.ethernet.active = Nmcli.ethernetDevices[0]; - } else if (Nmcli.networks.length > 0) { - root.session.network.active = Nmcli.networks[0]; + ToggleButton { + toggled: !root.session.ethernet.active && !root.session.network.active + icon: "settings" + accent: "Primary" + + onClicked: { + if (root.session.ethernet.active || root.session.network.active) { + root.session.ethernet.active = null; + root.session.network.active = null; + } else { + // Toggle to show settings - prefer ethernet if available, otherwise wireless + if (Nmcli.ethernetDevices.length > 0) { + root.session.ethernet.active = Nmcli.ethernetDevices[0]; + } else if (Nmcli.networks.length > 0) { + root.session.network.active = Nmcli.networks[0]; + } } } } } - } - - CollapsibleSection { - id: ethernetListSection - Layout.fillWidth: true - title: qsTr("Ethernet") - expanded: true + CollapsibleSection { + id: ethernetListSection - ColumnLayout { Layout.fillWidth: true - spacing: Appearance.spacing.small + title: qsTr("Ethernet") + expanded: true - RowLayout { + ColumnLayout { Layout.fillWidth: true spacing: Appearance.spacing.small - StyledText { - text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) - font.pointSize: Appearance.font.size.normal - font.weight: 500 + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } } - } - StyledText { - Layout.fillWidth: true - text: qsTr("All available ethernet devices") - color: Colours.palette.m3outline - } + StyledText { + Layout.fillWidth: true + text: qsTr("All available ethernet devices") + color: Colours.palette.m3outline + } - Repeater { - id: ethernetRepeater + Repeater { + id: ethernetRepeater - Layout.fillWidth: true - model: Nmcli.ethernetDevices + Layout.fillWidth: true + model: Nmcli.ethernetDevices - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - Layout.fillWidth: true + Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal - StateLayer { - function onClicked(): void { - root.session.network.active = null; - root.session.ethernet.active = modelData; + StateLayer { + function onClicked(): void { + root.session.network.active = null; + root.session.ethernet.active = modelData; + } } - } - RowLayout { - id: rowLayout + RowLayout { + id: rowLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - StyledRect { - implicitWidth: implicitHeight - implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + StyledRect { + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - radius: Appearance.rounding.normal - color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + radius: Appearance.rounding.normal + color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - MaterialIcon { - id: icon + MaterialIcon { + id: icon - anchors.centerIn: parent - text: "cable" - font.pointSize: Appearance.font.size.large - fill: modelData.connected ? 1 : 0 - color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + anchors.centerIn: parent + text: "cable" + font.pointSize: Appearance.font.size.large + fill: modelData.connected ? 1 : 0 + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } } - } - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 + StyledText { + Layout.fillWidth: true + elide: Text.ElideRight + maximumLineCount: 1 - text: modelData.interface || qsTr("Unknown") - } + text: modelData.interface || qsTr("Unknown") + } - StyledText { - text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected") - color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - font.weight: modelData.connected ? 500 : 400 - } + StyledText { + text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected") + color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + font.weight: modelData.connected ? 500 : 400 + } - StyledRect { - implicitWidth: implicitHeight - implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 + StyledRect { + implicitWidth: implicitHeight + implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0) + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0) - StateLayer { - function onClicked(): void { - if (modelData.connected && modelData.connection) { - Nmcli.disconnectEthernet(modelData.connection, () => {}); - } else { - Nmcli.connectEthernet(modelData.connection || "", modelData.interface || "", () => {}); + StateLayer { + function onClicked(): void { + if (modelData.connected && modelData.connection) { + Nmcli.disconnectEthernet(modelData.connection, () => {}); + } else { + Nmcli.connectEthernet(modelData.connection || "", modelData.interface || "", () => {}); + } } } - } - MaterialIcon { - id: connectIcon + MaterialIcon { + id: connectIcon - anchors.centerIn: parent - text: modelData.connected ? "link_off" : "link" - color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + anchors.centerIn: parent + text: modelData.connected ? "link_off" : "link" + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } } } - } - implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + } } } } - } - - CollapsibleSection { - id: wirelessListSection - Layout.fillWidth: true - title: qsTr("Wireless") - expanded: true + CollapsibleSection { + id: wirelessListSection - ColumnLayout { Layout.fillWidth: true - spacing: Appearance.spacing.small + title: qsTr("Wireless") + expanded: true - RowLayout { + ColumnLayout { Layout.fillWidth: true spacing: Appearance.spacing.small - StyledText { - text: qsTr("Networks (%1)").arg(Nmcli.networks.length) - font.pointSize: Appearance.font.size.normal - font.weight: 500 + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Networks (%1)").arg(Nmcli.networks.length) + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } + + StyledText { + visible: Nmcli.scanning + text: qsTr("Scanning...") + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.small + } } StyledText { - visible: Nmcli.scanning - text: qsTr("Scanning...") - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.small + Layout.fillWidth: true + text: qsTr("All available WiFi networks") + color: Colours.palette.m3outline } - } - - StyledText { - Layout.fillWidth: true - text: qsTr("All available WiFi networks") - color: Colours.palette.m3outline - } - Repeater { - id: wirelessRepeater + Repeater { + id: wirelessRepeater - Layout.fillWidth: true - model: ScriptModel { - values: [...Nmcli.networks].sort((a, b) => { - // Put active/connected network first - if (a.active !== b.active) - return b.active - a.active; - // Then sort by signal strength - return b.strength - a.strength; - }) - } + Layout.fillWidth: true + model: ScriptModel { + values: [...Nmcli.networks].sort((a, b) => { + // Put active/connected network first + if (a.active !== b.active) + return b.active - a.active; + // Then sort by signal strength + return b.strength - a.strength; + }) + } - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - Layout.fillWidth: true + Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.network.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (modelData && root.session.network.active === modelData) ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal StateLayer { function onClicked(): void { + if (!modelData) { + return; + } root.session.ethernet.active = null; root.session.network.active = modelData; // Check if we need to refresh saved connections when selecting a network - if (modelData && modelData.ssid) { + if (modelData.ssid) { checkSavedProfileForNetwork(modelData.ssid); } } } - RowLayout { - id: wirelessRowLayout + RowLayout { + id: wirelessRowLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - StyledRect { - implicitWidth: implicitHeight - implicitHeight: wirelessIcon.implicitHeight + Appearance.padding.normal * 2 + StyledRect { + implicitWidth: implicitHeight + implicitHeight: wirelessIcon.implicitHeight + Appearance.padding.normal * 2 - radius: Appearance.rounding.normal - color: modelData.active ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + radius: Appearance.rounding.normal + color: (modelData && modelData.active) ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - MaterialIcon { - id: wirelessIcon + MaterialIcon { + id: wirelessIcon - anchors.centerIn: parent - text: Icons.getNetworkIcon(modelData.strength) - font.pointSize: Appearance.font.size.large - fill: modelData.active ? 1 : 0 - color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + anchors.centerIn: parent + text: Icons.getNetworkIcon(modelData && modelData.strength !== undefined ? modelData.strength : 0) + font.pointSize: Appearance.font.size.large + fill: (modelData && modelData.active) ? 1 : 0 + color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } } - } - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 + StyledText { + Layout.fillWidth: true + elide: Text.ElideRight + maximumLineCount: 1 - text: modelData.ssid || qsTr("Unknown") - } + text: (modelData && modelData.ssid) ? modelData.ssid : qsTr("Unknown") + } - StyledText { - text: modelData.active ? qsTr("Connected") : (modelData.isSecure ? qsTr("Secured") : qsTr("Open")) - color: modelData.active ? Colours.palette.m3primary : Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - font.weight: modelData.active ? 500 : 400 - } + StyledText { + text: (modelData && modelData.active) ? qsTr("Connected") : ((modelData && modelData.isSecure) ? qsTr("Secured") : qsTr("Open")) + color: (modelData && modelData.active) ? Colours.palette.m3primary : Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + font.weight: (modelData && modelData.active) ? 500 : 400 + } - StyledRect { - implicitWidth: implicitHeight - implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2 + StyledRect { + implicitWidth: implicitHeight + implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2 - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.active ? 1 : 0) + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, (modelData && modelData.active) ? 1 : 0) - StateLayer { - function onClicked(): void { - if (modelData.active) { - Nmcli.disconnectFromNetwork(); - } else { - handleWirelessConnect(modelData); + StateLayer { + function onClicked(): void { + if (modelData && modelData.active) { + Nmcli.disconnectFromNetwork(); + } else if (modelData) { + handleWirelessConnect(modelData); + } } } - } - MaterialIcon { - id: wirelessConnectIcon + MaterialIcon { + id: wirelessConnectIcon - anchors.centerIn: parent - text: modelData.active ? "link_off" : "link" - color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + anchors.centerIn: parent + text: (modelData && modelData.active) ? "link_off" : "link" + color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } } } - } - implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2 + } } } } } } - } - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } } - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: rightBorder.innerRadius - color: "transparent" - clip: true - - // Right pane - networking details/settings - Loader { - id: loader - property var ethernetPane: root.session.ethernet.active - property var wirelessPane: root.session.network.active - property var pane: ethernetPane || wirelessPane - property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") + Item { + Layout.fillWidth: true + Layout.fillHeight: true + ClippingRectangle { anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 - - opacity: 1 - scale: 1 - transformOrigin: Item.Center + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + radius: rightBorder.innerRadius + color: "transparent" clip: true - asynchronous: true - sourceComponent: pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings - - Behavior on paneId { - SequentialAnimation { - ParallelAnimation { - Anim { - target: loader - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - Anim { - target: loader - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - } - PropertyAction {} - ParallelAnimation { - Anim { - target: loader - property: "opacity" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel + + // Right pane - networking details/settings + Loader { + id: loader + + property var ethernetPane: root.session.ethernet.active + property var wirelessPane: root.session.network.active + property var pane: ethernetPane || wirelessPane + property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + opacity: 1 + scale: 1 + transformOrigin: Item.Center + + clip: true + asynchronous: true + sourceComponent: pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings + + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + target: loader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } } - Anim { - target: loader - property: "scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel + PropertyAction {} + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: loader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } } } - } - onPaneChanged: { - paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + onPaneChanged: { + paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + } } } - } - InnerBorder { - id: rightBorder + InnerBorder { + id: rightBorder - leftThickness: Appearance.padding.normal / 2 - } + leftThickness: Appearance.padding.normal / 2 + } - Component { - id: settings + Component { + id: settings - StyledFlickable { - id: settingsFlickable + StyledFlickable { + id: settingsFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height - clip: true + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: settingsFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } - NetworkSettings { - id: settingsInner + NetworkSettings { + id: settingsInner - anchors.left: parent.left - anchors.right: parent.right - session: root.session + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } } } - } - Component { - id: ethernetDetails + Component { + id: ethernetDetails - EthernetDetails { - session: root.session + EthernetDetails { + session: root.session + } } - } - Component { - id: wirelessDetails + Component { + id: wirelessDetails - WirelessDetails { - session: root.session + WirelessDetails { + session: root.session + } } } } WirelessPasswordDialog { - Layout.fillWidth: true - Layout.fillHeight: true + anchors.fill: parent session: root.session z: 1000 } diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index f3381b7..4b350be 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -131,14 +131,17 @@ Item { Layout.alignment: Qt.AlignHCenter Layout.topMargin: Appearance.spacing.small - visible: connectButton.connecting + visible: connectButton.connecting || connectButton.hasError text: { + if (connectButton.hasError) { + return qsTr("Connection failed. Please check your password and try again."); + } if (connectButton.connecting) { return qsTr("Connecting..."); } return ""; } - color: Colours.palette.m3onSurfaceVariant + color: connectButton.hasError ? Colours.palette.m3error : Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small font.weight: 400 wrapMode: Text.WordWrap @@ -153,18 +156,31 @@ Item { focus: true Keys.onPressed: event => { + // Ensure we have focus when receiving keyboard input + if (!activeFocus) { + forceActiveFocus(); + } + + // Clear error when user starts typing + if (connectButton.hasError && event.text && event.text.length > 0) { + connectButton.hasError = false; + } + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { if (connectButton.enabled) { connectButton.clicked(); } + event.accepted = true; } else if (event.key === Qt.Key_Backspace) { if (event.modifiers & Qt.ControlModifier) { passwordBuffer = ""; } else { passwordBuffer = passwordBuffer.slice(0, -1); } + event.accepted = true; } else if (event.text && event.text.length > 0) { passwordBuffer += event.text; + event.accepted = true; } } @@ -178,6 +194,7 @@ Item { Qt.callLater(() => { passwordContainer.forceActiveFocus(); passwordContainer.passwordBuffer = ""; + connectButton.hasError = false; }); } } @@ -198,13 +215,29 @@ Item { StyledRect { anchors.fill: parent radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - border.width: passwordContainer.activeFocus ? 2 : 1 - border.color: passwordContainer.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline + color: passwordContainer.activeFocus ? Qt.lighter(Colours.tPalette.m3surfaceContainer, 1.05) : Colours.tPalette.m3surfaceContainer + border.width: passwordContainer.activeFocus || connectButton.hasError ? 4 : (root.visible ? 1 : 0) + border.color: { + if (connectButton.hasError) { + return Colours.palette.m3error; + } + if (passwordContainer.activeFocus) { + return Colours.palette.m3primary; + } + return root.visible ? Colours.palette.m3outline : "transparent"; + } Behavior on border.color { CAnim {} } + + Behavior on border.width { + CAnim {} + } + + Behavior on color { + CAnim {} + } } StateLayer { @@ -329,6 +362,9 @@ Item { TextButton { id: connectButton + property bool connecting: false + property bool hasError: false + Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 inactiveColour: Colours.palette.m3primary @@ -336,8 +372,6 @@ Item { text: qsTr("Connect") enabled: passwordContainer.passwordBuffer.length > 0 && !connecting - property bool connecting: false - onClicked: { if (!root.network || connecting) { return; @@ -348,6 +382,9 @@ Item { return; } + // Clear any previous error + hasError = false; + // Set connecting state connecting = true; enabled = false; @@ -361,11 +398,27 @@ Item { // Shouldn't happen since we provided password connectionMonitor.stop(); connecting = false; + hasError = true; enabled = true; text = qsTr("Connect"); - } else - // Connection failed, monitor will handle timeout - {} + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + if (root.network && root.network.ssid) { + Nmcli.forgetNetwork(root.network.ssid); + } + } else { + // Connection failed immediately - show error + connectionMonitor.stop(); + connecting = false; + hasError = true; + enabled = true; + text = qsTr("Connect"); + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + if (root.network && root.network.ssid) { + Nmcli.forgetNetwork(root.network.ssid); + } + } }); // Start monitoring connection @@ -386,18 +439,8 @@ Item { if (isConnected) { // Successfully connected - give it a moment for network list to update - Qt.callLater(() => { - // Double-check connection is still active - if (root.visible && Nmcli.active && Nmcli.active.ssid) { - const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); - if (stillConnected) { - connectionMonitor.stop(); - connectButton.connecting = false; - connectButton.text = qsTr("Connect"); - closeDialog(); - } - } - }, 500); + // Use Timer for actual delay + connectionSuccessTimer.start(); return; } @@ -407,8 +450,14 @@ Item { if (connectionMonitor.repeatCount > 10) { connectionMonitor.stop(); connectButton.connecting = false; + connectButton.hasError = true; connectButton.enabled = true; connectButton.text = qsTr("Connect"); + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + if (root.network && root.network.ssid) { + Nmcli.forgetNetwork(root.network.ssid); + } } } } @@ -432,6 +481,23 @@ Item { } } + Timer { + id: connectionSuccessTimer + interval: 500 + onTriggered: { + // Double-check connection is still active + if (root.visible && Nmcli.active && Nmcli.active.ssid) { + const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); + if (stillConnected) { + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.text = qsTr("Connect"); + closeDialog(); + } + } + } + } + Connections { target: Nmcli function onActiveChanged() { @@ -443,8 +509,12 @@ Item { if (root.visible && root.network && root.network.ssid === ssid && connectButton.connecting) { connectionMonitor.stop(); connectButton.connecting = false; + connectButton.hasError = true; connectButton.enabled = true; connectButton.text = qsTr("Connect"); + passwordContainer.passwordBuffer = ""; + // Delete the failed connection + Nmcli.forgetNetwork(ssid); } } } @@ -457,6 +527,7 @@ Item { isClosing = true; passwordContainer.passwordBuffer = ""; connectButton.connecting = false; + connectButton.hasError = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); } diff --git a/services/Nmcli.qml b/services/Nmcli.qml index 24a93da..36bd3e6 100644 --- a/services/Nmcli.qml +++ b/services/Nmcli.qml @@ -1272,7 +1272,15 @@ Singleton { stdout: SplitParser { onRead: root.refreshOnConnectionChange() } - onExited: Qt.callLater(() => monitorProc.running = true, 2000) + onExited: monitorRestartTimer.start() + } + + Timer { + id: monitorRestartTimer + interval: 2000 + onTriggered: { + monitorProc.running = true; + } } function refreshOnConnectionChange(): void { -- cgit v1.2.3-freya From 70ec8cea651c0f49e9ccf25b6e8685d81ac6710b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 19:11:41 -0500 Subject: refactor: NetworkConnection util created, migrated all functions --- modules/bar/popouts/Network.qml | 34 +++--- modules/bar/popouts/WirelessPassword.qml | 3 +- modules/controlcenter/network/NetworkingPane.qml | 43 +------- modules/controlcenter/network/WirelessDetails.qml | 38 +------ modules/controlcenter/network/WirelessList.qml | 44 +------- .../network/WirelessPasswordDialog.qml | 3 +- utils/NetworkConnection.qml | 122 +++++++++++++++++++++ 7 files changed, 144 insertions(+), 143 deletions(-) create mode 100644 utils/NetworkConnection.qml (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml index b9f66c4..0e99613 100644 --- a/modules/bar/popouts/Network.qml +++ b/modules/bar/popouts/Network.qml @@ -131,27 +131,19 @@ ColumnLayout { Nmcli.disconnectFromNetwork(); } else { root.connectingToSsid = networkItem.modelData.ssid; - // Check if network is secure - if (networkItem.modelData.isSecure) { - // Try to connect first - will show password dialog if password is needed - Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, result => { - if (result && result.needsPassword) { - // Password is required - show password dialog - root.passwordNetwork = networkItem.modelData; - root.showPasswordDialog = true; - root.wrapper.currentName = "wirelesspassword"; - } else if (result && result.success) { - // Connection successful with saved password - root.connectingToSsid = ""; - } else { - // Connection failed for other reasons - root.connectingToSsid = ""; - } - }); - } else { - // Open network, no password needed - Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, null); - } + NetworkConnection.handleConnect( + networkItem.modelData, + null, + (network) => { + // Password is required - show password dialog + root.passwordNetwork = network; + root.showPasswordDialog = true; + root.wrapper.currentName = "wirelesspassword"; + } + ); + + // Clear connecting state if connection succeeds immediately (saved profile) + // This is handled by the onActiveChanged connection below } } } diff --git a/modules/bar/popouts/WirelessPassword.qml b/modules/bar/popouts/WirelessPassword.qml index d91c87c..5da50b6 100644 --- a/modules/bar/popouts/WirelessPassword.qml +++ b/modules/bar/popouts/WirelessPassword.qml @@ -4,6 +4,7 @@ import qs.components import qs.components.controls import qs.services import qs.config +import qs.utils import Quickshell import QtQuick import QtQuick.Layouts @@ -452,7 +453,7 @@ ColumnLayout { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + NetworkConnection.connectWithPassword(root.network, password, result => { if (result && result.success) // Connection successful, monitor will handle the rest {} else if (result && result.needsPassword) { diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 9a7a4e1..d76e8f5 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -415,7 +415,7 @@ Item { if (modelData && modelData.active) { Nmcli.disconnectFromNetwork(); } else if (modelData) { - handleWirelessConnect(modelData); + NetworkConnection.handleConnect(modelData, root.session, null); } } } @@ -586,46 +586,5 @@ Item { Nmcli.loadSavedConnections(() => {}); } } - - function handleWirelessConnect(network): void { - if (Nmcli.active && Nmcli.active.ssid !== network.ssid) { - Nmcli.disconnectFromNetwork(); - Qt.callLater(() => { - connectToWirelessNetwork(network); - }); - } else { - connectToWirelessNetwork(network); - } - } - - function connectToWirelessNetwork(network): void { - if (network.isSecure) { - const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid); - - if (hasSavedProfile) { - Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); - } else { - Nmcli.connectToNetworkWithPasswordCheck( - network.ssid, - network.isSecure, - (result) => { - if (result.needsPassword) { - if (Nmcli.pendingConnection) { - Nmcli.connectionCheckTimer.stop(); - Nmcli.immediateCheckTimer.stop(); - Nmcli.immediateCheckTimer.checkCount = 0; - Nmcli.pendingConnection = null; - } - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = network; - } - }, - network.bssid - ); - } - } else { - Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); - } - } } diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 09abff3..57c06c8 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -8,6 +8,7 @@ import qs.components.effects import qs.components.containers import qs.services import qs.config +import qs.utils import QtQuick import QtQuick.Layouts @@ -125,7 +126,7 @@ Item { checked: root.network?.active ?? false toggle.onToggled: { if (checked) { - root.handleConnect(); + NetworkConnection.handleConnect(root.network, root.session, null); } else { Nmcli.disconnectFromNetwork(); } @@ -207,39 +208,4 @@ Item { } } - function handleConnect(): void { - if (Nmcli.active && Nmcli.active.ssid !== root.network.ssid) { - Nmcli.disconnectFromNetwork(); - Qt.callLater(() => { - connectToNetwork(); - }); - } else { - connectToNetwork(); - } - } - - function connectToNetwork(): void { - if (root.network.isSecure) { - const hasSavedProfile = Nmcli.hasSavedProfile(root.network.ssid); - - if (hasSavedProfile) { - Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); - } else { - Nmcli.connectToNetworkWithPasswordCheck(root.network.ssid, root.network.isSecure, result => { - if (result.needsPassword) { - if (Nmcli.pendingConnection) { - Nmcli.connectionCheckTimer.stop(); - Nmcli.immediateCheckTimer.stop(); - Nmcli.immediateCheckTimer.checkCount = 0; - Nmcli.pendingConnection = null; - } - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = root.network; - } - }, root.network.bssid); - } - } else { - Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); - } - } } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 00af47a..18f728c 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -7,6 +7,7 @@ import qs.components.controls import qs.components.containers import qs.services import qs.config +import qs.utils import QtQuick import QtQuick.Layouts @@ -193,7 +194,7 @@ ColumnLayout { if (modelData.active) { Nmcli.disconnectFromNetwork(); } else { - handleConnect(modelData); + NetworkConnection.handleConnect(modelData, root.session, null); } } } @@ -217,45 +218,4 @@ ColumnLayout { Nmcli.loadSavedConnections(() => {}); } } - - function handleConnect(network): void { - if (Nmcli.active && Nmcli.active.ssid !== network.ssid) { - Nmcli.disconnectFromNetwork(); - Qt.callLater(() => { - connectToNetwork(network); - }); - } else { - connectToNetwork(network); - } - } - - function connectToNetwork(network): void { - if (network.isSecure) { - const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid); - - if (hasSavedProfile) { - Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); - } else { - Nmcli.connectToNetworkWithPasswordCheck( - network.ssid, - network.isSecure, - (result) => { - if (result.needsPassword) { - if (Nmcli.pendingConnection) { - Nmcli.connectionCheckTimer.stop(); - Nmcli.immediateCheckTimer.stop(); - Nmcli.immediateCheckTimer.checkCount = 0; - Nmcli.pendingConnection = null; - } - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = network; - } - }, - network.bssid - ); - } - } else { - Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); - } - } } \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 4b350be..0f1a5cd 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -8,6 +8,7 @@ import qs.components.effects import qs.components.containers import qs.services import qs.config +import qs.utils import Quickshell import QtQuick import QtQuick.Layouts @@ -391,7 +392,7 @@ Item { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + NetworkConnection.connectWithPassword(root.network, password, result => { if (result && result.success) // Connection successful, monitor will handle the rest {} else if (result && result.needsPassword) { diff --git a/utils/NetworkConnection.qml b/utils/NetworkConnection.qml new file mode 100644 index 0000000..c7595b1 --- /dev/null +++ b/utils/NetworkConnection.qml @@ -0,0 +1,122 @@ +pragma Singleton + +import qs.services +import QtQuick + +/** + * NetworkConnection + * + * Centralized utility for network connection logic. Provides a single source of truth + * for connecting to wireless networks, eliminating code duplication across + * controlcenter components and bar popouts. + * + * Usage: + * ```qml + * import qs.utils + * + * // With Session object (controlcenter) + * NetworkConnection.handleConnect(network, session); + * + * // Without Session object (bar popouts) - provide password dialog callback + * NetworkConnection.handleConnect(network, null, (network) => { + * // Show password dialog + * root.passwordNetwork = network; + * root.showPasswordDialog = true; + * }); + * ``` + */ +QtObject { + id: root + + /** + * Handle network connection with automatic disconnection if needed. + * If there's an active network different from the target, disconnects first, + * then connects to the target network. + * + * @param network The network object to connect to (must have ssid property) + * @param session Optional Session object (for controlcenter - must have network property with showPasswordDialog and pendingNetwork) + * @param onPasswordNeeded Optional callback function(network) called when password is needed (for bar popouts) + */ + function handleConnect(network, session, onPasswordNeeded): void { + if (!network) { + return; + } + + if (Nmcli.active && Nmcli.active.ssid !== network.ssid) { + Nmcli.disconnectFromNetwork(); + Qt.callLater(() => { + root.connectToNetwork(network, session, onPasswordNeeded); + }); + } else { + root.connectToNetwork(network, session, onPasswordNeeded); + } + } + + /** + * Connect to a wireless network. + * Handles both secured and open networks, checks for saved profiles, + * and shows password dialog if needed. + * + * @param network The network object to connect to (must have ssid, isSecure, bssid properties) + * @param session Optional Session object (for controlcenter - must have network property with showPasswordDialog and pendingNetwork) + * @param onPasswordNeeded Optional callback function(network) called when password is needed (for bar popouts) + */ + function connectToNetwork(network, session, onPasswordNeeded): void { + if (!network) { + return; + } + + if (network.isSecure) { + const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid); + + if (hasSavedProfile) { + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); + } else { + // Use password check with callback + Nmcli.connectToNetworkWithPasswordCheck( + network.ssid, + network.isSecure, + (result) => { + if (result.needsPassword) { + // Clear pending connection if exists + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; + } + + // Handle password dialog - use session if available, otherwise use callback + if (session && session.network) { + session.network.showPasswordDialog = true; + session.network.pendingNetwork = network; + } else if (onPasswordNeeded) { + onPasswordNeeded(network); + } + } + }, + network.bssid + ); + } + } else { + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); + } + } + + /** + * Connect to a wireless network with a provided password. + * Used by password dialogs when the user has already entered a password. + * + * @param network The network object to connect to (must have ssid, bssid properties) + * @param password The password to use for connection + * @param onResult Optional callback function(result) called with connection result + */ + function connectWithPassword(network, password, onResult): void { + if (!network) { + return; + } + + Nmcli.connectToNetwork(network.ssid, password || "", network.bssid || "", onResult || null); + } +} + -- cgit v1.2.3-freya From 147410e39bf4e0474deca3980dcaa724464cf5c3 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:15:40 -0500 Subject: cleanup: removed unnecessary comments --- modules/controlcenter/ControlCenter.qml | 1 - modules/controlcenter/Panes.qml | 18 ------------ modules/controlcenter/Session.qml | 1 - .../controlcenter/appearance/AppearancePane.qml | 28 ++----------------- modules/controlcenter/audio/AudioPane.qml | 1 - modules/controlcenter/bluetooth/Details.qml | 1 - modules/controlcenter/bluetooth/Settings.qml | 4 +-- modules/controlcenter/components/DeviceDetails.qml | 7 ----- modules/controlcenter/components/DeviceList.qml | 10 ++----- .../controlcenter/components/PaneTransition.qml | 12 -------- .../controlcenter/components/SplitPaneLayout.qml | 8 ------ .../components/SplitPaneWithDetails.qml | 2 -- modules/controlcenter/launcher/LauncherPane.qml | 27 ++---------------- modules/controlcenter/network/NetworkingPane.qml | 2 -- modules/controlcenter/network/WirelessDetails.qml | 9 ------ modules/controlcenter/network/WirelessList.qml | 3 -- .../network/WirelessPasswordDialog.qml | 27 ++---------------- modules/controlcenter/state/BluetoothState.qml | 5 ---- modules/controlcenter/state/EthernetState.qml | 1 - modules/controlcenter/state/LauncherState.qml | 1 - modules/controlcenter/state/NetworkState.qml | 3 -- modules/controlcenter/taskbar/TaskbarPane.qml | 32 ---------------------- 22 files changed, 9 insertions(+), 194 deletions(-) (limited to 'modules/controlcenter/network/WirelessPasswordDialog.qml') diff --git a/modules/controlcenter/ControlCenter.qml b/modules/controlcenter/ControlCenter.qml index 3642a33..fdb824e 100644 --- a/modules/controlcenter/ControlCenter.qml +++ b/modules/controlcenter/ControlCenter.qml @@ -97,6 +97,5 @@ Item { } } - // Expose initialOpeningComplete for NavRail to prevent tab switching during opening animation readonly property bool initialOpeningComplete: panes.initialOpeningComplete } diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index b9256a9..833a411 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -19,7 +19,6 @@ ClippingRectangle { required property Session session - // Expose initialOpeningComplete so parent can check if opening animation is done readonly property bool initialOpeningComplete: layout.initialOpeningComplete color: "transparent" @@ -27,7 +26,6 @@ ClippingRectangle { focus: false activeFocusOnTab: false - // Clear focus when clicking anywhere in the panes area MouseArea { anchors.fill: parent z: -1 @@ -37,7 +35,6 @@ ClippingRectangle { } } - // Clear focus when switching panes Connections { target: root.session @@ -54,8 +51,6 @@ ClippingRectangle { clip: true property bool animationComplete: true - // Track if initial opening animation has completed - // During initial opening, only the active pane loads to avoid hiccups property bool initialOpeningComplete: false Timer { @@ -66,8 +61,6 @@ ClippingRectangle { } } - // Timer to detect when initial opening animation completes - // Uses large duration to cover both normal and detached opening cases Timer { id: initialOpeningTimer interval: Appearance.anim.durations.large @@ -94,7 +87,6 @@ ClippingRectangle { Connections { target: root.session function onActiveIndexChanged(): void { - // Mark animation as incomplete and start delay timer layout.animationComplete = false; animationDelayTimer.restart(); } @@ -110,28 +102,21 @@ ClippingRectangle { implicitWidth: root.width implicitHeight: root.height - // Track if this pane has ever been loaded to enable caching property bool hasBeenLoaded: false - // Function to compute if this pane should be active function updateActive(): void { const diff = Math.abs(root.session.activeIndex - pane.paneIndex); const isActivePane = diff === 0; let shouldBeActive = false; - // During initial opening animation, only load the active pane - // This prevents hiccups from multiple panes loading simultaneously if (!layout.initialOpeningComplete) { shouldBeActive = isActivePane; } else { - // After initial opening, allow current and adjacent panes for smooth transitions if (diff <= 1) { shouldBeActive = true; } else if (pane.hasBeenLoaded) { - // For distant panes that have been loaded before, keep them active to preserve cached data shouldBeActive = true; } else { - // For new distant panes, wait until animation completes to avoid heavy loading during transition shouldBeActive = layout.animationComplete; } } @@ -152,12 +137,10 @@ ClippingRectangle { } onActiveChanged: { - // Mark pane as loaded when it becomes active if (active && !pane.hasBeenLoaded) { pane.hasBeenLoaded = true; } - // Load the component with initial properties when activated if (active && !item) { loader.setSource(pane.componentPath, { "session": root.session @@ -166,7 +149,6 @@ ClippingRectangle { } onItemChanged: { - // Mark pane as loaded when item is created if (item) { pane.hasBeenLoaded = true; } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 9c6a754..0408a1a 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -11,7 +11,6 @@ QtObject { property int activeIndex: 0 property bool navExpanded: false - // Pane-specific state objects readonly property BluetoothState bt: BluetoothState {} readonly property NetworkState network: NetworkState {} readonly property EthernetState ethernet: EthernetState {} diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 3ba0549..5b7e859 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -22,7 +22,6 @@ Item { required property Session session - // Appearance settings property real animDurationsScale: Config.appearance.anim.durations.scale ?? 1 property string fontFamilyMaterial: Config.appearance.font.family.material ?? "Material Symbols Rounded" property string fontFamilyMono: Config.appearance.font.family.mono ?? "CaskaydiaCove NF" @@ -37,7 +36,6 @@ Item { property real borderRounding: Config.border.rounding ?? 1 property real borderThickness: Config.border.thickness ?? 1 - // Background settings property bool desktopClockEnabled: Config.background.desktopClock.enabled ?? false property bool backgroundEnabled: Config.background.enabled ?? true property bool visualiserEnabled: Config.background.visualiser.enabled ?? false @@ -127,13 +125,8 @@ Item { anchors.fill: parent asynchronous: true active: { - // Lazy load: only activate when: - // 1. Right pane is loaded AND - // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) - // This prevents loading all wallpapers when control center opens but appearance pane isn't visible const isActive = root.session.activeIndex === 3; const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; - // Access loader through SplitPaneLayout's rightLoader const splitLayout = root.children[0]; const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); @@ -150,7 +143,6 @@ Item { onActiveChanged: { if (!active && wallpaperLoader.item) { const container = wallpaperLoader.item; - // Access timer through wallpaperGrid if (container && container.wallpaperGrid) { const grid = container.wallpaperGrid; if (grid.imageUpdateTimer) { @@ -186,20 +178,17 @@ Item { } } - // Lazy loading model: loads one image at a time, only when touching bottom - // This prevents GridView from creating all delegates at once QtObject { id: lazyModel property var sourceList: null - property int loadedCount: 0 // Total items available to load - property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) + property int loadedCount: 0 + property int visibleCount: 0 property int totalCount: 0 function initialize(list) { sourceList = list; totalCount = list ? list.length : 0; - // Start with enough items to fill the initial viewport (~3 rows) const initialRows = 3; const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; const initialCount = Math.min(initialRows * cols, totalCount); @@ -216,7 +205,6 @@ Item { } function updateVisibleCount(neededCount) { - // Always round up to complete rows to avoid incomplete rows in the grid const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; const maxVisible = Math.min(neededCount, loadedCount); const rows = Math.ceil(maxVisible / cols); @@ -237,7 +225,6 @@ Item { readonly property int minCellWidth: 200 + Appearance.spacing.normal readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - // Height based on visible items only - prevents GridView from creating all delegates readonly property int layoutPreferredHeight: { if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { return 0; @@ -255,7 +242,6 @@ Item { topMargin: 0 bottomMargin: 0 - // Use ListModel for incremental updates to prevent flashing when new items are added ListModel { id: wallpaperListModel } @@ -270,7 +256,6 @@ Item { const newCount = lazyModel.visibleCount; const currentCount = wallpaperListModel.count; - // Only append new items - never remove or replace existing ones if (newCount > currentCount) { const flickable = wallpaperGridContainer.parentFlickable; const oldScrollY = flickable ? flickable.contentY : 0; @@ -279,7 +264,6 @@ Item { wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); } - // Preserve scroll position after model update if (flickable) { Qt.callLater(function() { if (Math.abs(flickable.contentY - oldScrollY) < 1) { @@ -382,7 +366,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -434,7 +417,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -450,8 +432,6 @@ Item { } } - - // Parent Flickable handles scrolling interactive: false @@ -786,11 +766,9 @@ Item { function onClicked(): void { const variant = modelData.variant; - // Optimistic update - set immediately for responsive UI Schemes.currentVariant = variant; Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); @@ -873,11 +851,9 @@ Item { const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - // Optimistic update - set immediately for responsive UI Schemes.currentScheme = schemeKey; Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 9b0c7d2..694e178 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -40,7 +40,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Audio header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index b260458..5299045 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -440,7 +440,6 @@ StyledFlickable { } } - // FAB Menu (positioned absolutely relative to flickable) ColumnLayout { anchors.right: fabRoot.right anchors.bottom: fabRoot.top diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml index b3245ab..c547240 100644 --- a/modules/controlcenter/bluetooth/Settings.qml +++ b/modules/controlcenter/bluetooth/Settings.qml @@ -328,7 +328,7 @@ ColumnLayout { anchors.left: parent.left - text: qsTr("Rename adapter (currently does not work)") // FIXME: remove disclaimer when fixed + text: qsTr("Rename adapter (currently does not work)") color: Colours.palette.m3outline font.pointSize: Appearance.font.size.small } @@ -345,8 +345,6 @@ ColumnLayout { readOnly: !root.session.bt.editingAdapterName onAccepted: { root.session.bt.editingAdapterName = false; - // Doesn't work for now, will be added to QS later - // root.session.bt.currentAdapter.name = text; } leftPadding: Appearance.padding.normal diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index 768e77a..d2e8835 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -18,10 +18,7 @@ Item { property Component headerComponent: null property list sections: [] - // Optional: Custom content to insert after header but before sections property Component topContent: null - - // Optional: Custom content to insert after all sections property Component bottomContent: null implicitWidth: layout.implicitWidth @@ -35,7 +32,6 @@ Item { anchors.top: parent.top spacing: Appearance.spacing.normal - // Header component (e.g., ConnectionHeader or SettingsHeader) Loader { id: headerLoader @@ -44,7 +40,6 @@ Item { visible: root.headerComponent !== null } - // Top content (optional) Loader { id: topContentLoader @@ -53,7 +48,6 @@ Item { visible: root.topContent !== null } - // Sections Repeater { model: root.sections @@ -65,7 +59,6 @@ Item { } } - // Bottom content (optional) Loader { id: bottomContentLoader diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml index a6821d8..75dd913 100644 --- a/modules/controlcenter/components/DeviceList.qml +++ b/modules/controlcenter/components/DeviceList.qml @@ -28,7 +28,6 @@ ColumnLayout { spacing: Appearance.spacing.small - // Header with action buttons (optional) Loader { id: headerLoader @@ -37,7 +36,6 @@ ColumnLayout { visible: root.headerComponent !== null && root.showHeader } - // Title and description row RowLayout { Layout.fillWidth: true Layout.topMargin: root.headerComponent ? 0 : 0 @@ -61,10 +59,8 @@ ColumnLayout { } } - // Expose view for access from parent components property alias view: view - // Description text StyledText { visible: root.description !== "" Layout.fillWidth: true @@ -72,20 +68,18 @@ ColumnLayout { color: Colours.palette.m3outline } - // List view StyledListView { id: view Layout.fillWidth: true - // Use contentHeight to show all items without estimation implicitHeight: contentHeight model: root.model delegate: root.delegate spacing: Appearance.spacing.small / 2 - interactive: false // Disable individual scrolling - parent pane handles it - clip: false // Don't clip - let parent handle scrolling + interactive: false + clip: false } } diff --git a/modules/controlcenter/components/PaneTransition.qml b/modules/controlcenter/components/PaneTransition.qml index 1da4afb..d1814b5 100644 --- a/modules/controlcenter/components/PaneTransition.qml +++ b/modules/controlcenter/components/PaneTransition.qml @@ -3,26 +3,17 @@ pragma ComponentBehavior: Bound import qs.config import QtQuick -// Reusable pane transition animation component -// Provides standard fade-out/scale-down → update → fade-in/scale-up animation -// Used when switching between detail/settings views in panes SequentialAnimation { id: root - // The Loader element to animate required property Item target - - // Optional list of PropertyActions to execute during the transition - // These typically update the component being displayed property list propertyActions - // Animation parameters (with sensible defaults) property real scaleFrom: 1.0 property real scaleTo: 0.8 property real opacityFrom: 1.0 property real opacityTo: 0.0 - // Fade out and scale down ParallelAnimation { NumberAnimation { target: root.target @@ -45,8 +36,6 @@ SequentialAnimation { } } - // Execute property actions (component switching, state updates, etc.) - // This is where the component change happens while invisible ScriptAction { script: { for (let i = 0; i < root.propertyActions.length; i++) { @@ -58,7 +47,6 @@ SequentialAnimation { } } - // Fade in and scale up ParallelAnimation { NumberAnimation { target: root.target diff --git a/modules/controlcenter/components/SplitPaneLayout.qml b/modules/controlcenter/components/SplitPaneLayout.qml index 7bd7db0..8b4f0d9 100644 --- a/modules/controlcenter/components/SplitPaneLayout.qml +++ b/modules/controlcenter/components/SplitPaneLayout.qml @@ -15,19 +15,14 @@ RowLayout { property Component leftContent: null property Component rightContent: null - // Left pane configuration property real leftWidthRatio: 0.4 property int leftMinimumWidth: 420 property var leftLoaderProperties: ({}) - - // Right pane configuration property var rightLoaderProperties: ({}) - // Expose loaders for customization (access via splitLayout.leftLoader or splitLayout.rightLoader) property alias leftLoader: leftLoader property alias rightLoader: rightLoader - // Left pane Item { id: leftPane @@ -57,7 +52,6 @@ RowLayout { asynchronous: true sourceComponent: root.leftContent - // Apply any additional properties from leftLoaderProperties Component.onCompleted: { for (const key in root.leftLoaderProperties) { leftLoader[key] = root.leftLoaderProperties[key]; @@ -74,7 +68,6 @@ RowLayout { } } - // Right pane Item { id: rightPane @@ -101,7 +94,6 @@ RowLayout { asynchronous: true sourceComponent: root.rightContent - // Apply any additional properties from rightLoaderProperties Component.onCompleted: { for (const key in root.rightLoaderProperties) { rightLoader[key] = root.rightLoaderProperties[key]; diff --git a/modules/controlcenter/components/SplitPaneWithDetails.qml b/modules/controlcenter/components/SplitPaneWithDetails.qml index 6af8c1a..e873923 100644 --- a/modules/controlcenter/components/SplitPaneWithDetails.qml +++ b/modules/controlcenter/components/SplitPaneWithDetails.qml @@ -19,7 +19,6 @@ Item { property var activeItem: null property var paneIdGenerator: function(item) { return item ? String(item) : ""; } - // Optional: Additional component to overlay on top (e.g., password dialogs) property Component overlayComponent: null SplitPaneLayout { @@ -82,7 +81,6 @@ Item { } } - // Overlay component (e.g., password dialogs) Loader { id: overlayLoader diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 803d7e0..47f87cc 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -62,26 +62,20 @@ Item { const appId = root.selectedApp.id || root.selectedApp.entry?.id; - // Create a new array to ensure change detection const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; if (isHidden) { - // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { hiddenApps.push(appId); } } else { - // Remove from hiddenApps const index = hiddenApps.indexOf(appId); if (index !== -1) { hiddenApps.splice(index, 1); } } - // Update Config Config.launcher.hiddenApps = hiddenApps; - - // Persist changes to disk Config.save(); } @@ -90,15 +84,13 @@ Item { id: allAppsDb path: `${Paths.state}/apps.sqlite` - entries: DesktopEntries.applications.values // No filter - show all apps + entries: DesktopEntries.applications.values } property string searchText: "" function filterApps(search: string): list { - // If search is empty, return all apps directly if (!search || search.trim() === "") { - // Convert QQmlListProperty to array const apps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { apps.push(allAppsDb.apps[i]); @@ -110,7 +102,6 @@ Item { return []; } - // Prepare apps for fuzzy search const preparedApps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { const app = allAppsDb.apps[i]; @@ -121,14 +112,12 @@ Item { }); } - // Perform fuzzy search const results = Fuzzy.go(search, preparedApps, { all: true, keys: ["name"], scoreFn: r => r[0].score }); - // Return sorted by score (highest first) return results .sort((a, b) => b._score - a._score) .map(r => r.obj._item); @@ -192,7 +181,6 @@ Item { if (root.session.launcher.active) { root.session.launcher.active = null; } else { - // Toggle to show settings - if there are apps, select the first one, otherwise show settings if (root.filteredApps.length > 0) { root.session.launcher.active = root.filteredApps[0]; } @@ -302,13 +290,7 @@ Item { Layout.fillWidth: true Layout.fillHeight: true asynchronous: true - active: { - // Lazy load: activate when left pane is loaded - // The ListView will load asynchronously, and search will work because filteredApps - // is updated regardless of whether the ListView is loaded - // Access loader through parent - this will be set when component loads - return true; - } + active: true sourceComponent: StyledListView { id: appsListView @@ -418,11 +400,9 @@ Item { sourceComponent: rightLauncherPane.targetComponent active: true - // Expose displayedApp to loaded components property var displayedApp: rightLauncherPane.displayedApp onItemChanged: { - // Ensure displayedApp is set when item is created (for async loading) if (item && rightLauncherPane.pane && rightLauncherPane.displayedApp !== rightLauncherPane.pane) { rightLauncherPane.displayedApp = rightLauncherPane.pane; } @@ -508,12 +488,10 @@ Item { id: appDetailsLayout anchors.fill: parent - // Get displayedApp from parent Loader (the Loader has displayedApp property we set) readonly property var displayedApp: parent && parent.displayedApp !== undefined ? parent.displayedApp : null spacing: Appearance.spacing.normal - // Show SettingsHeader when no app is selected, or show app icon + title when app is selected SettingsHeader { Layout.leftMargin: Appearance.padding.large * 2 Layout.rightMargin: Appearance.padding.large * 2 @@ -523,7 +501,6 @@ Item { title: qsTr("Launcher Applications") } - // App icon and title display (shown when app is selected) Item { Layout.alignment: Qt.AlignHCenter Layout.leftMargin: Appearance.padding.large * 2 diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index e28d35c..4446428 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -45,7 +45,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Network header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller @@ -102,7 +101,6 @@ Item { root.session.ethernet.active = null; root.session.network.active = null; } else { - // Toggle to show settings - prefer ethernet if available, otherwise wireless if (Nmcli.ethernetDevices.length > 0) { root.session.ethernet.active = Nmcli.ethernetDevices[0]; } else if (Nmcli.networks.length > 0) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7f6a4aa..cf16400 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -27,7 +27,6 @@ DeviceDetails { } onNetworkChanged: { - // Restart timer when network changes connectionUpdateTimer.stop(); if (network && network.ssid) { connectionUpdateTimer.start(); @@ -48,11 +47,9 @@ DeviceDetails { updateDeviceDetails(); } function onWirelessDeviceDetailsChanged() { - // When details are updated, check if we should stop the timer if (network && network.ssid) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive && Nmcli.wirelessDeviceDetails && Nmcli.wirelessDeviceDetails !== null) { - // We have details for the active network, stop the timer connectionUpdateTimer.stop(); } } @@ -65,22 +62,16 @@ DeviceDetails { repeat: true running: network && network.ssid onTriggered: { - // Periodically check if network becomes active and update details if (network) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive) { - // Network is active - check if we have details if (!Nmcli.wirelessDeviceDetails || Nmcli.wirelessDeviceDetails === null) { - // Network is active but we don't have details yet, fetch them Nmcli.getWirelessDeviceDetails("", () => { - // After fetching, check if we got details - if not, timer will try again }); } else { - // We have details, can stop the timer connectionUpdateTimer.stop(); } } else { - // Network is not active, clear details if (Nmcli.wirelessDeviceDetails !== null) { Nmcli.wirelessDeviceDetails = null; } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 4726712..9dabe9d 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -33,10 +33,8 @@ DeviceList { model: ScriptModel { values: [...Nmcli.networks].sort((a, b) => { - // Put active/connected network first if (a.active !== b.active) return b.active - a.active; - // Then sort by signal strength return b.strength - a.strength; }) } @@ -114,7 +112,6 @@ DeviceList { StateLayer { function onClicked(): void { root.session.network.active = modelData; - // Check if we need to refresh saved connections when selecting a network if (modelData && modelData.ssid) { root.checkSavedProfileForNetwork(modelData.ssid); } diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 0f1a5cd..7c046af 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -19,7 +19,6 @@ Item { required property Session session readonly property var network: { - // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { return session.network.pendingNetwork; } @@ -157,12 +156,10 @@ Item { focus: true Keys.onPressed: event => { - // Ensure we have focus when receiving keyboard input if (!activeFocus) { forceActiveFocus(); } - // Clear error when user starts typing if (connectButton.hasError && event.text && event.text.length > 0) { connectButton.hasError = false; } @@ -191,7 +188,6 @@ Item { target: root.session.network function onShowPasswordDialogChanged(): void { if (root.session.network.showPasswordDialog) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); passwordContainer.passwordBuffer = ""; @@ -205,7 +201,6 @@ Item { target: root function onVisibleChanged(): void { if (root.visible) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); }); @@ -383,46 +378,36 @@ Item { return; } - // Clear any previous error hasError = false; - - // Set connecting state connecting = true; enabled = false; text = qsTr("Connecting..."); - // Connect to network NetworkConnection.connectWithPassword(root.network, password, result => { - if (result && result.success) - // Connection successful, monitor will handle the rest - {} else if (result && result.needsPassword) { - // Shouldn't happen since we provided password + if (result && result.success) { + } else if (result && result.needsPassword) { connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } else { - // Connection failed immediately - show error connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } }); - // Start monitoring connection connectionMonitor.start(); } } @@ -435,19 +420,14 @@ Item { return; } - // Check if we're connected to the target network (case-insensitive SSID comparison) const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected) { - // Successfully connected - give it a moment for network list to update - // Use Timer for actual delay connectionSuccessTimer.start(); return; } - // Check for connection failures - if pending connection was cleared but we're not connected if (Nmcli.pendingConnection === null && connectButton.connecting) { - // Wait a bit more before giving up (allow time for connection to establish) if (connectionMonitor.repeatCount > 10) { connectionMonitor.stop(); connectButton.connecting = false; @@ -455,7 +435,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } @@ -486,7 +465,6 @@ Item { id: connectionSuccessTimer interval: 500 onTriggered: { - // Double-check connection is still active if (root.visible && Nmcli.active && Nmcli.active.ssid) { const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (stillConnected) { @@ -514,7 +492,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection Nmcli.forgetNetwork(ssid); } } diff --git a/modules/controlcenter/state/BluetoothState.qml b/modules/controlcenter/state/BluetoothState.qml index db8c7e1..00497ce 100644 --- a/modules/controlcenter/state/BluetoothState.qml +++ b/modules/controlcenter/state/BluetoothState.qml @@ -4,13 +4,8 @@ import QtQuick QtObject { id: root - // Active selected device property BluetoothDevice active: null - - // Current adapter being used property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter - - // UI state flags property bool editingAdapterName: false property bool fabMenuOpen: false property bool editingDeviceName: false diff --git a/modules/controlcenter/state/EthernetState.qml b/modules/controlcenter/state/EthernetState.qml index 25b243a..c5da7aa 100644 --- a/modules/controlcenter/state/EthernetState.qml +++ b/modules/controlcenter/state/EthernetState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected ethernet interface property var active: null } diff --git a/modules/controlcenter/state/LauncherState.qml b/modules/controlcenter/state/LauncherState.qml index cd9eeb6..c5da7aa 100644 --- a/modules/controlcenter/state/LauncherState.qml +++ b/modules/controlcenter/state/LauncherState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected application property var active: null } diff --git a/modules/controlcenter/state/NetworkState.qml b/modules/controlcenter/state/NetworkState.qml index 651a35c..da13e65 100644 --- a/modules/controlcenter/state/NetworkState.qml +++ b/modules/controlcenter/state/NetworkState.qml @@ -3,10 +3,7 @@ import QtQuick QtObject { id: root - // Active selected wireless network property var active: null - - // Password dialog state property bool showPasswordDialog: false property var pendingNetwork: null } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 1c3adbc..f452b07 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -18,15 +18,10 @@ Item { required property Session session - // Clock property bool clockShowIcon: Config.bar.clock.showIcon ?? true - - // Bar Behavior property bool persistent: Config.bar.persistent ?? true property bool showOnHover: Config.bar.showOnHover ?? true property int dragThreshold: Config.bar.dragThreshold ?? 20 - - // Status Icons property bool showAudio: Config.bar.status.showAudio ?? true property bool showMicrophone: Config.bar.status.showMicrophone ?? true property bool showKbLayout: Config.bar.status.showKbLayout ?? false @@ -34,25 +29,17 @@ Item { property bool showBluetooth: Config.bar.status.showBluetooth ?? true property bool showBattery: Config.bar.status.showBattery ?? true property bool showLockStatus: Config.bar.status.showLockStatus ?? true - - // Tray Settings property bool trayBackground: Config.bar.tray.background ?? false property bool trayCompact: Config.bar.tray.compact ?? false property bool trayRecolour: Config.bar.tray.recolour ?? false - - // Workspaces property int workspacesShown: Config.bar.workspaces.shown ?? 5 property bool workspacesActiveIndicator: Config.bar.workspaces.activeIndicator ?? true property bool workspacesOccupiedBg: Config.bar.workspaces.occupiedBg ?? false property bool workspacesShowWindows: Config.bar.workspaces.showWindows ?? false property bool workspacesPerMonitor: Config.bar.workspaces.perMonitorWorkspaces ?? true - - // Scroll Actions property bool scrollWorkspaces: Config.bar.scrollActions.workspaces ?? true property bool scrollVolume: Config.bar.scrollActions.volume ?? true property bool scrollBrightness: Config.bar.scrollActions.brightness ?? true - - // Popouts property bool popoutActiveWindow: Config.bar.popouts.activeWindow ?? true property bool popoutTray: Config.bar.popouts.tray ?? true property bool popoutStatusIcons: Config.bar.popouts.statusIcons ?? true @@ -60,7 +47,6 @@ Item { anchors.fill: parent Component.onCompleted: { - // Update entries if (Config.bar.entries) { entriesModel.clear(); for (let i = 0; i < Config.bar.entries.length; i++) { @@ -74,15 +60,10 @@ Item { } function saveConfig(entryIndex, entryEnabled) { - // Update clock setting Config.bar.clock.showIcon = root.clockShowIcon; - - // Update bar behavior Config.bar.persistent = root.persistent; Config.bar.showOnHover = root.showOnHover; Config.bar.dragThreshold = root.dragThreshold; - - // Update status icons Config.bar.status.showAudio = root.showAudio; Config.bar.status.showMicrophone = root.showMicrophone; Config.bar.status.showKbLayout = root.showKbLayout; @@ -90,35 +71,24 @@ Item { Config.bar.status.showBluetooth = root.showBluetooth; Config.bar.status.showBattery = root.showBattery; Config.bar.status.showLockStatus = root.showLockStatus; - - // Update tray settings Config.bar.tray.background = root.trayBackground; Config.bar.tray.compact = root.trayCompact; Config.bar.tray.recolour = root.trayRecolour; - - // Update workspaces Config.bar.workspaces.shown = root.workspacesShown; Config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator; Config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg; Config.bar.workspaces.showWindows = root.workspacesShowWindows; Config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor; - - // Update scroll actions Config.bar.scrollActions.workspaces = root.scrollWorkspaces; Config.bar.scrollActions.volume = root.scrollVolume; Config.bar.scrollActions.brightness = root.scrollBrightness; - - // Update popouts Config.bar.popouts.activeWindow = root.popoutActiveWindow; Config.bar.popouts.tray = root.popoutTray; Config.bar.popouts.statusIcons = root.popoutStatusIcons; - // Update entries from the model (same approach as clock - use provided value if available) const entries = []; for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); - // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) - // Otherwise use the value from the model let enabled = entry.enabled; if (entryIndex !== undefined && i === entryIndex) { enabled = entryEnabled; @@ -129,8 +99,6 @@ Item { }); } Config.bar.entries = entries; - - // Persist changes to disk Config.save(); } -- cgit v1.2.3-freya