README

mizchi/crater-layout/tree does not have a README file

#
CacheStats

pub struct CacheStats {
cache_hits : Int
cache_misses : Int
nodes_computed : Int
}

Statistics for cache performance

#
CacheStats::hit_rate

fn CacheStats::hit_rate(self : CacheStats) -> Double

#
CacheStats::new

fn CacheStats::new() -> CacheStats

#
ConstraintKey

pub struct ConstraintKey {
available_width : Double
available_height : Double?
sizing_mode :
SizingMode

} derive(Eq)

Constraint key for cache lookup

#
ConstraintKey::approx_eq

fn ConstraintKey::approx_eq(self : ConstraintKey, other : ConstraintKey) -> Bool

Check if two constraints are approximately equal

#
ConstraintKey::new

fn ConstraintKey::new(available_width : Double, available_height : Double?, sizing_mode :
SizingMode
) -> ConstraintKey

#
ConstraintSpace

pub struct ConstraintSpace {
available_width : Double
available_height : Double?
sizing_mode :
SizingMode

parent_width : Double
parent_height : Double?
viewport_width : Double
viewport_height : Double
}

Extended constraint space for cache comparison

#
ConstraintSpace::new

fn ConstraintSpace::new(available_width : Double, available_height : Double?, sizing_mode :
SizingMode
, parent_width : Double, parent_height : Double?, viewport_width? : Double, viewport_height? : Double) -> ConstraintSpace

#
ConstraintSpace::to_context

Convert to LayoutContext for existing compute functions

#
ConstraintSpace::to_key

Convert to ConstraintKey for cache lookup

#
DependencyKind

pub(all) enum DependencyKind {
Static
ParentWidth
ParentHeight
ParentBoth
Intrinsic
Viewport
} derive(Eq,
Debug
)

What a node's layout depends on

#
DependencyKind::merge

Merge two dependency kinds into a combined dependency

#
IntrinsicState

pub(all) enum IntrinsicState {
Resolved(width~ : Double, height~ : Double)
Pending(placeholder_width~ : Double, placeholder_height~ : Double)
Error
}

State of an intrinsic resource

#
LayoutCache

pub struct LayoutCache {
constraint : ConstraintKey
result :
Layout

}

Cached layout result

#
LayoutNode

pub struct LayoutNode {
id : String
uid : Int
style :
Style

children : Array[LayoutNode]
measure :
MeasureFunc
?
text : String?
dirty : Bool
children_dirty : Bool
cached_layout : LayoutCache?
dependencies : DependencyKind
computed_x : Double
computed_y : Double
computed_width : Double
computed_height : Double
has_new_layout : Bool
intrinsic_state : IntrinsicState?
on_dirty : (Int) -> Unit?
}

Mutable layout node with dirty tracking

#
LayoutNode::add_child

fn LayoutNode::add_child(self : LayoutNode, child : LayoutNode) -> LayoutNode

Add child at end

#
LayoutNode::clear_dirty

fn LayoutNode::clear_dirty(self : LayoutNode) -> Unit

Clear dirty bits after layout

#
LayoutNode::create

fn LayoutNode::create() -> LayoutNode

Create a new node with default style (Yoga-compatible)

#
LayoutNode::create_with_id

fn LayoutNode::create_with_id(id : String) -> LayoutNode

Create a new node with ID

#
LayoutNode::from_node

Create LayoutNode from immutable Node

#
LayoutNode::get_child

fn LayoutNode::get_child(self : LayoutNode, index : Int) -> LayoutNode?

Get child at index

#
LayoutNode::get_child_count

fn LayoutNode::get_child_count(self : LayoutNode) -> Int

Get number of children

#
LayoutNode::get_has_new_layout

fn LayoutNode::get_has_new_layout(self : LayoutNode) -> Bool

Check if node has new layout since last mark_layout_seen

#
LayoutNode::get_layout_height

fn LayoutNode::get_layout_height(self : LayoutNode) -> Double

Get computed height

#
LayoutNode::get_layout_width

fn LayoutNode::get_layout_width(self : LayoutNode) -> Double

Get computed width

#
LayoutNode::get_layout_x

fn LayoutNode::get_layout_x(self : LayoutNode) -> Double

Get computed X position (relative to parent)

#
LayoutNode::get_layout_y

fn LayoutNode::get_layout_y(self : LayoutNode) -> Double

Get computed Y position (relative to parent)

#
LayoutNode::insert_child

fn LayoutNode::insert_child(self : LayoutNode, child : LayoutNode, index : Int) -> LayoutNode

