diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 531fa26..bc81120 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -6,8 +6,8 @@ found is in `docs/LAYOUT_LOG.md`. ## The Iris layout repair is submitted -**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `c2b8bf8`, -and past the reviewed `cadfba0` it is eleven rounds, each described in +**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `2dba90b`, +and past the reviewed `cadfba0` it is twelve rounds, each described in `docs/LAYOUT_LOG.md`: - **The repair**, `add6774` and `84dad21` -- collapsed-share placement, @@ -50,6 +50,11 @@ and past the reviewed `cadfba0` it is eleven rounds, each described in call sites, the helper the eighth sweep shared being unable to see the defect it had just fixed, two bare arrays where the framework names the pair, forty-five lines nothing references, and one word for two things. +- **A leftover as a minimum, and images in the trees**, `b295c8b` and + `2dba90b` -- a share under a parent that divides nothing losing the + overflow its pixels asked for, said as the place the parent gives and + sharing one comparison with the span; and `Image` grown in the generated + trees, which is the only widget here whose hint is a length in pixels. The settled design of the vocabulary rounds is in `docs/LAYOUT.md` under "Three names, and the one argument that says them". Bryan settled the API @@ -106,9 +111,9 @@ the name it is reachable by. **Always**, because they cost nothing: format, workspace clippy under `-D warnings` with and without `layout-diagnostics`, the workspace tests, and the **cold dump**. `layout_dump` over 400 depth-5 trees is **34,488** boxes --- counted again on 2026-09-20, the fourth through eighth sweeps having all -repeated 34,492, which is a `wc -l` of the whole run rather than of its box -lines -- and it is the only thing that catches two same-typed values being +-- **34,571 since `2dba90b` grew images in the trees**; counted again on +2026-09-20, the fourth through eighth sweeps having all repeated 34,492, which +is a `wc -l` of the whole run rather than of its box lines -- and it is the only thing that catches two same-typed values being swapped, which is the failure mode of a rename or a move. Count the lines that are a box (`grep -cE '^[0-9]+ [0-9]+ '`), not the output. The repair moved 650 of those boxes, all from the collapsed-share correction; every commit since has been @@ -178,11 +183,10 @@ glyphs do not follow a shortened entry. - A span can overflow itself without bound, so boxes of negative length reach children and nothing states what a widget may assume about one. - A `leftover` under a parent that does not divide is a minimum size -- - `max(box, px + rel*box)` (Bryan, 2026-09-20). A span does that; the - non-dividing path drops the overflow where the fixed part is longer than - the box. Measured in `docs/LAYOUT_LOG.md` under the ninth sweep, with the - window contract a fix needs. Nothing here mixes the two, so nothing is - wrong on screen. + `max(box, px + rel*box)` (Bryan, 2026-09-20), which is a `SizeRule::Min` of + `rel(1.0)` and shares `Painter::longer_than` with the span. Done in + `b295c8b` for a widget its parent asks; `root_layout` still reads the old + way, for which see `docs/LAYOUT_LOG.md`. - `Fixed::div` by zero answers `MIN`/`MAX` while `ratio` answers `ZERO`; both are caller bugs under `debug_assert`, but the fallbacks differ. - `docs/LAYOUT.md` §4, §5 and the density section name `Painter::place`, @@ -191,7 +195,9 @@ glyphs do not follow a shortened entry. `OnResize::Translate` or `OrthoSize`. - `LazySpan`, then `SizeRule::{Min, Max, Clamp}`. A cap may not contain `leftover`; whether `Max` narrows the child's drawing box is a product - decision. + decision. `Min` is `Painter::longer_than` again -- a `leftover` where + nothing divides it is already a `Min` of `rel(1.0)` -- so it belongs on + that path rather than beside it. - `Scroll` taking a direction rather than one axis. Other product work is in `docs/PLAN.md` and the focused documents it links. diff --git a/docs/LAYOUT_LOG.md b/docs/LAYOUT_LOG.md index 73f92da..ab57be0 100644 --- a/docs/LAYOUT_LOG.md +++ b/docs/LAYOUT_LOG.md @@ -6,6 +6,28 @@ nothing here is rediscovered. Each entry says who found it and when. it (settled design, the measurement method) belongs in `docs/LAYOUT.md`, and the current plan is in `docs/HANDOFF.md`. +## Images in the generated trees (2026-09-20) + +`Image` is the only widget here whose size hint is a length in pixels, so it is +the only one that exercises a rule beside a hint -- which is what the two +findings above are about, and why neither was reachable from a generated tree. +Bryan asked for one in the trees, as a committed 64x64 checkerboard reusing one +texture handle. `2dba90b`: `Kind::Image` is a fifth leaf drawn one time in five, +stepping to a plain rect under the shrinker; the picture is +`src/assets/checkerboard.png`, purple and black in 8 px cells, included rather +than opened; and a `TextureHandle` is a counted reference, so the first image in +a tree uploads and the rest clone it. Measured: seed 1 at depth 4 grows 13 +images and holds 1 texture, and `a_tree_of_images_uploads_one_texture` asserts +it. + +The cost is that a seed no longer names the tree it used to, so the corpus in +`generated.rs` is a corpus and not a set of regression cases -- 20 and 86 no +longer grow the trees whose defects they caught. Both live on as shrunk fixtures +in `unsettled.rs`, which are trees rather than numbers, and both files now say +so. New dump baseline: **34,571** boxes over the 400 depth-5 trees. All three +scans pass over the new trees (400 at depth 5 in 62.79s, 1,000 at depth 6 in +160.20s, 2,000 at depth 4 in 299.58s). + ## Ninth sweep: the widget vocabulary, and the eighth sweep's fix (2026-09-20) Over the part no earlier round named -- `src/widget/trait_fns.rs` and @@ -65,16 +87,35 @@ learns the fixed part and falls back to the offer. The same length without the share does overflow (drawn -50..450, its alignment centring it), so what swallows it is the share and not the overflow. -Fixing it means answering the longer of the offer and the fixed part, which is -not a `Len`: a maximum of two linear forms is not linear in the box, so it has -to be resolved where the box is known and the crossover pinned as a window -contract -- which is exactly what `Span` does with `has_room` and -`painter.window_holds(axis, holds.through(room))`. In `widget_at` that range -belongs to the parent, whose box decides it, so a parent of such a child would -redraw across the crossover. **Not done: it is what a length means, and it is -Bryan's to say whether that cost is wanted.** Nothing in the repository mixes -`px` or `rel` with `leftover` outside the span's own test, so nothing is wrong -on screen today. +**Fixed in `b295c8b`**, after Bryan asked for it if it was cheap and pointed out +that the machinery is a minimum size of `rel(1.0)` and should share a path with +one. What made it cheap was saying it as the place the parent gives rather than +as a declaration: where the fixed part is the longer, `widget_at` hands the +child `fixed.as_desc().fills()`, which is what a declared length already comes +to, and `active.placed` keeps it -- so nothing about `Declared`, `ActiveData` or +the resize path had to change. A place that is already the child's placement is +skipped, because a parent that divides has given the share whatever it was owed; +without that guard a span's slot was re-placed and its child moved from 0..500 +to -50..450. + +The comparison is one operation now, `Painter::longer_than`, and both callers +share it: the span's room for the shares it divides, and a share past the box it +was given. It narrows the widget's window range where the span replaced it, +since a comparison the framework makes on an arbitrary parent's behalf is one +more reason its drawing holds rather than the only one. `SizeRule::Min` of +`rel(1.0)` is the same operation and belongs on the same path when it lands. + +Two things were tried and dropped. An escalation for a changed rule +(`shares_past_a_length`) turned out to buy nothing: the reported size is the +rule resolved, so a changed rule changes the answer and the parent refuses its +own drawing -- the tests pass without it. And `Holds::crossing`, a named +constructor for the range, had one caller and read better spelled there. + +**The root still reads the old way.** `root_layout` asks +`Widgets::declared_lens` with no painter, so a mixed share on the root is the +window rather than the longer of the two. It would mean the comparison in a +second place, and the root's box is the window and is relaid out on every +resize; nothing here puts a share on a root. **Marking a widget for redraw had no name.** Twenty-one sites under `tests/` said it as `widgets_mut().get_dyn_mut(id);` with the widget thrown away: five