Summary: This diff introduces new, little bit sophisticated round-to-pixel-grid algorithm. **Motivation:** Previous simple and straightforward solution works in most cases but sometimes produce the not-so-great result. A while ago Nick Lockwood described this problem and proposed the solution in RN's RCTShadowView class: For example, say you have the following structure: // +--------+---------+--------+ // | |+-------+| | // | || || | // | |+-------+| | // +--------+---------+--------+ Say the screen width is 320 pts so the three big views will get the following x bounds from our layout system: {0, 106.667}, {106.667, 213.333}, {213.333, 320} Assuming screen scale is 2, these numbers must be rounded to the nearest 0.5 to fit the pixel grid: {0, 106.5}, {106.5, 213.5}, {213.5, 320} You'll notice that the three widths are 106.5, 107, 106.5. This is great for the parent views but it gets trickier when we consider rounding for the subview. When we go to round the bounds for the subview in the middle, it's relative bounds are {0, 106.667} which gets rounded to {0, 106.5}. This will cause the subview to be one pixel smaller than it should be. This is why we need to pass in the absolute position in order to do the rounding relative to the screen's grid rather than the view's grid. After passing in the absolutePosition of {106.667, y}, we do the following calculations: absoluteLeft = round(absolutePosition.x + viewPosition.left) = round(106.667 + 0) = 106.5 absoluteRight = round(absolutePosition.x + viewPosition.left + viewSize.width) + round(106.667 + 0 + 106.667) = 213.5 width = 213.5 - 106.5 = 107 You'll notice that this is the same width we calculated for the parent view because we've taken its position into account. I believe this is awesome. I also believe that we have to decouple this logic from RN and put it into awesome Yoga. So I did it in this diff. **Fun fact:** The original implementation of this algorithm in RN had (and still have) a bug, which was found by Dustin dshahidehpour and fixed in D4133643. Therefore that diff was unlanded because it broke something unrelated inside RN text engine. I will fix that problem in RN later. **Why do we need to change test methodology?** Because the way we receive layout metrics from Chrome browser actually directly related to rounding problem. Previously we used `offsetHeight` and `offsetWidth` properties of the DOM node, which contain naively rounded values from `computedStyle` or `getBoundingClientRect`. (Which is we are trying to fix!) So, I added the new function that computes node size using two-step-rounding approach, conceptually similar to one that implemented in Yoga. Note: Chrome browser performs rounding layout as part of rendering process and actual values that can ve computed by counting actual pixel are different from these natively rounded ones. **Why do some tests now have different desired values?** These changes actually prove that my approach is correct and more useful for actual view rendering goals. So, let's take a look at test with changed values `rounding_fractial_input_3`: Previously: 64+25+24=114 (Incorrect!) Now: 65+24+25=114 (Correct!) Previously: 64+25+24=114 (Incorrect!) Now: 65+24+25=114 (Correct!) Reviewed By: emilsjolander Differential Revision: D4941266 fbshipit-source-id: 07500f5cc93c628219500e9e07291438e9d5d36c
Yoga

Building
Yoga builds with buck. Make sure you install buck before contributing to Yoga. Yoga's main implementation is in C, with bindings to supported languages and frameworks. When making changes to Yoga please ensure the changes are also propagated to these bindings when applicable.
Testing
For testing we rely on gtest as a submodule. After cloning Yoga run git submodule init
followed by git submodule update
.
For any changes you make you should ensure that all the tests are passing. In case you make any fixes or additions to the library please also add tests for that change to ensure we don't break anything in the future. Tests are located in the tests
directory. Run the tests by executing buck test //:yoga
.
Instead of manually writing a test which ensures parity with web implementations of Flexbox you can run gentest/gentest.rb
to generated a test for you. You can write html which you want to verify in Yoga, in gentest/fixtures
folder, such as the following.
<div id="my_test" style="width: 100px; height: 100px; align-items: center;">
<div style="width: 50px; height: 50px;"></div>
</div>
Run gentest/gentest.rb
to generate test code and re-run buck test //:yoga
to validate the behavior. One test case will be generated for every root div
in the input html.
You may need to install the latest watir-webdriver gem (gem install watir-webdriver
) and ChromeDriver to run gentest/gentest.rb
Ruby script.
.NET
.NET testing is not integrated in buck yet, you might need to set up .NET testing environment. We have a script which to launch C# test on macOS, csharp/tests/Facebook.Yoga/test_macos.sh
.
Code style
For the main C implementation of Yoga clang-format is used to ensure a consistent code style. Please run bash format.sh
before submitting a pull request. For other languages just try to follow the current code style.
Benchmarks
Benchmarks are located in benchmark/YGBenchmark.c
and can be run with buck run //benchmark:benchmark
. If you think your change has affected performance please run this before and after your change to validate that nothing has regressed. Benchmarks are run on every commit in CI.