Refine the frame-as-length step: one window unit, region nodes as translations

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Fable 5.1 committed 2026-09-18 19:53:48 -04:00
1 parent f33981e1e6
commit aa1a85b316
1 file changed
+25 -12
+25 -12
View File
@@ -250,10 +250,20 @@ second draw back.
### 2. Make the frame a length and the box a region
The frame stops being a coordinate system. Coordinates start at a region
node (or the root) and every box under it is a region in that node's
coordinates; a widget's frame is a *length* of the node's box, per axis, and
is only what fractions resolve against:
The frame stops being a coordinate system. **There is one coordinate unit,
the window**: every box in the tree is a region in window units, a widget's
frame is a *length* in the same units and is only what fractions resolve
against, and a region node's move entry is a *translation* -- a region `rel
1` long anchored at the node's box start -- rather than a box. The last
point is forced, not chosen: a node whose unit was its frame would need its
box expressed in frame units, which is a division of two lengths and not a
`rel + px` length (a `.region_node()` child of a pad in a span room is the
case). Pending Bryan's yes, 2026-09-18: it changes one cost. A node that
moves, or scrolls, is one entry write whatever narrowed it (today only a
narrowed node gets that; a transparent one is re-expressed), and a node
whose box changes *length* at the same window size is re-expressed as a
subtree (today a narrowed node is one write). The shader's `resolve_move`
composes a `rel 1` entry as a translation already and does not change.
```rust
pub struct Painter<'a> {
@@ -264,9 +274,12 @@ pub struct Painter<'a> {
pub(super) frame: UiVec2,
/// Where this widget's drawing goes, in the node's coordinates.
pub(super) extent: UiRegion,
/// The node's box in pixels. The frame in pixels is `frame.to_px(node_px)`
/// and the box's is `extent.size().to_px(node_px)`.
pub(super) node_px: PxVec2,
/// The window in pixels: the frame in pixels is `frame.to_px(window)`
/// and the box's is `extent.size().to_px(window)`. Every `Holds` range
/// is in these pixels, so `in_parent` combines them without `through`,
/// and a widget's own frame reads convert once: `Holds::at(frame_px)
/// .through(frame)`.
pub(super) window: PxVec2,
...
}
@@ -314,15 +327,15 @@ What this changes in the core, and why it is not merely more code:
- `Painter::resolve` becomes `region.within(&self.extent)`; the second
`within` through the frame goes.
- `in_parent` no longer maps a child's frame holds through the frame length
per level: every widget under one node holds for the node's pixel box, so
the holds `and` directly, and only a region node maps through its box.
per level: every widget holds for the window's pixels, so the holds `and`
directly, at region nodes too.
- `Part::of` gains one `within_len` per `From` end. `Of` and `All` are as
they were.
- `DrawInfo::frame`/`frame_abs`, `ActiveData::frame`/`frame_abs`, `local`
in `Placing`, `recompose_subtree` and `asked_px` all change meaning:
`frame` is a `UiVec2` length, `frame_abs` goes, a node's move entry is the
node's frame length anchored at its box start (so `rel` inside means the
frame), and `asked_px` walks to the node rather than the root.
`frame` is a `UiVec2` length, `frame_abs` goes, a node's move entry is a
`rel 1` region at the node's box start, and `asked_px` becomes the window
size.
- `Stack` needs a part that is "a box of this length, placed by the child's
alignment" (`Part::Sized(Len)`, the same thing a declared length does),
since `narrow` no longer makes the box.