import QtQuick import qs.Commons import qs.Ui // Local copy of qs.Ui.PanelHero with one addition: `titleComponent`. // // The stock hero renders `title` as a plain Text and offers no slot to put // anything else there, so showing the Offscale wordmark instead of a string // needed either editing a shared /usr/share component (never) or owning a // copy here. Everything else is unchanged from the original, so diffing // against /usr/share/omarchy/shell/Ui/PanelHero.qml shows exactly what we did. Item { id: root property Component iconComponent: null property string title: "" // When set, replaces the title Text. `title` is still used as the accessible // / fallback string, so callers should keep setting it. property Component titleComponent: null property string meta: "" property string detail: "" property color foreground: Color.foreground property string fontFamily: Style.font.family property real iconSize: Style.font.display property real iconOpacity: 1.0 property alias metaOpacity: metaText.opacity // Optional control pinned to the trailing edge of the hero — a ToggleSwitch, // a small button. The hero centers it against the labels and reserves the // space itself, so callers never do the geometry. property Component trailingControl: null readonly property color dim: Qt.darker(foreground, 1.4) readonly property real trailingInset: trailingLoader.item && trailingLoader.item.visible ? trailingLoader.width + Style.space(12) : 0 width: parent ? parent.width : implicitWidth implicitHeight: Math.max(iconLoader.implicitHeight, heroLabels.implicitHeight, trailingLoader.implicitHeight) Loader { id: iconLoader sourceComponent: root.iconComponent anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter opacity: root.iconOpacity } Column { id: heroLabels anchors.left: iconLoader.right anchors.leftMargin: Style.space(14) anchors.right: parent.right anchors.rightMargin: root.trailingInset anchors.verticalCenter: parent.verticalCenter // Stock PanelHero uses space(2) = 3px, which is fine for two lines of text // but too tight under an image wordmark. space(5) = 8px gives the machine // name room to read as its own line. spacing: Style.space(5) Row { id: titleRow visible: root.title !== "" || root.titleComponent !== null || detailPill.visible width: parent.width // Either the custom title component or the stock Text — never both. Loader { id: titleSlot visible: root.titleComponent !== null active: root.titleComponent !== null sourceComponent: root.titleComponent anchors.verticalCenter: parent.verticalCenter } Text { id: titleText visible: root.titleComponent === null && root.title !== "" text: root.title width: visible ? Math.min(implicitWidth, Math.max(0, parent.width - (detailPill.visible ? detailPill.implicitWidth + Style.space(8) : 0))) : 0 color: root.foreground font.family: root.fontFamily font.pixelSize: Style.font.title font.bold: true elide: Text.ElideRight } Item { width: Math.max(0, parent.width - titleSlot.width - titleText.width - detailPill.implicitWidth) height: 1 } BorderSurface { id: detailPill visible: root.detail !== "" implicitWidth: detailText.implicitWidth + Style.space(10) implicitHeight: detailText.implicitHeight + Style.space(4) anchors.verticalCenter: parent.verticalCenter color: "transparent" borderSpec: Border.controlSpec("normal", root.foreground, Color.accent) radius: Style.cornerRadius Text { id: detailText anchors.centerIn: parent text: root.detail color: root.dim font.family: root.fontFamily font.pixelSize: Style.font.body font.bold: true } } } Text { id: metaText width: parent.width text: root.meta.toUpperCase() visible: text !== "" color: root.dim font.family: root.fontFamily font.pixelSize: Style.font.caption font.bold: true font.letterSpacing: 1.2 elide: Text.ElideRight } } Loader { id: trailingLoader sourceComponent: root.trailingControl anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter } }