Skip to main content

Module layout

Module layout 

Source
Expand description

Layout engine trait and Taffy backend.

§Design decisions

§Trait surface (plan adaptation)

The plan listed set_measure(id, …) as part of the trait. Taffy 0.10’s measure story is a closure passed to compute_layout_with_measure rather than a per-node callback registered up-front (see taffy_tree.rs:905-922 and the measure.rs example). Putting set_measure on the trait would force every backend to own a registry, but the signature of the stored function is backend-specific. Therefore:

  • The trait has set_measure(id, Box<dyn MeasureFn>) where MeasureFn is a type alias for the concrete closure shape that taffy needs. Any yoga-ffi backend that has the same terminal-cell measure contract can implement the same trait. If a future backend needs a different shape, the trait gets a second method; that is the narrowest breaking change possible.
  • TaffyEngine owns a HashMap<u32, Box<dyn MeasureFn>> keyed by dom node id. calculate passes a single closure to compute_layout_with_measure that dispatches into that map.
  • M1-4 wires real measurement by calling set_measure and does NOT need to touch the trait definition.

§Rounding (yoga-compatible pixel-grid post-pass)

render-node-to-output.ts:129-130 uses the yoga-computed values directly as array column/row indices, with no Math.round/floor/ceil:

const x = offsetX + yogaNode.getComputedLeft();   // line 129
const y = offsetY + yogaNode.getComputedTop();    // line 130

Ink trusts yoga’s own pixel rounding, which yoga applies after the flex solve in roundLayoutResultsToPixelGrid (yoga 3.2.1 yoga/algorithm/PixelGrid.cpp). Taffy 0.10 also rounds when use_rounding = true, but with a different rule (cumulative round-half-away in compute::round_layout, taffy compute/mod.rs:219). That difference produces ±1 divergences against the ink oracle on tie-breaks (e.g. a 3.5-cell leading space: yoga floors a text node’s position to 3, taffy rounds to 4) and on text-node sizing.

We therefore (M1-7):

  1. Disable taffy’s rounding (TaffyTree::disable_rounding() in TaffyEngine::new); tree.layout() then returns unrounded floats.
  2. After compute_layout_with_measure, run a faithful port of roundLayoutResultsToPixelGrid / roundValueToPixelGrid (taffy_engine.rs round_node / round_value_to_pixel_grid), specialized to pointScaleFactor == 1.0, in f64 with yoga’s inexactEquals epsilon (0.0001). The recursion carries unrounded absolute offsets; positions round from the parent-relative value; dimensions round as round(absRight) - round(absLeft). Nodes with a registered measure fn are treated as yoga NodeType::Text (floored, not rounded down, so glyphs are never truncated).
  3. Store the rounded, parent-relative integer rects keyed by dom id; computed() returns from that map (falling back to the live taffy layout only for a node that was never laid out). x/y are i32 (parent-relative, can be negative with margins); width/height are u16 (terminal cells fit).

Structs§

Rect
The computed position and size of a node in terminal cells.
TaffyEngine
Taffy 0.10 backend.

Traits§

LayoutEngine
Stable interface over a layout backend.

Type Aliases§

MeasureFn
A measure function for a leaf node.