7.3 KiB · text History 35c3541
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami

import "../Grafana.js" as G

// Stat: one tile per series with its reduced value, coloured by
// thresholds the way colorMode asks (the text, the tile, or neither),
// over a faint sparkline when graphMode is "area".
Item {
    id: stat

    property var frame: null

    readonly property var panel: frame ? frame.panel : ({})
    readonly property var options: panel.options || {}
    readonly property string calc: ((options.reduceOptions && options.reduceOptions.calcs) || ["lastNotNull"])[0] || "lastNotNull"
    readonly property string colorMode: options.colorMode || "value"
    readonly property string textMode: options.textMode || "auto"
    readonly property bool sparkline: (options.graphMode || "area") === "area"
    readonly property bool horizontal: options.orientation === "horizontal" || (options.orientation !== "vertical" && width > height * 2.5)

    readonly property var tiles: {
        var out = []
        var list = frame ? frame.series : []
        var tc = Kirigami.Theme.textColor.toString()
        for (var i = 0; i < list.length && i < 24; i++) {
            var s = list[i]
            var cfg = G.fieldConfig(panel, s)
            var v = G.reduce(s.values, calc)
            var color = G.seriesColor(cfg, i, v, { min: cfg.min, max: cfg.max }, tc)
            out.push({ name: G.displayName(s, cfg), parts: G.formatParts(v, cfg.unit, cfg.decimals),
                       color: color, values: s.values, times: s.times })
        }
        return out
    }

    readonly property int cols: tiles.length <= 1 ? 1 : horizontal ? 1 : Math.ceil(Math.sqrt(tiles.length * width / Math.max(1, height)))

    GridLayout {
        anchors.fill: parent
        columns: Math.max(1, Math.min(stat.cols, stat.tiles.length))
        rowSpacing: 2
        columnSpacing: 2

        Repeater {
            model: stat.tiles

            delegate: Rectangle {
                id: tile

                required property var modelData
                readonly property bool bg: stat.colorMode === "background" || stat.colorMode === "background_solid"
                readonly property color fg: bg ? "white"
                                            : stat.colorMode === "none" ? Kirigami.Theme.textColor
                                            : (modelData.color === "transparent" ? Kirigami.Theme.textColor : modelData.color)
                readonly property bool showName: stat.textMode === "name" || stat.textMode === "value_and_name"
                                                 || (stat.textMode === "auto" && stat.tiles.length > 1)
                readonly property bool showValue: stat.textMode !== "name" && stat.textMode !== "none"

                Layout.fillWidth: true
                Layout.fillHeight: true
                radius: Kirigami.Units.cornerRadius
                color: bg ? modelData.color : "transparent"
                gradient: bg && stat.colorMode === "background" ? grad : null

                Gradient {
                    id: grad
                    GradientStop { position: 0; color: Qt.lighter(tile.modelData.color, 1.15) }
                    GradientStop { position: 1; color: Qt.darker(tile.modelData.color, 1.15) }
                }

                Canvas {
                    id: spark
                    anchors.left: parent.left
                    anchors.right: parent.right
                    anchors.bottom: parent.bottom
                    height: parent.height * 0.45
                    visible: stat.sparkline && tile.modelData.values.length > 1
                    onWidthChanged: requestPaint()
                    onHeightChanged: requestPaint()
                    Connections {
                        target: tile
                        function onModelDataChanged() { spark.requestPaint() }
                    }
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.reset()
                        var vals = tile.modelData.values, times = tile.modelData.times
                        var lo = Infinity, hi = -Infinity
                        for (var i = 0; i < vals.length; i++) {
                            if (vals[i] === null) continue
                            lo = Math.min(lo, vals[i]); hi = Math.max(hi, vals[i])
                        }
                        if (!isFinite(lo) || times.length < 2) return
                        if (hi === lo) { hi += 1; lo -= 1 }
                        var t0 = times[0], t1 = times[times.length - 1]
                        var c = tile.bg ? "#ffffff" : (tile.modelData.color === "transparent" || tile.modelData.color === Kirigami.Theme.textColor.toString()
                                                        ? Kirigami.Theme.highlightColor.toString() : tile.modelData.color)
                        ctx.beginPath()
                        var started = false
                        for (var j = 0; j < vals.length; j++) {
                            if (vals[j] === null) continue
                            var x = (times[j] - t0) / (t1 - t0) * width
                            var y = height - (vals[j] - lo) / (hi - lo) * height * 0.9
                            if (!started) { ctx.moveTo(x, height); ctx.lineTo(x, y); started = true }
                            else ctx.lineTo(x, y)
                        }
                        ctx.lineTo(width, height)
                        ctx.closePath()
                        ctx.fillStyle = G.withAlpha(c.charAt(0) === "#" ? c : "#888888", 0.18)
                        ctx.fill()
                    }
                }

                ColumnLayout {
                    anchors.fill: parent
                    anchors.margins: 2
                    spacing: 0

                    Text {
                        visible: tile.showName
                        Layout.fillWidth: true
                        Layout.preferredHeight: parent.height * (tile.showValue ? 0.3 : 1)
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        fontSizeMode: Text.Fit
                        font.pixelSize: 64
                        minimumPixelSize: 7
                        elide: Text.ElideRight
                        color: tile.bg ? "white" : Kirigami.Theme.textColor
                        opacity: 0.8
                        text: tile.modelData.name
                        textFormat: Text.PlainText
                    }

                    Text {
                        visible: tile.showValue
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        fontSizeMode: Text.Fit
                        font.pixelSize: 200
                        minimumPixelSize: 8
                        font.weight: Font.DemiBold
                        color: tile.modelData.parts.empty ? Kirigami.Theme.disabledTextColor : tile.fg
                        textFormat: Text.PlainText
                        text: tile.modelData.parts.empty ? tile.modelData.parts.text
                              : tile.modelData.parts.prefix + tile.modelData.parts.text + tile.modelData.parts.suffix
                    }
                }
            }
        }
    }
}