C++ / fmt (pull_request) Failing after 5s
C++ / build (pull_request) Failing after 11s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 10s
Python / static (pull_request) Failing after 27s
Rust / build (pull_request) Successful in 54s
Python / verify (pull_request) Failing after 1m26s
Rust / fmt (pull_request) Successful in 46s
Rust / clippy (pull_request) Successful in 1m6s
288 lines
6.8 KiB
QML
288 lines
6.8 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import Quickshell.Services.Notifications
|
|
import ZShell.Components
|
|
import ZShell.Config
|
|
import ZShell.Services
|
|
import qs.Components
|
|
import qs.Services
|
|
|
|
CustomRect {
|
|
id: root
|
|
|
|
required property NotificationRow modelData
|
|
required property bool expanded
|
|
required property color expandedColour
|
|
required property color avatarColour
|
|
required property color buttonColour
|
|
property bool showAvatar: true
|
|
property bool single: false
|
|
property bool ready: true
|
|
property real headerHeight
|
|
property real headerTitleWidth
|
|
|
|
readonly property bool critical: modelData.urgency === NotificationUrgency.Critical
|
|
readonly property bool low: modelData.urgency === NotificationUrgency.Low
|
|
readonly property CustomText body: (expandedContent.item as ExpandedBody)?.body ?? null
|
|
readonly property real padding: single ? 0 : Tokens.padding.medium
|
|
readonly property real headerSummaryOffset: -(headerHeight + summaryHeightMetrics.height) / 2
|
|
readonly property real inset: expanded ? padding : 0
|
|
readonly property real summaryOffset: single && !expanded ? headerSummaryOffset : inset
|
|
readonly property real nonAnimHeight: {
|
|
if (!expanded)
|
|
return summaryHeightMetrics.height;
|
|
const contentHeight = summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + padding * 2;
|
|
return single ? contentHeight : Math.max(contentHeight, Config.notifs.sizes.image + Tokens.padding.extraSmall * 2);
|
|
}
|
|
|
|
signal linkActivated
|
|
|
|
implicitHeight: nonAnimHeight
|
|
|
|
radius: Tokens.rounding.medium
|
|
color: {
|
|
const c = root.critical ? Colors.palette.m3secondaryContainer : root.expandedColour;
|
|
return expanded && !single ? c : Qt.alpha(c, 0);
|
|
}
|
|
|
|
TextMetrics {
|
|
id: summaryHeightMetrics
|
|
|
|
font: dummySummary.font
|
|
text: " " // Use this height to prevent weird characters from changing the line height
|
|
}
|
|
|
|
TextMetrics {
|
|
id: summaryMetrics
|
|
|
|
font: dummySummary.font
|
|
text: root.modelData.summary
|
|
elide: Text.ElideRight
|
|
elideWidth: root.single ? root.headerTitleWidth : root.width
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: avatar
|
|
|
|
shouldBeActive: root.expanded && root.showAvatar
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.topMargin: Tokens.padding.extraSmall
|
|
anchors.leftMargin: -(Config.notifs.sizes.image + Tokens.spacing.medium)
|
|
|
|
sourceComponent: NotifAvatar {
|
|
owner: root.modelData
|
|
image: root.modelData.image
|
|
icon: root.modelData.appIcon
|
|
urgency: root.modelData.urgency
|
|
lowFillColour: root.avatarColour
|
|
summary: root.modelData.summary
|
|
progress: root.modelData.progress / 100
|
|
}
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: compactSummary
|
|
|
|
shouldBeActive: !root.expanded
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: root.inset
|
|
anchors.topMargin: root.summaryOffset
|
|
|
|
sourceComponent: CustomText {
|
|
animate: root.ready
|
|
text: summaryMetrics.elidedText
|
|
color: root.critical ? Colors.palette.m3onSecondaryContainer : root.low ? Colors.palette.m3onSurfaceVariant : Colors.palette.m3onSurface
|
|
}
|
|
|
|
Behavior on anchors.leftMargin {
|
|
Anim {}
|
|
}
|
|
|
|
Behavior on anchors.topMargin {
|
|
Anim {}
|
|
}
|
|
}
|
|
|
|
CustomText {
|
|
id: dummySummary
|
|
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
|
|
visible: false
|
|
text: root.modelData.summary
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: compactBody
|
|
|
|
shouldBeActive: !root.expanded
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: root.inset + (root.single ? 0 : dummySummary.width + Tokens.spacing.small)
|
|
anchors.topMargin: root.single && root.expanded ? summary.implicitHeight + expandedContent.anchors.topMargin : root.inset
|
|
|
|
sourceComponent: CustomText {
|
|
text: root.modelData.body.replace(/\n/g, " ")
|
|
color: root.critical ? Colors.palette.m3secondary : root.low ? Colors.palette.m3outline : Colors.palette.m3onSurfaceVariant
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Behavior on anchors.leftMargin {
|
|
Anim {}
|
|
}
|
|
|
|
Behavior on anchors.topMargin {
|
|
Anim {}
|
|
}
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: summary
|
|
|
|
shouldBeActive: root.expanded
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: root.inset
|
|
anchors.topMargin: root.summaryOffset
|
|
width: root.width - root.padding * 2 - timeStr.implicitWidth - Tokens.spacing.small
|
|
|
|
sourceComponent: CustomText {
|
|
text: root.modelData.summary
|
|
color: root.critical ? Colors.palette.m3onSecondaryContainer : root.low ? Colors.palette.m3onSurfaceVariant : Colors.palette.m3onSurface
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
|
|
Behavior on anchors.leftMargin {
|
|
Anim {}
|
|
}
|
|
|
|
Behavior on anchors.topMargin {
|
|
Anim {}
|
|
}
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: timeStr
|
|
|
|
shouldBeActive: root.expanded && !root.single
|
|
anchors.top: parent.top
|
|
anchors.right: parent.right
|
|
anchors.margins: root.inset
|
|
|
|
sourceComponent: CustomText {
|
|
animate: true
|
|
text: root.modelData.timeStr
|
|
color: root.critical ? Colors.palette.m3secondary : Colors.palette.m3outline
|
|
font: Tokens.font.body.small
|
|
}
|
|
|
|
Behavior on anchors.margins {
|
|
Anim {}
|
|
}
|
|
}
|
|
|
|
WrappedLoader {
|
|
id: expandedContent
|
|
|
|
shouldBeActive: root.expanded
|
|
anchors.top: summary.bottom
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.margins: root.inset
|
|
anchors.topMargin: Tokens.spacing.extraSmall
|
|
|
|
sourceComponent: ExpandedBody {}
|
|
|
|
Behavior on anchors.margins {
|
|
Anim {}
|
|
}
|
|
}
|
|
|
|
Behavior on implicitHeight {
|
|
Anim {}
|
|
}
|
|
|
|
component ExpandedBody: SnapLayout {
|
|
readonly property alias body: bodyText
|
|
|
|
orientation: Qt.Vertical
|
|
spacing: Tokens.spacing.medium
|
|
|
|
CustomText {
|
|
id: bodyText
|
|
|
|
Layout.fillWidth: true
|
|
textFormat: Text.MarkdownText
|
|
text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body here! :/")
|
|
color: root.critical ? Colors.palette.m3secondary : root.low ? Colors.palette.m3outline : Colors.palette.m3onSurfaceVariant
|
|
wrapMode: Text.WordWrap
|
|
|
|
onLinkActivated: link => {
|
|
Qt.openUrlExternally(link);
|
|
root.linkActivated();
|
|
}
|
|
}
|
|
|
|
NotifActions {
|
|
Layout.fillWidth: true
|
|
notif: root.modelData
|
|
buttonColour: root.buttonColour
|
|
}
|
|
}
|
|
component WrappedLoader: Loader {
|
|
id: comp
|
|
|
|
required property bool shouldBeActive
|
|
|
|
active: false
|
|
opacity: 0
|
|
|
|
// Makes the loader load on the same frame shouldBeActive becomes true, which ensures size is set
|
|
states: State {
|
|
name: "active"
|
|
when: comp.shouldBeActive
|
|
|
|
PropertyChanges {
|
|
comp.opacity: 1
|
|
comp.active: true
|
|
}
|
|
}
|
|
transitions: [
|
|
Transition {
|
|
from: ""
|
|
to: "active"
|
|
|
|
SequentialAnimation {
|
|
PropertyAction {
|
|
property: "active"
|
|
}
|
|
Anim {
|
|
type: Anim.DefaultEffects
|
|
property: "opacity"
|
|
}
|
|
}
|
|
},
|
|
Transition {
|
|
from: "active"
|
|
to: ""
|
|
|
|
SequentialAnimation {
|
|
Anim {
|
|
type: Anim.DefaultEffects
|
|
property: "opacity"
|
|
}
|
|
PropertyAction {
|
|
property: "active"
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|