diff options
Diffstat (limited to 'modules/bar/popouts/Network.qml')
| -rw-r--r-- | modules/bar/popouts/Network.qml | 211 |
1 files changed, 187 insertions, 24 deletions
diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml index f21a92d..0e99613 100644 --- a/modules/bar/popouts/Network.qml +++ b/modules/bar/popouts/Network.qml @@ -12,35 +12,48 @@ import QtQuick.Layouts ColumnLayout { id: root + required property Item wrapper + property string connectingToSsid: "" + property string view: "wireless" // "wireless" or "ethernet" + property var passwordNetwork: null + property bool showPasswordDialog: false spacing: Appearance.spacing.small width: Config.bar.sizes.networkWidth + // Wireless section StyledText { - Layout.topMargin: Appearance.padding.normal + visible: root.view === "wireless" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.padding.normal : 0 Layout.rightMargin: Appearance.padding.small - text: qsTr("Wifi %1").arg(Network.wifiEnabled ? "enabled" : "disabled") + text: qsTr("Wireless") font.weight: 500 } Toggle { + visible: root.view === "wireless" + Layout.preferredHeight: visible ? implicitHeight : 0 label: qsTr("Enabled") - checked: Network.wifiEnabled - toggle.onToggled: Network.enableWifi(checked) + checked: Nmcli.wifiEnabled + toggle.onToggled: Nmcli.enableWifi(checked) } StyledText { - Layout.topMargin: Appearance.spacing.small + visible: root.view === "wireless" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.spacing.small : 0 Layout.rightMargin: Appearance.padding.small - text: qsTr("%1 networks available").arg(Network.networks.length) + text: qsTr("%1 networks available").arg(Nmcli.networks.length) color: Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small } Repeater { + visible: root.view === "wireless" model: ScriptModel { - values: [...Network.networks].sort((a, b) => { + values: [...Nmcli.networks].sort((a, b) => { if (a.active !== b.active) return b.active - a.active; return b.strength - a.strength; @@ -50,10 +63,12 @@ ColumnLayout { RowLayout { id: networkItem - required property Network.AccessPoint modelData + required property Nmcli.AccessPoint modelData readonly property bool isConnecting: root.connectingToSsid === modelData.ssid readonly property bool loading: networkItem.isConnecting + visible: root.view === "wireless" + Layout.preferredHeight: visible ? implicitHeight : 0 Layout.fillWidth: true Layout.rightMargin: Appearance.padding.small spacing: Appearance.spacing.small @@ -96,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) @@ -111,20 +124,32 @@ ColumnLayout { StateLayer { color: networkItem.modelData.active ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - disabled: networkItem.loading || !Network.wifiEnabled + disabled: networkItem.loading || !Nmcli.wifiEnabled function onClicked(): void { if (networkItem.modelData.active) { - Network.disconnectFromNetwork(); + Nmcli.disconnectFromNetwork(); } else { root.connectingToSsid = networkItem.modelData.ssid; - Network.connectToNetwork(networkItem.modelData.ssid, ""); + 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 } } } MaterialIcon { - id: connectIcon + id: wirelessConnectIcon anchors.centerIn: parent animate: true @@ -142,7 +167,9 @@ ColumnLayout { } StyledRect { - Layout.topMargin: Appearance.spacing.small + visible: root.view === "wireless" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.spacing.small : 0 Layout.fillWidth: true implicitHeight: rescanBtn.implicitHeight + Appearance.padding.small * 2 @@ -151,10 +178,10 @@ ColumnLayout { StateLayer { color: Colours.palette.m3onPrimaryContainer - disabled: Network.scanning || !Network.wifiEnabled + disabled: Nmcli.scanning || !Nmcli.wifiEnabled function onClicked(): void { - Network.rescanWifi(); + Nmcli.rescanWifi(); } } @@ -163,17 +190,19 @@ ColumnLayout { anchors.centerIn: parent spacing: Appearance.spacing.small - opacity: Network.scanning ? 0 : 1 + opacity: Nmcli.scanning ? 0 : 1 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 } @@ -188,26 +217,160 @@ ColumnLayout { strokeWidth: Appearance.padding.small / 2 bgColour: "transparent" implicitHeight: parent.implicitHeight - Appearance.padding.smaller * 2 - running: Network.scanning + running: Nmcli.scanning + } + } + + // Ethernet section + StyledText { + visible: root.view === "ethernet" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.padding.normal : 0 + Layout.rightMargin: Appearance.padding.small + text: qsTr("Ethernet") + font.weight: 500 + } + + StyledText { + visible: root.view === "ethernet" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.topMargin: visible ? Appearance.spacing.small : 0 + Layout.rightMargin: Appearance.padding.small + text: qsTr("%1 devices available").arg(Nmcli.ethernetDevices.length) + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + } + + Repeater { + visible: root.view === "ethernet" + model: ScriptModel { + values: [...Nmcli.ethernetDevices].sort((a, b) => { + if (a.connected !== b.connected) + return b.connected - a.connected; + return (a.interface || "").localeCompare(b.interface || ""); + }).slice(0, 8) + } + + RowLayout { + id: ethernetItem + + required property var modelData + readonly property bool loading: false + + visible: root.view === "ethernet" + Layout.preferredHeight: visible ? implicitHeight : 0 + Layout.fillWidth: true + Layout.rightMargin: Appearance.padding.small + spacing: Appearance.spacing.small + + opacity: 0 + scale: 0.7 + + Component.onCompleted: { + opacity = 1; + scale = 1; + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + MaterialIcon { + text: "cable" + color: ethernetItem.modelData.connected ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant + } + + StyledText { + Layout.leftMargin: Appearance.spacing.small / 2 + Layout.rightMargin: Appearance.spacing.small / 2 + Layout.fillWidth: true + text: ethernetItem.modelData.interface || qsTr("Unknown") + elide: Text.ElideRight + font.weight: ethernetItem.modelData.connected ? 500 : 400 + color: ethernetItem.modelData.connected ? Colours.palette.m3primary : Colours.palette.m3onSurface + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: connectIcon.implicitHeight + Appearance.padding.small + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primary, ethernetItem.modelData.connected ? 1 : 0) + + CircularIndicator { + anchors.fill: parent + running: ethernetItem.loading + } + + StateLayer { + color: ethernetItem.modelData.connected ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + disabled: ethernetItem.loading + + function onClicked(): void { + if (ethernetItem.modelData.connected && ethernetItem.modelData.connection) { + Nmcli.disconnectEthernet(ethernetItem.modelData.connection, () => {}); + } else { + Nmcli.connectEthernet(ethernetItem.modelData.connection || "", ethernetItem.modelData.interface || "", () => {}); + } + } + } + + MaterialIcon { + id: connectIcon + + anchors.centerIn: parent + animate: true + text: ethernetItem.modelData.connected ? "link_off" : "link" + color: ethernetItem.modelData.connected ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + + opacity: ethernetItem.loading ? 0 : 1 + + Behavior on opacity { + Anim {} + } + } + } } } - // Reset connecting state when network changes Connections { - target: Network + target: Nmcli function onActiveChanged(): void { - if (Network.active && root.connectingToSsid === Network.active.ssid) { + 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) { + root.showPasswordDialog = false; + root.passwordNetwork = null; + if (root.wrapper.currentName === "wirelesspassword") { + root.wrapper.currentName = "network"; + } + } } } function onScanningChanged(): void { - if (!Network.scanning) + if (!Nmcli.scanning) scanIcon.rotation = 0; } } + Connections { + target: root.wrapper + function onCurrentNameChanged(): void { + // Clear password network when leaving password dialog + if (root.wrapper.currentName !== "wirelesspassword" && root.showPasswordDialog) { + root.showPasswordDialog = false; + root.passwordNetwork = null; + } + } + } + component Toggle: RowLayout { required property string label property alias checked: toggle.checked |