1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
# 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`, `TransitionRow.qml` and
`TransitionView.qml`, in the Appearance group, listed in `shell.qml` after
`WallEngineModule`. A `MockScreen` at the top, then one line per transition
and one for Random, under the headings Transition, Active and In Random.
- Each line shows a frame halfway through its transition. A click on the line
plays it on the mock monitor and changes nothing else.
- The Active column is a radio mark, editing the draft's pick. Marking a
transition also plays it. Random has no frame and plays nothing.
- A `Switch` per transition edits the draft's pool, and stays available
whatever is Active.
- `SaveBar` sits in the header through `Page.actions`, with `saveText` Apply
and `discardText` Reset.
- `valid` is false when Random is Active with nothing switched on, and the
note says so.
- `TransitionView` plays `Wallpapers.currentH` into `Wallpapers.currentV`, so
no gallery scan is needed. It uses a random `origin` per play on the monitor
and a fixed one in the frames. With either path empty it draws nothing.
## 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.
|