pragma ComponentBehavior: Bound import QtQuick import "../services" as S import NovaStats as NS // One section of the systemd applet: a header with title, aggregate counts, // state chip; an auto-expanded list of failed units (hidden when empty); a // lazy-loaded, collapsed-by-default list of running units. Column { id: root required property color accentColor required property string machineName required property string title required property string marker required property string systemState required property int runningCount required property int totalCount required property var failedUnits required property var runningUnits signal contentResized onHeightChanged: root.contentResized() width: parent?.width ?? 0 property bool _runningExpanded: false readonly property int _failedCount: (failedUnits ?? []).length // Header Item { width: root.width height: 32 Rectangle { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 color: _hdrHover.hovered ? NS.ThemeService.base02 : "transparent" radius: NS.ThemeService.radius z: -1 } HoverHandler { id: _hdrHover } Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.right: _stateChip.left anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter text: root.title + (root.marker !== "" ? " " + root.marker : "") color: NS.ThemeService.base05 font.pixelSize: NS.ThemeService.fontSize font.family: NS.ThemeService.fontFamily elide: Text.ElideRight } // Aggregate counts: "n running, m/total failed" or "n running" if no failures. Text { id: _counts anchors.right: _stateChip.left anchors.rightMargin: 8 anchors.verticalCenter: parent.verticalCenter text: { if (root.totalCount === 0) return ""; const r = root.runningCount + " running"; if (root._failedCount > 0) return r + ", " + root._failedCount + "/" + root.totalCount + " failed"; return r; } color: NS.ThemeService.base04 font.pixelSize: NS.ThemeService.fontSize - 3 font.family: NS.ThemeService.fontFamily } Rectangle { id: _stateChip anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter visible: root.systemState !== "unknown" color: { const st = root.systemState; if (st === "running") return NS.ThemeService.base0B; if (st === "degraded") return NS.ThemeService.base0A; if (st === "pending") return NS.ThemeService.base04; return NS.ThemeService.base08; } opacity: 0.85 radius: 3 width: _stateLbl.width + 8 height: 14 Text { id: _stateLbl anchors.centerIn: parent text: root.systemState color: NS.ThemeService.base00 font.pixelSize: NS.ThemeService.fontSize - 3 font.family: NS.ThemeService.fontFamily } } } // Failed units (auto-expanded; entire block hidden when there are none). Repeater { model: root._failedCount > 0 ? root.failedUnits : [] delegate: SystemdUnitRow { required property var modelData unitName: modelData.name description: modelData.description ?? "" subState: modelData.subState ?? "" scope: modelData.scope ?? "system" machineName: root.machineName accentColor: root.accentColor } } // Running units toggle row (only meaningful when there are running units to show). Item { visible: (root.runningUnits ?? []).length > 0 width: root.width height: 26 Rectangle { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 color: _runHdrHover.hovered ? NS.ThemeService.base02 : "transparent" radius: NS.ThemeService.radius z: -1 } HoverHandler { id: _runHdrHover } Text { anchors.left: parent.left anchors.leftMargin: 24 anchors.verticalCenter: parent.verticalCenter text: "running units (" + (root.runningUnits ?? []).length + ")" color: NS.ThemeService.base04 font.pixelSize: NS.ThemeService.fontSize - 2 font.family: NS.ThemeService.fontFamily font.letterSpacing: 1 } Text { anchors.right: parent.right anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: root._runningExpanded ? "" : "" color: NS.ThemeService.base04 font.pixelSize: NS.ThemeService.fontSize - 2 font.family: NS.ThemeService.iconFontFamily } TapHandler { onTapped: root._runningExpanded = !root._runningExpanded } } // Lazy-loaded running units list. Repeater materializes rows only when the // model is non-empty, so feeding `[]` while collapsed avoids per-row cost. Repeater { model: root._runningExpanded ? root.runningUnits : [] delegate: SystemdUnitRow { required property var modelData unitName: modelData.name description: modelData.description ?? "" subState: modelData.subState ?? "" scope: modelData.scope ?? "system" machineName: root.machineName accentColor: root.accentColor } } }