Files
Panama/config/dot/quickshell/modules/datemenu/NotificationGroup.qml

173 lines
5.2 KiB
QML

// One app's worth of notifications in the date menu, GNOME-style: a header
// carrying the app's identity and a clear button, over a collapsible stack of
// cards.
//
// The header is the disclosure control in its entirety — a 4px chevron is a
// poor click target, and GNOME lets you hit the whole row.
import QtQuick
import Quickshell
import Quickshell.Widgets
import qs.config
import qs.services
import qs.modules.notifications
import qs.modules.quicksettings
import qs.widgets
Column {
id: root
// One entry from Notifs.groups: { app, icon, desktopEntry, items }.
required property var group
property bool expanded: true
signal toggled
signal cleared
spacing: 4
// The app icon the notifications themselves declared, falling back to the
// desktop entry — same resolution order NotificationCard uses.
readonly property string iconSource: {
if (root.group.icon)
return Quickshell.iconPath(root.group.icon, true);
const entry = DesktopEntries.byId(root.group.desktopEntry);
return entry ? Quickshell.iconPath(entry.icon, true) : "";
}
Rectangle {
id: header
width: parent.width
height: 28
radius: Theme.cardRadius - 4
border.width: 0
color: headerMouse.containsMouse ? Theme.alpha(Theme.fg, 0.07) : "transparent"
Behavior on color {
ColorAnimation {
duration: headerMouse.containsMouse ? Theme.durFast : Theme.durNormal
}
}
MouseArea {
id: headerMouse
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggled()
}
Row {
anchors.left: parent.left
anchors.leftMargin: 6
anchors.right: clearButton.left
anchors.rightMargin: 6
anchors.verticalCenter: parent.verticalCenter
spacing: 7
IconImage {
anchors.verticalCenter: parent.verticalCenter
implicitSize: 15
asynchronous: true
source: root.iconSource
visible: source !== ""
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: root.group.app
color: Theme.fgDim
elide: Text.ElideRight
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.DemiBold
}
// A bare count next to a collapsed group is the only way to know
// how much is hidden, so it is always shown once there is more
// than one.
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: countLabel.implicitWidth + 12
height: 16
radius: Theme.pillRadius
border.width: 0
visible: root.group.items.length > 1
color: Theme.alpha(Theme.fg, 0.12)
Text {
id: countLabel
anchors.centerIn: parent
text: root.group.items.length
color: Theme.fgDim
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSizeSmall
}
}
}
IconButton {
id: clearButton
anchors.right: chevron.left
anchors.rightMargin: 2
anchors.verticalCenter: parent.verticalCenter
size: 22
iconSize: 12
tint: Theme.fgMuted
icon: "edit-clear-all-symbolic"
iconFallback: "window-close-symbolic"
onClicked: root.cleared()
}
ThemedIcon {
id: chevron
anchors.right: parent.right
anchors.rightMargin: 6
anchors.verticalCenter: parent.verticalCenter
size: 12
icon: "pan-down-symbolic"
iconFallback: "go-down-symbolic"
tint: Theme.fgMuted
// Pointing down when open, right when closed — the standard
// disclosure direction, animated only on the click that caused it.
rotation: root.expanded ? 0 : -90
Behavior on rotation {
NumberAnimation {
duration: Theme.durNormal
easing.type: Easing.OutCubic
}
}
}
}
// Section animates its own height and hides itself at zero, which also
// collapses the spacing the parent Column would otherwise leave behind.
Section {
width: parent.width
expanded: root.expanded
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: 4
Repeater {
model: root.group.items
NotificationCard {
required property var modelData
width: parent.width
compact: true
notification: modelData
onDismissed: Notifs.dismiss(modelData)
}
}
}
}
}