220 lines
8.2 KiB
QML
220 lines
8.2 KiB
QML
// GNOME's message tray: everything that has arrived, grouped by app.
|
|
//
|
|
// Drops from the top centre because that is where GNOME's tray lives and the
|
|
// muscle memory is the whole point. Layer-shell centres a surface on whichever
|
|
// axis it is not anchored to, so anchoring only `top` does it.
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
import Quickshell.Widgets
|
|
import qs.config
|
|
import qs.services
|
|
import qs.modules.quicksettings
|
|
import qs.widgets
|
|
|
|
PanelWindow {
|
|
id: root
|
|
|
|
visible: ShellState.notificationsOpen
|
|
color: "transparent"
|
|
|
|
anchors.top: true
|
|
margins.top: Theme.barHeight + Theme.barGap * 2
|
|
exclusiveZone: 0
|
|
|
|
implicitWidth: 440
|
|
implicitHeight: surface.implicitHeight
|
|
|
|
WlrLayershell.namespace: "qs-popover-notifications"
|
|
WlrLayershell.layer: WlrLayer.Overlay
|
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
|
|
|
onVisibleChanged: {
|
|
if (root.visible)
|
|
Notifs.markAllRead();
|
|
}
|
|
|
|
HyprlandFocusGrab {
|
|
windows: [root]
|
|
active: root.visible
|
|
onCleared: ShellState.close()
|
|
}
|
|
|
|
Rectangle {
|
|
id: surface
|
|
anchors.fill: parent
|
|
implicitHeight: content.implicitHeight + Theme.popoverPadding * 2
|
|
radius: Theme.popoverRadius
|
|
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
|
|
border.width: 1
|
|
border.color: Theme.alpha(Theme.fg, 0.08)
|
|
|
|
// The prism edge — see widgets/PrismEdge.qml. Sits just inside the 1px
|
|
// border so the two don't fight for the same row of pixels.
|
|
PrismEdge {
|
|
anchors.top: parent.top
|
|
anchors.topMargin: 1
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
inset: parent.radius
|
|
}
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
focus: true
|
|
Keys.onEscapePressed: ShellState.close()
|
|
|
|
Column {
|
|
id: content
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
anchors.margins: Theme.popoverPadding
|
|
spacing: Theme.itemSpacing
|
|
|
|
// ── Header ──────────────────────────────────────────────────
|
|
Item {
|
|
width: parent.width
|
|
height: 32
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 4
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "Notifications"
|
|
color: Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeLarge
|
|
font.weight: Font.DemiBold
|
|
}
|
|
|
|
Row {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 4
|
|
|
|
IconButton {
|
|
size: 30
|
|
iconSize: 16
|
|
icon: "notifications-disabled-symbolic"
|
|
tint: Notifs.doNotDisturb ? Theme.accent : Theme.fgDim
|
|
onClicked: Notifs.doNotDisturb = !Notifs.doNotDisturb
|
|
}
|
|
|
|
Rectangle {
|
|
width: clearLabel.implicitWidth + 20
|
|
height: 28
|
|
radius: Theme.pillRadius
|
|
border.width: 0
|
|
visible: Notifs.hasNotifications
|
|
color: clearMouse.containsMouse ? Theme.alpha(Theme.fg, 0.16) : Theme.alpha(Theme.fg, 0.08)
|
|
|
|
Behavior on color {
|
|
ColorAnimation {
|
|
duration: Theme.durFast
|
|
}
|
|
}
|
|
|
|
Text {
|
|
id: clearLabel
|
|
anchors.centerIn: parent
|
|
text: "Clear all"
|
|
color: Theme.fg
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Font.Medium
|
|
}
|
|
|
|
MouseArea {
|
|
id: clearMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: Notifs.dismissAll()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── Empty state ─────────────────────────────────────────────
|
|
Item {
|
|
width: parent.width
|
|
height: 120
|
|
visible: !Notifs.hasNotifications
|
|
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: Notifs.doNotDisturb ? "Do Not Disturb is on" : "No notifications"
|
|
color: Theme.fgMuted
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
}
|
|
}
|
|
|
|
// ── Grouped history ─────────────────────────────────────────
|
|
ScrollColumn {
|
|
width: parent.width
|
|
maxHeight: 620
|
|
spacing: Theme.itemSpacing
|
|
visible: Notifs.hasNotifications
|
|
|
|
Repeater {
|
|
model: Notifs.groups
|
|
|
|
Column {
|
|
id: group
|
|
|
|
required property var modelData
|
|
|
|
width: parent.width
|
|
spacing: 4
|
|
|
|
Item {
|
|
width: parent.width
|
|
height: 26
|
|
|
|
Text {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 4
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: group.modelData.app
|
|
color: Theme.fgDim
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Font.DemiBold
|
|
}
|
|
|
|
IconButton {
|
|
anchors.right: parent.right
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
size: 24
|
|
iconSize: 13
|
|
tint: Theme.fgDim
|
|
icon: "edit-clear-all-symbolic"
|
|
iconFallback: "window-close-symbolic"
|
|
onClicked: Notifs.dismissApp(group.modelData.app)
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
model: group.modelData.items
|
|
|
|
NotificationCard {
|
|
required property var modelData
|
|
|
|
width: group.width
|
|
compact: true
|
|
notification: modelData
|
|
onDismissed: Notifs.dismiss(modelData)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|