taffy
taffy copied to clipboard
Negative margins in flexbox result in incorrect layout
taffy
version
0.5.2
Platform
Rust
What you did
I'm trying to use negative margins to bleed child elements into the parent padding, however the resulting layout appears to be incorrect.
The simplest example I've come up with is the following code:
use taffy::prelude::*;
fn main() {
let mut tree: TaffyTree<NodeContext> = TaffyTree::new();
let padding = 5.;
let negative_padding = -1. * padding;
let child_1_style = Style {
flex_basis: auto(),
flex_grow: 0.,
flex_shrink: 0.,
// margin: Rect {
// top: length(negative_padding),
// left: length(negative_padding),
// right: length(negative_padding),
// bottom: length(0.),
// },
..Default::default()
};
let child_1_node = tree
.new_leaf_with_context(child_1_style, NodeContext::Spacer)
.unwrap();
let child_2_style = Style {
flex_basis: auto(),
flex_grow: 1.,
flex_shrink: 1.,
..Default::default()
};
let child_2_node = tree
.new_leaf_with_context(child_2_style, NodeContext::Spacer)
.unwrap();
let root_style = Style {
size: Size {
width: Dimension::Percent(1.),
height: Dimension::Percent(1.),
},
display: Display::Flex,
flex_direction: FlexDirection::Column,
justify_content: Some(JustifyContent::FlexStart),
padding: Rect {
top: length(padding),
left: length(padding),
right: length(padding),
bottom: length(padding),
},
..Default::default()
};
let root_node = tree
.new_with_children(root_style, &[child_1_node, child_2_node])
.unwrap();
tree.compute_layout_with_measure(
root_node,
Size {
width: length(1000.),
height: length(800.),
},
|known_dimensions, available_space, _node_id, node_context, _style| {
measure_function(known_dimensions, available_space, node_context)
},
)
.unwrap();
tree.print_tree(root_node);
}
enum NodeContext {
Spacer,
}
fn measure_function(
known_dimensions: taffy::geometry::Size<Option<f32>>,
_available_space: taffy::geometry::Size<taffy::style::AvailableSpace>,
node_context: Option<&mut NodeContext>,
) -> Size<f32> {
if let Size {
width: Some(width),
height: Some(height),
} = known_dimensions
{
return Size { width, height };
}
match node_context {
None => Size::ZERO,
Some(NodeContext::Spacer) => Size {
width: 10.,
height: 10.,
},
}
}
Note that root_node
has padding of 5px
, child_1_node
should be as small vertically as necessary, child_2_node
should take the remaining space. I'm computing the layout for a size of 1000 x 800.
The above code has the negative margins on child_1_style
commented out, and the output is as expected:
TREE
└── FLEX COL [x: 0 y: 0 w: 1000 h: 800 content_w: 995 content_h: 795 border: l:0 r:0 t:0 b:0, padding: l:5 r:5 t:5 b:5] (NodeId(4294967299))
├── LEAF [x: 5 y: 5 w: 990 h: 10 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967297))
└── LEAF [x: 5 y: 15 w: 990 h: 780 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967298))
child_1
starts at y: 5
due to the parent's top padding, and has a height of 10.
child_2
starts at y: 15
and has height of 780
, leaving 5
for the parent's bottom padding.
This roughly corresponds to this layout (ignore overall size, it's a rough HTML equivalent):
However, if I uncomment the negative margins on child_1
, I would expect the following layout:
But instead I get this:
TREE
└── FLEX COL [x: 0 y: 0 w: 1000 h: 800 content_w: 1000 content_h: 800 border: l:0 r:0 t:0 b:0, padding: l:5 r:5 t:5 b:5] (NodeId(4294967299))
├── LEAF [x: 0 y: 0 w: 1000 h: 10 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967297))
└── LEAF [x: 5 y: 10 w: 990 h: 790 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967298))
child_1
is correctly starting at y: 0
, bleeding into the parent padding as expected, with a height of 10.
child_2
correctly starts at y: 10
, but the height is 790
, leaving no space for the parent's padding at the bottom. I would expect it to have a height of 785.
Note child_2
does not have any negative margins, and should not be bleeding into the parent's padding. It seems like it's correctly been given extra space due to child_1
moving up, but has used twice as much extra space as it should have.
If you add an extra child which does bleed into the bottom margin:
let child_3_style = Style {
flex_basis: auto(),
flex_grow: 0.,
flex_shrink: 0.,
margin: Rect {
top: length(0.),
left: length(negative_padding),
right: length(negative_padding),
bottom: length(negative_padding),
},
..Default::default()
};
let child_3_node = tree
.new_leaf_with_context(child_3_style, NodeContext::Spacer)
.unwrap();
You get the following:
TREE
└── FLEX COL [x: 0 y: 0 w: 1000 h: 800 content_w: 1000 content_h: 810 border: l:0 r:0 t:0 b:0, padding: l:5 r:5 t:5 b:5] (NodeId(4294967300))
├── LEAF [x: 0 y: 0 w: 1000 h: 10 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967297))
├── LEAF [x: 5 y: 10 w: 990 h: 790 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967298))
└── LEAF [x: 0 y: 800 w: 1000 h: 10 content_w: 10 content_h: 10 border: l:0 r:0 t:0 b:0, padding: l:0 r:0 t:0 b:0] (NodeId(4294967299))
child_3
is rendered completely outside the box (y: 800
), when it should look like this:
What went wrong
Negative margins seem to result in the layout being computed incorrectly.
Related to https://github.com/DioxusLabs/taffy/issues/475