1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

enhancement: managed NetworkManager ethernet configurations connectio… (#473)

* enhancement: managed NetworkManager ethernet configurations connection from control panel

* server API minimal version
This commit is contained in:
Massimo Branchini
2025-10-17 14:05:42 +02:00
committed by GitHub
parent a804fb849e
commit b21f6e80b3
6 changed files with 497 additions and 31 deletions

View File

@@ -0,0 +1,162 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Modals.Common
import qs.Services
import qs.Widgets
DankModal {
id: root
property bool networkWiredInfoModalVisible: false
property string networkID: ""
property var networkData: null
function showNetworkInfo(id, data) {
networkID = id
networkData = data
networkWiredInfoModalVisible = true
open()
NetworkService.fetchWiredNetworkInfo(data.uuid)
}
function hideDialog() {
networkWiredInfoModalVisible = false
close()
networkID = ""
networkData = null
}
visible: networkWiredInfoModalVisible
width: 600
height: 500
enableShadow: true
onBackgroundClicked: hideDialog()
onVisibleChanged: {
if (!visible) {
networkID = ""
networkData = null
}
}
content: Component {
Item {
anchors.fill: parent
Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
Row {
width: parent.width
Column {
width: parent.width - 40
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Network Information")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledText {
text: `Details for "${networkID}"`
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
elide: Text.ElideRight
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
onClicked: root.hideDialog()
}
}
Rectangle {
id: detailsRect
width: parent.width
height: parent.height - 140
radius: Theme.cornerRadius
color: Theme.surfaceHover
border.color: Theme.outlineStrong
border.width: 1
clip: true
DankFlickable {
anchors.fill: parent
anchors.margins: Theme.spacingM
contentHeight: detailsText.contentHeight
StyledText {
id: detailsText
width: parent.width
text: NetworkService.networkWiredInfoDetails && NetworkService.networkWiredInfoDetails.replace(/\\n/g, '\n') || "No information available"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap
}
}
}
Item {
width: parent.width
height: 40
Rectangle {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: Math.max(70, closeText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: closeArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
StyledText {
id: closeText
anchors.centerIn: parent
text: I18n.tr("Close")
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.hideDialog()
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}
}

View File

@@ -29,6 +29,22 @@ Rectangle {
NetworkService.removeRef()
}
property int currentPreferenceIndex: {
const pref = NetworkService.userPreference
const status = NetworkService.networkStatus
let index = 1
if (pref === "ethernet") {
index = 0
} else if (pref === "wifi") {
index = 1
} else {
index = status === "ethernet" ? 0 : 1
}
return index
}
Row {
id: headerRow
anchors.left: parent.left
@@ -38,7 +54,7 @@ Rectangle {
anchors.rightMargin: Theme.spacingM
anchors.topMargin: Theme.spacingS
height: 40
StyledText {
id: headerText
text: I18n.tr("Network Settings")
@@ -47,32 +63,16 @@ Rectangle {
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: Math.max(0, parent.width - headerText.implicitWidth - preferenceControls.width - Theme.spacingM)
height: parent.height
}
DankButtonGroup {
id: preferenceControls
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.ethernetConnected
property int currentPreferenceIndex: {
const pref = NetworkService.userPreference
const status = NetworkService.networkStatus
let index = 1
if (pref === "ethernet") {
index = 0
} else if (pref === "wifi") {
index = 1
} else {
index = status === "ethernet" ? 0 : 1
}
return index
}
visible: true
model: ["Ethernet", "WiFi"]
currentIndex: currentPreferenceIndex
@@ -92,7 +92,7 @@ Rectangle {
anchors.right: parent.right
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: NetworkService.wifiToggling
visible: currentPreferenceIndex === 1 && NetworkService.wifiToggling
height: visible ? 80 : 0
Column {
@@ -131,7 +131,7 @@ Rectangle {
anchors.right: parent.right
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: !NetworkService.wifiEnabled && !NetworkService.wifiToggling
visible: currentPreferenceIndex === 1 && !NetworkService.wifiEnabled && !NetworkService.wifiToggling
height: visible ? 120 : 0
Column {
@@ -179,7 +179,179 @@ Rectangle {
cursorShape: Qt.PointingHandCursor
onClicked: NetworkService.toggleWifiRadio()
}
}
}
}
DankFlickable {
id: wiredContent
anchors.top: headerRow.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: currentPreferenceIndex === 0
contentHeight: wiredColumn.height
clip: true
Column {
id: wiredColumn
width: parent.width
spacing: Theme.spacingS
Repeater {
model: sortedNetworks
property var sortedNetworks: {
const currentUuid = NetworkService.ethernetConnectionUuid
const networks = NetworkService.wiredConnections
let sorted = [...networks]
sorted.sort((a, b) => {
if (a.isActive && !b.isActive) return -1
if (!a.isActive && b.isActive) return 1
return a.id.localeCompare(b.id)
})
return sorted
}
delegate: Rectangle {
required property var modelData
required property int index
width: parent.width
height: 50
radius: Theme.cornerRadius
color: wiredNetworkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
border.color: Theme.primary
border.width: 0
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingM
spacing: Theme.spacingS
DankIcon {
name: "lan"
size: Theme.iconSize - 4
color: modelData.isActive ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: 200
StyledText {
text: modelData.id || "Unknown Config"
font.pixelSize: Theme.fontSizeMedium
color: modelData.isActive ? Theme.primary : Theme.surfaceText
font.weight: modelData.isActive ? Font.Medium : Font.Normal
elide: Text.ElideRight
width: parent.width
}
}
}
DankActionButton {
id: wiredOptionsButton
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
iconName: "more_horiz"
buttonSize: 28
onClicked: {
if (wiredNetworkContextMenu.visible) {
wiredNetworkContextMenu.close()
} else {
wiredNetworkContextMenu.currentID = modelData.id
wiredNetworkContextMenu.currentUUID = modelData.uuid
wiredNetworkContextMenu.currentConnected = modelData.isActive
wiredNetworkContextMenu.popup(wiredOptionsButton, -wiredNetworkContextMenu.width + wiredOptionsButton.width, wiredOptionsButton.height + Theme.spacingXS)
}
}
}
MouseArea {
id: wiredNetworkMouseArea
anchors.fill: parent
anchors.rightMargin: wiredOptionsButton.width + Theme.spacingS
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: function(event) {
if (modelData.uuid !== NetworkService.ethernetConnectionUuid) {
NetworkService.connectToSpecificWiredConfig(modelData.uuid)
}
event.accepted = true
}
}
}
}
}
}
Menu {
id: wiredNetworkContextMenu
width: 150
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
property string currentID: ""
property string currentUUID: ""
property bool currentConnected: false
background: Rectangle {
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.width: 0
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
}
MenuItem {
text: "Activate"
height: !wiredNetworkContextMenu.currentConnected ? 32 : 0
contentItem: StyledText {
text: parent.text
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
leftPadding: Theme.spacingS
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.cornerRadius / 2
}
onTriggered: {
if (!networkContextMenu.currentConnected) {
NetworkService.connectToSpecificWiredConfig(wiredNetworkContextMenu.currentUUID)
}
}
}
MenuItem {
text: I18n.tr("Network Info")
height: wiredNetworkContextMenu.currentConnected ? 32 : 0
contentItem: StyledText {
text: parent.text
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
leftPadding: Theme.spacingS
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.cornerRadius / 2
}
onTriggered: {
let networkData = NetworkService.getWiredNetworkInfo(wiredNetworkContextMenu.currentUUID)
networkWiredInfoModal.showNetworkInfo(wiredNetworkContextMenu.currentID, networkData)
}
}
}
@@ -192,10 +364,10 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
visible: NetworkService.wifiInterface && NetworkService.wifiEnabled && !NetworkService.wifiToggling
visible: currentPreferenceIndex === 1 && NetworkService.wifiEnabled && !NetworkService.wifiToggling
contentHeight: wifiColumn.height
clip: true
Column {
id: wifiColumn
width: parent.width
@@ -282,20 +454,20 @@ Rectangle {
spacing: Theme.spacingXS
StyledText {
text: modelData.ssid === NetworkService.currentWifiSSID ? "Connected" : (modelData.secured ? "Secured" : "Open")
text: modelData.ssid === NetworkService.currentWifiSSID ? "Connected" : (modelData.secured ? "Secured" : "Open")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: modelData.saved ? "Saved" : ""
text: modelData.saved ? "Saved" : ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
visible: text.length > 0
}
StyledText {
text: "• " + modelData.signal + "%"
text: (modelData.saved ? "• " : "") + modelData.signal + "%"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
@@ -449,6 +621,8 @@ Rectangle {
NetworkInfoModal {
id: networkInfoModal
}
NetworkWiredInfoModal {
id: networkWiredInfoModal
}
}

View File

@@ -14,7 +14,7 @@ Singleton {
property bool dmsAvailable: false
property var capabilities: []
property int apiVersion: 0
readonly property int expectedApiVersion: 1
readonly property int expectedApiVersion: 5
property var availablePlugins: []
property var installedPlugins: []
property bool isConnected: false

View File

@@ -20,6 +20,8 @@ Singleton {
property bool ethernetConnected: false
property string ethernetConnectionUuid: ""
property var wiredConnections: []
property string wifiIP: ""
property string wifiInterface: ""
property bool wifiConnected: false
@@ -73,6 +75,10 @@ Singleton {
property string networkInfoSSID: ""
property string networkInfoDetails: ""
property bool networkInfoLoading: false
property string networkWiredInfoUUID: ""
property string networkWiredInfoDetails: ""
property bool networkWiredInfoLoading: false
signal networksUpdated
signal connectionChanged

View File

@@ -20,6 +20,8 @@ Singleton {
property bool ethernetConnected: false
property string ethernetConnectionUuid: ""
property var wiredConnections: []
property string wifiIP: ""
property string wifiInterface: ""
property bool wifiConnected: false
@@ -69,6 +71,10 @@ Singleton {
property string networkInfoSSID: ""
property string networkInfoDetails: ""
property bool networkInfoLoading: false
property string networkWiredInfoUUID: ""
property string networkWiredInfoDetails: ""
property bool networkWiredInfoLoading: false
property int refCount: 0
property bool stateInitialized: false
@@ -179,6 +185,8 @@ Singleton {
ethernetConnected = state.ethernetConnected || false
ethernetConnectionUuid = state.ethernetConnectionUuid || ""
wiredConnections = state.wiredConnections || []
wifiIP = state.wifiIP || ""
wifiInterface = state.wifiDevice || ""
wifiConnected = state.wifiConnected || false
@@ -219,6 +227,31 @@ Singleton {
connectionChanged()
}
function connectToSpecificWiredConfig(uuid) {
if (!networkAvailable || isConnecting) return
isConnecting = true
connectionError = ""
connectionStatus = "connecting"
const params = { uuid: uuid }
DMSService.sendRequest("network.ethernet.connect.config", params, response => {
if (response.error) {
connectionError = response.error
lastConnectionError = response.error
connectionStatus = "failed"
ToastService.showError(`Failed to activate configuration`)
} else {
connectionError = ""
connectionStatus = "connected"
ToastService.showInfo(`Configuration activated`)
}
isConnecting = false
})
}
function scanWifi() {
if (!networkAvailable || isScanning || !wifiEnabled) return
@@ -413,6 +446,61 @@ Singleton {
autoScan = false
autoRefreshEnabled = false
}
function fetchWiredNetworkInfo(uuid) {
if (!networkAvailable) return
networkWiredInfoUUID = uuid
networkWiredInfoLoading = true
networkWiredInfoDetails = "Loading network information..."
DMSService.sendRequest("network.ethernet.info", { uuid: uuid }, response => {
networkWiredInfoLoading = false
if (response.error) {
networkWiredInfoDetails = "Failed to fetch network information"
} else if (response.result) {
formatWiredNetworkInfo(response.result)
}
})
}
function formatWiredNetworkInfo(info) {
let details = ""
if (!info) {
details = "Network information not found or network not available."
} else {
details += "Inteface: " + info.iface + "\\n"
details += "Driver: " + info.driver + "\\n"
details += "MAC Addr: " + info.hwAddr + "\\n"
details += "Speed: " + info.speed + " Mb/s\\n\\n"
details += "IPv4 informations:\\n"
for (const ip4 of info.IPv4s.ips) {
details += " IPv4 address: " + ip4 + "\\n"
}
details += " Gateway: " + info.IPv4s.gateway + "\\n"
details += " DNS: " + info.IPv4s.dns + "\\n"
if (info.IPv6s.ips) {
details += "\\nIPv6 informations:\\n"
for (const ip6 of info.IPv6s.ips) {
details += " IPv6 address: " + ip6 + "\\n"
}
if (info.IPv6s.gateway.length > 0) {
details += " Gateway: " + info.IPv6s.gateway + "\\n"
}
if (info.IPv6s.dns.length > 0) {
details += " DNS: " + info.IPv6s.dns + "\\n"
}
}
}
networkWiredInfoDetails = details
}
function fetchNetworkInfo(ssid) {
if (!networkAvailable) return
@@ -483,6 +571,17 @@ Singleton {
}
}
function getWiredNetworkInfo(uuid) {
const network = wiredConnections.find(n => n.uuid === uuid)
if (!network) {
return null
}
return {
"uuid": uuid,
}
}
function refreshNetworkState() {
if (networkAvailable) {
getState()

View File

@@ -19,6 +19,8 @@ Singleton {
property bool ethernetConnected: activeService?.ethernetConnected ?? false
property string ethernetConnectionUuid: activeService?.ethernetConnectionUuid ?? ""
property var wiredConnections: activeService?.wiredConnections ?? []
property string wifiIP: activeService?.wifiIP ?? ""
property string wifiInterface: activeService?.wifiInterface ?? ""
property bool wifiConnected: activeService?.wifiConnected ?? false
@@ -57,6 +59,10 @@ Singleton {
property string networkInfoSSID: activeService?.networkInfoSSID ?? ""
property string networkInfoDetails: activeService?.networkInfoDetails ?? ""
property bool networkInfoLoading: activeService?.networkInfoLoading ?? false
property string networkWiredInfoUUID: activeService?.networkWiredInfoUUID ?? ""
property string networkWiredInfoDetails: activeService?.networkWiredInfoDetails ?? ""
property bool networkWiredInfoLoading: activeService?.networkWiredInfoLoading ?? false
property int refCount: activeService?.refCount ?? 0
property bool stateInitialized: activeService?.stateInitialized ?? false
@@ -221,6 +227,12 @@ Singleton {
}
}
function fetchWiredNetworkInfo(uuid) {
if (activeService && activeService.fetchWiredNetworkInfo) {
activeService.fetchWiredNetworkInfo(uuid)
}
}
function getNetworkInfo(ssid) {
if (activeService && activeService.getNetworkInfo) {
return activeService.getNetworkInfo(ssid)
@@ -228,9 +240,22 @@ Singleton {
return null
}
function getWiredNetworkInfo(uuid) {
if (activeService && activeService.getWiredNetworkInfo) {
return activeService.getWiredNetworkInfo(uuid)
}
return null
}
function refreshNetworkState() {
if (activeService && activeService.refreshNetworkState) {
activeService.refreshNetworkState()
}
}
function connectToSpecificWiredConfig(uuid) {
if (activeService && activeService.connectToSpecificWiredConfig) {
activeService.connectToSpecificWiredConfig(uuid)
}
}
}