blob: 3676e67077ce80643b5b57bb757001dc297fd5db (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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.
import Quickshell
import Quickshell.Wayland
import QtQuick
// The balloon stack, bottom-right of DP-1, over conky.
Scope {
id: root
// A tick drives both expiry and the end of a snooze without waiting for a
// file change. 250ms keeps ronema's -t 1 near-instant.
property double now: Date.now()
Timer {
interval: 250
running: true
repeat: true
onTriggered: root.now = Date.now()
}
// The Repeater model is Notify.queue itself, so its identity only changes
// when the daemon rewrites the file, not on the 250ms tick. Expiry and
// snooze are the balloon's own business: the tick reassigns `now`, each
// balloon re-evaluates its visible binding and leaves the column at
// expiry. Rebuilding the model on the tick would recreate every delegate
// (reloading icons, resetting hover) four times a second.
readonly property var live: Notify.queue || []
PanelWindow {
id: win
// From the queue, never from layout: a hidden window stops polishing,
// and Column computes implicitHeight during polish, so deriving
// visibility from it would never turn true again after the first
// hide. Queue membership is a plain property the binding re-reads
// whether or not the window is mapped, so a later notification
// re-maps it. Expired entries that stay in the queue keep a
// zero-height window mapped, which is harmless.
visible: root.live.length > 0
screen: Quickshell.screens.find(s => s.name === "DP-1") ?? Quickshell.screens[0]
anchors { bottom: true; right: true }
margins { bottom: 12; right: 12 }
implicitWidth: 340
implicitHeight: column.implicitHeight
color: "transparent"
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "quickshell-notifications"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
Column {
id: column
width: parent.width
anchors { bottom: parent.bottom; right: parent.right }
spacing: 8
Repeater {
model: root.live
NotificationBalloon { notification: modelData; now: root.now }
}
}
}
}
|