pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Services.Notifications import ZShell.Components import ZShell.Config import ZShell.Services import qs.Components import qs.Services CustomRect { id: root required property NotificationGroup group required property Flickable container required property bool expanded property bool expandable: true property int baseSurface: 0 property int baseLayer: baseSurface readonly property list notifs: [...group.activeNotifs] readonly property bool single: notifs.length === 1 readonly property alias rowPressed: notifList.rowPressed readonly property list surfaces: [Colors.palette.m3surfaceContainer, Colors.palette.m3surfaceContainerHigh, Colors.palette.m3surfaceContainerHighest] readonly property int nonAnimHeight: { const headerHeight = header.implicitHeight + (expanded ? Tokens.spacing.extraSmall : 0); const columnHeight = headerHeight + notifList.layoutHeight; return Math.round(Math.max(Config.notifs.sizes.image, columnHeight) + Tokens.padding.medium * 2); } signal requestExpand(expand: bool) signal swiped(notif: NotificationRow) signal linkActivated(notif: NotificationRow) function surface(offset: int, layer: int): color { return Colors.layer(surfaces[Math.min(baseSurface + offset, surfaces.length - 1)], baseLayer + layer); } Component.onCompleted: group.lock(this) anchors.left: parent?.left anchors.right: parent?.right implicitHeight: nonAnimHeight clip: true radius: Tokens.rounding.large color: group.urgency === NotificationUrgency.Critical && (single || !expanded) ? Colors.palette.m3secondaryContainer : surface(0, 1) Behavior on implicitHeight { Anim {} } SnapLayout { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: Tokens.padding.medium objectName: "notif-content" orientation: Qt.Horizontal spacing: Tokens.spacing.medium NotifAvatar { Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.topMargin: root.expanded && !root.single ? header.implicitHeight + Tokens.spacing.extraSmall + Tokens.padding.extraSmall : 0 owner: root.expanded ? root.notifs[0] ?? root.group : root.group image: root.expanded ? root.notifs[0]?.image ?? "" : root.group.image icon: root.expanded ? root.notifs[0]?.appIcon ?? "" : root.group.appIcon urgency: root.expanded ? root.notifs[0]?.urgency ?? NotificationUrgency.Normal : root.group.urgency lowFillColour: root.surface(2, 2) summary: root.notifs[0]?.summary ?? "" progress: (root.notifs[0]?.progress ?? -1) / 100 Behavior on Layout.topMargin { Anim {} } } Column { Layout.fillWidth: true spacing: root.expanded ? Tokens.spacing.extraSmall : 0 Behavior on spacing { Anim {} } NotifGroupHeader { id: header anchors.left: parent.left anchors.right: parent.right appName: root.group.appName single: root.single timeStr: root.notifs[0]?.timeStr ?? "" count: root.notifs.length urgency: root.group.urgency pillColour: root.surface(1, 2) expandable: root.expandable expanded: root.expanded onToggle: root.requestExpand(!root.expanded) } NotifGroupList { id: notifList notifs: root.notifs expanded: root.expanded single: root.single headerHeight: header.implicitHeight headerTitleWidth: header.titleWidth expandable: root.expandable container: root.container itemColour: root.surface(1, 2) avatarColour: root.surface(2, 2) buttonColour: root.surface(2, 3) onRequestExpand: expand => root.requestExpand(expand) onSwiped: notif => root.swiped(notif) onLinkActivated: notif => root.linkActivated(notif) } } } }