Files
noctalia-shell/Modules/Panels/SystemStats/SystemStatsPanel.qml
T
2026-03-22 20:01:38 -04:00

404 lines
12 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Modules.MainScreen
import qs.Services.System
import qs.Services.UI
import qs.Widgets
SmartPanel {
id: root
Component.onCompleted: SystemStatService.registerComponent("panel-systemstats")
Component.onDestruction: SystemStatService.unregisterComponent("panel-systemstats")
preferredWidth: Math.round(440 * Style.uiScaleRatio)
panelContent: Item {
id: panelContent
property real contentPreferredHeight: mainColumn.implicitHeight + Style.margin2L
readonly property real cardHeight: 90 * Style.uiScaleRatio
// Get diskPath from bar's SystemMonitor widget if available, otherwise use "/"
readonly property string diskPath: {
const sysMonWidget = BarService.lookupWidget("SystemMonitor");
if (sysMonWidget && sysMonWidget.diskPath) {
return sysMonWidget.diskPath;
}
return "/";
}
ColumnLayout {
id: mainColumn
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
// HEADER
NBox {
Layout.fillWidth: true
implicitHeight: headerRow.implicitHeight + Style.margin2M
RowLayout {
id: headerRow
anchors.fill: parent
anchors.margins: Style.marginM
spacing: Style.marginM
NIcon {
icon: "device-analytics"
pointSize: Style.fontSizeXXL
color: Color.mPrimary
}
NText {
text: I18n.tr("system-monitor.title")
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
color: Color.mOnSurface
Layout.fillWidth: true
}
NIconButton {
icon: "close"
tooltipText: I18n.tr("common.close")
baseSize: Style.baseWidgetSize * 0.8
onClicked: {
root.close();
}
}
}
}
// CPU Card (dual-line: usage % + temperature °C)
NBox {
Layout.fillWidth: true
Layout.preferredHeight: panelContent.cardHeight
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
anchors.bottomMargin: Style.radiusM * 0.5
spacing: Style.marginXS
RowLayout {
Layout.fillWidth: true
spacing: Style.marginXS
NIcon {
icon: "cpu-usage"
pointSize: Style.fontSizeXS
color: Color.mPrimary
}
NText {
text: `${Math.round(SystemStatService.cpuUsage)}% (${SystemStatService.cpuFreq.replace(/[^0-9.]/g, "")} GHz)`
pointSize: Style.fontSizeXS
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
}
NIcon {
icon: "cpu-temperature"
pointSize: Style.fontSizeXS
color: Color.mSecondary
}
NText {
text: `${Math.round(SystemStatService.cpuTemp)}°C`
pointSize: Style.fontSizeXS
color: Color.mSecondary
font.family: Settings.data.ui.fontFixed
Layout.rightMargin: Style.marginS
}
Item {
Layout.fillWidth: true
}
NText {
text: I18n.tr("system-monitor.cpu-usage")
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
}
NGraph {
Layout.fillWidth: true
Layout.fillHeight: true
values: SystemStatService.cpuHistory
values2: SystemStatService.cpuTempHistory
minValue: 0
maxValue: 100
minValue2: Math.max(SystemStatService.cpuTempHistoryMin - 5, 0)
maxValue2: Math.max(SystemStatService.cpuTempHistoryMax + 5, 1)
color: Color.mPrimary
color2: Color.mSecondary
strokeWidth: Math.max(1, Style.uiScaleRatio)
fill: true
fillOpacity: 0.15
updateInterval: SystemStatService.cpuUsageIntervalMs
}
}
}
// Memory Card (single-line + optional swap indicator)
NBox {
Layout.fillWidth: true
Layout.preferredHeight: panelContent.cardHeight
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
anchors.bottomMargin: Style.radiusM * 0.5
spacing: Style.marginXS
RowLayout {
Layout.fillWidth: true
spacing: Style.marginXS
NIcon {
icon: "memory"
pointSize: Style.fontSizeXS
color: Color.mPrimary
}
NText {
text: `${Math.round(SystemStatService.memPercent)}% (${(SystemStatService.memGb).toFixed(1)} GiB)`
pointSize: Style.fontSizeXS
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
}
Item {
Layout.fillWidth: true
}
NText {
text: I18n.tr("common.memory")
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
}
NGraph {
Layout.fillWidth: true
Layout.fillHeight: true
values: SystemStatService.memHistory
minValue: 0
maxValue: 100
color: Color.mPrimary
strokeWidth: Math.max(1, Style.uiScaleRatio)
fill: true
fillOpacity: 0.15
updateInterval: SystemStatService.memIntervalMs
}
}
}
// Network Card (dual-line: RX + TX speeds)
NBox {
Layout.fillWidth: true
Layout.preferredHeight: panelContent.cardHeight
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
anchors.bottomMargin: Style.radiusM * 0.5
spacing: Style.marginXS
RowLayout {
Layout.fillWidth: true
spacing: Style.marginXS
NIcon {
icon: "download-speed"
pointSize: Style.fontSizeXS
color: Color.mPrimary
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed).replace(/([0-9.]+)([A-Za-z]+)/, "$1 $2") + "/s"
pointSize: Style.fontSizeXS
color: Color.mPrimary
font.family: Settings.data.ui.fontFixed
Layout.rightMargin: Style.marginS
}
NIcon {
icon: "upload-speed"
pointSize: Style.fontSizeXS
color: Color.mSecondary
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.txSpeed).replace(/([0-9.]+)([A-Za-z]+)/, "$1 $2") + "/s"
pointSize: Style.fontSizeXS
color: Color.mSecondary
font.family: Settings.data.ui.fontFixed
}
Item {
Layout.fillWidth: true
}
NText {
text: I18n.tr("common.network")
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
}
NGraph {
Layout.fillWidth: true
Layout.fillHeight: true
values: SystemStatService.rxSpeedHistory
values2: SystemStatService.txSpeedHistory
minValue: 0
maxValue: SystemStatService.rxMaxSpeed
minValue2: 0
maxValue2: SystemStatService.txMaxSpeed
color: Color.mPrimary
color2: Color.mSecondary
strokeWidth: Math.max(1, Style.uiScaleRatio)
fill: true
fillOpacity: 0.15
updateInterval: SystemStatService.networkIntervalMs
animateScale: true
}
}
}
// Detailed Stats section
NBox {
Layout.fillWidth: true
implicitHeight: detailsColumn.implicitHeight + Style.margin2M
ColumnLayout {
id: detailsColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Style.marginM
spacing: Style.marginXS
// Load Average
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
visible: SystemStatService.nproc > 0
NIcon {
icon: "cpu-usage"
pointSize: Style.fontSizeM
color: Color.mPrimary
}
NText {
text: I18n.tr("system-monitor.load-average") + ":"
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NText {
text: `${SystemStatService.loadAvg1.toFixed(2)} ${SystemStatService.loadAvg5.toFixed(2)} ${SystemStatService.loadAvg15.toFixed(2)}`
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
}
}
// GPU Temperature (only if available)
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
visible: SystemStatService.gpuAvailable
NIcon {
icon: "gpu-temperature"
pointSize: Style.fontSizeM
color: Color.mPrimary
}
NText {
text: I18n.tr("system-monitor.gpu-temp") + ":"
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NText {
text: `${Math.round(SystemStatService.gpuTemp)}°C`
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
}
}
// Disk usage
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
NIcon {
icon: "storage"
pointSize: Style.fontSizeM
color: Color.mPrimary
}
NText {
text: I18n.tr("system-monitor.disk") + ":"
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NText {
text: {
const usedGb = SystemStatService.diskUsedGb[panelContent.diskPath] || 0;
const sizeGb = SystemStatService.diskSizeGb[panelContent.diskPath] || 0;
const percent = SystemStatService.diskPercents[panelContent.diskPath] || 0;
return `${percent}% (${usedGb.toFixed(1)} / ${sizeGb.toFixed(1)} GB)`;
}
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
elide: Text.ElideMiddle
}
}
// Swap details (only visible if swap is enabled)
RowLayout {
Layout.fillWidth: true
spacing: Style.marginS
visible: SystemStatService.swapTotalGb > 0
NIcon {
icon: "exchange"
pointSize: Style.fontSizeM
color: Color.mPrimary
}
NText {
text: I18n.tr("bar.system-monitor.swap-usage-label") + ":"
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NText {
text: `${(SystemStatService.swapGb).toFixed(1)} / ${(SystemStatService.swapTotalGb).toFixed(1)} GiB`
pointSize: Style.fontSizeXS
color: Color.mOnSurface
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
}
}
}
}
}
}
}