Skip to content

Add Display::Contents support#943

Closed
rlch wants to merge 5 commits into
DioxusLabs:mainfrom
rlch:display-contents-upstream
Closed

Add Display::Contents support#943
rlch wants to merge 5 commits into
DioxusLabs:mainfrom
rlch:display-contents-upstream

Conversation

@rlch
Copy link
Copy Markdown

@rlch rlch commented Apr 1, 2026

Implements CSS display: contents for TaffyTree. A contents node generates no box — its children are promoted to the parent's child list during layout.

Builds on #534 but redesigned to avoid the RefCell cache thrashing that caused the perf regression there.

Changes

  • Display::Contents variant added to the enum
  • Contents nodes get zero layout but children are not hidden
  • child_ids() / child_count() / get_child_id() flatten through contents nodes recursively
  • Flattened children pre-computed once per layout pass, stored in a SecondaryMap
  • contents_dirty flag — zero overhead when no contents nodes exist

Performance

Scenario Without patch With patch (no contents) With patch (20% contents)
relayout/100 nodes 11.4 µs 11.4 µs 16.6 µs
relayout/1000 nodes 119.6 µs 119.6 µs 167.9 µs

When no contents nodes exist: zero overhead (one bool check per layout).
When contents nodes are present: ~40% overhead from the resolve walk + extra tree nodes.

Criterion benchmarks included in benches/benches/display_contents.rs.

Tests

  • Basic flex promotion (5 items, 2 via contents wrapper)
  • Nested contents (contents within contents)
  • Contents vs None behavior
  • Style roundtrip
  • Zero-overhead fast path verification

Closes #533

rlch added 5 commits April 1, 2026 13:19
Adds Display::Contents to the Display enum. Contents nodes generate no
box — they are invisible to layout, and their children should be laid
out as if they were direct children of the node's parent (CSS
`display: contents` semantics).

The layout dispatch treats Contents like hidden (zero layout, clear
cache) but does NOT recursively hide children. Child flattening is
the responsibility of the TraversePartialTree implementation.
Tests verify:
- Contents nodes get zero layout (no box generated)
- Contents children are not recursively hidden like Display::None
- Style roundtrip (set/get Display::Contents)

Note: child flattening tests belong in the consumer's
TraversePartialTree implementation, not here.
When a node's direct children include Display::Contents nodes, their
children are recursively promoted into the parent's child list. This
is resolved once before each layout pass (not on every child_ids call).

Performance: O(0) when no contents nodes exist (tracked by counter).
When contents nodes are present, O(n) walk to pre-compute flattened
children stored in a SecondaryMap. No RefCell, no per-call allocation,
no cache thrashing — addresses the performance concerns from PR DioxusLabs#534.

Tests verify: basic promotion, nested contents, contents vs none
behavior, style roundtrip, and zero-overhead fast path.
Criterion benchmarks comparing layout performance with and without
Display::Contents nodes across flat trees (10/100/1000), deep trees
(4^6 = 4096 nodes), and relayout scenarios.

Results: zero overhead when no contents nodes exist (counter skip).
With 20% contents nodes at 1000 items: ~186µs relayout vs ~123µs
baseline — well within frame budget for typical UI trees.
Simpler and less error-prone than maintaining a counter across every
mutation method. The flag is set whenever a Display::Contents-related
mutation occurs, and cleared after resolve if no contents nodes exist.

Benchmarks show equal or slightly better performance vs the counter.
@rlch rlch closed this Apr 1, 2026
@rlch rlch deleted the display-contents-upstream branch April 1, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Display::Contents

1 participant