facebook / yoga

Yoga is an embeddable layout engine targeting web standards.

Home Page:https://yogalayout.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Percentage padding not updated when the parent size changes

Omxjep3434 opened this issue · comments

Report

When a child has a percentage padding, it is initially correct in regard to the parent's size, but it is not updated in subsequent Yoga executions unless the child becomes dirty.

I am testing this with the Javascript Yoga npm package v2.0.1, but this may be an issue with the layout engine.

  • I have searched existing issues and this is not a duplicate
  • I can't imagine this hasn't been reported, but I searched for issues with keywords "percent" and "percentage". This is the only issue of interest that I found, but I don't think it's related: #930

Issues and Steps to Reproduce

Here is some very basic code with console output to reproduce.

function percentPadding(yoga) {
    const config = yoga.Config.create();
    config.setPointScaleFactor(0);

    const parent = yoga.Node.createWithConfig(config);
    parent.setWidth(10);

    const child = yoga.Node.createWithConfig(config);
    child.setWidth(5);
    child.setPaddingPercent(Edge.Left, 50);

    parent.insertChild(child, 0);

    // On the first execution, the child's padding is as expected: 50% * 10 = 5.
    console.log("** Executing **");
    parent.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);

    // Now change the parent width to 5. The expected child padding is: 50% * 5 = 2.5.
    // However, we can see that the padding is still 5.
    parent.setWidth(5);
    console.log("** Executing **");
    parent.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);

    // If we make an arbitrary change, just to make the child dirty, the padding is now correct at 2.5
    child.setBorder(Edge.Top, 0);
    console.log("** Executing **");
    parent.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);
}

function main() {
    const yoga = await loadYoga();

    percentPadding(yoga);
}

Do you know if this same issue happen if the parent is not the tree root? Wondering if there might be some funkiness around setting a different root available space when triggering the layout, but also setting a definite width on the root.

Normally layout is reevaluated if available width/height constraints imposed by parent don’t change, and we’re laying out in same mode. But there have been plenty of bugs around here before.

The flex basis issue is a bit different because it has its own caching mechanism.

I confirmed the issue happens if the parent isn't the root. I have slightly modified snippet here:

function percentPadding(yoga) {
    const config = yoga.Config.create();
    config.setPointScaleFactor(0);

    const root = yoga.Node.createWithConfig(config);
    root.setWidth(10);

    const parent = yoga.Node.createWithConfig(config);
    parent.setWidth(10);

    const child = yoga.Node.createWithConfig(config);
    child.setWidth(5);
    child.setPaddingPercent(Edge.Left, 50);

    parent.insertChild(child, 0);
    root.insertChild(parent, 0);

    // On the first execution, the child's padding is as expected: 50% * 10 = 5.
    console.log("** Executing **");
    root.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);

    // Now change the parent width to 5. The expected child padding is: 50% * 5 = 2.5.
    // However, we can see that the padding is still 5.
    parent.setWidth(5);
    console.log("** Executing **");
    root.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);

    // If we make an arbitrary change, just to make the child dirty, the padding is now correct at 2.5
    child.setBorder(Edge.Top, 0);
    console.log("** Executing **");
    root.calculateLayout(10, 10);
    console.log(`Child - padding left: ${child.getComputedPadding(Edge.Left)}`);
}

function main() {
    const yoga = await loadYoga();

    percentPadding(yoga);
}

@NickGerleman I believe the "available space" and the size for percentage resolution aren't necessarily always the same. For example, you may wish to size a child under a max-content constraint (unconstrained) but still resolve percentages. e.g. when sizing the main axis of flexbox items the "available space" is infinite but percentages should still resolve against the parent container's size if it's definite.

I believe the cache key should contain (separately) both:

  • the sizing constraint in each axis (which will be either an exact size constraint or else an "available space" constraint (either a definite pixel value, min-content, max-content, etc))
  • The percentage resolution sizes in each axis

I was going to post that this is what Taffy does, but although we're storing two values in the cache key (https://github.com/DioxusLabs/taffy/blob/main/src/tree/cache.rs#L11) we're grouping the non-exact sizing constraint with the percentage resolution size rather than the exact sizing constraint which I think is wrong.