import QtQuick import QtQuick.Layouts import ZShell.Internal import qs.Modules.Resources import qs.Helpers import qs.Components import qs.Config CustomRect { id: root required property Wrapper wrapper color: DynamicColors.tPalette.m3surfaceContainer implicitHeight: 220 implicitWidth: 300 radius: Appearance.rounding.large - Appearance.padding.normal Ref { service: NetworkUsage } ColumnLayout { id: layout anchors.bottomMargin: Appearance.padding.larger anchors.fill: parent anchors.margins: Appearance.padding.large spacing: 0 RowLayout { spacing: Appearance.spacing.small MaterialIcon { color: DynamicColors.palette.m3primary text: "swap_vert" } CustomText { text: qsTr("Network") } } Item { Layout.bottomMargin: Appearance.spacing.small Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: Appearance.spacing.larger SparklineItem { id: sparkline property bool initialized: false property real smoothMax: targetMax property real targetMax: 1024 anchors.fill: parent historyLength: NetworkUsage.historyLength line1: NetworkUsage.uploadBuffer // qmllint disable missing-type line1Color: DynamicColors.palette.m3secondary line1FillAlpha: 0.15 line2: NetworkUsage.downloadBuffer // qmllint disable missing-type line2Color: DynamicColors.palette.m3tertiary line2FillAlpha: 0.2 maxValue: smoothMax slideProgress: 1 Behavior on smoothMax { enabled: sparkline.initialized Anim { } } Component.onCompleted: { sparkline.targetMax = Math.max(NetworkUsage.downloadBuffer.maximum, NetworkUsage.uploadBuffer.maximum, 1024); sparkline.smoothMax = Qt.binding(() => sparkline.targetMax); sparkline.initialized = true; } Connections { function onValuesChanged(): void { sparkline.targetMax = Math.max(NetworkUsage.downloadBuffer.maximum, NetworkUsage.uploadBuffer.maximum, 1024); slideAnim.restart(); } target: NetworkUsage.downloadBuffer } NumberAnimation { id: slideAnim duration: Config.dashboard.resourceUpdateInterval easing.type: Easing.Linear from: 0 property: "slideProgress" target: sparkline to: 1 } } // "Collecting data" placeholder CustomText { anchors.centerIn: parent color: DynamicColors.palette.m3outline text: qsTr("Collecting data...") visible: NetworkUsage.downloadBuffer.count < 2 } } // Download row RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.small MaterialIcon { color: DynamicColors.palette.m3tertiary text: "download" } CustomText { color: DynamicColors.palette.m3onSurfaceVariant text: qsTr("Download") } Item { Layout.fillWidth: true } CustomText { color: DynamicColors.palette.m3tertiary text: { const fmt = NetworkUsage.formatBytes(NetworkUsage.downloadSpeed ?? 0); return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}` : "0.0 B/s"; } } } // Upload row RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.small MaterialIcon { color: DynamicColors.palette.m3secondary text: "upload" } CustomText { color: DynamicColors.palette.m3onSurfaceVariant text: qsTr("Upload") } Item { Layout.fillWidth: true } CustomText { color: DynamicColors.palette.m3secondary text: { const fmt = NetworkUsage.formatBytes(NetworkUsage.uploadSpeed ?? 0); return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}` : "0.0 B/s"; } } } // Session totals RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.small MaterialIcon { color: DynamicColors.palette.m3onSurfaceVariant text: "history" } CustomText { color: DynamicColors.palette.m3onSurfaceVariant text: qsTr("Total") } Item { Layout.fillWidth: true } CustomText { color: DynamicColors.palette.m3onSurfaceVariant text: { const down = NetworkUsage.formatBytesTotal(NetworkUsage.downloadTotal ?? 0); const up = NetworkUsage.formatBytesTotal(NetworkUsage.uploadTotal ?? 0); return (down && up) ? `↓${down.value.toFixed(1)}${down.unit} ↑${up.value.toFixed(1)}${up.unit}` : "↓0.0B ↑0.0B"; } } } } }