aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/appearance
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-06 17:44:16 +0100
committerDanilo M. <danix@danix.xyz>2026-11-06 17:44:16 +0100
commitf14d4ac615fe3ae62d77ed828b6150665a687033 (patch)
tree1b8be5e1f2b7cce6212cc486215fe34e97ad3e11 /desktop/modules/appearance
parentdf12ed91675a64afd0b0248d2487e9a94d860830 (diff)
downloadquickshell-f14d4ac615fe3ae62d77ed828b6150665a687033.tar.gz
quickshell-f14d4ac615fe3ae62d77ed828b6150665a687033.zip
wallpaper: play the transition by name
Transitions.next() is asked once per run and the name goes out through swapping, so both screens play the same effect, and the cover runs it through WallShader, which the settings page's thumbnails will share. progress is a plain 0 to 1 for every shader now; burn.frag keeps the range it was tuned for by remapping it. newImg gains a layer so it can be a shader's target: an Image sampled directly gives the whole picture, not its PreserveAspectCrop crop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/appearance')
-rw-r--r--desktop/modules/appearance/WallShader.qml27
-rw-r--r--desktop/modules/appearance/WallpaperSpread.qml38
-rw-r--r--desktop/modules/appearance/Wallpapers.qml9
-rw-r--r--desktop/modules/appearance/burn.frag4
-rw-r--r--desktop/modules/appearance/burn.frag.qsbbin3983 -> 4027 bytes
5 files changed, 56 insertions, 22 deletions
diff --git a/desktop/modules/appearance/WallShader.qml b/desktop/modules/appearance/WallShader.qml
new file mode 100644
index 0000000..19826ac
--- /dev/null
+++ b/desktop/modules/appearance/WallShader.qml
@@ -0,0 +1,27 @@
+// 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 QtQuick
+
+// One transition, by name, for the cover and the settings page's thumbnails.
+// Goes in the old picture's layer.effect, which fills in source. Every shader
+// draws the old picture at progress 0 and the new one at 1, and picks what it
+// needs from these properties. target must be an item with a layer: an Image
+// sampled directly gives the whole picture, not its PreserveAspectCrop crop.
+ShaderEffect {
+ property string name: "burn"
+ property var source
+ property var target
+ property real progress: 0
+ property real aspect: 1
+ property point origin: Qt.point(0.5, 0.5)
+ fragmentShader: Qt.resolvedUrl(name + ".frag.qsb")
+}
diff --git a/desktop/modules/appearance/WallpaperSpread.qml b/desktop/modules/appearance/WallpaperSpread.qml
index 02e5f94..f9e484b 100644
--- a/desktop/modules/appearance/WallpaperSpread.qml
+++ b/desktop/modules/appearance/WallpaperSpread.qml
@@ -16,8 +16,8 @@ import QtQuick
// The transition when Apply sets a wallpaper. swaybg has none: wallp kills
// the old one and starts a new one, which can show a frame of nothing. A
// surface on the bottom layer, above swaybg and below every window, covers
-// the swap: the old picture burns away over the new one, and the
-// surface goes once wallp has finished, by when the new swaybg is drawing
+// the swap: the old picture gives way to the new one, through the transition
+// Transitions picked, and the surface goes once wallp has finished, by when the new swaybg is drawing
// the same picture underneath. Sets from a terminal get no transition: by
// the time wall_h changes, swaybg has already swapped.
Scope {
@@ -34,6 +34,7 @@ Scope {
property bool running: false
property bool done: false
property real progress: 0
+ property string effect: "burn"
property point origin: Qt.point(0.5, 0.5)
visible: running
@@ -47,12 +48,12 @@ Scope {
Connections {
target: Wallpapers
- function onSwapping(which, from, to) {
+ function onSwapping(which, from, to, effect) {
if (which !== win.modelData.which || !win.screen) return;
anim.stop();
- // Behind the char band, so no pixel starts out scorched.
- win.progress = -0.05;
- // A different place each time the fire starts.
+ win.effect = effect;
+ win.progress = 0;
+ // A different place each time it starts.
win.origin = Qt.point(Math.random(), Math.random());
win.done = false;
oldImg.source = from;
@@ -69,7 +70,7 @@ Scope {
}
}
- // Both decoded before the first block drops, or the old one
+ // Both decoded before anything moves, or the old one
// would pop in late over a half-drawn screen.
readonly property bool ready: running && oldImg.status === Image.Ready
&& newImg.status === Image.Ready
@@ -88,7 +89,7 @@ Scope {
// -m fill is a centred crop, which is what PreserveAspectCrop does.
// Both hidden until both are decoded: the new one usually lands
// first, and drawn under an old one still loading it flashed
- // the new picture before the burn. Until then the surface is
+ // the new picture before the transition. Until then the surface is
// clear and swaybg shows the old picture itself.
Image {
id: newImg
@@ -98,6 +99,8 @@ Scope {
sourceSize: Qt.size(win.width, win.height)
asynchronous: true
cache: false
+ // A layer, so the shaders that read it get the crop.
+ layer.enabled: win.running
}
Image {
@@ -109,11 +112,12 @@ Scope {
asynchronous: true
cache: false
layer.enabled: win.running
- layer.effect: ShaderEffect {
- property real progress: win.progress
- property real aspect: win.width / Math.max(1, win.height)
- property point origin: win.origin
- fragmentShader: Qt.resolvedUrl("burn.frag.qsb")
+ layer.effect: WallShader {
+ name: win.effect
+ target: newImg
+ progress: win.progress
+ aspect: win.width / Math.max(1, win.height)
+ origin: win.origin
}
}
@@ -121,10 +125,10 @@ Scope {
id: anim
target: win
property: "progress"
- // Past 1: fbm can push a threshold a little over it. Linear,
- // since an ease spends its slow ends where nothing burns.
- to: 1.05
- duration: 3500
+ // Linear, since an ease spends its slow ends where nothing
+ // happens.
+ to: 1
+ duration: Transitions.duration
easing.type: Easing.Linear
onFinished: if (win.done) win.running = false
}
diff --git a/desktop/modules/appearance/Wallpapers.qml b/desktop/modules/appearance/Wallpapers.qml
index 582fb22..2b8faf7 100644
--- a/desktop/modules/appearance/Wallpapers.qml
+++ b/desktop/modules/appearance/Wallpapers.qml
@@ -59,7 +59,7 @@ Singleton {
signal applied(bool ok, string message)
// Sent before wallp runs, while current still names the old picture, so
// WallpaperSpread can cover the swap with it.
- signal swapping(string which, string from, string to)
+ signal swapping(string which, string from, string to, string effect)
// Every run's end, Apply's or the engine's. applied is Apply's alone: it
// reports to the save bar and closes the window.
signal finished(bool ok)
@@ -333,8 +333,9 @@ Singleton {
applying = true;
root.kind = kind;
let covers = 0;
- if (h !== "" && currentH !== "" && h !== currentH) { covers++; swapping("H", currentH, h); }
- if (v !== "" && currentV !== "" && v !== currentV) { covers++; swapping("V", currentV, v); }
+ const effect = Transitions.next();
+ if (h !== "" && currentH !== "" && h !== currentH) { covers++; swapping("H", currentH, h, effect); }
+ if (v !== "" && currentV !== "" && v !== currentV) { covers++; swapping("V", currentV, v, effect); }
const args = [];
if (h !== "") args.push(`H=${JSON.stringify(h)}`);
if (v !== "") args.push(`V=${JSON.stringify(v)}`);
@@ -349,7 +350,7 @@ Singleton {
// Covers still to come up before wallp may swap swaybg under them. The
// cover decodes its pictures asynchronously, and starting wallp with
// swapping showed the new picture through the still empty cover for a
- // moment, then the old one over it, then the burn. WallpaperSpread
+ // moment, then the old one over it, then the transition. WallpaperSpread
// calls covered() once it is drawing; a cover that never comes (a
// screen missing, a picture that fails to load) is waited out.
property int uncovered: 0
diff --git a/desktop/modules/appearance/burn.frag b/desktop/modules/appearance/burn.frag
index a5cb9c3..4f91a81 100644
--- a/desktop/modules/appearance/burn.frag
+++ b/desktop/modules/appearance/burn.frag
@@ -45,7 +45,9 @@ void main() {
vec2 asp = vec2(aspect, 1.0);
float d = length((qt_TexCoord0 - origin) * asp) / length(max(origin, 1.0 - origin) * asp);
float t = mix(d, fbm(qt_TexCoord0 * asp * 5.0), 0.5);
- float e = t - progress;
+ // Starts behind the char band, so no pixel starts out scorched, and ends
+ // past 1, since fbm can push a threshold a little over it.
+ float e = t - (progress * 1.1 - 0.05);
if (e <= 0.0) { fragColor = vec4(0.0); return; }
// Front to intact paper: flame, then char, then the picture.
diff --git a/desktop/modules/appearance/burn.frag.qsb b/desktop/modules/appearance/burn.frag.qsb
index 599a911..5344661 100644
--- a/desktop/modules/appearance/burn.frag.qsb
+++ b/desktop/modules/appearance/burn.frag.qsb
Binary files differ