Artwork appeared in one step the instant its pixels were ready, which
reads as a glitch on a page where covers land a few at a time.
It belongs in this file and nowhere else: AlbumCard, PlaylistCard,
TrackCard, ArtistCard, SlimCard, LabelCard and RadioCard all mount a
RoundedImage, and 81 files in the tree do. Per-card animations would be
seven copies that drift, and would still miss the detail headers and the
rows.
The trigger is `ready`, not the path and not `status`. `ready` is this
file's readiness contract, MONOTONE WITHIN ONE SOURCE, which is exactly
what a fade needs: the derivative swap sends `status` back to Loading
mid-life, and fading again there would flash a cover already on screen.
A recycled delegate does re-fade, which is correct — it is a different
cover. The Component.onCompleted arm is not belt-and-braces: an
already-cached pixmap reaches Image.Ready DURING creation, before
onReadyChanged is connected, and without it that cover would sit at
opacity 0 forever.
Measured against the repaint doctrine rather than argued:
at rest 0 presents/s (byte-identical frames over 3 s)
during a page load Home 5.7 -> 6.0/s, Library 11.1 -> 12.4/s
Qt advances every running animation on one tick and the window presents
once per frame however many items are dirty, so fifty covers arriving
together cost the same presents as one. OpacityAnimator, not a Behavior,
so the fade keeps advancing while the GUI thread is still paying off a
mount — which is precisely when covers land.
cc