Insert child at index

#
LayoutNode::is_dirty

fn LayoutNode::is_dirty(self : LayoutNode) -> Bool

Check if node is dirty

#
LayoutNode::leaf

fn LayoutNode::leaf(id : String, style :
Style
) -> LayoutNode

#
LayoutNode::mark_children_dirty

fn LayoutNode::mark_children_dirty(self : LayoutNode) -> Unit

Mark that children need re-layout

#
LayoutNode::mark_dirty

fn LayoutNode::mark_dirty(self : LayoutNode) -> Unit

Mark this node as dirty and invalidate cache Also propagates children_dirty to parent chain via callback

#
LayoutNode::mark_layout_seen

fn LayoutNode::mark_layout_seen(self : LayoutNode) -> Unit

Mark that layout has been read (for incremental updates)

#
LayoutNode::needs_layout

fn LayoutNode::needs_layout(self : LayoutNode) -> Bool

Check if this node or any children need layout

#
LayoutNode::new

fn LayoutNode::new(id : String, style :
Style
, children : Array[LayoutNode]) -> LayoutNode

#
LayoutNode::remove_all_children

fn LayoutNode::remove_all_children(self : LayoutNode) -> Unit

Remove all children

#
LayoutNode::remove_child_at

fn LayoutNode::remove_child_at(self : LayoutNode, index : Int) -> LayoutNode?

Remove child at index

#
LayoutNode::set_align_content

fn LayoutNode::set_align_content(self : LayoutNode, value :
Alignment
) -> LayoutNode

Set align content

#
LayoutNode::set_align_items

Set align items

#
LayoutNode::set_align_self

Set align self

#
LayoutNode::set_border

fn LayoutNode::set_border(self : LayoutNode, value : Double) -> LayoutNode

Set all border widths

#
LayoutNode::set_column_gap

fn LayoutNode::set_column_gap(self : LayoutNode, value : Double) -> LayoutNode

Set column gap in pixels

#
LayoutNode::set_display

Set display type

#
LayoutNode::set_flex_basis

fn LayoutNode::set_flex_basis(self : LayoutNode, value : Double) -> LayoutNode

Set flex basis in pixels

#
LayoutNode::set_flex_basis_auto

fn LayoutNode::set_flex_basis_auto(self : LayoutNode) -> LayoutNode

Set flex basis to auto

#
LayoutNode::set_flex_direction

Set flex direction

#
LayoutNode::set_flex_grow

fn LayoutNode::set_flex_grow(self : LayoutNode, value : Double) -> LayoutNode

Set flex grow

#
LayoutNode::set_flex_shrink

fn LayoutNode::set_flex_shrink(self : LayoutNode, value : Double) -> LayoutNode

Set flex shrink

#
LayoutNode::set_flex_wrap

Set flex wrap

#
LayoutNode::set_gap

fn LayoutNode::set_gap(self : LayoutNode, value : Double) -> LayoutNode

Set both row and column gap

#
LayoutNode::set_grid_area

fn LayoutNode::set_grid_area(self : LayoutNode, area : String?) -> LayoutNode

Set grid area by name

#
LayoutNode::set_grid_auto_columns

Set grid auto columns

#
LayoutNode::set_grid_auto_flow

Set grid auto flow

#
LayoutNode::set_grid_auto_rows

Set grid auto rows

#
LayoutNode::set_grid_column

Set grid column placement

#
LayoutNode::set_grid_row

Set grid row placement

#
LayoutNode::set_grid_template_areas

fn LayoutNode::set_grid_template_areas(self : LayoutNode, areas : Array[String]) -> LayoutNode

Set grid template areas

#
LayoutNode::set_grid_template_columns

Set grid template columns

#
LayoutNode::set_grid_template_rows

Set grid template rows

#
LayoutNode::set_height

fn LayoutNode::set_height(self : LayoutNode, value : Double) -> LayoutNode

Set height in pixels

#
LayoutNode::set_height_auto

fn LayoutNode::set_height_auto(self : LayoutNode) -> LayoutNode

Set height to auto

#
LayoutNode::set_height_percent

fn LayoutNode::set_height_percent(self : LayoutNode, value : Double) -> LayoutNode

Set height as percentage (0.0-1.0)

#
LayoutNode::set_intrinsic_pending

fn LayoutNode::set_intrinsic_pending(self : LayoutNode, placeholder_width : Double, placeholder_height : Double) -> Unit

Configure a node to use a pending intrinsic placeholder size.

