diff --git a/shell/applets/SparklineCanvas.qml b/shell/applets/SparklineCanvas.qml index d3e025f..dc68691 100644 --- a/shell/applets/SparklineCanvas.qml +++ b/shell/applets/SparklineCanvas.qml @@ -13,9 +13,14 @@ Canvas { readonly property real _max: maxValue !== null && maxValue !== undefined ? maxValue : (history.length ? Math.max(...history) : 100) readonly property real _min: minValue !== null && minValue !== undefined ? minValue : (history.length ? Math.min(...history) : 0) - // Max x-axis slots (0 = use history.length, right-aligns shorter data) + // Max x-axis slots - bars get narrower as data fills up, then oldest drops off property int maxSamples: 0 + // Logarithmic x-axis: compresses old data on the left, expands recent data on the right. + // logCurve controls strength (0 = linear, 3 = strong compression). Only visual - does not affect data. + property bool logScale: false + property real logCurve: 3 + // Optional background tint using `color` at low opacity property real backgroundTint: 0 // 0 = off, e.g. 0.15 for memory, 0.08 for temperature @@ -47,9 +52,7 @@ Canvas { if (!d.length || (areaMode && d.length < 2)) return; - const samples = maxSamples > 0 ? maxSamples : d.length; - const step = width / samples; - const offset = samples - d.length; + const n = d.length; const lo = _min; const hi = _max; const range = hi - lo || 1; @@ -57,6 +60,20 @@ Canvas { return height - height * ((v - lo) / range); } + // x-axis mapping: returns [x, barWidth] for data index i + const k = logScale ? logCurve : 0; + const expK = k > 0 ? Math.exp(k) - 1 : 0; + function xOf(i) { + if (k > 0) { + const x0 = width * (Math.exp(k * i / n) - 1) / expK; + const x1 = width * (Math.exp(k * (i + 1) / n) - 1) / expK; + return [x0, x1 - x0]; + } + const step = width / (maxSamples > n ? maxSamples : n); + const off = maxSamples > n ? (maxSamples - n) * step : 0; + return [off + i * step, step]; + } + // Background tint if (backgroundTint > 0) { ctx.fillStyle = Qt.rgba(color.r, color.g, color.b, backgroundTint).toString(); @@ -93,22 +110,24 @@ Canvas { if (areaMode) { // Filled area under curve - const xOff = offset * step; const baseY = lo < 0 ? yOf(0) : height; + const [x0] = xOf(0); ctx.beginPath(); - ctx.moveTo(xOff, baseY); - for (let i = 0; i < d.length; i++) { - ctx.lineTo(xOff + i * step, yOf(d[i])); + ctx.moveTo(x0, baseY); + for (let i = 0; i < n; i++) { + const [x] = xOf(i); + ctx.lineTo(x, yOf(d[i])); } - ctx.lineTo(xOff + (d.length - 1) * step, baseY); + const [xLast] = xOf(n - 1); + ctx.lineTo(xLast, baseY); ctx.closePath(); ctx.fillStyle = Qt.rgba(color.r, color.g, color.b, areaOpacity).toString(); ctx.fill(); // Top stroke ctx.beginPath(); - for (let i = 0; i < d.length; i++) { - const x = xOff + i * step; + for (let i = 0; i < n; i++) { + const [x] = xOf(i); if (i === 0) ctx.moveTo(x, yOf(d[i])); else @@ -123,13 +142,14 @@ Canvas { const zy = lo < 0 ? yOf(0) : height; if (!hasCF) ctx.fillStyle = color.toString(); - for (let i = 0; i < d.length; i++) { + for (let i = 0; i < n; i++) { if (hasCF) ctx.fillStyle = colorFunction(d[i]).toString(); + const [bx, bw] = xOf(i); const dy = yOf(d[i]); const barTop = Math.min(dy, zy); const barH = Math.max(1, Math.abs(dy - zy)); - ctx.fillRect((offset + i) * step, barTop, Math.max(1, step - 0.5), barH); + ctx.fillRect(bx, barTop, Math.max(1, bw - 0.5), barH); } } } diff --git a/test/qmllint-baseline.txt b/test/qmllint-baseline.txt index c11e850..cd655d5 100644 --- a/test/qmllint-baseline.txt +++ b/test/qmllint-baseline.txt @@ -14,7 +14,6 @@ shell/applets/NetworkApplet.qml: Unqualified access [unqualified] shell/applets/NotifApplet.qml: Member "_notif" not found on type "QQuickItem" [missing-property] shell/applets/NotifApplet.qml: Member "_type" not found on type "QQuickItem" [missing-property] shell/applets/NotifApplet.qml: Unqualified access [unqualified] -shell/applets/SparklineCanvas.qml: Property "colorFunction" is a var property. It may or may not be a method. Use a regular function instead. [use-proper-function] shell/applets/TemperatureApplet.qml: Unqualified access [unqualified] shell/applets/VolumeApplet.qml: Unqualified access [unqualified] shell/lock/Lock.qml: Unqualified access [unqualified]