198 lines
6.8 KiB
QML
198 lines
6.8 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
Item {
|
|
id: widget
|
|
|
|
readonly property var activeModules: ["system", "cpu", "memory", "network", "disk", "diskmounts"]
|
|
readonly property var rootMount: {
|
|
const mounts = DgopService.diskMounts || [];
|
|
return mounts.find(mount => mount.mount === "/") || mounts[0] || null;
|
|
}
|
|
readonly property real diskPercent: {
|
|
if (!rootMount || !rootMount.percent)
|
|
return 0;
|
|
|
|
return parseFloat(String(rootMount.percent).replace("%", "")) || 0;
|
|
}
|
|
|
|
function formatRate(bytesPerSecond) {
|
|
const value = bytesPerSecond || 0;
|
|
if (value < 1024)
|
|
return value.toFixed(0) + " B/s";
|
|
if (value < 1024 * 1024)
|
|
return (value / 1024).toFixed(0) + " K/s";
|
|
if (value < 1024 * 1024 * 1024)
|
|
return (value / (1024 * 1024)).toFixed(1) + " M/s";
|
|
return (value / (1024 * 1024 * 1024)).toFixed(1) + " G/s";
|
|
}
|
|
|
|
function formatGiB(megabytes) {
|
|
return ((megabytes || 0) / 1024).toFixed(1) + " GiB";
|
|
}
|
|
|
|
// Stats only get polled while this widget exists.
|
|
Component.onCompleted: DgopService.addRef(widget.activeModules)
|
|
Component.onDestruction: DgopService.removeRef(widget.activeModules)
|
|
|
|
component StatTile: Rectangle {
|
|
id: tile
|
|
|
|
property string icon: ""
|
|
property string label: ""
|
|
property string value: ""
|
|
property string detail: ""
|
|
property real fraction: 0
|
|
property color accent: Theme.primary
|
|
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(Theme.surfaceLight, Theme.popupTransparency)
|
|
border.color: Theme.outlineLight
|
|
border.width: Theme.layerOutlineWidth
|
|
|
|
DankIcon {
|
|
id: tileIcon
|
|
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Theme.spacingM
|
|
anchors.top: parent.top
|
|
anchors.topMargin: Theme.spacingM
|
|
name: tile.icon
|
|
size: Theme.iconSize - 6
|
|
color: tile.accent
|
|
}
|
|
|
|
StyledText {
|
|
anchors.left: tileIcon.right
|
|
anchors.leftMargin: Theme.spacingXS
|
|
anchors.verticalCenter: tileIcon.verticalCenter
|
|
text: tile.label
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
StyledText {
|
|
id: tileValue
|
|
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Theme.spacingM
|
|
anchors.verticalCenter: tileIcon.verticalCenter
|
|
text: tile.value
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
StyledText {
|
|
id: tileDetail
|
|
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: Theme.spacingM
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: Theme.spacingM
|
|
anchors.bottom: tileTrack.top
|
|
anchors.bottomMargin: Theme.spacingXS
|
|
text: tile.detail
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Rectangle {
|
|
id: tileTrack
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
anchors.leftMargin: Theme.spacingM
|
|
anchors.rightMargin: Theme.spacingM
|
|
anchors.bottomMargin: Theme.spacingM
|
|
height: 5
|
|
radius: height / 2
|
|
color: Theme.withAlpha(Theme.outline, 0.4)
|
|
|
|
Rectangle {
|
|
width: parent.width * Math.max(0, Math.min(1, tile.fraction))
|
|
height: parent.height
|
|
radius: parent.radius
|
|
color: tile.accent
|
|
|
|
Behavior on width {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
easing.type: Easing.OutCubic
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Grid {
|
|
anchors.fill: parent
|
|
columns: 2
|
|
rowSpacing: Theme.spacingS
|
|
columnSpacing: Theme.spacingS
|
|
|
|
readonly property real tileWidth: (width - columnSpacing) / 2
|
|
readonly property real tileHeight: (height - rowSpacing) / 2
|
|
|
|
StatTile {
|
|
width: parent.tileWidth
|
|
height: parent.tileHeight
|
|
icon: "memory"
|
|
label: "CPU"
|
|
value: Math.round(DgopService.cpuUsage || 0) + "%"
|
|
detail: DgopService.cpuTemperature > 0 ? DgopService.cpuTemperature.toFixed(0) + "°C" : (DgopService.cpuCores + " cores")
|
|
fraction: (DgopService.cpuUsage || 0) / 100
|
|
accent: DgopService.cpuUsage > 85 ? Theme.error : (DgopService.cpuUsage > 60 ? Theme.warning : Theme.primary)
|
|
}
|
|
|
|
StatTile {
|
|
width: parent.tileWidth
|
|
height: parent.tileHeight
|
|
icon: "developer_board"
|
|
label: "Memory"
|
|
value: Math.round(DgopService.memoryUsage || 0) + "%"
|
|
detail: widget.formatGiB(DgopService.usedMemoryMB) + " / " + widget.formatGiB(DgopService.totalMemoryMB)
|
|
fraction: (DgopService.memoryUsage || 0) / 100
|
|
accent: DgopService.memoryUsage > 85 ? Theme.error : (DgopService.memoryUsage > 60 ? Theme.warning : Theme.primary)
|
|
}
|
|
|
|
StatTile {
|
|
width: parent.tileWidth
|
|
height: parent.tileHeight
|
|
icon: "swap_vert"
|
|
label: "Network"
|
|
value: "↓ " + widget.formatRate(DgopService.networkRxRate)
|
|
detail: "↑ " + widget.formatRate(DgopService.networkTxRate)
|
|
// Scaled against 100 MiB/s so the bar stays readable on fast links.
|
|
fraction: Math.min(1, ((DgopService.networkRxRate || 0) + (DgopService.networkTxRate || 0)) / (100 * 1024 * 1024))
|
|
accent: Theme.primary
|
|
}
|
|
|
|
StatTile {
|
|
width: parent.tileWidth
|
|
height: parent.tileHeight
|
|
icon: "storage"
|
|
label: widget.rootMount ? widget.rootMount.mount : "Disk"
|
|
value: Math.round(widget.diskPercent) + "%"
|
|
detail: "R " + widget.formatRate(DgopService.diskReadRate) + " W " + widget.formatRate(DgopService.diskWriteRate)
|
|
fraction: widget.diskPercent / 100
|
|
accent: widget.diskPercent > 90 ? Theme.error : (widget.diskPercent > 75 ? Theme.warning : Theme.primary)
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
anchors.centerIn: parent
|
|
width: parent.width - Theme.spacingL * 2
|
|
text: "dgop is not available - install it to see system stats."
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
wrapMode: Text.WordWrap
|
|
visible: !DgopService.dgopAvailable
|
|
}
|
|
}
|