Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negative margins in flexbox result in incorrect layout #706

Open
jamesthurley opened this issue Aug 20, 2024 · 0 comments
Open

Negative margins in flexbox result in incorrect layout #706

jamesthurley opened this issue Aug 20, 2024 · 0 comments
Labels
bug Something isn't working good first issue Good for newcomers

Comments

@jamesthurley
Copy link

jamesthurley commented Aug 20, 2024

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):
image

However, if I uncomment the negative margins on child_1, I would expect the following layout:

image

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:

image

What went wrong

Negative margins seem to result in the layout being computed incorrectly.

Related to #475

@jamesthurley jamesthurley added the bug Something isn't working label Aug 20, 2024
@nicoburns nicoburns added the good first issue Good for newcomers label Oct 22, 2024
@nicoburns nicoburns mentioned this issue Oct 22, 2024
38 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants