148 lines
5.0 KiB
QML
148 lines
5.0 KiB
QML
/*
|
|
Copyright (C) 2025 Rohith Namboothiri
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
import "../theme"
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property string title: ""
|
|
// Optional status string shown in the header (e.g. "Connected")
|
|
property string statusText: ""
|
|
// If true, show status only when collapsed (recommended for compact headers).
|
|
property bool statusWhenCollapsedOnly: true
|
|
property bool expanded: true
|
|
property alias contentItem: contentLoader.sourceComponent
|
|
default property alias content: contentLoader.sourceComponent
|
|
|
|
// Derive content height from the loaded item's implicit/explicit height.
|
|
readonly property real contentHeight: contentLoader.item
|
|
? Math.max(contentLoader.item.implicitHeight || 0,
|
|
contentLoader.item.height || 0)
|
|
: 0
|
|
|
|
Layout.fillWidth: true
|
|
implicitHeight: headerRow.height + (expanded ? root.contentHeight + 8 : 0)
|
|
|
|
Tokens { id: t }
|
|
|
|
Behavior on implicitHeight { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } }
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: 16
|
|
color: t.surface2
|
|
border.color: t.stroke
|
|
border.width: 1
|
|
}
|
|
|
|
Column {
|
|
anchors.fill: parent
|
|
anchors.margins: 4
|
|
spacing: 0
|
|
|
|
// Header (tap to expand/collapse)
|
|
Rectangle {
|
|
id: headerRow
|
|
width: parent.width
|
|
height: 48
|
|
radius: 14
|
|
color: headerMouse.containsMouse ? Qt.rgba(1,1,1,0.04) : "transparent"
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 14
|
|
anchors.rightMargin: 14
|
|
spacing: 10
|
|
|
|
Label {
|
|
text: root.title
|
|
font.bold: true
|
|
font.pixelSize: 15
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Rectangle {
|
|
id: statusPill
|
|
visible: root.statusText !== "" && (!root.statusWhenCollapsedOnly || !root.expanded)
|
|
radius: 999
|
|
height: 26
|
|
color: Qt.rgba(t.accent.r, t.accent.g, t.accent.b, 0.12)
|
|
border.color: Qt.rgba(t.accent.r, t.accent.g, t.accent.b, 0.45)
|
|
border.width: 1
|
|
clip: true
|
|
|
|
// Responsive: cap width so it never crushes the title.
|
|
Layout.maximumWidth: Math.max(120, headerRow.width * 0.42)
|
|
Layout.preferredWidth: Math.min(statusTextLabel.implicitWidth + 18, statusPill.Layout.maximumWidth)
|
|
|
|
Label {
|
|
id: statusTextLabel
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 9
|
|
anchors.rightMargin: 9
|
|
text: root.statusText
|
|
font.pixelSize: 12
|
|
color: t.text
|
|
opacity: 0.9
|
|
verticalAlignment: Text.AlignVCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
|
|
Label {
|
|
text: root.expanded ? "\uf077" : "\uf078"
|
|
font.family: "FontAwesome"
|
|
font.pixelSize: 12
|
|
opacity: 0.7
|
|
Behavior on text { PropertyAnimation { duration: 120 } }
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: headerMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.expanded = !root.expanded
|
|
}
|
|
}
|
|
|
|
// Content area (clipped when collapsed)
|
|
Item {
|
|
width: parent.width
|
|
height: root.expanded ? root.contentHeight : 0
|
|
clip: true
|
|
opacity: root.expanded ? 1 : 0
|
|
|
|
Behavior on height { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } }
|
|
Behavior on opacity { NumberAnimation { duration: 140 } }
|
|
|
|
Loader {
|
|
id: contentLoader
|
|
width: parent.width - 24
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
active: true
|
|
}
|
|
}
|
|
}
|
|
}
|