feat: constrain flex bases and redistribute bounded growth and shrink
Verify Tessera GUI / verify (push) Failing after 9m4s

This commit is contained in:
2026-10-23 00:42:48 +08:00
parent 78fb66277d
commit 1fda07ebc5
12 changed files with 298 additions and 55 deletions
+6 -3
View File
@@ -1085,9 +1085,9 @@ ContainerBlock.insert(position,block) accepts a zero-based position through the
Source measure and arrange overrides execute bottom-up and top-down in one read-only VM layout call. Structural Mount links exist before first measurement; bounds queries reject premature geometry. Native base layout uses current occurrence child measurements, and Arrangement must assign every stable child slot exactly once. Layout dependencies changed by lifecycle hooks trigger another pass before publication. Page constraints now use the current logical window viewport. Column/Button propagate cross-axis width and Row propagates cross-axis height; desired sizes are clamped. ui.viewport() provides a managed snapshot whose private input participates in rendering dependencies. Native resize updates are coalesced on the same worker without adding source inputs. Source measurement caches use Mount/method/children/constraints and actual subcall object reads, including returned-result decoding; Mount-reading methods conservatively bypass caching. ui.stats() exposes sourceMeasureHits/sourceMeasureMisses outside read-only rendering. Native TextBlock wrapping now uses the occurrence width in measurement/cache/painting; source measurement/arrangement now repeats until constraints and geometry settle; additional allocation policies remain pending; ScrollBlock scrolling is described below.
Source layout solves repeat in one read-only call until constraints/sizes/bounds are unchanged, reusing one bytecode frame. Source-arranged changed dimensions become exact per-axis constraints for this solve; source container measurements differing from native results constrain content using the same container policy. Selected limits reset on later inputs/resizes. Nonconsecutive repeated states report a cycle. No fixed pass/hop cutoff is used; all passes share the 1000000-instruction/128-frame budget and budget failure is explicit. Measurement clears provisional Mount geometry, and only complete settled frames publish. ui.stats().layoutPasses reports work; source/cache counters include all passes. VM annotation checks distinguish validation-only from content reads, preserving Mount-typed independent measurement caching; actual Mount reads still bypass conservatively. General flex allocation and resumable solving remain pending. See examples/layout-negotiation.endor.
Source layout solves repeat in one read-only call until constraints/sizes/bounds are unchanged, reusing one bytecode frame. Source-arranged changed dimensions become exact per-axis constraints for this solve; source container measurements differing from native results constrain content using the same container policy. Selected limits reset on later inputs/resizes. Nonconsecutive repeated states report a cycle. No fixed pass/hop cutoff is used; all passes share the 1000000-instruction/128-frame budget and budget failure is explicit. Measurement clears provisional Mount geometry, and only complete settled frames publish. ui.stats().layoutPasses reports work; source/cache counters include all passes. VM annotation checks distinguish validation-only from content reads, preserving Mount-typed independent measurement caching; actual Mount reads still bypass conservatively. Resumable solving remains pending. See examples/layout-negotiation.endor.
TextBlock now wraps words/glyphs at its available width and measures the resulting height. Native and source-base measurement paths use current constraints; native shape/font caches include wrap width. Painting carries allocated width/height, clips glyphs and keeps ancestor scroll clipping. Source text PaintCommand may supply both wrapWidth/wrapHeight (finite nonnegative logical dimensions up to 1000000); neither preserves legacy unwrapped paint, and a partial pair is rejected. Native TextBlock.paint supplies the pair. These are derived draw bounds, not another business object. Management UI text remains unwrapped. General flex basis/min/max, fixed-child shrinking, resumable solving and broad fractional-DPI validation remain pending. See examples/wrapped-text.endor.
TextBlock now wraps words/glyphs at its available width and measures the resulting height. Native and source-base measurement paths use current constraints; native shape/font caches include wrap width. Painting carries allocated width/height, clips glyphs and keeps ancestor scroll clipping. Source text PaintCommand may supply both wrapWidth/wrapHeight (finite nonnegative logical dimensions up to 1000000); neither preserves legacy unwrapped paint, and a partial pair is rejected. Native TextBlock.paint supplies the pair. These are derived draw bounds, not another business object. Management UI text remains unwrapped. Resumable solving and broad fractional-DPI validation remain pending. See examples/wrapped-text.endor.
Source paint overrides execute in the actual frame pipeline under a read-only heap policy. Source hitTest overrides similarly refine native pointer targets in VM paint order; Point.inside(bounds) and Point.inCircle(cx, cy, radius) provide primitive shape queries. Managed DisplayList/ObjectList/PaintCommand and Constraints/Bounds/Point results are ordinary runtime objects.
@@ -1119,7 +1119,10 @@ ScrollBlock.axis is a managed visual attribute: "both" (default), "vertical" or
ContainerBlock.mainAlign accepts "start" (default), "center", "end" or "space-between". Native arrangement uses allocated Bounds after padding/minimum gap, distributes positive remaining main-axis space and keeps child sizes unchanged. Overflow keeps start placement. Explicit source arrange placements remain authoritative; this is alignment, not flex sizing.
Block.flex is a nonnegative weight (default 0). Native containers reserve fixed measured children, padding and gaps, then share remaining allocated main-axis space between positive weights with zero basis. Actual child allocation feeds remeasurement. It does not request parent expansion or implement general flex basis/min/max/fixed-child shrinking.
Block.flex is a nonnegative weight (default 0). Native containers reserve fixed measured children, padding and gaps, then share remaining allocated main-axis space between positive weights with zero basis. Actual child allocation feeds remeasurement. It does not request parent expansion. Further controls are described below.
Flex main-axis policy: flexBasis is "auto" by default (zero basis when flex > 0, otherwise the first measured size of this occurrence in the current solve); a number supplies an explicit basis. flexMin defaults to 0, flexMax to 1000000, and flexShrink to 0. All numbers are finite/nonnegative and at most 1000000; flexMin must not exceed flexMax. The parent clamps bases to min/max, distributes positive space by flex, and redistributes space left after a maximum is reached. Deficits shrink by flexShrink times the original basis, freezing at minima. A fixed child can opt into shrink without growth. Unresolvable minimum overflow is preserved, and remaining positive space follows mainAlign. The policy applies to width in a Row and height in Column-like containers; these are not independent width/height bounds. Source arrange overrides remain authoritative. Preferred sizes are derived per occurrence and reset for each new solve, never shared through Block state. See examples/flex-constraints.endor. Layout continuation remains unfinished.
## UI host object