#
LayoutNode::set_intrinsic_resolved

fn LayoutNode::set_intrinsic_resolved(self : LayoutNode, width : Double, height : Double) -> Unit

Configure a node to use a resolved intrinsic size.

#
LayoutNode::set_justify_content

fn LayoutNode::set_justify_content(self : LayoutNode, value :
Alignment
) -> LayoutNode

Set justify content

#
LayoutNode::set_justify_items

fn LayoutNode::set_justify_items(self : LayoutNode, value :
Alignment
) -> LayoutNode

Set justify items (grid container)

#
LayoutNode::set_justify_self

Set justify self (grid item)

#
LayoutNode::set_margin

fn LayoutNode::set_margin(self : LayoutNode, value : Double) -> LayoutNode

Set all margins

#
LayoutNode::set_margin_bottom

fn LayoutNode::set_margin_bottom(self : LayoutNode, value : Double) -> LayoutNode

Set margin bottom

#
LayoutNode::set_margin_left

fn LayoutNode::set_margin_left(self : LayoutNode, value : Double) -> LayoutNode

Set margin left

#
LayoutNode::set_margin_right

fn LayoutNode::set_margin_right(self : LayoutNode, value : Double) -> LayoutNode

Set margin right

#
LayoutNode::set_margin_top

fn LayoutNode::set_margin_top(self : LayoutNode, value : Double) -> LayoutNode

Set margin top

#
LayoutNode::set_max_height

fn LayoutNode::set_max_height(self : LayoutNode, value : Double) -> LayoutNode

Set max-height in pixels

#
LayoutNode::set_max_width

fn LayoutNode::set_max_width(self : LayoutNode, value : Double) -> LayoutNode

Set max-width in pixels

#
LayoutNode::set_min_height

fn LayoutNode::set_min_height(self : LayoutNode, value : Double) -> LayoutNode

Set min-height in pixels

#
LayoutNode::set_min_width

fn LayoutNode::set_min_width(self : LayoutNode, value : Double) -> LayoutNode

Set min-width in pixels

#
LayoutNode::set_padding

fn LayoutNode::set_padding(self : LayoutNode, value : Double) -> LayoutNode

Set all padding

#
LayoutNode::set_padding_bottom

fn LayoutNode::set_padding_bottom(self : LayoutNode, value : Double) -> LayoutNode

Set padding bottom

#
LayoutNode::set_padding_left

fn LayoutNode::set_padding_left(self : LayoutNode, value : Double) -> LayoutNode

Set padding left

#
LayoutNode::set_padding_right

fn LayoutNode::set_padding_right(self : LayoutNode, value : Double) -> LayoutNode

Set padding right

#
LayoutNode::set_padding_top

fn LayoutNode::set_padding_top(self : LayoutNode, value : Double) -> LayoutNode

Set padding top

#
LayoutNode::set_position_type

fn LayoutNode::set_position_type(self : LayoutNode, value :
Position
) -> LayoutNode

Set position type

#
LayoutNode::set_row_gap

fn LayoutNode::set_row_gap(self : LayoutNode, value : Double) -> LayoutNode

Set row gap in pixels

#
LayoutNode::set_width

fn LayoutNode::set_width(self : LayoutNode, value : Double) -> LayoutNode

Set width in pixels

#
LayoutNode::set_width_auto

fn LayoutNode::set_width_auto(self : LayoutNode) -> LayoutNode

Set width to auto

#
LayoutNode::set_width_percent

fn LayoutNode::set_width_percent(self : LayoutNode, value : Double) -> LayoutNode

Set width as percentage (0.0-1.0)

#
LayoutNode::to_node

Convert LayoutNode to immutable Node (for existing layout functions) Preserves uid for cache lookup

#
LayoutNode::with_measure

#
LayoutTree

pub struct LayoutTree {
root : LayoutNode
parent_map : Map[Int, Int]
node_map : Map[Int, LayoutNode]
viewport_width : Double
viewport_height : Double
resources : ResourceRegistry
}

Layout tree with parent tracking for dirty propagation

#
LayoutTree::attach_resource

fn LayoutTree::attach_resource(self : LayoutTree, rid : ResourceId, node_uid : Int, placeholder_width? : Double, placeholder_height? : Double) -> Unit

Attach an additional node to an existing resource (for shared resources like same src)

#
LayoutTree::calculate_layout

fn LayoutTree::calculate_layout(self : LayoutTree, width : Double, height : Double) ->
Layout

Calculate layout (Yoga-compatible name) Returns the root layout and stores computed layouts on each node

