// Copyright (C) 2026 Danilo M. // // 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 } } } } }