Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/grida-canvas/src/htmlcss/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,16 @@ fn collect_element_with_counter(

// Widgets with intrinsic sizes need their own Taffy node
// for sizing to work — don't flatten them into inline groups.
let is_inline = child.display == types::Display::Inline
|| child.display == types::Display::InlineBlock;
// Same for inline-block with explicit sizing: its width/height
// would be lost inside the inline path. Layout later emulates
// inline-block flow by mapping the parent to Taffy flex-wrap
// when all of its element children are inline-block.
let inline_block_with_size = child.display == types::Display::InlineBlock
&& (child.width != types::CssLength::Auto
|| child.height != types::CssLength::Auto);
let is_inline = (child.display == types::Display::Inline
|| child.display == types::Display::InlineBlock)
&& !inline_block_with_size;
Comment on lines +439 to +444
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve inline flow for sized inline-block mixed with text

Marking every sized display:inline-block as non-inline here forces it out of InlineGroup, but the later emulation only runs when the parent has no text/inline groups and at least two inline-block elements. In mixed content (for example text + inline-block + text) or a single inline-block, the parent stays block and this node is laid out as a block child, inserting unintended line breaks around content that should remain in one inline formatting context.

Useful? React with 👍 / 👎.

if is_inline && !child.widget.is_widget() && child.replaced.is_none() {
collect_inline_items(&child, &mut pending_inline);
} else {
Expand Down
33 changes: 33 additions & 0 deletions crates/grida-canvas/src/htmlcss/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ fn build_taffy_node(
apply_replaced_intrinsic_size(&mut style, replaced, images);
}

// Emulate inline-block sibling flow via Taffy flex-wrap. Taffy has no
// inline formatting context, so a block container holding only
// inline-block siblings would otherwise stack them vertically. Only
// apply when ≥2 inline-block element children exist and no text or
// non-inline-block elements would be misrouted through flex.
if style.display == taffy::Display::Block && should_emulate_inline_block_container(el) {
style.display = taffy::Display::Flex;
style.flex_wrap = taffy::FlexWrap::Wrap;
Comment on lines +135 to +136
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Force row direction when emulating inline-block container

After switching a block container to flex for inline-block emulation, the code keeps whatever flex-direction was parsed from CSS; that property is inert on block elements but can still compute to column, and once the display is changed it becomes active and stacks children vertically. To reliably emulate inline-block siblings, this branch should also set flex_direction to row (and avoid inheriting flex-only controls that were previously ignored).

Useful? React with 👍 / 👎.

// Override default `stretch` so inline-blocks keep their
// own block-size instead of filling the container's line height.
style.align_items = Some(taffy::AlignItems::Start);
}

// Build child nodes
let mut child_ids: Vec<TaffyNodeId> = Vec::new();

Expand Down Expand Up @@ -176,6 +189,26 @@ fn build_taffy_node(
taffy.new_with_children(style, &child_ids).unwrap()
}

/// Returns true when `el` should lay out its children as a horizontal
/// flex-wrap row to emulate inline-block flow. Only safe when the
/// container holds ≥2 inline-block element siblings and no text or
/// non-inline-block element children (those would require a real inline
/// formatting context to mix with inline-blocks correctly).
fn should_emulate_inline_block_container(el: &StyledElement) -> bool {
let mut inline_block_count = 0usize;
for child in &el.children {
match child {
StyledNode::Element(child_el) => match child_el.display {
types::Display::InlineBlock => inline_block_count += 1,
types::Display::None => {}
_ => return false,
},
StyledNode::Text(_) | StyledNode::InlineGroup(_) => return false,
}
}
inline_block_count >= 2
}

/// Taffy context for text/inline leaf nodes. Stores inline items so the
/// measure function can build a Skia Paragraph with placeholders at any
/// available width.
Expand Down
51 changes: 51 additions & 0 deletions fixtures/test-html/L0/layout-display-inline-block.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Layout: display: inline-block</title>
<style>
html,
body {
min-height: 800px;
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
background: #fff;
}

.row {
font-size: 0;
}

.box {
display: inline-block;
width: 80px;
height: 80px;
vertical-align: top;
}

.a {
background: #c00;
}
.b {
background: #0a0;
}
.c {
background: #05f;
}
.d {
background: #fa0;
}
</style>
</head>
<body>
<div class="row">
<div class="box a"></div>
<div class="box b"></div>
<div class="box c"></div>
<div class="box d"></div>
</div>
</body>
</html>
3 changes: 2 additions & 1 deletion fixtures/test-html/suites/L0.exact.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
{ "path": "../L0/paint-border-style-dashed.html" },
{ "path": "../L0/paint-filter-drop-shadow.html" },
{ "path": "../L0/paint-transform-matrix.html" },
{ "path": "../L0/paint-filter-blur.html" }
{ "path": "../L0/paint-filter-blur.html" },
{ "path": "../L0/layout-display-inline-block.html" }
]
}
Loading