#
LayoutTree::collect_dirty_rects

Collect bounding rects of all nodes that have new layouts Returns absolute positions (relative to viewport origin) Use this after calculate_layout() to get regions that need redrawing

#
LayoutTree::collect_dirty_union

Collect dirty rects and merge overlapping ones into a single union rect More efficient when many small regions change

#
LayoutTree::compute_full

Compute layout using existing layout system (full recomputation)

#
LayoutTree::compute_incremental

fn LayoutTree::compute_incremental(self : LayoutTree) ->
Layout

Compute layout with incremental optimization Uses per-node caching for better performance

#
LayoutTree::compute_with_stats

fn LayoutTree::compute_with_stats(self : LayoutTree, stats : CacheStats) ->
Layout

Compute with stats tracking (for testing/debugging)

#
LayoutTree::fail_resource

fn LayoutTree::fail_resource(self : LayoutTree, rid : ResourceId) -> Unit

Mark a resource as failed (e.g., image load error)

#
LayoutTree::find_node

fn LayoutTree::find_node(self : LayoutTree, uid : Int) -> LayoutNode?

Find a node by uid

#
LayoutTree::find_node_by_id

fn LayoutTree::find_node_by_id(self : LayoutTree, node_id : String) -> LayoutNode?

Find a node by string id

#
LayoutTree::flow_dirty_uids

fn LayoutTree::flow_dirty_uids(self : LayoutTree, seed : Array[Int]) -> Array[Int]

Expand a seed set of directly-changed node uids into the full set that must be re-laid-out, accounting for crater's absolute geometry cache: resizing a box shifts the absolute position of every box that comes after it in flow. So for each changed node, every following sibling (and its whole subtree) at that node's level — and, walking up, at every ancestor's level — must also be dirtied, because their absolute positions move. Ancestors themselves are handled by mark_node_dirty's children_dirty propagation, so they need not be listed here.

Feed the result to reconcile_from as dirty_uids: with a correct seed (the nodes whose own layout inputs changed) this makes the incremental result equal a full recompute while still reusing everything before the change.

#
LayoutTree::from_node

fn LayoutTree::from_node(node :
Node
, viewport_width : Double, viewport_height : Double) -> LayoutTree

Create tree from immutable Node

#
LayoutTree::get_parent

fn LayoutTree::get_parent(self : LayoutTree, uid : Int) -> LayoutNode?

Get parent of a node

#
LayoutTree::get_pending_resources

fn LayoutTree::get_pending_resources(self : LayoutTree) -> Array[ResourceId]

Get all pending resources

#
LayoutTree::get_resources

fn LayoutTree::get_resources(self : LayoutTree) -> ResourceRegistry

Get the resource registry for external management This allows decoupling resource management from LayoutTree

#
LayoutTree::is_resource_pending

fn LayoutTree::is_resource_pending(self : LayoutTree, rid : ResourceId) -> Bool

Check if a resource is still pending

#
LayoutTree::mark_all_layouts_seen

fn LayoutTree::mark_all_layouts_seen(self : LayoutTree) -> Unit

Mark all nodes' layouts as seen (clear has_new_layout flags) Call this after processing dirty regions

#
LayoutTree::mark_node_dirty

fn LayoutTree::mark_node_dirty(self : LayoutTree, uid : Int) -> Unit

Mark a node as dirty and propagate to ancestors

#
LayoutTree::needs_layout

fn LayoutTree::needs_layout(self : LayoutTree) -> Bool

Check if any layout is needed

#
LayoutTree::new

fn LayoutTree::new(root : LayoutNode, viewport_width : Double, viewport_height : Double) -> LayoutTree

#
LayoutTree::reconcile_from

fn LayoutTree::reconcile_from(prev : LayoutTree, new_root :
Node
, dirty_uids : Array[Int], viewport_width : Double, viewport_height : Double) -> LayoutTree

Incremental reflow across a rebuilt render node tree.

The dynamic-rendering path rebuilds the render node tree from the mutated DOM on each change (full cascade — correct for every selector), which would normally throw away all cached geometry. reconcile_from bridges a freshly built LayoutTree to a prior one: it migrates each node's cached layout from the previous tree by matching uid (the stable dom_id-derived identity — see docs/incremental-reflow-design.md phase A), so unchanged subtrees stay cache hits, while nodes that actually changed are dirtied and recomputed.

Two sources of dirtiness:
  • dirty_uids: nodes whose computed style / content changed (the caller determines these — e.g. by comparing computed styles after the cascade).
  • structural changes: a node whose set of child uids differs from the prior tree (a child added / removed / reordered) is detected here and dirtied.

