summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorATMDA <atdma2600@gmail.com>2025-11-14 11:59:36 -0500
committerATMDA <atdma2600@gmail.com>2025-11-14 11:59:36 -0500
commit540cebdbbdafe51e0f946e7a256320537a332612 (patch)
treea0626e385ceee06f03df7d1506043ce24d944c53 /modules
parentnmcli: refactor to be readable/extensible (diff)
downloadcaelestia-shell-540cebdbbdafe51e0f946e7a256320537a332612.tar.gz
caelestia-shell-540cebdbbdafe51e0f946e7a256320537a332612.tar.bz2
caelestia-shell-540cebdbbdafe51e0f946e7a256320537a332612.zip
controlcenter: merging wireless/ethernet panes (wip/stash)
Diffstat (limited to 'modules')
-rw-r--r--modules/controlcenter/NavRail.qml5
-rw-r--r--modules/controlcenter/Panes.qml7
-rw-r--r--modules/controlcenter/Session.qml2
-rw-r--r--modules/controlcenter/network/NetworkingPane.qml579
-rw-r--r--modules/controlcenter/network/NetworkingSettings.qml106
5 files changed, 698 insertions, 1 deletions
diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml
index ca36048..abe2ea2 100644
--- a/modules/controlcenter/NavRail.qml
+++ b/modules/controlcenter/NavRail.qml
@@ -191,6 +191,11 @@ Item {
icon: "apps"
label: "launcher"
}
+
+ NavItem {
+ icon: "router"
+ label: "networking"
+ }
}
component NavItem: Item {
diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml
index 11e60d3..0958902 100644
--- a/modules/controlcenter/Panes.qml
+++ b/modules/controlcenter/Panes.qml
@@ -76,6 +76,13 @@ ClippingRectangle {
}
}
+ Pane {
+ index: 7
+ sourceComponent: NetworkingPane {
+ session: root.session
+ }
+ }
+
Behavior on y {
Anim {}
}
diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml
index d44898e..1ff9688 100644
--- a/modules/controlcenter/Session.qml
+++ b/modules/controlcenter/Session.qml
@@ -2,7 +2,7 @@ import Quickshell.Bluetooth
import QtQuick
QtObject {
- readonly property list<string> panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher"]
+ readonly property list<string> panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher", "networking"]
required property var root
property bool floating: false
diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml
new file mode 100644
index 0000000..f220960
--- /dev/null
+++ b/modules/controlcenter/network/NetworkingPane.qml
@@ -0,0 +1,579 @@
+pragma ComponentBehavior: Bound
+
+import ".."
+import "../ethernet"
+import "."
+import qs.components
+import qs.components.controls
+import qs.components.effects
+import qs.components.containers
+import qs.services
+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
+
+ // Left pane - networking list with collapsible sections
+ StyledFlickable {
+ 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
+
+ ColumnLayout {
+ id: leftContent
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: Appearance.spacing.normal
+
+ // 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
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ ToggleButton {
+ toggled: Nmcli.wifiEnabled
+ icon: "wifi"
+ accent: "Tertiary"
+
+ onClicked: {
+ Nmcli.toggleWifi(null);
+ }
+ }
+
+ ToggleButton {
+ toggled: Nmcli.scanning
+ icon: "wifi_find"
+ accent: "Secondary"
+
+ 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 = ethernetListView.model.get(0)?.modelData ?? null;
+ } else if (Nmcli.networks.length > 0) {
+ root.session.network.active = wirelessListView.model.get(0)?.modelData ?? null;
+ }
+ }
+ }
+ }
+ }
+
+ CollapsibleSection {
+ id: ethernetListSection
+
+ Layout.fillWidth: true
+ title: qsTr("Ethernet")
+ expanded: true
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ StyledText {
+ text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length)
+ font.pointSize: Appearance.font.size.large
+ font.weight: 500
+ }
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ text: qsTr("All available ethernet devices")
+ color: Colours.palette.m3outline
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: Nmcli.ethernetDevices.length > 0 ? Math.min(400, Math.max(200, Nmcli.ethernetDevices.length * 80)) : 200
+
+ StyledListView {
+ id: ethernetListView
+
+ anchors.fill: parent
+
+ model: Nmcli.ethernetDevices
+
+ spacing: Appearance.spacing.small / 2
+ clip: true
+
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: ethernetListView
+ }
+
+ delegate: StyledRect {
+ required property var modelData
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0)
+ radius: Appearance.rounding.normal
+ border.width: root.session.ethernet.active === modelData ? 1 : 0
+ border.color: Colours.palette.m3primary
+
+ StateLayer {
+ function onClicked(): void {
+ root.session.ethernet.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.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh
+
+ 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
+ }
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ maximumLineCount: 1
+
+ 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
+ }
+
+ StyledRect {
+ implicitWidth: implicitHeight
+ implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2
+
+ 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 || "", () => {});
+ }
+ }
+ }
+
+ MaterialIcon {
+ id: connectIcon
+
+ 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
+ }
+ }
+ }
+ }
+ }
+
+ CollapsibleSection {
+ id: wirelessListSection
+
+ Layout.fillWidth: true
+ title: qsTr("Wireless")
+ expanded: true
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ StyledText {
+ text: qsTr("Networks (%1)").arg(Nmcli.networks.length)
+ font.pointSize: Appearance.font.size.large
+ font.weight: 500
+ }
+
+ StyledText {
+ visible: Nmcli.scanning
+ text: qsTr("Scanning...")
+ color: Colours.palette.m3primary
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ text: qsTr("All available WiFi networks")
+ color: Colours.palette.m3outline
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: Nmcli.networks.length > 0 ? Math.min(400, Math.max(200, Nmcli.networks.length * 80)) : 200
+
+ StyledListView {
+ id: wirelessListView
+
+ anchors.fill: parent
+
+ model: Nmcli.networks
+
+ spacing: Appearance.spacing.small / 2
+ clip: true
+
+ StyledScrollBar.vertical: StyledScrollBar {
+ flickable: wirelessListView
+ }
+
+ 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;
+ // Check if we need to refresh saved connections when selecting a network
+ if (modelData && modelData.ssid) {
+ checkSavedProfileForNetwork(modelData.ssid);
+ }
+ }
+ }
+
+ RowLayout {
+ id: wirelessRowLayout
+
+ 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: wirelessIcon.implicitHeight + Appearance.padding.normal * 2
+
+ radius: Appearance.rounding.normal
+ color: modelData.active ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh
+
+ MaterialIcon {
+ id: wirelessIcon
+
+ 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: wirelessConnectIcon.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) {
+ Nmcli.disconnectFromNetwork();
+ } else {
+ handleWirelessConnect(modelData);
+ }
+ }
+ }
+
+ MaterialIcon {
+ id: wirelessConnectIcon
+
+ anchors.centerIn: parent
+ text: modelData.active ? "link_off" : "link"
+ color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface
+ }
+ }
+ }
+
+ implicitHeight: wirelessRowLayout.implicitHeight + 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"
+
+ // 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: false
+ 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
+ }
+ Anim {
+ target: loader
+ property: "scale"
+ to: 1
+ easing.bezierCurve: Appearance.anim.curves.standardDecel
+ }
+ }
+ }
+ }
+
+ onPaneChanged: {
+ paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "");
+ }
+ }
+ }
+
+ InnerBorder {
+ id: rightBorder
+
+ leftThickness: Appearance.padding.normal / 2
+ }
+
+ Component {
+ id: settings
+
+ StyledFlickable {
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: settingsInner.height
+
+ NetworkingSettings {
+ id: settingsInner
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ session: root.session
+ }
+ }
+ }
+
+ Component {
+ id: ethernetDetails
+
+ EthernetDetails {
+ session: root.session
+ }
+ }
+
+ Component {
+ id: wirelessDetails
+
+ 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
+ }
+
+ function checkSavedProfileForNetwork(ssid: string): void {
+ if (ssid && ssid.length > 0) {
+ 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/NetworkingSettings.qml b/modules/controlcenter/network/NetworkingSettings.qml
new file mode 100644
index 0000000..2475fed
--- /dev/null
+++ b/modules/controlcenter/network/NetworkingSettings.qml
@@ -0,0 +1,106 @@
+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: "router"
+ font.pointSize: Appearance.font.size.extraLarge * 3
+ font.bold: true
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("Networking settings")
+ font.pointSize: Appearance.font.size.large
+ font.bold: true
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("Ethernet")
+ description: qsTr("Ethernet device information")
+ }
+
+ SectionContainer {
+ contentSpacing: Appearance.spacing.small / 2
+
+ PropertyRow {
+ label: qsTr("Total devices")
+ value: qsTr("%1").arg(Nmcli.ethernetDevices.length)
+ }
+
+ PropertyRow {
+ showTopMargin: true
+ label: qsTr("Connected devices")
+ value: qsTr("%1").arg(Nmcli.ethernetDevices.filter(d => d.connected).length)
+ }
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("Wireless")
+ description: qsTr("WiFi network settings")
+ }
+
+ SectionContainer {
+ ToggleRow {
+ label: qsTr("WiFi enabled")
+ checked: Nmcli.wifiEnabled
+ toggle.onToggled: {
+ Nmcli.enableWifi(checked);
+ }
+ }
+ }
+
+ SectionHeader {
+ Layout.topMargin: Appearance.spacing.large
+ title: qsTr("Current connection")
+ description: qsTr("Active network connection information")
+ }
+
+ SectionContainer {
+ contentSpacing: Appearance.spacing.small / 2
+
+ PropertyRow {
+ label: qsTr("Network")
+ value: Nmcli.active ? Nmcli.active.ssid : (Nmcli.activeEthernet ? Nmcli.activeEthernet.interface : qsTr("Not connected"))
+ }
+
+ PropertyRow {
+ showTopMargin: true
+ visible: Nmcli.active !== null
+ label: qsTr("Signal strength")
+ value: Nmcli.active ? qsTr("%1%").arg(Nmcli.active.strength) : qsTr("N/A")
+ }
+
+ PropertyRow {
+ showTopMargin: true
+ visible: Nmcli.active !== null
+ label: qsTr("Security")
+ value: Nmcli.active ? (Nmcli.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A")
+ }
+
+ PropertyRow {
+ showTopMargin: true
+ visible: Nmcli.active !== null
+ label: qsTr("Frequency")
+ value: Nmcli.active ? qsTr("%1 MHz").arg(Nmcli.active.frequency) : qsTr("N/A")
+ }
+ }
+}
+