render: place right-float navigation bands
This commit is contained in:
1 parent
a5f1f93f77
commit
4b4d7007d9
1 file changed
+136
@@ -2743,6 +2743,142 @@ fn build_children_with_float_zone(
|
|||||||
) -> Vec<taffy::NodeId> {
|
) -> Vec<taffy::NodeId> {
|
||||||
let is_float = |cid: NodeId| styles.get(&cid).map(|s| s.float.is_some()).unwrap_or(false);
|
let is_float = |cid: NodeId| styles.get(&cid).map(|s| s.float.is_some()).unwrap_or(false);
|
||||||
|
|
||||||
|
// A block made entirely from inline-ish flow content and two or more
|
||||||
|
// right floats is the classic utility/navigation bar. Right floats are
|
||||||
|
// placed from the inline end inward, so their visual order is the reverse
|
||||||
|
// of source order, while ordinary inline content keeps filling from the
|
||||||
|
// start of the same band. Serializing each encountered float into its own
|
||||||
|
// synthetic row reverses those two groups and can leave the entire bar
|
||||||
|
// shrink-wrapped at the start.
|
||||||
|
//
|
||||||
|
// Model this bounded one-band case as [flow | reversed right-float group].
|
||||||
|
// A nested group preserves every float's authored margins; only the
|
||||||
|
// anonymous group receives the auto margin used to represent the free
|
||||||
|
// space between the two sides.
|
||||||
|
let right_floats: Vec<NodeId> = dom_children
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|cid| {
|
||||||
|
styles.get(cid).map_or(false, |style| {
|
||||||
|
style.float == Some(crate::Float::Right)
|
||||||
|
&& style.display != crate::Display::None
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let has_left_float = dom_children.iter().any(|cid| {
|
||||||
|
styles.get(cid).map_or(false, |style| {
|
||||||
|
style.float == Some(crate::Float::Left)
|
||||||
|
&& style.display != crate::Display::None
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let flow_is_inline = dom_children.iter().all(|cid| {
|
||||||
|
let Some(node) = tree.get_node(*cid) else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if !node.is_element() || is_float(*cid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
styles
|
||||||
|
.get(cid)
|
||||||
|
.map_or(true, |style| style.display != crate::Display::Block)
|
||||||
|
});
|
||||||
|
if right_floats.len() >= 2 && !has_left_float && flow_is_inline {
|
||||||
|
// Removing out-of-flow items must not remove the one collapsible
|
||||||
|
// space between the inline items on either side of them. Collapse
|
||||||
|
// any run of formatting whitespace to one representative node, while
|
||||||
|
// dropping leading/trailing whitespace at the band edges.
|
||||||
|
let mut flow_dom = Vec::new();
|
||||||
|
let mut pending_whitespace = None;
|
||||||
|
let mut has_flow_content = false;
|
||||||
|
for &cid in dom_children {
|
||||||
|
if is_float(cid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let is_whitespace = tree.get_node(cid).map_or(false, |node| {
|
||||||
|
!node.is_element() && tree.text_content(cid).trim().is_empty()
|
||||||
|
});
|
||||||
|
if is_whitespace {
|
||||||
|
if has_flow_content && pending_whitespace.is_none() {
|
||||||
|
pending_whitespace = Some(cid);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if has_flow_content {
|
||||||
|
if let Some(whitespace) = pending_whitespace.take() {
|
||||||
|
flow_dom.push(whitespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flow_dom.push(cid);
|
||||||
|
has_flow_content = true;
|
||||||
|
}
|
||||||
|
let mut row_children: Vec<taffy::NodeId> = flow_dom
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|cid| {
|
||||||
|
build_any(
|
||||||
|
tree,
|
||||||
|
cid,
|
||||||
|
taffy_tree,
|
||||||
|
id_map,
|
||||||
|
words,
|
||||||
|
engine,
|
||||||
|
ifc_items,
|
||||||
|
styles,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let right_children: Vec<taffy::NodeId> = right_floats
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.filter_map(|cid| {
|
||||||
|
build(
|
||||||
|
tree,
|
||||||
|
*cid,
|
||||||
|
taffy_tree,
|
||||||
|
id_map,
|
||||||
|
words,
|
||||||
|
engine,
|
||||||
|
ifc_items,
|
||||||
|
styles,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if !row_children.is_empty() && !right_children.is_empty() {
|
||||||
|
let right_group_style = taffy::Style {
|
||||||
|
display: taffy::style::Display::Flex,
|
||||||
|
flex_direction: taffy::FlexDirection::Row,
|
||||||
|
flex_wrap: taffy::FlexWrap::Wrap,
|
||||||
|
margin: taffy::Rect {
|
||||||
|
top: taffy::style::LengthPercentageAuto::length(0.0),
|
||||||
|
right: taffy::style::LengthPercentageAuto::length(0.0),
|
||||||
|
bottom: taffy::style::LengthPercentageAuto::length(0.0),
|
||||||
|
left: taffy::style::LengthPercentageAuto::auto(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Ok(right_group) =
|
||||||
|
taffy_tree.new_with_children(right_group_style, &right_children)
|
||||||
|
{
|
||||||
|
row_children.push(right_group);
|
||||||
|
let row_style = taffy::Style {
|
||||||
|
display: taffy::style::Display::Flex,
|
||||||
|
flex_direction: taffy::FlexDirection::Row,
|
||||||
|
flex_wrap: taffy::FlexWrap::Wrap,
|
||||||
|
align_items: Some(taffy::AlignItems::FLEX_START),
|
||||||
|
size: taffy::Size {
|
||||||
|
width: taffy::Dimension::percent(1.0),
|
||||||
|
height: taffy::Dimension::auto(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Ok(row) =
|
||||||
|
taffy_tree.new_with_children(row_style, &row_children)
|
||||||
|
{
|
||||||
|
return vec![row];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Some(float_idx) = dom_children.iter().position(|&cid| is_float(cid)) else {
|
let Some(float_idx) = dom_children.iter().position(|&cid| is_float(cid)) else {
|
||||||
return dom_children.iter().flat_map(|&cid| build_any(tree, cid, taffy_tree, id_map, words, engine, ifc_items, styles)).collect();
|
return dom_children.iter().flat_map(|&cid| build_any(tree, cid, taffy_tree, id_map, words, engine, ifc_items, styles)).collect();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user