diff --git a/Modules/Settings/TopBarTab.qml b/Modules/Settings/TopBarTab.qml index bcaf4d4c..7172f0a9 100644 --- a/Modules/Settings/TopBarTab.qml +++ b/Modules/Settings/TopBarTab.qml @@ -132,6 +132,14 @@ Item { "description": "Visual divider between widgets", "icon": "remove", "enabled": true + }, + { + "id": "network_speed_monitor", + "text": "Network Speed Monitor", + "description": "Network download and upload speed display", + "icon": "network_check", + "warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined, + "enabled": DgopService.dgopAvailable }] property var defaultLeftWidgets: [{ "id": "launcherButton", diff --git a/Modules/Settings/WidgetsTab.qml b/Modules/Settings/WidgetsTab.qml index 5c4c7b53..abb2b70b 100644 --- a/Modules/Settings/WidgetsTab.qml +++ b/Modules/Settings/WidgetsTab.qml @@ -131,6 +131,13 @@ Item { "description": "Visual divider between widgets", "icon": "remove", "enabled": true + }, { + "id": "network_speed_monitor", + "text": "Network Speed Monitor", + "description": "Network download and upload speed display", + "icon": "network_check", + "warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined, + "enabled": DgopService.dgopAvailable }] property var defaultLeftWidgets: [{ "id": "launcherButton", diff --git a/Modules/TopBar/NetworkMonitor.qml b/Modules/TopBar/NetworkMonitor.qml new file mode 100644 index 00000000..e9c4c74f --- /dev/null +++ b/Modules/TopBar/NetworkMonitor.qml @@ -0,0 +1,115 @@ +import QtQuick +import QtQuick.Controls +import qs.Common +import qs.Services +import qs.Widgets +import qs.Modules.ProcessList + +Rectangle { + id: root + + property int availableWidth: 400 + readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2 + readonly property int maxNormalWidth: 456 + + function formatNetworkSpeed(bytesPerSec) { + if (bytesPerSec < 1024) + return bytesPerSec.toFixed(0) + " B/s" + else if (bytesPerSec < 1024 * 1024) + return (bytesPerSec / 1024).toFixed(1) + " KB/s" + else if (bytesPerSec < 1024 * 1024 * 1024) + return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s" + else + return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s" + } + + readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) + + width: contentRow.implicitWidth + horizontalPadding * 2 + height: widgetHeight + radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius + + color: { + if (SettingsData.topBarNoBackground) return "transparent" + const baseColor = networkArea.containsMouse ? Theme.primaryPressed : Theme.secondaryHover + return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, + baseColor.a * Theme.widgetTransparency) + } + + Component.onCompleted: { + DgopService.addRef(["network"]) + } + + Component.onDestruction: { + DgopService.removeRef(["network"]) + } + + MouseArea { + id: networkArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + } + + Row { + id: contentRow + + anchors.centerIn: parent + spacing: Theme.spacingS + + DankIcon { + name: "network_check" + size: Theme.iconSize - 8 + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + Row { + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + StyledText { + text: "↓" + font.pixelSize: Theme.fontSizeSmall + color: Theme.info + } + + StyledText { + text: DgopService.networkRxRate > 0 ? formatNetworkSpeed( + DgopService.networkRxRate) : "0 B/s" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + } + + Row { + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + StyledText { + text: "↑" + font.pixelSize: Theme.fontSizeSmall + color: Theme.error + } + + StyledText { + text: DgopService.networkTxRate > 0 ? formatNetworkSpeed( + DgopService.networkTxRate) : "0 B/s" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + } + } + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } +} diff --git a/Modules/TopBar/TopBar.qml b/Modules/TopBar/TopBar.qml index 19b50379..22d55a60 100644 --- a/Modules/TopBar/TopBar.qml +++ b/Modules/TopBar/TopBar.qml @@ -335,6 +335,8 @@ PanelWindow { return true case "separator": return true + case "network_speed_monitor": + return DgopService.dgopAvailable default: return false } @@ -382,6 +384,8 @@ PanelWindow { return spacerComponent case "separator": return separatorComponent + case "network_speed_monitor": + return networkComponent default: return null } @@ -945,6 +949,12 @@ PanelWindow { } } + Component { + id: networkComponent + + NetworkMonitor {} + } + Component { id: notificationButtonComponent