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 --- modules/controlcenter/Panes.qml | 2 +- modules/controlcenter/network/Details.qml | 213 --------- modules/controlcenter/network/NetworkList.qml | 228 --------- modules/controlcenter/network/NetworkPane.qml | 154 ------ modules/controlcenter/network/PasswordDialog.qml | 514 --------------------- modules/controlcenter/network/Settings.qml | 166 ------- modules/controlcenter/network/SimpleButton.qml | 36 ++ .../network/WirelessConnectionHelper.qml | 46 ++ modules/controlcenter/network/WirelessDetails.qml | 158 +++++++ modules/controlcenter/network/WirelessList.qml | 207 +++++++++ modules/controlcenter/network/WirelessPane.qml | 155 +++++++ .../network/WirelessPasswordDialog.qml | 299 ++++++++++++ modules/controlcenter/network/WirelessSettings.qml | 82 ++++ 13 files changed, 984 insertions(+), 1276 deletions(-) delete mode 100644 modules/controlcenter/network/Details.qml delete mode 100644 modules/controlcenter/network/NetworkList.qml delete mode 100644 modules/controlcenter/network/NetworkPane.qml delete mode 100644 modules/controlcenter/network/PasswordDialog.qml delete mode 100644 modules/controlcenter/network/Settings.qml create mode 100644 modules/controlcenter/network/SimpleButton.qml create mode 100644 modules/controlcenter/network/WirelessConnectionHelper.qml create mode 100644 modules/controlcenter/network/WirelessDetails.qml create mode 100644 modules/controlcenter/network/WirelessList.qml create mode 100644 modules/controlcenter/network/WirelessPane.qml create mode 100644 modules/controlcenter/network/WirelessPasswordDialog.qml create mode 100644 modules/controlcenter/network/WirelessSettings.qml (limited to 'modules') diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 94bea9a..11e60d3 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -36,7 +36,7 @@ ClippingRectangle { Pane { index: 1 - sourceComponent: NetworkPane { + sourceComponent: WirelessPane { session: root.session } } diff --git a/modules/controlcenter/network/Details.qml b/modules/controlcenter/network/Details.qml deleted file mode 100644 index 5e636a2..0000000 --- a/modules/controlcenter/network/Details.qml +++ /dev/null @@ -1,213 +0,0 @@ -pragma ComponentBehavior: Bound - -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: session.network.active - - Component.onCompleted: { - if (network && network.active) { - Network.updateWirelessDeviceDetails(); - } - } - - onNetworkChanged: { - if (network && network.active) { - Network.updateWirelessDeviceDetails(); - } else { - Network.wirelessDeviceDetails = null; - } - } - - 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; - } - } - } - - StyledFlickable { - anchors.fill: parent - - flickableDirection: Flickable.VerticalFlick - contentHeight: layout.height - - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.normal - - ConnectionHeader { - icon: root.network?.isSecure ? "lock" : "wifi" - title: root.network?.ssid ?? qsTr("Unknown") - } - - SectionHeader { - title: qsTr("Connection status") - description: qsTr("Connection settings for this network") - } - - SectionContainer { - ToggleRow { - label: qsTr("Connected") - checked: root.network?.active ?? false - toggle.onToggled: { - if (checked) { - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== root.network.ssid) { - Network.disconnectFromNetwork(); - // Wait a moment before connecting to new network - Qt.callLater(() => { - connectToNetwork(); - }); - } else { - connectToNetwork(); - } - } else { - Network.disconnectFromNetwork(); - } - } - - function connectToNetwork(): void { - if (root.network.isSecure) { - // Try connecting without password first (in case it's saved) - 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); - } - } - } - - Button { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal - visible: root.network && root.network.ssid && Network.savedConnections.includes(root.network.ssid) - color: Colours.palette.m3errorContainer - onColor: Colours.palette.m3onErrorContainer - text: qsTr("Forget Network") - - onClicked: { - if (root.network && root.network.ssid) { - // Disconnect first if connected - if (root.network.active) { - Network.disconnectFromNetwork(); - } - // Delete the connection profile - Network.forgetNetwork(root.network.ssid); - } - } - } - } - - SectionHeader { - title: qsTr("Network properties") - description: qsTr("Additional information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 - - PropertyRow { - label: qsTr("SSID") - value: root.network?.ssid ?? qsTr("Unknown") - } - - PropertyRow { - showTopMargin: true - label: qsTr("BSSID") - value: root.network?.bssid ?? qsTr("Unknown") - } - - PropertyRow { - showTopMargin: true - label: qsTr("Signal strength") - value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") - } - - PropertyRow { - showTopMargin: true - label: qsTr("Frequency") - value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") - } - - PropertyRow { - showTopMargin: true - label: qsTr("Security") - value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") - } - } - - SectionHeader { - title: qsTr("Connection information") - description: qsTr("Network connection details") - } - - SectionContainer { - ConnectionInfoSection { - deviceDetails: Network.wirelessDeviceDetails - } - } - - } - } - - component Button: StyledRect { - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - - Layout.fillWidth: true - implicitHeight: label.implicitHeight + Appearance.padding.normal * 2 - radius: Appearance.rounding.normal - - StateLayer { - id: stateLayer - color: parent.onColor - function onClicked(): void { - if (parent.enabled !== false) { - parent.clicked(); - } - } - } - - StyledText { - id: label - anchors.centerIn: parent - color: parent.onColor - } - - signal clicked - } - -} - - - - diff --git a/modules/controlcenter/network/NetworkList.qml b/modules/controlcenter/network/NetworkList.qml deleted file mode 100644 index 6c4158c..0000000 --- a/modules/controlcenter/network/NetworkList.qml +++ /dev/null @@ -1,228 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.containers -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -ColumnLayout { - id: root - - required property Session session - - spacing: Appearance.spacing.small - - RowLayout { - spacing: Appearance.spacing.smaller - - StyledText { - text: qsTr("Settings") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - ToggleButton { - toggled: Network.wifiEnabled - icon: "wifi" - accent: "Tertiary" - - onClicked: { - Network.toggleWifi(); - } - } - - ToggleButton { - toggled: Network.scanning - icon: "wifi_find" - accent: "Secondary" - - onClicked: { - Network.rescanWifi(); - } - } - - ToggleButton { - toggled: !root.session.network.active - icon: "settings" - accent: "Primary" - - onClicked: { - if (root.session.network.active) - root.session.network.active = null; - else { - root.session.network.active = view.model.get(0)?.modelData ?? null; - } - } - } - } - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Networks (%1)").arg(Network.networks.length) - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - - StyledText { - visible: Network.scanning - text: qsTr("Scanning...") - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.small - } - } - - StyledText { - text: qsTr("All available WiFi networks") - color: Colours.palette.m3outline - } - - StyledListView { - id: view - - Layout.fillWidth: true - Layout.fillHeight: true - - model: Network.networks - - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: view - } - - delegate: StyledRect { - required property var modelData - - anchors.left: parent.left - anchors.right: parent.right - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.network.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - border.width: root.session.network.active === modelData ? 1 : 0 - border.color: Colours.palette.m3primary - - StateLayer { - function onClicked(): void { - root.session.network.active = modelData; - } - } - - RowLayout { - id: rowLayout - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.normal - color: modelData.active ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - - MaterialIcon { - id: icon - - anchors.centerIn: parent - text: modelData.isSecure ? "lock" : "wifi" - font.pointSize: Appearance.font.size.large - fill: modelData.active ? 1 : 0 - color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 - - text: 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: qsTr("%1%").arg(modelData.strength) - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.active ? 1 : 0) - - StateLayer { - function onClicked(): void { - if (modelData.active) { - Network.disconnectFromNetwork(); - } else { - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== modelData.ssid) { - Network.disconnectFromNetwork(); - // Wait a moment before connecting to new network - Qt.callLater(() => { - connectToNetwork(); - }); - } else { - connectToNetwork(); - } - } - } - - function connectToNetwork(): void { - if (modelData.isSecure) { - // Try connecting without password first (in case it's saved) - Network.connectToNetworkWithPasswordCheck( - modelData.ssid, - modelData.isSecure, - () => { - // Callback: connection failed, show password dialog - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = modelData; - }, - modelData.bssid - ); - } else { - Network.connectToNetwork(modelData.ssid, "", modelData.bssid, null); - } - } - } - - MaterialIcon { - id: connectIcon - - anchors.centerIn: parent - text: modelData.active ? "link_off" : "link" - color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - } - - implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 - } - } -} diff --git a/modules/controlcenter/network/NetworkPane.qml b/modules/controlcenter/network/NetworkPane.qml deleted file mode 100644 index fe18274..0000000 --- a/modules/controlcenter/network/NetworkPane.qml +++ /dev/null @@ -1,154 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.effects -import qs.components.containers -import qs.config -import Quickshell.Widgets -import QtQuick -import QtQuick.Layouts - -RowLayout { - id: root - - required property Session session - - anchors.fill: parent - - spacing: 0 - - Item { - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - NetworkList { - 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 - - session: root.session - } - - 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" - - Loader { - id: loader - - property var pane: root.session.network.active - property string paneId: pane ? (pane.ssid || pane.bssid || "") : "" - - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 - - opacity: 1 - scale: 1 - transformOrigin: Item.Center - - clip: false - asynchronous: true - sourceComponent: pane ? details : 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 - } - Anim { - target: loader - property: "scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } - } - } - - onPaneChanged: { - paneId = pane ? (pane.ssid || pane.bssid || "") : ""; - } - } - } - - InnerBorder { - id: rightBorder - - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: settings - - StyledFlickable { - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height - - Settings { - id: settingsInner - - anchors.left: parent.left - anchors.right: parent.right - session: root.session - } - } - } - - Component { - id: details - - Details { - session: root.session - } - } - } - - PasswordDialog { - anchors.fill: parent - session: root.session - z: 1000 - } - - component Anim: NumberAnimation { - target: loader - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } -} diff --git a/modules/controlcenter/network/PasswordDialog.qml b/modules/controlcenter/network/PasswordDialog.qml deleted file mode 100644 index 7aa698b..0000000 --- a/modules/controlcenter/network/PasswordDialog.qml +++ /dev/null @@ -1,514 +0,0 @@ -pragma ComponentBehavior: Bound - -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: { - // Try pendingNetwork first, then fall back to active network selection - if (session.network.pendingNetwork) { - return session.network.pendingNetwork; - } - // Fallback to active network if available - if (session.network.active) { - return session.network.active; - } - return null; - } - - visible: session.network.showPasswordDialog - enabled: visible - focus: visible - - // Ensure network is set when dialog opens - Component.onCompleted: { - if (visible && !session.network.pendingNetwork && session.network.active) { - session.network.pendingNetwork = session.network.active; - } - } - - Connections { - target: root - function onVisibleChanged(): void { - if (visible && !session.network.pendingNetwork && session.network.active) { - session.network.pendingNetwork = session.network.active; - } - } - } - - Keys.onEscapePressed: { - root.session.network.showPasswordDialog = false; - passwordField.text = ""; - } - - 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: { - root.session.network.showPasswordDialog = false; - passwordField.text = ""; - } - } - } - - 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: { - root.session.network.showPasswordDialog = false; - passwordField.text = ""; - } - - 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") - - Component.onCompleted: { - if (root.visible) { - forceActiveFocus(); - } - } - - Connections { - target: root - function onVisibleChanged(): void { - if (root.visible) { - passwordField.forceActiveFocus(); - passwordField.text = ""; - Network.connectionStatus = ""; - } - } - } - - Connections { - target: Network - function onConnectionStatusChanged(): void { - // Status updated, ensure it's visible - } - } - - 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 - - Button { - id: cancelButton - - Layout.fillWidth: true - color: Colours.palette.m3secondaryContainer - onColor: Colours.palette.m3onSecondaryContainer - text: qsTr("Cancel") - - function onClicked(): void { - root.session.network.showPasswordDialog = false; - passwordField.text = ""; - } - } - - Button { - id: connectButton - - Layout.fillWidth: true - color: Colours.palette.m3primary - onColor: Colours.palette.m3onPrimary - text: qsTr("Connect") - enabled: passwordField.text.length > 0 - - property bool connecting: false - - function onClicked(): void { - Network.connectionStatus = ""; - - // Get password first - const password = passwordField.text; - - // Try multiple ways to get the network - let networkToUse = null; - - // Try 1: root.network (computed property) - if (root.network) { - networkToUse = root.network; - } - - // Try 2: pendingNetwork - if (!networkToUse && root.session.network.pendingNetwork) { - networkToUse = root.session.network.pendingNetwork; - } - - // Try 3: active network - if (!networkToUse && root.session.network.active) { - networkToUse = root.session.network.active; - root.session.network.pendingNetwork = networkToUse; - } - - // Check all conditions - const hasNetwork = !!networkToUse; - const hasPassword = password && password.length > 0; - const notConnecting = !connecting; - - if (hasNetwork && hasPassword && notConnecting) { - // Set status immediately - Network.connectionStatus = qsTr("Preparing to connect..."); - - // Keep dialog open and track connection - connecting = true; - connectButton.enabled = false; - connectButton.text = qsTr("Connecting..."); - - // Force immediate UI update - statusText.visible = true; - - // Store target SSID for later comparison - const ssidToConnect = networkToUse.ssid || ""; - const bssidToConnect = networkToUse.bssid || ""; - - // Store the SSID we're connecting to so we can compare later - // even if root.network changes - content.connectingToSsid = ssidToConnect; - - // Execute connection immediately - Network.connectToNetwork( - ssidToConnect, - password, - bssidToConnect, - () => { - // Callback if connection fails - keep dialog open - connecting = false; - connectButton.enabled = true; - connectButton.text = qsTr("Connect"); - content.connectingToSsid = ""; // Clear on failure - } - ); - - // Start connection check timer immediately - connectionCheckTimer.checkCount = 0; - connectionCheckTimer.start(); - - // Also check immediately after a short delay to catch quick connections - Qt.callLater(() => { - if (root.visible) { - closeDialogIfConnected(); - } - }); - } else { - // Show error in status - Network.connectionStatus = qsTr("Error: Cannot connect - missing network or password"); - } - } - } - } - - // Store the SSID we're connecting to when connection starts - property string connectingToSsid: "" - - property string targetSsid: { - // Track the SSID we're trying to connect to - // Prefer explicitly stored connectingToSsid, then computed values - if (connectingToSsid && connectingToSsid.length > 0) { - return connectingToSsid; - } - if (root.network && root.network.ssid) { - return root.network.ssid; - } - if (root.session.network.pendingNetwork && root.session.network.pendingNetwork.ssid) { - return root.session.network.pendingNetwork.ssid; - } - return ""; - } - - function closeDialogIfConnected(): bool { - // Check if we're connected to the network we're trying to connect to - const ssid = targetSsid; - - if (!ssid || ssid.length === 0) { - return false; - } - - if (!Network.active) { - return false; - } - - const activeSsid = Network.active.ssid || ""; - - if (activeSsid === ssid) { - // Connection succeeded - close dialog - connectionCheckTimer.stop(); - aggressiveCheckTimer.stop(); - connectionCheckTimer.checkCount = 0; - connectButton.connecting = false; - Network.connectionStatus = ""; - root.session.network.showPasswordDialog = false; - passwordField.text = ""; - content.connectingToSsid = ""; // Clear stored SSID - return true; - } - return false; - } - - Timer { - id: connectionCheckTimer - interval: 1000 // Check every 1 second for faster response - repeat: true - triggeredOnStart: false - property int checkCount: 0 - - onTriggered: { - checkCount++; - - // Try to close dialog if connected - const closed = content.closeDialogIfConnected(); - if (closed) { - return; - } - - if (connectButton.connecting) { - // Still connecting, check again - // Limit to 20 checks (20 seconds total) - if (checkCount >= 20) { - connectionCheckTimer.stop(); - connectionCheckTimer.checkCount = 0; - connectButton.connecting = false; - connectButton.enabled = true; - connectButton.text = qsTr("Connect"); - } - } else { - // Not connecting anymore, stop timer - connectionCheckTimer.stop(); - connectionCheckTimer.checkCount = 0; - } - } - } - - Connections { - target: Network - function onActiveChanged(): void { - // Check immediately when active network changes - if (root.visible) { - // Check immediately - if connected, close right away - if (content.closeDialogIfConnected()) { - return; - } - - // Also check after a delay in case the active network isn't fully updated yet - Qt.callLater(() => { - if (root.visible) { - content.closeDialogIfConnected(); - } - }); - } - } - } - - // Also check when dialog becomes visible - Connections { - target: root - function onVisibleChanged(): void { - if (root.visible) { - // Check immediately when dialog opens - Qt.callLater(() => { - if (root.visible) { - closeDialogIfConnected(); - } - }); - } - } - } - - // Aggressive polling timer - checks every 500ms when dialog is visible and connecting - // This ensures we catch the connection even if signals are missed - Timer { - id: aggressiveCheckTimer - interval: 500 - repeat: true - running: root.visible && connectButton.connecting - triggeredOnStart: true - - onTriggered: { - if (root.visible && connectButton.connecting) { - if (content.closeDialogIfConnected()) { - stop(); - } - } else { - stop(); - } - } - } - } - } - - component Button: StyledRect { - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - - function onClicked(): void { - } - - radius: Appearance.rounding.small - implicitHeight: label.implicitHeight + Appearance.padding.small * 2 - opacity: enabled ? 1 : 0.5 - - StateLayer { - id: stateLayer - - enabled: parent.enabled - color: parent.onColor - - function onClicked(): void { - if (enabled) { - parent.onClicked(); - } - } - } - - StyledText { - id: label - - anchors.centerIn: parent - animate: true - color: parent.onColor - font.pointSize: Appearance.font.size.normal - } - } -} - diff --git a/modules/controlcenter/network/Settings.qml b/modules/controlcenter/network/Settings.qml deleted file mode 100644 index 0794e6a..0000000 --- a/modules/controlcenter/network/Settings.qml +++ /dev/null @@ -1,166 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.effects -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -ColumnLayout { - id: root - - required property Session session - - spacing: Appearance.spacing.normal - - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "wifi" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Network settings") - font.pointSize: Appearance.font.size.large - font.bold: true - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("WiFi status") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("General WiFi settings") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: wifiStatus.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: wifiStatus - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.larger - - Toggle { - label: qsTr("WiFi enabled") - checked: Network.wifiEnabled - toggle.onToggled: { - Network.enableWifi(checked); - } - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Network information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Current network connection") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: networkInfo.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: networkInfo - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.small / 2 - - StyledText { - text: qsTr("Connected network") - } - - StyledText { - text: Network.active ? Network.active.ssid : qsTr("Not connected") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Signal strength") - } - - StyledText { - text: Network.active ? qsTr("%1%").arg(Network.active.strength) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Security") - } - - StyledText { - text: Network.active ? (Network.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Frequency") - } - - StyledText { - text: Network.active ? qsTr("%1 MHz").arg(Network.active.frequency) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - } - } - - component Toggle: RowLayout { - required property string label - property alias checked: toggle.checked - property alias toggle: toggle - - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - Layout.fillWidth: true - text: parent.label - } - - StyledSwitch { - id: toggle - - cLayer: 2 - } - } -} - diff --git a/modules/controlcenter/network/SimpleButton.qml b/modules/controlcenter/network/SimpleButton.qml new file mode 100644 index 0000000..49ab2f4 --- /dev/null +++ b/modules/controlcenter/network/SimpleButton.qml @@ -0,0 +1,36 @@ +import qs.components +import qs.components.effects +import qs.config +import QtQuick + +StyledRect { + id: root + + property color onColor: Colours.palette.m3onSurface + property alias disabled: stateLayer.disabled + property alias text: label.text + property alias enabled: stateLayer.enabled + + implicitWidth: label.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: label.implicitHeight + Appearance.padding.normal * 2 + radius: Appearance.rounding.normal + + StateLayer { + id: stateLayer + color: parent.onColor + function onClicked(): void { + if (parent.enabled !== false) { + parent.clicked(); + } + } + } + + StyledText { + id: label + anchors.centerIn: parent + color: parent.onColor + } + + signal clicked +} + diff --git a/modules/controlcenter/network/WirelessConnectionHelper.qml b/modules/controlcenter/network/WirelessConnectionHelper.qml new file mode 100644 index 0000000..d63b359 --- /dev/null +++ b/modules/controlcenter/network/WirelessConnectionHelper.qml @@ -0,0 +1,46 @@ +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 new file mode 100644 index 0000000..e858fbc --- /dev/null +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -0,0 +1,158 @@ +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: session.network.active + + readonly property var connectionHelper: WirelessConnectionHelper { + session: root.session + } + + Component.onCompleted: { + if (network && network.active) { + Network.updateWirelessDeviceDetails(); + } + } + + onNetworkChanged: { + if (network && network.active) { + Network.updateWirelessDeviceDetails(); + } else { + Network.wirelessDeviceDetails = null; + } + } + + 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; + } + } + } + + StyledFlickable { + anchors.fill: parent + + flickableDirection: Flickable.VerticalFlick + contentHeight: layout.height + + ColumnLayout { + id: layout + + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal + + ConnectionHeader { + icon: root.network?.isSecure ? "lock" : "wifi" + title: root.network?.ssid ?? qsTr("Unknown") + } + + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this network") + } + + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.network?.active ?? false + toggle.onToggled: { + if (checked) { + root.connectionHelper.connectToNetwork(root.network); + } else { + Network.disconnectFromNetwork(); + } + } + } + + SimpleButton { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + visible: root.network && root.network.ssid && Network.savedConnections.includes(root.network.ssid) + color: Colours.palette.m3errorContainer + onColor: Colours.palette.m3onErrorContainer + text: qsTr("Forget Network") + + onClicked: { + if (root.network && root.network.ssid) { + // Disconnect first if connected + if (root.network.active) { + Network.disconnectFromNetwork(); + } + // Delete the connection profile + Network.forgetNetwork(root.network.ssid); + } + } + } + } + + SectionHeader { + title: qsTr("Network properties") + description: qsTr("Additional information") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("SSID") + value: root.network?.ssid ?? qsTr("Unknown") + } + + PropertyRow { + showTopMargin: true + label: qsTr("BSSID") + value: root.network?.bssid ?? qsTr("Unknown") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Signal strength") + value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Frequency") + value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Security") + value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + } + } + + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") + } + + SectionContainer { + ConnectionInfoSection { + deviceDetails: Network.wirelessDeviceDetails + } + } + + } + } +} + diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml new file mode 100644 index 0000000..1a4ba00 --- /dev/null +++ b/modules/controlcenter/network/WirelessList.qml @@ -0,0 +1,207 @@ +pragma ComponentBehavior: Bound + +import ".." +import "." +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + readonly property var connectionHelper: WirelessConnectionHelper { + session: root.session + } + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + ToggleButton { + toggled: Network.wifiEnabled + icon: "wifi" + accent: "Tertiary" + + onClicked: { + Network.toggleWifi(); + } + } + + ToggleButton { + toggled: Network.scanning + icon: "wifi_find" + accent: "Secondary" + + onClicked: { + Network.rescanWifi(); + } + } + + ToggleButton { + toggled: !root.session.network.active + icon: "settings" + accent: "Primary" + + onClicked: { + if (root.session.network.active) + root.session.network.active = null; + else { + root.session.network.active = view.model.get(0)?.modelData ?? null; + } + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Networks (%1)").arg(Network.networks.length) + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + StyledText { + visible: Network.scanning + text: qsTr("Scanning...") + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.small + } + } + + StyledText { + text: qsTr("All available WiFi networks") + color: Colours.palette.m3outline + } + + StyledListView { + id: view + + Layout.fillWidth: true + Layout.fillHeight: true + + model: Network.networks + + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: view + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.network.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: root.session.network.active === modelData ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.session.network.active = modelData; + } + } + + RowLayout { + id: rowLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.normal + color: modelData.active ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + + MaterialIcon { + id: icon + + anchors.centerIn: parent + text: modelData.isSecure ? "lock" : "wifi" + font.pointSize: Appearance.font.size.large + fill: modelData.active ? 1 : 0 + color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + + StyledText { + Layout.fillWidth: true + elide: Text.ElideRight + maximumLineCount: 1 + + text: 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: qsTr("%1%").arg(modelData.strength) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.active ? 1 : 0) + + StateLayer { + function onClicked(): void { + if (modelData.active) { + Network.disconnectFromNetwork(); + } else { + root.connectionHelper.connectToNetwork(modelData); + } + } + } + + MaterialIcon { + id: connectIcon + + anchors.centerIn: parent + text: modelData.active ? "link_off" : "link" + color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + } + + implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + } + } +} + diff --git a/modules/controlcenter/network/WirelessPane.qml b/modules/controlcenter/network/WirelessPane.qml new file mode 100644 index 0000000..a23b6e8 --- /dev/null +++ b/modules/controlcenter/network/WirelessPane.qml @@ -0,0 +1,155 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.effects +import qs.components.containers +import qs.config +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + WirelessList { + 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 + + session: root.session + } + + 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" + + Loader { + id: loader + + property var pane: root.session.network.active + property string paneId: pane ? (pane.ssid || pane.bssid || "") : "" + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + opacity: 1 + scale: 1 + transformOrigin: Item.Center + + clip: false + asynchronous: true + sourceComponent: pane ? details : 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 + } + Anim { + target: loader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + + onPaneChanged: { + paneId = pane ? (pane.ssid || pane.bssid || "") : ""; + } + } + } + + InnerBorder { + id: rightBorder + + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: settings + + StyledFlickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + WirelessSettings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } + } + } + + Component { + id: details + + WirelessDetails { + session: root.session + } + } + } + + WirelessPasswordDialog { + anchors.fill: parent + session: root.session + z: 1000 + } + + component Anim: NumberAnimation { + target: loader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } +} + 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(); + } +} + diff --git a/modules/controlcenter/network/WirelessSettings.qml b/modules/controlcenter/network/WirelessSettings.qml new file mode 100644 index 0000000..7890099 --- /dev/null +++ b/modules/controlcenter/network/WirelessSettings.qml @@ -0,0 +1,82 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "wifi" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Network settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("WiFi status") + description: qsTr("General WiFi settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("WiFi enabled") + checked: Network.wifiEnabled + toggle.onToggled: { + Network.enableWifi(checked); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Network information") + description: qsTr("Current network connection") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Connected network") + value: Network.active ? Network.active.ssid : qsTr("Not connected") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Signal strength") + value: Network.active ? qsTr("%1%").arg(Network.active.strength) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Security") + value: Network.active ? (Network.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") + } + } +} + -- cgit v1.2.3-freya