From 7cd6716eb8d98a93c95939b1b55ae38fac65c215 Mon Sep 17 00:00:00 2001 From: Damocles Date: Fri, 17 Apr 2026 10:34:34 +0200 Subject: [PATCH] feat(overview-backdrop): softer wave, breathing pulse, random glitches --- modules/OverviewBackdrop.qml | 68 +++++++++++++++++++++++++++++++++++- modules/hex_wave.frag | 47 ++++++++++++++++++------- 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/modules/OverviewBackdrop.qml b/modules/OverviewBackdrop.qml index ab17be2..6abb7e8 100644 --- a/modules/OverviewBackdrop.qml +++ b/modules/OverviewBackdrop.qml @@ -32,6 +32,9 @@ PanelWindow { property real uSize: 50.0 property real uWavePhase: -200 + property real uBreath: 0 + property real uGlitch: 0 + property real uGlitchSeed: 0.0 property vector4d uResolution: Qt.vector4d(width, height, 0, 0) property color uC0: M.Theme.base0C property color uC1: M.Theme.base0E @@ -40,8 +43,10 @@ PanelWindow { Connections { target: M.NiriIpc function onOverviewOpenChanged() { - if (!M.NiriIpc.overviewOpen) + if (!M.NiriIpc.overviewOpen) { fx.uWavePhase = -200; + fx.uBreath = 0; + } } } @@ -59,5 +64,66 @@ PanelWindow { duration: 8000 } } + + // Breathing pulse while overview is open + SequentialAnimation on uBreath { + loops: Animation.Infinite + running: M.NiriIpc.overviewOpen && !M.Theme.reducedMotion + NumberAnimation { + from: 0 + to: 1 + duration: 2500 + easing.type: Easing.InOutSine + } + NumberAnimation { + from: 1 + to: 0 + duration: 2500 + easing.type: Easing.InOutSine + } + } + + // Random subtle glitches — fire every 12–37s, total ~250ms each + Timer { + interval: 20000 + repeat: true + running: !M.Theme.reducedMotion + onTriggered: { + interval = 12000 + Math.floor(Math.random() * 25000); + fx.uGlitchSeed = Math.random() * 1000.0; + _glitchAnim.start(); + } + } + + SequentialAnimation { + id: _glitchAnim + NumberAnimation { + target: fx + property: "uGlitch" + to: 0.7 + duration: 50 + easing.type: Easing.OutQuad + } + NumberAnimation { + target: fx + property: "uGlitch" + to: 0.15 + duration: 50 + } + NumberAnimation { + target: fx + property: "uGlitch" + to: 0.85 + duration: 60 + easing.type: Easing.OutQuad + } + NumberAnimation { + target: fx + property: "uGlitch" + to: 0 + duration: 100 + easing.type: Easing.InQuad + } + } } } diff --git a/modules/hex_wave.frag b/modules/hex_wave.frag index 6f204df..4a87666 100644 --- a/modules/hex_wave.frag +++ b/modules/hex_wave.frag @@ -8,6 +8,9 @@ layout(std140, binding = 0) uniform buf { float qt_Opacity; float uSize; float uWavePhase; + float uBreath; + float uGlitch; + float uGlitchSeed; vec4 uResolution; vec4 uC0; vec4 uC1; @@ -29,9 +32,24 @@ vec3 themeGradient(float t) { : mix(uC1.rgb, uC2.rgb, (t - 0.5) * 2.0); } +// Cheap hash for glitch bands +float hash(float n) { + return fract(sin(n) * 43758.5453123); +} + void main() { vec2 res = uResolution.xy; + + // Glitch: shift some horizontal bands slightly vec2 frag = qt_TexCoord0 * res; + if (uGlitch > 0.0) { + float band = floor(frag.y / 7.0); + float h = hash(band * 127.1 + uGlitchSeed * 311.7); + if (h > 0.72) { + float shift = (hash(band * 93.9 + uGlitchSeed) - 0.5) * 14.0 * uGlitch; + frag.x = clamp(frag.x + shift, 0.0, res.x); + } + } float dx = uSize * 1.5; float dy = uSize * 1.7320508; @@ -41,12 +59,12 @@ void main() { float row = round((frag.y - yoff) / dy); vec2 center = vec2(col * dx, row * dy + yoff); - // Wave factor + // Wave — wide gaussian so it reads more as a broad pulse than a sharp sweep float dist = center.x - uWavePhase; - float wf = exp(-dist * dist / 9000.0); + float wf = exp(-dist * dist / 40000.0); float baseR = uSize * 0.48; - float inradius = baseR * (1.0 + 0.35 * wf); + float inradius = baseR * (1.0 + 0.15 * wf); vec2 p = frag - center; float d = sdHexagon(p.yx, inradius); // swap for flat-top @@ -62,28 +80,31 @@ void main() { ? mix(uC0.rgb, uC1.rgb, fx * 2.0) : mix(uC1.rgb, uC2.rgb, (fx - 0.5) * 2.0); - // Alpha from distance to center + // Alpha from distance to center (vignette) float fy = clamp(center.y / res.y, 0.0, 1.0); float dc = length(vec2(fx - 0.5, fy - 0.5)); float a = 0.03 + dc * 0.06; - // Wave brighten - rgb = min(rgb + vec3(0.3 * wf), vec3(1.0)); - a += 0.12 * wf; + // Breathing pulse (when overview open) + a += 0.025 * uBreath; + rgb = min(rgb + vec3(0.04 * uBreath), vec3(1.0)); - // Rainbow shimmer on hex edges when wave passes + // Wave brighten (softer than before) + rgb = min(rgb + vec3(0.15 * wf), vec3(1.0)); + a += 0.07 * wf; + + // Shimmer on hex edges as wave passes float edgeWidth = 5.0; - float edgeFactor = smoothstep(-edgeWidth, 0.0, d); // 0 at interior, 1 at edge + float edgeFactor = smoothstep(-edgeWidth, 0.0, d); if (wf > 0.01 && edgeFactor > 0.0) { - // Vary shimmer color across theme gradient using angle + position float angle = atan(p.y, p.x); float rawT = fract((angle + 3.14159) / 6.28318 + center.x * 0.003 + center.y * 0.005); - float t = abs(rawT * 2.0 - 1.0); // triangle wave → uC0→uC1→uC2→uC1→uC0, no hard jump + float t = abs(rawT * 2.0 - 1.0); vec3 shimmerColor = themeGradient(t); float shimmer = edgeFactor * wf; - rgb = mix(rgb, shimmerColor, shimmer * 0.8); - a = mix(a, 0.5, shimmer * 0.6); + rgb = mix(rgb, shimmerColor, shimmer * 0.5); + a = mix(a, 0.5, shimmer * 0.35); } // Anti-alias outer edge