Record why a move recomposes, and settle alignment's ownership
The measurement is the part worth keeping: adding an offset to a moved subtree's stored regions drifts linearly and without bound, so the fraction path is there for accuracy, not generality. `tests/drift.rs` on the Iris branch pins it. Built-in alignment and size moves onto #18 rather than waiting for it, with ownership agreed -- properties on the `region_node` mechanism, a declared length winning per axis, and a size rule that carries min/max/clamp so a clamp is resolved where the box is decided instead of by a widget reading its own pixels.
This commit is contained in:
1 parent
8db0969d9d
commit
cf7b33da39
1 file changed
+57
-11
+57
-11
@@ -8,7 +8,7 @@ log.
|
|||||||
|
|
||||||
Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18
|
Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18
|
||||||
`split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local
|
`split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local
|
||||||
head is **`71c9c39`**, forty-seven commits, pushed to the fork. No PR reviews
|
head is **`0283c9d`**, forty-eight commits, pushed to the fork. No PR reviews
|
||||||
were present when checked on 2026-09-15; the verification summary is posted on
|
were present when checked on 2026-09-15; the verification summary is posted on
|
||||||
the PR.
|
the PR.
|
||||||
|
|
||||||
@@ -70,11 +70,11 @@ rewrite:
|
|||||||
`core/src/ui/holds.rs`, the retained tests, and the generated cold-layout oracle
|
`core/src/ui/holds.rs`, the retained tests, and the generated cold-layout oracle
|
||||||
pin those rules. Seeds 10 and 86 are now in the ordinary generated set.
|
pin those rules. Seeds 10 and 86 are now in the ordinary generated set.
|
||||||
|
|
||||||
## Verification at `71c9c39`
|
## Verification at `0283c9d`
|
||||||
|
|
||||||
- `cargo fmt --all --check`
|
- `cargo fmt --all --check`
|
||||||
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
|
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
|
||||||
- `cargo test --workspace --all-features`: 84 passed, 10 ignored
|
- `cargo test --workspace --all-features`: 85 passed, 10 ignored
|
||||||
- `cargo test --release --test generated -- --ignored
|
- `cargo test --release --test generated -- --ignored
|
||||||
a_long_run_of_seeds_agrees --exact`: 100 seeds passed in 64.77 s
|
a_long_run_of_seeds_agrees --exact`: 100 seeds passed in 64.77 s
|
||||||
- The `tabs` reference at 900x1200 remained byte-identical to `f437495`: zero
|
- The `tabs` reference at 900x1200 remained byte-identical to `f437495`: zero
|
||||||
@@ -158,6 +158,19 @@ tighter figures.
|
|||||||
- `Scroll` reports its content's first measured size. Its drawing can survive
|
- `Scroll` reports its content's first measured size. Its drawing can survive
|
||||||
container-length changes only over the interval in which clamping and its
|
container-length changes only over the interval in which clamping and its
|
||||||
current offset do not change.
|
current offset do not change.
|
||||||
|
- **A move must re-express each part as the same fraction of its new box, not
|
||||||
|
add an offset to the last answer.** A subtree's stored regions are the only
|
||||||
|
record of where it is, so an offset integrates its own rounding and nothing
|
||||||
|
recomputes it. Measured 2026-09-15 on `tests/drift.rs`: offsetting both ends
|
||||||
|
of a span shortens that fixture's row by 0.071 over 20,000 moves and 0.712
|
||||||
|
over 200,000, growing with the count; placing the far end from the near one
|
||||||
|
leaves 0.069, because the length is re-derived from the endpoints either
|
||||||
|
way. 20,000 moves is five minutes of scrolling at 60Hz, which is when it
|
||||||
|
passes the 0.05 physical pixels layout treats as the same place. The
|
||||||
|
fraction path is exact at 200,000. So `RegionRemap` is load-bearing for
|
||||||
|
accuracy rather than for generality, and its multiplies are not what is
|
||||||
|
being paid for. The generated oracle compares within 0.05 and `unsettled.rs`
|
||||||
|
runs too few frames to reach it, which is why `tests/drift.rs` exists.
|
||||||
|
|
||||||
## Rigs and reproduction
|
## Rigs and reproduction
|
||||||
|
|
||||||
@@ -213,15 +226,48 @@ instruction totals.
|
|||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
Let PR #18 review. The next small LAYOUT.md §2 item is `LazySpan`. Region nodes
|
The next small LAYOUT.md §2 item is `LazySpan`. Region nodes now cover the
|
||||||
now cover the independently movable-subtree use case; do not restore a
|
independently movable-subtree use case; do not restore a separate
|
||||||
separate child-placement API.
|
child-placement API. `docs/LAYOUT.md` §2 is stale: it still describes
|
||||||
|
`Painter::place`, which `71c9c39` replaced.
|
||||||
|
|
||||||
After #18 lands, the next structural design is built-in alignment and size.
|
**Built-in alignment and size is next, and goes on #18 rather than after it**
|
||||||
Today a declared size and the `Size` returned by `draw` are two sources of
|
(Bryan, 2026-09-15: #18 is unreviewed and already large enough that most lines
|
||||||
truth, and alignment can disappear behind a wrapper. Agree their ownership
|
get read anyway). Ownership is agreed:
|
||||||
before implementing it. Do not restore `OnResize::Translate`; retained
|
|
||||||
translation is now expressed by the same `Holds` contract and box chain.
|
- Declared size and alignment become **widget properties on the same mechanism
|
||||||
|
as `region_node`**, read by `Painter::widget_at`. `SetSize`, `MaxSize` and
|
||||||
|
`Aligned` are deleted, and `.width()`, `.sized()`, `.max_width()`, `.align()`
|
||||||
|
and `.center()` stop building widgets. `Painter` then owns measure-then-place,
|
||||||
|
which `Span`, `Aligned` and `Scroll` each implement separately today.
|
||||||
|
- A **declared length wins per axis**; the `Size` returned by `draw` answers
|
||||||
|
only the axes with no declaration. The rule becomes an enum per axis --
|
||||||
|
`Exact`, `Min`, `Max`, `Clamp` -- so a clamp is resolved where the box is
|
||||||
|
decided instead of by a wrapper. `MaxSize::draw` currently calls
|
||||||
|
`px_size()`, which pins its interval to one exact box on both axes and
|
||||||
|
redraws its whole subtree on any resize; the parent resolving the clamp
|
||||||
|
reads px itself and can declare the interval over which the answer holds.
|
||||||
|
- The clamp boundary is a **hard layout decision, not a tolerance**, the same
|
||||||
|
shape as `Span`'s leftover split. Its `Holds` range must be exact and split
|
||||||
|
at the crossover; generated seed 16 is what a tolerant endpoint costs.
|
||||||
|
- Two things to settle while implementing. `max_width` today does not
|
||||||
|
constrain anything -- `MaxSize` draws its child in its full box and only
|
||||||
|
caps the `Size` it reports upward -- so resolving the rule in the box is a
|
||||||
|
behaviour change. And `declared_len` derives from `size_hint`, so one
|
||||||
|
channel carries both "my size, for a span to divide around" and "resolve my
|
||||||
|
box to this length"; those separate.
|
||||||
|
|
||||||
|
Why it comes before anything else that touches layout: a widget's `region`
|
||||||
|
becomes **its reported size placed by its alignment inside the offer**, rather
|
||||||
|
than the offer itself. A span's measure-then-place then moves a child between
|
||||||
|
boxes of the same length, so it is a translation by construction, and the
|
||||||
|
measuring draw disappears wherever the size is knowable without one. That
|
||||||
|
changes what `.background()` sits behind, so the reference renders stop being
|
||||||
|
byte-identical to `ca2b4b2` and the generated cold-layout oracle becomes the
|
||||||
|
verification.
|
||||||
|
|
||||||
|
Do not restore `OnResize::Translate`; retained translation is now expressed by
|
||||||
|
the same `Holds` contract and box chain.
|
||||||
|
|
||||||
Other queued work, in dependency order:
|
Other queued work, in dependency order:
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user