Correctness contract: if dirty_uids is a superset of the nodes whose layout inputs changed, the layout computed from the returned tree equals a full from-scratch recompute. (Unlisted changes would read stale cache — hence the dynamic path pairs this with a conservative dirty set + fall-back to full.)

#
LayoutTree::register_pending_images

fn LayoutTree::register_pending_images(self : LayoutTree) -> Map[String, ResourceId]

Scan the tree for nodes with Pending intrinsic state and register them as resources Returns a map from uid (as string) to ResourceId This is useful after building a tree from HTML to register all pending images

#
LayoutTree::register_pending_images_with_src

fn LayoutTree::register_pending_images_with_src(self : LayoutTree, uid_to_src : Map[Int, String]) -> Map[String, ResourceId]

Scan the tree for nodes with Pending intrinsic state and register them as resources Uses provided src->uid mapping to share resources for same src Returns a map from src to ResourceId

#
LayoutTree::register_resource

fn LayoutTree::register_resource(self : LayoutTree, node_uid : Int, placeholder_width? : Double, placeholder_height? : Double) -> ResourceId

Register a pending resource for an intrinsic-sized element (e.g., image) Returns a ResourceId that can be used to resolve the size later

#
LayoutTree::resize_viewport

fn LayoutTree::resize_viewport(self : LayoutTree, width : Double, height : Double) -> Unit

Update viewport size and mark dependent nodes dirty

#
LayoutTree::resolve_resource

fn LayoutTree::resolve_resource(self : LayoutTree, rid : ResourceId, width : Double, height : Double) -> Unit

Resolve a resource with its actual size

#
ResourceId

pub(all) struct ResourceId(Int) derive(Eq, Hash,
Debug
)

Opaque resource identifier for external reference
impl Show for ResourceId

#
ResourceRegistry

pub struct ResourceRegistry {
next_id : Int
resource_nodes : Map[Int, Array[Int]]
pending :
HashSet
[Int]
}

Resource registry - tracks pending resources and their associated nodes

#
ResourceRegistry::add_node

fn ResourceRegistry::add_node(self : ResourceRegistry, rid : ResourceId, node_uid : Int) -> Unit

Associate a node UID with a resource ID

#
ResourceRegistry::get_all_pending

fn ResourceRegistry::get_all_pending(self : ResourceRegistry) -> Array[ResourceId]

Get all pending resource IDs

#
ResourceRegistry::get_nodes

fn ResourceRegistry::get_nodes(self : ResourceRegistry, rid : ResourceId) -> Array[Int]

Get all node UIDs associated with a resource

#
ResourceRegistry::is_pending

fn ResourceRegistry::is_pending(self : ResourceRegistry, rid : ResourceId) -> Bool

Check if a resource is still pending

#
ResourceRegistry::mark_pending

fn ResourceRegistry::mark_pending(self : ResourceRegistry, rid : ResourceId) -> Unit

Mark a resource as pending (not yet resolved)

#
ResourceRegistry::mark_resolved

fn ResourceRegistry::mark_resolved(self : ResourceRegistry, rid : ResourceId) -> Unit

Mark a resource as resolved (size known)

#
ResourceRegistry::new

#
ResourceRegistry::next_resource_id

fn ResourceRegistry::next_resource_id(self : ResourceRegistry) -> ResourceId

Generate a new unique resource ID

#
analyze_style_dependencies

fn analyze_style_dependencies(style :
Style
) -> DependencyKind

Analyze what a style depends on for layout

#
compute_node

fn compute_node(node : LayoutNode, constraint : ConstraintSpace, stats : CacheStats) ->
Layout

Compute layout for a LayoutNode with caching This wraps the existing compute functions with cache logic

#
compute_tree_incremental

fn compute_tree_incremental(tree : LayoutTree, stats : CacheStats) ->
Layout

Compute layout for entire tree with incremental optimization Uses cached dispatcher to enable child-level cache hits

#
create_fixed_measure

fn create_fixed_measure(width : Double, height : Double) ->
MeasureFunc

Create a fixed MeasureFunc for intrinsic sizing

#
default_placeholder_height

let default_placeholder_height : Double

#
default_placeholder_width

let default_placeholder_width : Double

HTML default placeholder size (per specification)

#
error_placeholder_height

let error_placeholder_height : Double

#
error_placeholder_width

let error_placeholder_width : Double

Error placeholder size (broken image icon)