mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-25 14:02:53 -05:00
305 lines
11 KiB
QML
305 lines
11 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
readonly property int historySize: 60
|
|
|
|
property var cpuHistory: []
|
|
property var memoryHistory: []
|
|
property var networkRxHistory: []
|
|
property var networkTxHistory: []
|
|
property var diskReadHistory: []
|
|
property var diskWriteHistory: []
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes < 1024)
|
|
return bytes.toFixed(0) + " B/s";
|
|
if (bytes < 1024 * 1024)
|
|
return (bytes / 1024).toFixed(1) + " KB/s";
|
|
if (bytes < 1024 * 1024 * 1024)
|
|
return (bytes / (1024 * 1024)).toFixed(1) + " MB/s";
|
|
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
|
|
}
|
|
|
|
function addToHistory(arr, val) {
|
|
const newArr = arr.slice();
|
|
newArr.push(val);
|
|
if (newArr.length > historySize)
|
|
newArr.shift();
|
|
return newArr;
|
|
}
|
|
|
|
function sampleData() {
|
|
cpuHistory = addToHistory(cpuHistory, DgopService.cpuUsage);
|
|
memoryHistory = addToHistory(memoryHistory, DgopService.memoryUsage);
|
|
networkRxHistory = addToHistory(networkRxHistory, DgopService.networkRxRate);
|
|
networkTxHistory = addToHistory(networkTxHistory, DgopService.networkTxRate);
|
|
diskReadHistory = addToHistory(diskReadHistory, DgopService.diskReadRate);
|
|
diskWriteHistory = addToHistory(diskWriteHistory, DgopService.diskWriteRate);
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
DgopService.addRef(["cpu", "memory", "network", "disk", "diskmounts", "system"]);
|
|
}
|
|
|
|
Component.onDestruction: {
|
|
DgopService.removeRef(["cpu", "memory", "network", "disk", "diskmounts", "system"]);
|
|
}
|
|
|
|
SystemClock {
|
|
id: sampleClock
|
|
precision: SystemClock.Seconds
|
|
onDateChanged: {
|
|
if (date.getSeconds() % 1 === 0)
|
|
root.sampleData();
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: Theme.spacingM
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: (root.height - Theme.spacingM * 2) / 2
|
|
spacing: Theme.spacingM
|
|
|
|
PerformanceCard {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
title: "CPU"
|
|
icon: "memory"
|
|
value: DgopService.cpuUsage.toFixed(1) + "%"
|
|
subtitle: DgopService.cpuModel || (DgopService.cpuCores + " cores")
|
|
accentColor: Theme.primary
|
|
history: root.cpuHistory
|
|
maxValue: 100
|
|
showSecondary: false
|
|
extraInfo: DgopService.cpuTemperature > 0 ? (DgopService.cpuTemperature.toFixed(0) + "°C") : ""
|
|
extraInfoColor: DgopService.cpuTemperature > 80 ? Theme.error : (DgopService.cpuTemperature > 60 ? Theme.warning : Theme.surfaceVariantText)
|
|
}
|
|
|
|
PerformanceCard {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
title: I18n.tr("Memory")
|
|
icon: "sd_card"
|
|
value: DgopService.memoryUsage.toFixed(1) + "%"
|
|
subtitle: DgopService.formatSystemMemory(DgopService.usedMemoryKB) + " / " + DgopService.formatSystemMemory(DgopService.totalMemoryKB)
|
|
accentColor: Theme.secondary
|
|
history: root.memoryHistory
|
|
maxValue: 100
|
|
showSecondary: false
|
|
extraInfo: DgopService.totalSwapKB > 0 ? ("Swap: " + DgopService.formatSystemMemory(DgopService.usedSwapKB)) : ""
|
|
extraInfoColor: Theme.surfaceVariantText
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: (root.height - Theme.spacingM * 2) / 2
|
|
spacing: Theme.spacingM
|
|
|
|
PerformanceCard {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
title: I18n.tr("Network")
|
|
icon: "swap_horiz"
|
|
value: "↓ " + root.formatBytes(DgopService.networkRxRate)
|
|
subtitle: "↑ " + root.formatBytes(DgopService.networkTxRate)
|
|
accentColor: Theme.info
|
|
history: root.networkRxHistory
|
|
history2: root.networkTxHistory
|
|
maxValue: 0
|
|
showSecondary: true
|
|
extraInfo: ""
|
|
extraInfoColor: Theme.surfaceVariantText
|
|
}
|
|
|
|
PerformanceCard {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
title: I18n.tr("Disk")
|
|
icon: "storage"
|
|
value: "R: " + root.formatBytes(DgopService.diskReadRate)
|
|
subtitle: "W: " + root.formatBytes(DgopService.diskWriteRate)
|
|
accentColor: Theme.warning
|
|
history: root.diskReadHistory
|
|
history2: root.diskWriteHistory
|
|
maxValue: 0
|
|
showSecondary: true
|
|
extraInfo: {
|
|
const rootMount = DgopService.diskMounts.find(m => m.mountpoint === "/");
|
|
if (rootMount) {
|
|
const usedPct = ((rootMount.used || 0) / Math.max(1, rootMount.total || 1) * 100).toFixed(0);
|
|
return "/ " + usedPct + "% used";
|
|
}
|
|
return "";
|
|
}
|
|
extraInfoColor: Theme.surfaceVariantText
|
|
}
|
|
}
|
|
}
|
|
|
|
component PerformanceCard: Rectangle {
|
|
id: card
|
|
|
|
property string title: ""
|
|
property string icon: ""
|
|
property string value: ""
|
|
property string subtitle: ""
|
|
property color accentColor: Theme.primary
|
|
property var history: []
|
|
property var history2: null
|
|
property real maxValue: 100
|
|
property bool showSecondary: false
|
|
property string extraInfo: ""
|
|
property color extraInfoColor: Theme.surfaceVariantText
|
|
|
|
radius: Theme.cornerRadius
|
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
border.color: Theme.outlineLight
|
|
border.width: 1
|
|
|
|
Canvas {
|
|
id: graphCanvas
|
|
anchors.fill: parent
|
|
anchors.margins: 4
|
|
renderStrategy: Canvas.Cooperative
|
|
|
|
property var hist: card.history
|
|
property var hist2: card.history2
|
|
|
|
onHistChanged: requestPaint()
|
|
onHist2Changed: requestPaint()
|
|
onWidthChanged: requestPaint()
|
|
onHeightChanged: requestPaint()
|
|
|
|
onPaint: {
|
|
const ctx = getContext("2d");
|
|
ctx.reset();
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
if (!hist || hist.length < 2)
|
|
return;
|
|
|
|
let max = card.maxValue;
|
|
if (max <= 0) {
|
|
max = 1;
|
|
for (let k = 0; k < hist.length; k++)
|
|
max = Math.max(max, hist[k]);
|
|
if (hist2) {
|
|
for (let l = 0; l < hist2.length; l++)
|
|
max = Math.max(max, hist2[l]);
|
|
}
|
|
max *= 1.1;
|
|
}
|
|
|
|
const c = card.accentColor;
|
|
const grad = ctx.createLinearGradient(0, 0, 0, height);
|
|
grad.addColorStop(0, Qt.rgba(c.r, c.g, c.b, 0.25));
|
|
grad.addColorStop(1, Qt.rgba(c.r, c.g, c.b, 0.02));
|
|
|
|
ctx.fillStyle = grad;
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, height);
|
|
for (let i = 0; i < hist.length; i++) {
|
|
const x = (width / (root.historySize - 1)) * i;
|
|
const y = height - (hist[i] / max) * height * 0.8;
|
|
ctx.lineTo(x, y);
|
|
}
|
|
ctx.lineTo((width / (root.historySize - 1)) * (hist.length - 1), height);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.8);
|
|
ctx.lineWidth = 2;
|
|
ctx.beginPath();
|
|
for (let j = 0; j < hist.length; j++) {
|
|
const px = (width / (root.historySize - 1)) * j;
|
|
const py = height - (hist[j] / max) * height * 0.8;
|
|
j === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
|
}
|
|
ctx.stroke();
|
|
|
|
if (hist2 && hist2.length >= 2 && card.showSecondary) {
|
|
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.4);
|
|
ctx.lineWidth = 1.5;
|
|
ctx.setLineDash([4, 4]);
|
|
ctx.beginPath();
|
|
for (let m = 0; m < hist2.length; m++) {
|
|
const sx = (width / (root.historySize - 1)) * m;
|
|
const sy = height - (hist2[m] / max) * height * 0.8;
|
|
m === 0 ? ctx.moveTo(sx, sy) : ctx.lineTo(sx, sy);
|
|
}
|
|
ctx.stroke();
|
|
ctx.setLineDash([]);
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
spacing: Theme.spacingXS
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
name: card.icon
|
|
size: Theme.iconSize
|
|
color: card.accentColor
|
|
}
|
|
|
|
StyledText {
|
|
text: card.title
|
|
font.pixelSize: Theme.fontSizeLarge
|
|
font.weight: Font.Bold
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
StyledText {
|
|
text: card.extraInfo
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.family: SettingsData.monoFontFamily
|
|
color: card.extraInfoColor
|
|
visible: card.extraInfo.length > 0
|
|
}
|
|
}
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
}
|
|
|
|
StyledText {
|
|
text: card.value
|
|
font.pixelSize: Theme.fontSizeXLarge
|
|
font.family: SettingsData.monoFontFamily
|
|
font.weight: Font.Bold
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
StyledText {
|
|
text: card.subtitle
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.family: SettingsData.monoFontFamily
|
|
color: Theme.surfaceVariantText
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
}
|