import QtQuick import "." as M M.BarSection { id: root spacing: Math.max(1, M.Theme.moduleSpacing - 2) tooltip: "" property int percent: M.SystemStats.memPercent property real usedGb: M.SystemStats.memUsedGb property real totalGb: M.SystemStats.memTotalGb property real availGb: M.SystemStats.memAvailGb property real cachedGb: M.SystemStats.memCachedGb property real buffersGb: M.SystemStats.memBuffersGb function _fmt(gb) { return gb >= 10 ? gb.toFixed(1) + "G" : gb.toFixed(2) + "G"; } property bool _pinned: false readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered readonly property bool _showPanel: _anyHover || _pinned property M.ProcessList _procs: M.ProcessList { sortBy: "mem" active: root._showPanel } on_AnyHoverChanged: { if (_anyHover) _unpinTimer.stop(); else if (_pinned) _unpinTimer.start(); } Timer { id: _unpinTimer interval: 500 onTriggered: root._pinned = false } M.BarIcon { icon: "\uEFC5" anchors.verticalCenter: parent.verticalCenter TapHandler { cursorShape: Qt.PointingHandCursor onTapped: root._pinned = !root._pinned } } M.BarLabel { label: root.percent + "%" minText: "100%" anchors.verticalCenter: parent.verticalCenter TapHandler { cursorShape: Qt.PointingHandCursor onTapped: root._pinned = !root._pinned } } M.HoverPanel { id: hoverPanel showPanel: root._showPanel screen: QsWindow.window?.screen ?? null anchorItem: root accentColor: root.accentColor panelNamespace: "nova-memory" contentWidth: 240 // Header Item { width: parent.width height: 28 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "Memory" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 1 font.family: M.Theme.fontFamily } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._fmt(root.usedGb) + " / " + root._fmt(root.totalGb) color: root.accentColor font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily font.bold: true } } // Usage bar Item { width: parent.width height: 14 Item { id: memBar anchors.left: parent.left anchors.leftMargin: 12 anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter height: 6 Rectangle { anchors.fill: parent color: M.Theme.base02 radius: 3 } // Cached (base0D, behind used) Rectangle { width: parent.width * Math.min(1, (root.usedGb + root.cachedGb) / Math.max(root.totalGb, 0.001)) height: parent.height color: M.Theme.base0D opacity: 0.4 radius: 3 Behavior on width { enabled: root._showPanel NumberAnimation { duration: 200 } } } // Used (accentColor, on top) Rectangle { width: parent.width * Math.min(1, root.usedGb / Math.max(root.totalGb, 0.001)) height: parent.height color: root.accentColor radius: 3 Behavior on width { enabled: root._showPanel NumberAnimation { duration: 200 } } } } } // Memory history sparkline Canvas { id: memSparkline anchors.left: parent.left anchors.leftMargin: 12 anchors.right: parent.right anchors.rightMargin: 12 height: 18 property var _hist: M.SystemStats.memHistory property color _col: root.accentColor on_HistChanged: if (root._showPanel) requestPaint() on_ColChanged: if (root._showPanel) requestPaint() Connections { target: root function on_ShowPanelChanged() { if (root._showPanel) memSparkline.requestPaint(); } } onPaint: { const ctx = getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, width, height); const d = _hist; if (!d.length) return; const bw = width / 30; ctx.fillStyle = Qt.rgba(_col.r, _col.g, _col.b, 0.15).toString(); ctx.fillRect(0, 0, width, height); ctx.fillStyle = _col.toString(); for (let i = 0; i < d.length; i++) { const h = Math.max(1, height * d[i] / 100); ctx.fillRect((30 - d.length + i) * bw, height - h, Math.max(1, bw - 0.5), h); } } } // Breakdown rows Item { width: parent.width height: 18 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "Used" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._fmt(root.usedGb) color: M.Theme.base05 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } } Item { width: parent.width height: 18 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "Cached" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._fmt(root.cachedGb) color: M.Theme.base05 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } } Item { width: parent.width height: 18 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "Available" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._fmt(root.availGb) color: M.Theme.base05 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } } // Process list separator Rectangle { width: parent.width - 16 height: 1 anchors.horizontalCenter: parent.horizontalCenter color: M.Theme.base03 } Item { width: hoverPanel.contentWidth height: 18 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "PROCESS" color: M.Theme.base03 font.pixelSize: M.Theme.fontSize - 3 font.family: M.Theme.fontFamily font.letterSpacing: 1 } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "MEM" color: M.Theme.base03 font.pixelSize: M.Theme.fontSize - 3 font.family: M.Theme.fontFamily font.letterSpacing: 1 } } // Top processes by memory Repeater { model: root._procs.processes delegate: Item { required property var modelData width: hoverPanel.contentWidth height: 20 Text { id: procCmd anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.cmd color: M.Theme.base05 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily elide: Text.ElideRight width: parent.width - 80 } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: modelData.mem.toFixed(1) + "%" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily width: 36 horizontalAlignment: Text.AlignRight } } } Item { width: 1 height: 4 } } }