feat(qt): reduceMotion seam and a coarse skeleton tick

The owner ruled 3-C: build the seam here so the kiosk port consumes it
instead of having to invent it. The earlier plan was to wait for kiosk to
define it, but kiosk is not implemented - it is a contract - so "wait for
kiosk" meant waiting on a date that does not exist.

The shape is no longer a guess, which was the only objection to building it
early. Two independent sources fix it: the kiosk contract mandates "FORCED
reduce-motion while in kiosk", and the reference computes the flag as
kiosk_profile || !use_gpu_renderer.

QbzShell.reduceMotion is derived, never a preference - do not add a Settings
row for it. The existing play-indicator toggle is a preference and a
different thing; binding them would make "animate the play indicator" change
this dropdown's pulse rate.

Only the kiosk half is wired, and the omission is deliberate rather than
partial: the renderer tier has no honest source here. It is a QML-side fact,
this port runs on the GPU in every real session, and the one place a probe
would report software is the offscreen smoke, which forces it by definition.
Wiring it would turn reduce-motion on exactly where it means nothing. The
kiosk literal is a named const with a pointer to their contract; flipping it
is one line on their side.

The cortinilla skeleton is the ONE consumer: its single host Timer drops
from 700ms to 2000ms. No new timer, no per-delegate animation - the
reference's own note records that this pulse was the main cost of opening
the cortinilla on weak GPUs.

Giving that Column an id also fixes a real scope bug on the way past. The
Timer read an unqualified `parent`, and Timer derives from QtObject, which
has no parent property - so it resolved up the scope chain, out of the block
entirely. The sibling rectangles' parent.parent[.parent] chains are now
explicit too.

EqualizerBars.qml and the immersive contract's D11 both recorded that Qt had
no reduce-motion twin. Both are corrected: the seam exists, those surfaces
are simply not consumers yet, and wiring each is its own one-liner.

Contract: qbz-nix-docs/qt-frontend/2026-08-03-cortinilla-parity, commit C4b
(section 5.g G1-G5, ruling R10; D-BUG-1 folded in).

Gate: build clean, 258/258 tests, both QML audits OK, offscreen smoke
0 complaints + home published. AC-39: exactly one behavioural consumer.

cc
This commit is contained in:
vicrodh committed 2026-08-03 16:48:07 -06:00
1 parent 033b838b98
commit 4e1703c436
3 files changed
+58 -7

No files matched your search

+15 -6
View File
@@ -193,15 +193,24 @@ Item {
// --- Loading skeleton (no cached instant-paint — one clean
// apply, Cortinilla.slint) -------------------------------
Column {
id: skeleton
visible: QbzSearch.cortinillaLoading
width: parent.width
topPadding: 4
property bool pulse: false
// ONE host Timer flipping ONE bool for all five rows —
// never a per-delegate animation. Under reduce-motion it
// drops to a ~8fps coarse tick, which is what the
// reference switches its pulse's time source to
// (Cortinilla.slint:128-131). That file records why: this
// pulse was the main cost of opening the cortinilla on
// weak GPUs, because every frame was a full-window
// repaint.
Timer {
interval: 700
running: parent.visible
interval: QbzShell.reduceMotion ? 2000 : 700
running: skeleton.visible
repeat: true
onTriggered: parent.pulse = !parent.pulse
onTriggered: skeleton.pulse = !skeleton.pulse
}
Repeater {
model: 5
@@ -215,7 +224,7 @@ Item {
height: 40
radius: 4
color: theme.surfaceElevated
opacity: parent.parent.pulse ? 0.48 : 0.16
opacity: skeleton.pulse ? 0.48 : 0.16
Behavior on opacity { NumberAnimation { duration: 650; easing.type: Easing.InOutQuad } }
}
Column {
@@ -227,7 +236,7 @@ Item {
height: 11
radius: 3
color: theme.surfaceElevated
opacity: parent.parent.parent.pulse ? 0.48 : 0.16
opacity: skeleton.pulse ? 0.48 : 0.16
Behavior on opacity { NumberAnimation { duration: 650; easing.type: Easing.InOutQuad } }
}
Rectangle {
@@ -235,7 +244,7 @@ Item {
height: 9
radius: 3
color: theme.surfaceElevated
opacity: parent.parent.parent.pulse ? 0.48 : 0.16
opacity: skeleton.pulse ? 0.48 : 0.16
Behavior on opacity { NumberAnimation { duration: 650; easing.type: Easing.InOutQuad } }
}
}