diff options
Diffstat (limited to 'docs/superpowers/specs')
| -rw-r--r-- | docs/superpowers/specs/2026-11-06-wallpaper-transitions-design.md | 122 |
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. |
