Refine network panel connection details
This commit is contained in:
+106
-130
@@ -30,8 +30,8 @@ Panel {
|
|||||||
property string prevIface: ""
|
property string prevIface: ""
|
||||||
property real downloadRate: 0 // bytes/sec
|
property real downloadRate: 0 // bytes/sec
|
||||||
property real uploadRate: 0 // bytes/sec
|
property real uploadRate: 0 // bytes/sec
|
||||||
property int ethernetPhraseIndex: 0
|
property int connectionPhraseIndex: 0
|
||||||
readonly property var ethernetPhrases: [
|
readonly property var connectionPhrases: [
|
||||||
"Wiring bits",
|
"Wiring bits",
|
||||||
"Handling packets",
|
"Handling packets",
|
||||||
"Sorting frames",
|
"Sorting frames",
|
||||||
@@ -40,7 +40,7 @@ Panel {
|
|||||||
"Counting collisions",
|
"Counting collisions",
|
||||||
"Bending light",
|
"Bending light",
|
||||||
]
|
]
|
||||||
readonly property string ethernetPhrase: ethernetPhrases[ethernetPhraseIndex % ethernetPhrases.length]
|
readonly property string connectionPhrase: connectionPhrases[connectionPhraseIndex % connectionPhrases.length]
|
||||||
readonly property bool networkManagerAvailable: Networking.backend === NetworkBackendType.NetworkManager
|
readonly property bool networkManagerAvailable: Networking.backend === NetworkBackendType.NetworkManager
|
||||||
readonly property var networkDevices: Networking.devices ? Networking.devices.values : []
|
readonly property var networkDevices: Networking.devices ? Networking.devices.values : []
|
||||||
readonly property var wifiDevice: findDevice(DeviceType.Wifi)
|
readonly property var wifiDevice: findDevice(DeviceType.Wifi)
|
||||||
@@ -82,11 +82,8 @@ Panel {
|
|||||||
property string focusSection: "dns" // "header" | "dns" | "wifi"
|
property string focusSection: "dns" // "header" | "dns" | "wifi"
|
||||||
property int headerIndex: 0
|
property int headerIndex: 0
|
||||||
readonly property bool canDisconnect: !!connectedWifiNetwork
|
readonly property bool canDisconnect: !!connectedWifiNetwork
|
||||||
// The disconnect button is suppressed on ethernet (see disconnectBtn.visible).
|
readonly property bool headerHasDisconnect: false
|
||||||
// headerActionCount/nav must agree, so the keyboard cursor never lands on a
|
readonly property int headerActionCount: 0
|
||||||
// hidden button.
|
|
||||||
readonly property bool headerHasDisconnect: canDisconnect && info.type !== "ethernet"
|
|
||||||
readonly property int headerActionCount: headerHasDisconnect ? 1 : 0
|
|
||||||
readonly property var dnsProviders: ["DHCP", "Cloudflare", "Google", "Custom"]
|
readonly property var dnsProviders: ["DHCP", "Cloudflare", "Google", "Custom"]
|
||||||
property int dnsIndex: 0
|
property int dnsIndex: 0
|
||||||
|
|
||||||
@@ -272,17 +269,24 @@ Panel {
|
|||||||
syncWifiNetworks()
|
syncWifiNetworks()
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSpeed(mbps) {
|
function formatHeaderSpeed(mbps) {
|
||||||
var v = parseInt(mbps, 10)
|
var v = parseInt(mbps, 10)
|
||||||
if (!v || v < 0) return ""
|
if (!v || v < 0) return ""
|
||||||
if (v >= 1000) return (v / 1000).toFixed(v % 1000 === 0 ? 0 : 1) + " Gbps"
|
if (v >= 1000) return (v / 1000).toFixed(v % 1000 === 0 ? 0 : 1) + "gbit"
|
||||||
return v + " Mbps"
|
return v + "mbit"
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatFreq(mhz) {
|
function formatHeaderFreq(mhz) {
|
||||||
var v = parseFloat(mhz)
|
var v = parseFloat(mhz)
|
||||||
if (!v) return ""
|
if (!v) return ""
|
||||||
return (v / 1000).toFixed(1) + " GHz"
|
var ghz = v / 1000
|
||||||
|
return ghz.toFixed(ghz % 1 === 0 ? 0 : 1) + "ghz"
|
||||||
|
}
|
||||||
|
|
||||||
|
function headerDetail() {
|
||||||
|
if (info.type === "ethernet") return formatHeaderSpeed(info.speed || "")
|
||||||
|
if (info.type === "wifi") return formatHeaderFreq(info.freq || "")
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDetails(raw) {
|
function updateDetails(raw) {
|
||||||
@@ -585,21 +589,21 @@ Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: ethernetPhraseTimer
|
id: connectionPhraseTimer
|
||||||
interval: 2800
|
interval: 2800
|
||||||
running: root.opened && root.info.type === "ethernet"
|
running: root.opened && (root.info.type === "ethernet" || (root.info.type === "wifi" && root.canDisconnect))
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: ethernetPhraseSwap.restart()
|
onTriggered: connectionPhraseSwap.restart()
|
||||||
}
|
}
|
||||||
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
id: ethernetPhraseSwap
|
id: connectionPhraseSwap
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
target: heroMeta; property: "opacity"
|
target: heroMeta; property: "opacity"
|
||||||
to: 0.0; duration: 180; easing.type: Easing.OutQuad
|
to: 0.0; duration: 180; easing.type: Easing.OutQuad
|
||||||
}
|
}
|
||||||
ScriptAction {
|
ScriptAction {
|
||||||
script: root.ethernetPhraseIndex = (root.ethernetPhraseIndex + 1) % root.ethernetPhrases.length
|
script: root.connectionPhraseIndex = (root.connectionPhraseIndex + 1) % root.connectionPhrases.length
|
||||||
}
|
}
|
||||||
PropertyAnimation {
|
PropertyAnimation {
|
||||||
target: heroMeta; property: "opacity"
|
target: heroMeta; property: "opacity"
|
||||||
@@ -610,8 +614,8 @@ Panel {
|
|||||||
Connections {
|
Connections {
|
||||||
target: root
|
target: root
|
||||||
function onInfoChanged() {
|
function onInfoChanged() {
|
||||||
if (root.info.type !== "ethernet") {
|
if (!(root.info.type === "ethernet" || (root.info.type === "wifi" && root.canDisconnect))) {
|
||||||
ethernetPhraseSwap.stop()
|
connectionPhraseSwap.stop()
|
||||||
heroMeta.opacity = 1.0
|
heroMeta.opacity = 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -734,7 +738,7 @@ Panel {
|
|||||||
// ---------- Hero: network icon · SSID + state · actions ----------
|
// ---------- Hero: network icon · SSID + state · actions ----------
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
implicitHeight: Math.max(heroIcon.implicitHeight, heroLabels.implicitHeight, headerActions.implicitHeight)
|
implicitHeight: Math.max(heroIcon.implicitHeight, heroLabels.implicitHeight)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: heroIcon
|
id: heroIcon
|
||||||
@@ -769,24 +773,54 @@ Panel {
|
|||||||
id: heroLabels
|
id: heroLabels
|
||||||
anchors.left: heroIcon.right
|
anchors.left: heroIcon.right
|
||||||
anchors.leftMargin: Style.space(14)
|
anchors.leftMargin: Style.space(14)
|
||||||
anchors.right: headerActions.left
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Style.space(10)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
spacing: Style.space(2)
|
spacing: Style.space(2)
|
||||||
|
|
||||||
Text {
|
Row {
|
||||||
id: heroSsid
|
id: heroSsid
|
||||||
width: parent.width
|
width: parent.width
|
||||||
text: {
|
|
||||||
|
readonly property string title: {
|
||||||
if (root.info.type === "wifi") return root.info.ssid || "Wi-Fi"
|
if (root.info.type === "wifi") return root.info.ssid || "Wi-Fi"
|
||||||
if (root.info.type === "ethernet") return "Ethernet"
|
if (root.info.type === "ethernet") return "Ethernet"
|
||||||
return root.info.iface || (root.kind === "disconnected" ? "Disconnected" : "No connection")
|
return root.info.iface || (root.kind === "disconnected" ? "Disconnected" : "No connection")
|
||||||
}
|
}
|
||||||
color: root.bar.foreground
|
readonly property string detail: root.headerDetail()
|
||||||
font.family: root.bar.fontFamily
|
|
||||||
font.pixelSize: Style.font.title
|
Text {
|
||||||
font.bold: true
|
text: heroSsid.title
|
||||||
elide: Text.ElideRight
|
width: Math.min(implicitWidth, Math.max(0, parent.width - (heroDetailPill.visible ? heroDetailPill.implicitWidth + Style.space(8) : 0)))
|
||||||
|
color: root.bar.foreground
|
||||||
|
font.family: root.bar.fontFamily
|
||||||
|
font.pixelSize: Style.font.title
|
||||||
|
font.bold: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: Math.max(0, parent.width - parent.children[0].width - heroDetailPill.implicitWidth); height: 1 }
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: heroDetailPill
|
||||||
|
visible: heroSsid.detail !== ""
|
||||||
|
implicitWidth: heroDetail.implicitWidth + Style.space(10)
|
||||||
|
implicitHeight: heroDetail.implicitHeight + Style.space(4)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Style.normalBorderFor(root.bar.foreground)
|
||||||
|
border.width: Style.normalBorderWidth
|
||||||
|
radius: Style.cornerRadius
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: heroDetail
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: heroSsid.detail
|
||||||
|
color: Qt.darker(root.bar.foreground, 1.4)
|
||||||
|
font.family: root.bar.fontFamily
|
||||||
|
font.pixelSize: Style.font.body
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -794,11 +828,11 @@ Panel {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
text: {
|
text: {
|
||||||
if (root.info.type === "wifi") {
|
if (root.info.type === "wifi") {
|
||||||
if (root.canDisconnect) return "CONNECTED"
|
if (root.canDisconnect) return root.connectionPhrase.toUpperCase()
|
||||||
if (root.kind === "disconnected") return "NOT CONNECTED"
|
if (root.kind === "disconnected") return "NOT CONNECTED"
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if (root.info.type === "ethernet") return root.ethernetPhrase.toUpperCase()
|
if (root.info.type === "ethernet") return root.connectionPhrase.toUpperCase()
|
||||||
if (root.kind === "disconnected") return "NOT CONNECTED"
|
if (root.kind === "disconnected") return "NOT CONNECTED"
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -812,115 +846,57 @@ Panel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
id: headerActions
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Style.space(4)
|
|
||||||
|
|
||||||
PanelActionButton {
|
|
||||||
id: disconnectBtn
|
|
||||||
// Hide on ethernet — the panel can't disconnect a wired link, and
|
|
||||||
// a wifi-off glyph next to the ethernet hero icon reads as a state
|
|
||||||
// contradiction.
|
|
||||||
visible: root.canDisconnect && root.info.type !== "ethernet"
|
|
||||||
enabled: !root.busy
|
|
||||||
hasCursor: root.cursorActive && root.focusSection === "header" && root.headerIndex === 0
|
|
||||||
iconText: ""
|
|
||||||
fontSize: Style.font.heading
|
|
||||||
size: Style.space(30)
|
|
||||||
tooltipText: "Disconnect"
|
|
||||||
foreground: root.bar.foreground
|
|
||||||
hoverColor: root.bar.urgent
|
|
||||||
fontFamily: root.bar.fontFamily
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
onHovered: function(h) {
|
|
||||||
if (!h) return
|
|
||||||
root.cursorActive = true
|
|
||||||
root.focusSection = "header"
|
|
||||||
root.headerIndex = 0
|
|
||||||
}
|
|
||||||
onClicked: root.disconnect(root.connectedWifiNetwork)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection details: IP, gateway, link speed, etc. Two equal-width
|
// Connection details: transfer metrics first, then IP/Gateway.
|
||||||
// columns spanning the panel — matches the Power panel's stats grid.
|
Column {
|
||||||
Row {
|
|
||||||
visible: !!root.info.iface
|
visible: !!root.info.iface
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Style.space(20)
|
spacing: Style.spacing.labelGap
|
||||||
|
|
||||||
Column {
|
Row {
|
||||||
width: (parent.width - parent.spacing) / 2
|
visible: root.info.rx_bytes !== undefined
|
||||||
spacing: Style.spacing.labelGap
|
width: parent.width
|
||||||
InfoPair {
|
spacing: Style.space(20)
|
||||||
visible: !!root.info.ip
|
|
||||||
label: "IP"
|
Column {
|
||||||
value: root.info.ip || ""
|
width: (parent.width - parent.spacing) / 2
|
||||||
copyable: true
|
spacing: Style.spacing.labelGap
|
||||||
tooltipText: "Copy IP"
|
InfoPair { label: "Receiving"; value: root.formatRate(root.downloadRate) }
|
||||||
|
InfoPair { label: "Downloaded"; value: root.formatBytes(parseFloat(root.info.rx_bytes || "0")) }
|
||||||
}
|
}
|
||||||
InfoPair {
|
|
||||||
visible: !!root.info.gateway
|
Column {
|
||||||
label: "Gateway"
|
width: (parent.width - parent.spacing) / 2
|
||||||
value: root.info.gateway || ""
|
spacing: Style.spacing.labelGap
|
||||||
copyable: true
|
InfoPair { label: "Sending"; value: root.formatRate(root.uploadRate) }
|
||||||
tooltipText: "Copy gateway"
|
InfoPair { label: "Uploaded"; value: root.formatBytes(parseFloat(root.info.tx_bytes || "0")) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Row {
|
||||||
width: (parent.width - parent.spacing) / 2
|
width: parent.width
|
||||||
spacing: Style.spacing.labelGap
|
spacing: Style.space(20)
|
||||||
|
Column {
|
||||||
// Ethernet details
|
width: (parent.width - parent.spacing) / 2
|
||||||
InfoPair {
|
InfoPair {
|
||||||
visible: root.info.type === "ethernet" && !!root.info.speed
|
visible: !!root.info.ip
|
||||||
label: "Link"
|
label: "IP Address"
|
||||||
value: root.formatSpeed(root.info.speed || "")
|
value: root.info.ip || ""
|
||||||
|
copyable: true
|
||||||
|
tooltipText: "Copy IP"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Column {
|
||||||
// Wi-Fi details
|
width: (parent.width - parent.spacing) / 2
|
||||||
InfoPair {
|
InfoPair {
|
||||||
visible: root.info.type === "wifi" && !!root.info.freq
|
visible: !!root.info.gateway
|
||||||
label: "Band"
|
label: "Gateway"
|
||||||
value: root.formatFreq(root.info.freq || "")
|
value: root.info.gateway || ""
|
||||||
|
copyable: true
|
||||||
|
tooltipText: "Copy gateway"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InfoPair {
|
|
||||||
visible: root.info.type === "wifi" && !!root.info.bitrate
|
|
||||||
label: "Link"
|
|
||||||
value: root.info.bitrate || ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelSeparator {
|
|
||||||
visible: !!root.info.iface && root.info.rx_bytes !== undefined
|
|
||||||
foreground: root.bar.foreground
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throughput: instantaneous rate (from sample-to-sample delta) plus
|
|
||||||
// cumulative bytes since the interface came up.
|
|
||||||
Row {
|
|
||||||
visible: !!root.info.iface && root.info.rx_bytes !== undefined
|
|
||||||
width: parent.width
|
|
||||||
spacing: Style.space(20)
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: (parent.width - parent.spacing) / 2
|
|
||||||
spacing: Style.spacing.labelGap
|
|
||||||
InfoPair { label: "Receiving"; value: root.formatRate(root.downloadRate) }
|
|
||||||
InfoPair { label: "Downloaded"; value: root.formatBytes(parseFloat(root.info.rx_bytes || "0")) }
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: (parent.width - parent.spacing) / 2
|
|
||||||
spacing: Style.spacing.labelGap
|
|
||||||
InfoPair { label: "Sending"; value: root.formatRate(root.uploadRate) }
|
|
||||||
InfoPair { label: "Uploaded"; value: root.formatBytes(parseFloat(root.info.tx_bytes || "0")) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user