summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorATMDA <atdma2600@gmail.com>2025-11-10 10:00:04 -0500
committerATMDA <atdma2600@gmail.com>2025-11-10 10:00:04 -0500
commit1debf488ee1ed24763a01c2e1bf5c3f4119de18f (patch)
tree3eed720e275da1acef4af648640fb52c6c7d6f32
parentlauncher: python execution like calculator functionality (diff)
downloadcaelestia-shell-1debf488ee1ed24763a01c2e1bf5c3f4119de18f.tar.gz
caelestia-shell-1debf488ee1ed24763a01c2e1bf5c3f4119de18f.tar.bz2
caelestia-shell-1debf488ee1ed24763a01c2e1bf5c3f4119de18f.zip
controlcenter: ethernet panel (debug)
-rw-r--r--modules/controlcenter/NavRail.qml9
-rw-r--r--modules/controlcenter/Panes.qml14
-rw-r--r--modules/controlcenter/Session.qml7
-rw-r--r--modules/controlcenter/ethernet/EthernetDetails.qml204
-rw-r--r--modules/controlcenter/ethernet/EthernetList.qml262
-rw-r--r--modules/controlcenter/ethernet/EthernetPane.qml156
-rw-r--r--modules/controlcenter/ethernet/EthernetSettings.qml155
-rw-r--r--services/Network.qml183
8 files changed, 983 insertions, 7 deletions
diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml
index 96bbb65..b4fbf94 100644
--- a/modules/controlcenter/NavRail.qml
+++ b/modules/controlcenter/NavRail.qml
@@ -158,8 +158,13 @@ Item {
NavItem {
Layout.topMargin: Appearance.spacing.large * 2
- icon: "network_manage"
- label: "network"
+ icon: "cable"
+ label: "ethernet"
+ }
+
+ NavItem {
+ icon: "wifi"
+ label: "wireless"
}
NavItem {
diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml
index 5b1039c..4f4a337 100644
--- a/modules/controlcenter/Panes.qml
+++ b/modules/controlcenter/Panes.qml
@@ -1,5 +1,6 @@
pragma ComponentBehavior: Bound
+import "ethernet"
import "bluetooth"
import "network"
import "audio"
@@ -26,27 +27,34 @@ ClippingRectangle {
Pane {
index: 0
- sourceComponent: NetworkPane {
+ sourceComponent: EthernetPane {
session: root.session
}
}
Pane {
index: 1
- sourceComponent: BtPane {
+ sourceComponent: NetworkPane {
session: root.session
}
}
Pane {
index: 2
- sourceComponent: AudioPane {
+ sourceComponent: BtPane {
session: root.session
}
}
Pane {
index: 3
+ sourceComponent: AudioPane {
+ session: root.session
+ }
+ }
+
+ Pane {
+ index: 4
sourceComponent: AppearancePane {
session: root.session
}
diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml
index 4ac09a4..f7c07e4 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: ["network", "bluetooth", "audio", "appearance"]
+ readonly property list<string> panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance"]
required property var root
property bool floating: false
@@ -12,6 +12,7 @@ QtObject {
readonly property Bt bt: Bt {}
readonly property Network network: Network {}
+ readonly property Ethernet ethernet: Ethernet {}
onActiveChanged: activeIndex = panes.indexOf(active)
onActiveIndexChanged: active = panes[activeIndex]
@@ -29,4 +30,8 @@ QtObject {
property bool showPasswordDialog: false
property var pendingNetwork
}
+
+ component Ethernet: QtObject {
+ property var active
+ }
}
diff --git a/modules/controlcenter/ethernet/EthernetDetails.qml b/modules/controlcenter/ethernet/EthernetDetails.qml
new file mode 100644
index 0000000..9be3ddc
--- /dev/null
+++ b/modules/controlcenter/ethernet/EthernetDetails.qml
@@ -0,0 +1,204 @@
+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 device: session.ethernet.active
+
+ 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
+
+ MaterialIcon {
+ Layout.alignment: Qt.AlignHCenter
+ animate: true
+ text: "cable"
+ font.pointSize: Appearance.font.size.extraLarge * 3
+ font.bold: true
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ animate: true
+ text: root.device?.interface ?? qsTr("Unknown")
+ font.pointSize: Appearance.font.size.large
+ font.bold: true
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Connection status")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Connection settings for this device")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
+
+ ColumnLayout {
+ id: deviceStatus
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: Appearance.padding.large
+
+ spacing: Appearance.spacing.larger
+
+ Toggle {
+ label: qsTr("Connected")
+ checked: root.device?.connected ?? false
+ toggle.onToggled: {
+ if (checked) {
+ if (root.device?.connection) {
+ Network.connectEthernet(root.device.connection);
+ }
+ } else {
+ if (root.device?.connection) {
+ Network.disconnectEthernet(root.device.connection);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Device properties")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Additional information")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
+
+ ColumnLayout {
+ id: deviceProps
+
+ 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("Interface")
+ }
+
+ StyledText {
+ text: root.device?.interface ?? qsTr("Unknown")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Connection")
+ }
+
+ StyledText {
+ text: root.device?.connection || qsTr("Not connected")
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("State")
+ }
+
+ StyledText {
+ text: root.device?.state ?? qsTr("Unknown")
+ 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/ethernet/EthernetList.qml b/modules/controlcenter/ethernet/EthernetList.qml
new file mode 100644
index 0000000..d239fc6
--- /dev/null
+++ b/modules/controlcenter/ethernet/EthernetList.qml
@@ -0,0 +1,262 @@
+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: !root.session.ethernet.active
+ icon: "settings"
+ accent: "Primary"
+
+ function onClicked(): void {
+ if (root.session.ethernet.active)
+ root.session.ethernet.active = null;
+ else {
+ root.session.ethernet.active = view.model.get(0)?.modelData ?? null;
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: Appearance.spacing.small
+
+ StyledText {
+ text: qsTr("Devices (%1)").arg(Network.ethernetDeviceCount || Network.ethernetDevices.length)
+ font.pointSize: Appearance.font.size.large
+ font.weight: 500
+ }
+ }
+
+ StyledText {
+ text: qsTr("All available ethernet devices")
+ color: Colours.palette.m3outline
+ }
+
+ StyledListView {
+ id: view
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: Network.ethernetDevices
+
+ 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.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
+
+ 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) {
+ Network.disconnectEthernet(modelData.connection);
+ } else if (modelData.connection) {
+ Network.connectEthernet(modelData.connection);
+ }
+ }
+ }
+
+ 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
+ }
+ }
+
+ component ToggleButton: StyledRect {
+ id: toggleBtn
+
+ required property bool toggled
+ property string icon
+ property string label
+ property string accent: "Secondary"
+
+ function onClicked(): void {
+ }
+
+ Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0)
+ implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2
+ implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2
+
+ radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale)
+ color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`]
+
+ StateLayer {
+ id: toggleStateLayer
+
+ color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
+
+ function onClicked(): void {
+ toggleBtn.onClicked();
+ }
+ }
+
+ RowLayout {
+ id: toggleBtnInner
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.normal
+
+ MaterialIcon {
+ id: toggleBtnIcon
+
+ visible: !!text
+ fill: toggleBtn.toggled ? 1 : 0
+ text: toggleBtn.icon
+ color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
+ font.pointSize: Appearance.font.size.large
+
+ Behavior on fill {
+ Anim {}
+ }
+ }
+
+ Loader {
+ asynchronous: true
+ active: !!toggleBtn.label
+ visible: active
+
+ sourceComponent: StyledText {
+ text: toggleBtn.label
+ color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`]
+ }
+ }
+ }
+
+ Behavior on radius {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+
+ Behavior on Layout.preferredWidth {
+ Anim {
+ duration: Appearance.anim.durations.expressiveFastSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/controlcenter/ethernet/EthernetPane.qml b/modules/controlcenter/ethernet/EthernetPane.qml
new file mode 100644
index 0000000..fc3e1c0
--- /dev/null
+++ b/modules/controlcenter/ethernet/EthernetPane.qml
@@ -0,0 +1,156 @@
+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
+
+ EthernetList {
+ 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.ethernet.active
+ property string paneId: pane ? (pane.interface || "") : ""
+
+ 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.interface || "") : "";
+ }
+ }
+ }
+
+ InnerBorder {
+ id: rightBorder
+
+ leftThickness: Appearance.padding.normal / 2
+ }
+
+ Component {
+ id: settings
+
+ StyledFlickable {
+ flickableDirection: Flickable.VerticalFlick
+ contentHeight: settingsInner.height
+
+ EthernetSettings {
+ id: settingsInner
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ session: root.session
+ }
+ }
+ }
+
+ Component {
+ id: details
+
+ EthernetDetails {
+ session: root.session
+ }
+ }
+ }
+
+ component Anim: NumberAnimation {
+ target: loader
+ duration: Appearance.anim.durations.normal / 2
+ easing.type: Easing.BezierSpline
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/modules/controlcenter/ethernet/EthernetSettings.qml b/modules/controlcenter/ethernet/EthernetSettings.qml
new file mode 100644
index 0000000..b780b55
--- /dev/null
+++ b/modules/controlcenter/ethernet/EthernetSettings.qml
@@ -0,0 +1,155 @@
+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: "cable"
+ font.pointSize: Appearance.font.size.extraLarge * 3
+ font.bold: true
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("Ethernet settings")
+ font.pointSize: Appearance.font.size.large
+ font.bold: true
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Ethernet devices")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledText {
+ text: qsTr("Available ethernet devices")
+ color: Colours.palette.m3outline
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: ethernetInfo.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
+
+ ColumnLayout {
+ id: ethernetInfo
+
+ 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("Total devices")
+ }
+
+ StyledText {
+ text: qsTr("%1").arg(Network.ethernetDeviceCount || Network.ethernetDevices.length)
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Connected devices")
+ }
+
+ StyledText {
+ text: qsTr("%1").arg(Network.ethernetDevices.filter(d => d.connected).length)
+ color: Colours.palette.m3outline
+ font.pointSize: Appearance.font.size.small
+ }
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.large
+ text: qsTr("Debug Info")
+ font.pointSize: Appearance.font.size.larger
+ font.weight: 500
+ }
+
+ StyledRect {
+ Layout.fillWidth: true
+ implicitHeight: debugInfo.implicitHeight + Appearance.padding.large * 2
+
+ radius: Appearance.rounding.normal
+ color: Colours.tPalette.m3surfaceContainer
+
+ ColumnLayout {
+ id: debugInfo
+
+ 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("Process running: %1").arg(Network.ethernetProcessRunning ? "Yes" : "No")
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ text: qsTr("List length: %1").arg(Network.ethernetDevices.length)
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ text: qsTr("Device count: %1").arg(Network.ethernetDeviceCount)
+ font.pointSize: Appearance.font.size.small
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.normal
+ text: qsTr("Debug: %1").arg(Network.ethernetDebugInfo || "No info")
+ font.pointSize: Appearance.font.size.small
+ color: Colours.palette.m3outline
+ wrapMode: Text.Wrap
+ Layout.maximumWidth: parent.width
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/services/Network.qml b/services/Network.qml
index 3ceadab..1dee367 100644
--- a/services/Network.qml
+++ b/services/Network.qml
@@ -7,11 +7,24 @@ import QtQuick
Singleton {
id: root
+ Component.onCompleted: {
+ // Trigger ethernet device detection after initialization
+ Qt.callLater(() => {
+ getEthernetDevices();
+ });
+ }
+
readonly property list<AccessPoint> networks: []
readonly property AccessPoint active: networks.find(n => n.active) ?? null
property bool wifiEnabled: true
readonly property bool scanning: rescanProc.running
+ property list<var> ethernetDevices: []
+ readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null
+ property int ethernetDeviceCount: 0
+ property string ethernetDebugInfo: ""
+ property bool ethernetProcessRunning: false
+
function enableWifi(enabled: bool): void {
const cmd = enabled ? "on" : "off";
enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]);
@@ -70,11 +83,27 @@ Singleton {
wifiStatusProc.running = true;
}
+ function getEthernetDevices(): void {
+ getEthernetDevicesProc.running = true;
+ }
+
+
+ function connectEthernet(connectionName: string): void {
+ connectEthernetProc.exec(["nmcli", "connection", "up", connectionName]);
+ }
+
+ function disconnectEthernet(connectionName: string): void {
+ disconnectEthernetProc.exec(["nmcli", "connection", "down", connectionName]);
+ }
+
Process {
running: true
command: ["nmcli", "m"]
stdout: SplitParser {
- onRead: getNetworks.running = true
+ onRead: {
+ getNetworks.running = true;
+ getEthernetDevices();
+ }
}
}
@@ -336,6 +365,158 @@ Singleton {
}
}
+ Process {
+ id: getEthernetDevicesProc
+
+ running: false
+ command: ["nmcli", "-g", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"]
+ environment: ({
+ LANG: "C.UTF-8",
+ LC_ALL: "C.UTF-8"
+ })
+ onRunningChanged: {
+ root.ethernetProcessRunning = running;
+ if (!running) {
+ // Process finished, update debug info
+ Qt.callLater(() => {
+ if (root.ethernetDebugInfo === "" || root.ethernetDebugInfo.includes("Process exited")) {
+ root.ethernetDebugInfo = "Process finished, waiting for output...";
+ }
+ });
+ }
+ }
+ onExited: {
+ Qt.callLater(() => {
+ const outputLength = ethernetStdout.text ? ethernetStdout.text.length : 0;
+ root.ethernetDebugInfo = "Process exited with code: " + exitCode + ", output length: " + outputLength;
+ if (outputLength > 0) {
+ // Output was captured, process it
+ const output = ethernetStdout.text.trim();
+ root.ethernetDebugInfo = "Processing output from onExited, length: " + output.length + "\nOutput: " + output.substring(0, 200);
+ root.processEthernetOutput(output);
+ } else {
+ root.ethernetDebugInfo = "No output captured in onExited";
+ }
+ });
+ }
+ stdout: StdioCollector {
+ id: ethernetStdout
+ onStreamFinished: {
+ const output = text.trim();
+ root.ethernetDebugInfo = "Output received in onStreamFinished! Length: " + output.length + ", First 100 chars: " + output.substring(0, 100);
+
+ if (!output || output.length === 0) {
+ root.ethernetDebugInfo = "No output received (empty)";
+ return;
+ }
+
+ root.processEthernetOutput(output);
+ }
+ }
+ }
+
+ function processEthernetOutput(output: string): void {
+ const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED";
+ const rep = new RegExp("\\\\:", "g");
+ const rep2 = new RegExp(PLACEHOLDER, "g");
+
+ const lines = output.split("\n");
+ root.ethernetDebugInfo = "Processing " + lines.length + " lines";
+
+ const allDevices = lines.map(d => {
+ const dev = d.replace(rep, PLACEHOLDER).split(":");
+ return {
+ interface: dev[0]?.replace(rep2, ":") ?? "",
+ type: dev[1]?.replace(rep2, ":") ?? "",
+ state: dev[2]?.replace(rep2, ":") ?? "",
+ connection: dev[3]?.replace(rep2, ":") ?? ""
+ };
+ });
+
+ root.ethernetDebugInfo = "All devices: " + allDevices.length + ", Types: " + allDevices.map(d => d.type).join(", ");
+
+ const ethernetOnly = allDevices.filter(d => d.type === "ethernet");
+ root.ethernetDebugInfo = "Ethernet devices found: " + ethernetOnly.length;
+
+ const ethernetDevices = ethernetOnly.map(d => {
+ const state = d.state || "";
+ const connected = state === "100 (connected)" || state === "connected" || state.startsWith("connected");
+ return {
+ interface: d.interface,
+ type: d.type,
+ state: state,
+ connection: d.connection,
+ connected: connected,
+ ipAddress: "",
+ gateway: "",
+ dns: [],
+ subnet: "",
+ macAddress: "",
+ speed: ""
+ };
+ });
+
+ root.ethernetDebugInfo = "Ethernet devices processed: " + ethernetDevices.length + ", First device: " + (ethernetDevices[0]?.interface || "none");
+
+ // Update the list - replace the entire array to ensure QML detects the change
+ // Create a new array and assign it to the property
+ const newDevices = [];
+ for (let i = 0; i < ethernetDevices.length; i++) {
+ newDevices.push(ethernetDevices[i]);
+ }
+
+ // Replace the entire list
+ root.ethernetDevices = newDevices;
+
+ // Force QML to detect the change by updating a property
+ root.ethernetDeviceCount = ethernetDevices.length;
+
+ // Force QML to re-evaluate the list by accessing it
+ Qt.callLater(() => {
+ const count = root.ethernetDevices.length;
+ root.ethernetDebugInfo = "Final: Found " + ethernetDevices.length + " devices, List length: " + count + ", Parsed all: " + allDevices.length + ", Output length: " + output.length;
+ });
+ }
+
+
+ Process {
+ id: connectEthernetProc
+
+ onExited: {
+ getEthernetDevices();
+ }
+ stdout: SplitParser {
+ onRead: getEthernetDevices()
+ }
+ stderr: StdioCollector {
+ onStreamFinished: {
+ const error = text.trim();
+ if (error && error.length > 0 && !error.includes("successfully") && !error.includes("Connection activated")) {
+ console.warn("Ethernet connection error:", error);
+ }
+ }
+ }
+ }
+
+ Process {
+ id: disconnectEthernetProc
+
+ onExited: {
+ getEthernetDevices();
+ }
+ stdout: SplitParser {
+ onRead: getEthernetDevices()
+ }
+ stderr: StdioCollector {
+ onStreamFinished: {
+ const error = text.trim();
+ if (error && error.length > 0 && !error.includes("successfully") && !error.includes("disconnected")) {
+ console.warn("Ethernet disconnection error:", error);
+ }
+ }
+ }
+ }
+
component AccessPoint: QtObject {
required property var lastIpcObject
readonly property string ssid: lastIpcObject.ssid