From 2f39f248fcbe76f488c29c80940974fe77eda249 Mon Sep 17 00:00:00 2001 From: jbwfu <75001777+jbwfu@users.noreply.github.com> Date: Wed, 17 Jun 2026 00:52:25 +0800 Subject: [PATCH] fix(network): keep Wi-Fi when password prompt is canceled (#2651) --- quickshell/DMSShell.qml | 2 +- quickshell/Modals/WifiPasswordModal.qml | 73 +++++++------------ .../ControlCenter/ControlCenterPopout.qml | 2 +- .../ControlCenter/Details/NetworkDetail.qml | 21 ++---- .../Modules/Network/WifiConnectionActions.qml | 47 ++++++++++++ .../Modules/Settings/NetworkWifiTab.qml | 23 +++--- quickshell/Services/PopoutService.qml | 10 ++- 7 files changed, 101 insertions(+), 77 deletions(-) create mode 100644 quickshell/Modules/Network/WifiConnectionActions.qml diff --git a/quickshell/DMSShell.qml b/quickshell/DMSShell.qml index 40ab91c9..3ec1b82f 100644 --- a/quickshell/DMSShell.qml +++ b/quickshell/DMSShell.qml @@ -653,7 +653,7 @@ Item { if (!wifiPasswordModalLoader.item) return; - if (wifiPasswordModalLoader.item.visible && timeSinceLastPrompt < 1000) { + if (wifiPasswordModalLoader.item.shouldBeVisible && timeSinceLastPrompt < 1000) { NetworkService.cancelCredentials(lastCredentialsToken); lastCredentialsToken = token; lastCredentialsTime = now; diff --git a/quickshell/Modals/WifiPasswordModal.qml b/quickshell/Modals/WifiPasswordModal.qml index d60fe05b..a1539f6c 100644 --- a/quickshell/Modals/WifiPasswordModal.qml +++ b/quickshell/Modals/WifiPasswordModal.qml @@ -1,12 +1,22 @@ import QtQuick -import Quickshell import qs.Common +import qs.Modals.Common import qs.Services import qs.Widgets -FloatingWindow { +DankModal { id: root + layerNamespace: "dms:wifi-password" + keepPopoutsOpen: true + allowStacking: true + shouldBeVisible: false + modalWidth: 420 + modalHeight: calculatedHeight + enableShadow: true + onBackgroundClicked: clearAndClose() + directContent: contentFocusScope + property bool disablePopupTransparency: true property string wifiPasswordSSID: "" property string wifiPasswordInput: "" @@ -102,7 +112,7 @@ FloatingWindow { const network = NetworkService.wifiNetworks.find(n => n.ssid === ssid); requiresEnterprise = network?.enterprise || false; - visible = true; + open(); Qt.callLater(focusFirstField); } @@ -126,7 +136,7 @@ FloatingWindow { secretValues = {}; requiresEnterprise = false; - visible = true; + open(); Qt.callLater(focusFirstField); } @@ -144,6 +154,7 @@ FloatingWindow { isVpnPrompt = (connectionType === "vpn" || connectionType === "wireguard"); wifiPasswordSSID = isVpnPrompt ? connectionName : ssid; + savePasswordCheckbox.checked = !isVpnPrompt; requiresEnterprise = setting === "802-1x"; @@ -152,7 +163,7 @@ FloatingWindow { wifiAnonymousIdentityInput = ""; wifiDomainInput = ""; - visible = true; + open(); Qt.callLater(() => { if (reason === "wrong-password" && fieldsInfo.length === 0) { passwordInput.text = ""; @@ -162,7 +173,7 @@ FloatingWindow { } function hide() { - visible = false; + close(); } function getFieldLabel(fieldName) { @@ -242,23 +253,8 @@ FloatingWindow { secretValues = {}; } - objectName: "wifiPasswordModal" - title: { - if (promptReason === "pkcs11") - return I18n.tr("Smartcard PIN"); - if (isVpnPrompt) - return I18n.tr("VPN Password"); - if (isHiddenNetwork) - return I18n.tr("Hidden Network"); - return I18n.tr("Wi-Fi Password"); - } - minimumSize: Qt.size(420, calculatedHeight) - maximumSize: Qt.size(420, calculatedHeight) - color: Theme.surfaceContainer - visible: false - - onVisibleChanged: { - if (visible) { + onShouldBeVisibleChanged: { + if (shouldBeVisible) { Qt.callLater(focusFirstField); return; } @@ -287,7 +283,7 @@ FloatingWindow { return; wifiPasswordSSID = NetworkService.connectingSSID; wifiPasswordInput = ""; - visible = true; + open(); NetworkService.passwordDialogShouldReopen = false; } } @@ -296,7 +292,7 @@ FloatingWindow { id: contentFocusScope anchors.fill: parent - focus: true + focus: root.shouldBeVisible Keys.onEscapePressed: event => { clearAndClose(); @@ -318,8 +314,6 @@ FloatingWindow { anchors.right: buttonRow.left anchors.rightMargin: Theme.spacingM height: headerCol.height - onPressed: windowControls.tryStartMove() - onDoubleClicked: windowControls.tryToggleMaximize() Column { id: headerCol @@ -380,14 +374,6 @@ FloatingWindow { anchors.right: parent.right spacing: Theme.spacingXS - DankActionButton { - visible: windowControls.canMaximize - iconName: root.maximized ? "fullscreen_exit" : "fullscreen" - iconSize: Theme.iconSize - 4 - iconColor: Theme.surfaceText - onClicked: windowControls.tryToggleMaximize() - } - DankActionButton { iconName: "close" iconSize: Theme.iconSize - 4 @@ -419,7 +405,7 @@ FloatingWindow { textColor: Theme.surfaceText placeholderText: I18n.tr("Network Name (SSID)") backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible keyNavigationTab: passwordInput onAccepted: passwordInput.forceActiveFocus() } @@ -449,7 +435,7 @@ FloatingWindow { echoMode: modelData.isSecret && !passwordVisible ? TextInput.Password : TextInput.Normal placeholderText: getFieldLabel(modelData.name) backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible Keys.onTabPressed: event => { if (index < fieldsInfo.length - 1) { @@ -519,7 +505,7 @@ FloatingWindow { text: wifiUsernameInput placeholderText: I18n.tr("Username") backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible keyNavigationTab: passwordInput keyNavigationBacktab: domainMatchInput onTextEdited: wifiUsernameInput = text @@ -552,7 +538,7 @@ FloatingWindow { echoMode: passwordVisible ? TextInput.Normal : TextInput.Password placeholderText: (requiresEnterprise && !isVpnPrompt) ? I18n.tr("Password") : "" backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible keyNavigationTab: (requiresEnterprise && !isVpnPrompt) ? anonInput : null keyNavigationBacktab: (requiresEnterprise && !isVpnPrompt) ? usernameInput : null onTextEdited: wifiPasswordInput = text @@ -589,7 +575,7 @@ FloatingWindow { text: wifiAnonymousIdentityInput placeholderText: I18n.tr("Anonymous Identity (optional)") backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible keyNavigationTab: domainMatchInput keyNavigationBacktab: passwordInput onTextEdited: wifiAnonymousIdentityInput = text @@ -620,7 +606,7 @@ FloatingWindow { text: wifiDomainInput placeholderText: I18n.tr("Domain (optional)") backgroundColor: "transparent" - enabled: root.visible + enabled: root.shouldBeVisible keyNavigationTab: usernameInput keyNavigationBacktab: anonInput onTextEdited: wifiDomainInput = text @@ -757,8 +743,5 @@ FloatingWindow { } } - FloatingWindowControls { - id: windowControls - targetWindow: root - } + onOpened: Qt.callLater(() => contentFocusScope.forceActiveFocus()) } diff --git a/quickshell/Modules/ControlCenter/ControlCenterPopout.qml b/quickshell/Modules/ControlCenter/ControlCenterPopout.qml index 29bae752..5bff6aa0 100644 --- a/quickshell/Modules/ControlCenter/ControlCenterPopout.qml +++ b/quickshell/Modules/ControlCenter/ControlCenterPopout.qml @@ -93,7 +93,7 @@ DankPopout { shouldBeVisible: false property bool credentialsPromptOpen: NetworkService.credentialsRequested - property bool wifiPasswordModalOpen: PopoutService.wifiPasswordModal?.visible ?? false + property bool wifiPasswordModalOpen: PopoutService.wifiPasswordModal?.shouldBeVisible ?? false property bool polkitModalOpen: PopoutService.polkitAuthModal?.visible ?? false property bool anyModalOpen: credentialsPromptOpen || wifiPasswordModalOpen || polkitModalOpen || powerMenuOpen diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index 364478fd..1bb527d8 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import Quickshell import qs.Common +import qs.Modules.Network import qs.Services import qs.Widgets import qs.Modals @@ -749,11 +750,9 @@ Rectangle { event.accepted = true; return; } - if (modelData.secured && !modelData.saved && (DMSService.apiVersion < 7 || modelData.enterprise)) { - PopoutService.showWifiPasswordModal(modelData.ssid); - } else { - NetworkService.connectToWifi(modelData.ssid); - } + WifiConnectionActions.connectToNetwork(modelData, { + connected: wifiDelegate.isConnected + }); event.accepted = true; } } @@ -804,15 +803,9 @@ Rectangle { } onTriggered: { - if (networkContextMenu.currentConnected) { - NetworkService.disconnectWifi(); - return; - } - if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved && (DMSService.apiVersion < 7 || networkContextMenu.currentEnterprise)) { - PopoutService.showWifiPasswordModal(networkContextMenu.currentSSID); - return; - } - NetworkService.connectToWifi(networkContextMenu.currentSSID); + WifiConnectionActions.connectToNetworkFromDetails(networkContextMenu.currentSSID, networkContextMenu.currentSecured, networkContextMenu.currentSaved, networkContextMenu.currentEnterprise, networkContextMenu.currentConnected, { + disconnectWhenConnected: true + }); } } diff --git a/quickshell/Modules/Network/WifiConnectionActions.qml b/quickshell/Modules/Network/WifiConnectionActions.qml new file mode 100644 index 00000000..eb20ad87 --- /dev/null +++ b/quickshell/Modules/Network/WifiConnectionActions.qml @@ -0,0 +1,47 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import Quickshell +import qs.Services + +Singleton { + id: root + + function connectToNetwork(network, options) { + if (!network) + return; + + const actionOptions = options || {}; + const ssid = network.ssid || ""; + if (!ssid) + return; + + const connected = actionOptions.connected ?? network.connected ?? (ssid === NetworkService.currentWifiSSID); + if (connected) { + if (actionOptions.disconnectWhenConnected ?? false) + NetworkService.disconnectWifi(); + return; + } + + if (shouldPromptForCredentials(network)) { + PopoutService.showWifiPasswordModal(ssid); + return; + } + + NetworkService.connectToWifi(ssid); + } + + function connectToNetworkFromDetails(ssid, secured, saved, enterprise, connected, options) { + connectToNetwork({ + ssid: ssid, + secured: secured, + saved: saved, + enterprise: enterprise, + connected: connected + }, options); + } + + function shouldPromptForCredentials(network) { + return (network.secured ?? false) && !(network.saved ?? false); + } +} diff --git a/quickshell/Modules/Settings/NetworkWifiTab.qml b/quickshell/Modules/Settings/NetworkWifiTab.qml index 00a73d45..7fc34570 100644 --- a/quickshell/Modules/Settings/NetworkWifiTab.qml +++ b/quickshell/Modules/Settings/NetworkWifiTab.qml @@ -4,6 +4,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import qs.Common +import qs.Modules.Network import qs.Modules.Settings.Widgets import qs.Modals.Common import qs.Services @@ -673,15 +674,10 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (isConnected) { - NetworkService.disconnectWifi(); - return; - } - if (modelData.secured && !modelData.saved && (DMSService.apiVersion < 7 || modelData.enterprise)) { - PopoutService.showWifiPasswordModal(modelData.ssid); - return; - } - NetworkService.connectToWifi(modelData.ssid); + WifiConnectionActions.connectToNetwork(modelData, { + connected: isConnected, + disconnectWhenConnected: true + }); } } } @@ -1184,11 +1180,10 @@ Item { } onTriggered: { - if (isConnected) { - NetworkService.disconnectWifi(); - return; - } - NetworkService.connectToWifi(modelData.ssid); + WifiConnectionActions.connectToNetwork(modelData, { + connected: isConnected, + disconnectWhenConnected: true + }); } } diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index 58ed737e..cf1c146e 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -756,8 +756,11 @@ Singleton { function showWifiPasswordModal(ssid) { if (wifiPasswordModalLoader) wifiPasswordModalLoader.active = true; - if (wifiPasswordModal) + if (wifiPasswordModal) { wifiPasswordModal.show(ssid); + } else { + Qt.callLater(() => wifiPasswordModal?.show(ssid)); + } } function showWifiQRCodeModal(ssid) { @@ -770,8 +773,11 @@ Singleton { function showHiddenNetworkModal() { if (wifiPasswordModalLoader) wifiPasswordModalLoader.active = true; - if (wifiPasswordModal) + if (wifiPasswordModal) { wifiPasswordModal.showHidden(); + } else { + Qt.callLater(() => wifiPasswordModal?.showHidden()); + } } function hideWifiPasswordModal() {