aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-06 17:42:18 +0100
committerDanilo M. <danix@danix.xyz>2026-11-06 17:42:18 +0100
commit187682aa4bd212632204d23706c5cb317028b5d3 (patch)
treeab30ae7394eedbfa007a2f7e9a2fb3ed1a55266b /docs/superpowers/specs
parent2ae4deaf7ac420ad5871e3957fb027a856ce7edf (diff)
downloadquickshell-187682aa4bd212632204d23706c5cb317028b5d3.tar.gz
quickshell-187682aa4bd212632204d23706c5cb317028b5d3.zip
wallpaper: choose a transition, by name or at random
A named pick plays as it is; random draws from the ticked pool and skips what played last while anything else is there. Saved values are checked here too, so a stale or hand-edited name lands on something playable. A JS library rather than QML, to test it under node as Shuffle.js is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/superpowers/specs')
-rw-r--r--docs/superpowers/specs/2026-11-06-wallpaper-transitions-design.md122
1 files changed, 122 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-11-06-wallpaper-transitions-design.md b/docs/superpowers/specs/2026-11-06-wallpaper-transitions-design.md
new file mode 100644
index 0000000..7a9d4b7
--- /dev/null
+++ b/docs/superpowers/specs/2026-11-06-wallpaper-transitions-design.md
@@ -0,0 +1,122 @@
+# Wallpaper transitions
+
+Apply and the wallpaper engine play one effect today, the burn. This adds six
+more, a settings page to preview and choose between them, and a Random option
+that draws from a chosen subset.
+
+## Transitions
+
+`burn`, `ink`, `shatter`, `ripple`, `melt`, `blinds`, `crosswarp`.
+
+| Name | Effect |
+| --- | --- |
+| burn | the existing effect, unchanged to the eye |
+| ink | old picture dissolves through warped noise, soft edge |
+| shatter | Voronoi cells crack outward from `origin`, each sliding and fading on its own delay |
+| ripple | expanding ring refracts the picture, new one revealed behind it |
+| melt | columns drip down at noise-driven speeds |
+| blinds | slats rotate shut in a wave across the screen |
+| crosswarp | both pictures stretch along their brightness and flow into each other |
+
+## Shaders
+
+One `.frag` and one compiled `.qsb` per transition, in
+`desktop/modules/appearance/`, following `burn.frag`. An uber-shader with a
+`mode` uniform was rejected: every effect would pay for every branch, and one
+error would take out all seven.
+
+Each shader declares what it uses out of `progress`, `aspect`, `origin`,
+`source` (old picture) and `target` (new picture). The wrapper's properties
+are a superset; a property with no matching uniform is harmless, the reverse
+is not.
+
+Every shader must meet two conditions, because the cover appears over and
+leaves over swaybg:
+
+- at `progress` 0 it draws the old picture exactly;
+- at `progress` 1 it draws the new picture exactly, by full transparency over
+ `newImg` or by sampling `target`.
+
+`progress` runs 0 to 1 for all of them. `burn.frag` remaps it internally, with
+`progress * 1.1 - 0.05`, to keep the range it was tuned for, and the cover's
+`-0.05` start and `1.05` end go away.
+
+## WallShader.qml
+
+One `ShaderEffect` wrapper with `name`, `source`, `target`, `progress`,
+`aspect` and `origin`, resolving `name` to its `.qsb`. Both the cover and the
+thumbnails use it, so a preview runs the real shader. It is not called
+`Transition`: QtQuick has that type and it would shadow the file.
+
+In `WallpaperSpread.qml` it replaces the `ShaderEffect` in `oldImg`'s
+`layer.effect`. `newImg` gains `layer.enabled` so it can be passed as `target`.
+Both pictures must reach the shader through a layer: an `Image` used directly
+as a texture gives the whole picture, not the `PreserveAspectCrop` crop, and
+the cover would no longer line up with swaybg.
+
+## Transitions.qml
+
+A singleton beside `WallEngine.qml`, holding:
+
+- `all`, the seven names with their labels;
+- `duration`, 3500ms, used by the cover and the thumbnails;
+- `pick`, a transition name or `"random"`, and `pool`, the ticked names, both
+ saved;
+- `draftPick`, `draftPool`, `dirty`, `valid`, `apply()`, `reset()`;
+- `next()`, returning the name to play for a run.
+
+`next()` returns `pick` unless it is `"random"`, in which case it draws from
+`pool`, excluding the last name played when the pool holds two or more. The
+draw and the validation of saved values live in `TransitionPick.js` beside
+it, as `Shuffle.js` does, so they can be tested outside `qs`.
+
+Saved through a new `Prefs.setWallTransition()`, under `wallTransition` in
+`desktop.json`, default `{ pick: "burn", pool: <all seven> }`, so nothing
+changes until the user chooses. An unknown name read from the file is dropped
+from `pool`, and an unknown `pick` falls back to `burn`. `next()` falls back
+to `burn` if the pool ends up empty.
+
+## Settings page
+
+`TransitionsModule.qml`, `TransitionsTab.qml` and `TransitionTile.qml`, in the Appearance group,
+listed in `shell.qml` after `WallEngineModule`. A grid of seven tiles plus a
+Random tile.
+
+- Clicking a tile selects it in the draft and plays it once in its thumbnail,
+ then it rests. Clicking it again replays it. Random selects without playing.
+- Each of the seven tiles carries a tick, which edits the draft's pool. Ticks
+ stay available whatever is selected.
+- `SaveBar` sits in the header through `Page.actions`, with `saveText` Apply
+ and `discardText` Reset.
+- `valid` is false when Random is selected with nothing ticked, and the note
+ says so.
+- A thumbnail plays `Wallpapers.currentH` into `Wallpapers.currentV`, so no
+ gallery scan is needed. It uses a random `origin` per play. With either path
+ empty the tile is flat and still selectable.
+
+## Runtime
+
+`Wallpapers.run()` calls `Transitions.next()` once per run and passes the name
+as a fourth argument of `swapping`, so both screens play the same effect.
+`WallpaperSpread` sets it on its `WallShader`. Apply and the engine share
+`run()`, so both follow the choice. Sets from a terminal still get no
+transition.
+
+## Left out
+
+A duration setting, a choice per screen, and a full-screen try button.
+
+## Checks
+
+- All seven `.frag` compile with `qsb --qt6`.
+- `test_transitions.js` covers the draw: the pool is respected, no immediate
+ repeat with two or more, a pool of one repeats, an empty pool gives `burn`.
+- A fresh `qs -p desktop` loads with no new warnings.
+- The motion itself is judged by eye by the user. A `shell.qml` edit needs
+ `./reload.sh desktop` from the user's terminal.
+
+## Documentation
+
+`desktop/modules/appearance/README.md`: "The burn" becomes a section on
+transitions, and the page is described. `AGENTS.md`: the settings window's
+page list gains transitions.