From 595d42fbd4ee206d2b16d033e1be337f9c97eee7 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Tue, 6 Oct 2026 10:02:41 +0200 Subject: feat(notifications): window border on the balloons The balloons now wear the Hyprland window border: 2px, the udt gradient read from ~/.cache/udt/udt-border.lua (the file look_and_feel.lua reads, watched so a wallpaper change follows), rotating, with the windows' corners. A Rectangle border takes one colour and its radius is circular, while the windows use rounding 15 at rounding_power 4, a superellipse. So the body and the border are both drawn by a Shape from an SVG path whose corners are the cubic fit to |x|^4 + |y|^4 = 1, and the border is a ring filled odd-even, which keeps the translucent body from showing the gradient through. One angle drives every balloon, so they turn together, and the animation runs only while the stack is mapped. Hyprland's borderangle (speed 30) reads as 3s a turn, which looked slow on a balloon that size, so the turn is 1.5s, tuned by eye. Co-Authored-By: Claude Opus 5.5 --- notifications/Balloons.qml | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'notifications/Balloons.qml') diff --git a/notifications/Balloons.qml b/notifications/Balloons.qml index 3676e67..d22e4a1 100644 --- a/notifications/Balloons.qml +++ b/notifications/Balloons.qml @@ -10,6 +10,7 @@ // GNU General Public License for more details. import Quickshell +import Quickshell.Io import Quickshell.Wayland import QtQuick @@ -35,6 +36,37 @@ Scope { // (reloading icons, resetting hover) four times a second. readonly property var live: Notify.queue || [] + // The window border gradient, from the same file Hyprland reads, so the + // balloons wear the windows' border. udt-accent writes it as Hyprland + // colours, `rgb(rrggbb)` or `rgba(rrggbbaa)`; Qt wants `#aarrggbb`. The + // fallback is the triple look_and_feel.lua falls back to. + property var borderColors: ["#c6a0f6", "#b7bdf8", "#8aadf4"] + FileView { + path: `${Quickshell.env("HOME")}/.cache/udt/udt-border.lua` + watchChanges: true + onFileChanged: reload() + onLoaded: { + const re = /rgba?\(([0-9a-fA-F]{6})([0-9a-fA-F]{2})?\)/g; + const out = []; + let m; + while ((m = re.exec(text())) !== null) out.push("#" + (m[2] ?? "") + m[1]); + if (out.length > 0) root.borderColors = out; + } + } + + // Hyprland's borderangle is speed 30, linear, loop, which reads as 3s a + // turn, but on a balloon that size looked slow next to the windows, so + // the turn is tuned by eye rather than taken from the config. One angle for every balloon, so they turn together like the + // windows do, and only while the stack is mapped. + property real borderAngle: 0 + NumberAnimation on borderAngle { + from: 0 + to: 2 * Math.PI + duration: 1500 + loops: Animation.Infinite + running: win.visible + } + PanelWindow { id: win @@ -65,7 +97,12 @@ Scope { Repeater { model: root.live - NotificationBalloon { notification: modelData; now: root.now } + NotificationBalloon { + notification: modelData + now: root.now + borderColors: root.borderColors + borderAngle: root.borderAngle + } } } } -- cgit v1.2.3