Hold the output as the box every chain bottoms out in

A position was composed up the slot chain to a normalized region and then
multiplied by the output's size, so the window was the one box in the
system that was not a box. Seeding the chain with a root slot holding it
in pixels makes composing through it leave everything below in pixels,
which is what the multiplication was doing.

`within` already does the arithmetic: a child at `rel` 1 inside a span of
`px` 0 to `px` 1920 composes to `px` 1920 and `rel` 0, so the trailing
`to_px` becomes the identity rather than a step. The shader walks the
same chain and needs no change for the same reason.

This is the shape the resize machinery wants before it can be deleted: a
resize becomes one slot written, which `try_reuse` and `redraws_under`
already carry. Nothing is removed yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-14 22:32:53 -04:00
1 parent 7c50a3e51b
commit 9f4311774b
2 files changed
+28 -3

No files matched your search

+23 -2
View File
@@ -2,7 +2,7 @@
use crate::layout_diagnostics::{self as diag, Counter, ReuseOutcome, TimerKind};
use crate::{
ActiveData, Axis, DrawLayers, IdLike, MaskIdx, MoveIdx, Moves, OnResize, Painter, PixelRegion,
Size, StrongWidget, UiRegion, UiRsc, WidgetId, Widgets,
Size, StrongWidget, UiRegion, UiRsc, UiScalar, UiSpan, WidgetId, Widgets,
util::{HashMap, HashSet, Vec2},
};
@@ -19,6 +19,8 @@ pub struct UiRenderState {
pub(super) output_size: Vec2,
old_root: Option<WidgetId>,
/// The slot every chain bottoms out in, holding the output as a box.
root_move: MoveIdx,
resized: [bool; 2],
/// Content/state dirtiness whose retained size cannot answer a layout
/// question until that widget has drawn again.
@@ -50,6 +52,22 @@ impl UiRenderState {
draw_started: Default::default(),
slots: Default::default(),
moves: Default::default(),
root_move: MoveIdx::NONE,
}
}
/// The window as a box, so a chain bottoms out in one rather than in a
/// multiplication applied after it. Composing through a box held in
/// pixels leaves everything below it in pixels, which is why nothing
/// downstream has to know the output's size to resolve a position.
fn write_root(&mut self) {
let region = UiRegion::new(
UiSpan::new(UiScalar::ZERO, UiScalar::px(self.output_size.x)),
UiSpan::new(UiScalar::ZERO, UiScalar::px(self.output_size.y)),
);
match self.root_move == MoveIdx::NONE {
true => self.root_move = self.moves.push(MoveIdx::NONE, region),
false => self.moves.set(self.root_move, region),
}
}
@@ -59,6 +77,7 @@ impl UiRenderState {
*resized |= size.axis(axis) != self.output_size.axis(axis);
}
self.output_size = size;
self.write_root();
}
pub fn output_size(&self) -> Vec2 {
@@ -149,6 +168,7 @@ impl UiRenderState {
let _layout = diag::timer(TimerKind::FullLayout);
self.clear(rsc);
// free all resources & cache
self.write_root();
if let Some(id) = root {
self.draw_inner(
0,
@@ -156,7 +176,7 @@ impl UiRenderState {
UiRegion::FULL,
None,
1,
MoveIdx::NONE,
self.root_move,
false,
MaskIdx::NONE,
None,
@@ -589,6 +609,7 @@ impl UiRenderState {
}
self.slots.clear();
self.moves.clear();
self.root_move = MoveIdx::NONE;
self.layers.clear();
self.invalid_sizes.clear();
self.resize_marks.clear();
+5 -1
View File
@@ -180,5 +180,9 @@ fn only_a_container_that_places_its_children_lengthens_the_chain() {
h.set_root((bar, buried).span(Dir::RIGHT));
let slot = h.render.active[&leaf.id()].parent_move;
assert_eq!(h.render.moves.depth(slot), 1, "one span above the leaf");
assert_eq!(
h.render.moves.depth(slot),
2,
"the span above the leaf, and the root the window is held in"
);
}