Compare commits

...

259 Commits

Author SHA1 Message Date
Nick Gerleman
af57b2164d Fix JS build on Windows (#1648)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1648

Node made a breaking change in a security release for 18/20 where `spawn()` no longer loads `.bat` files by default. 69ffc6d50d. Execute command in shell.

Reviewed By: javache

Differential Revision: D56230965

fbshipit-source-id: 52e9bd8a76664bd07ea25b6355ac54fcb24cbb9a
2024-04-19 12:14:03 -07:00
Nick Gerleman
1b9b878b9a Set version to 3.0.4 2024-04-17 00:02:16 -07:00
Nick Gerleman
866f503bde Don't run test validation off main branch (#1644)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1644

Because Chrome behaviors can change

Reviewed By: cortinico

Differential Revision: D56088135

fbshipit-source-id: 7f760786dde061df9af034368e2184117e6e6846
2024-04-17 00:01:02 -07:00
Nick Gerleman
29f016c1ea Isolate Distributed JavaScript (#1645)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1645

Right now we publish Yoga by transforming in-place, and rewriting the entrypoint to point to the generated vanilla JavaScript.

It is nice to include the original source, e.g. so that users can use sourcemaps when debugging, but putting these files on top of each other has been causing problems, like https://github.com/facebook/yoga/issues/1637#issuecomment-2049099690

This changes the packaging step to instead put all the outputs into a "dist" folder, and point the package entrypoints there. We still include original source for sourcemap usage.

Reviewed By: yungsters

Differential Revision: D56093470

fbshipit-source-id: ecd52dddd9040294aae66747cf1fdf48c7f602e7
2024-04-17 00:00:55 -07:00
Nick Gerleman
ed5d2ffc2d Set version to 3.0.3 2024-04-09 16:13:40 -07:00
Willson Haw
8a9758a2cc Add JavaScript bindings for YGHasNewLayout (#1631)
Summary:
Adds JavaScript bindings for YGHasNewLayout which introduces
two new node methods: `hasNewLayout()` and `markLayoutSeen()`.

Closes https://github.com/facebook/yoga/issues/681

Pull Request resolved: https://github.com/facebook/yoga/pull/1631

Reviewed By: joevilches

Differential Revision: D55213296

Pulled By: NickGerleman

fbshipit-source-id: 161288c3f54c2b82a6b2b842387916fe8713c2c9
2024-04-09 16:04:35 -07:00
Bela Bohlender
334eebc484 Entry point without TLA for js package (#1638)
Summary:
Follow up on https://github.com/facebook/yoga/issues/1637

TLDR: tooling for TLA is not there yet; An additional entry point without top-level-await is appropriate

- adds ./load entry to js package
- uses .js file extensions to prevent requiring [allowImportingTsExtensions](https://www.typescriptlang.org/tsconfig#allowImportingTsExtensions)

Pull Request resolved: https://github.com/facebook/yoga/pull/1638

Reviewed By: joevilches

Differential Revision: D55614636

Pulled By: NickGerleman

fbshipit-source-id: 126a94aa68d22d32b938282cfa1a5059bb9df337
2024-04-09 16:03:21 -07:00
Nick Gerleman
5b106e5dd5 Bump README 2024-04-03 20:28:56 -07:00
Nick Gerleman
397d304e14 Set version to 3.0.2 2024-03-12 22:19:15 -07:00
Nick Gerleman
95cf06543f typings -> types 2024-03-12 22:17:59 -07:00
Nick Gerleman
17450191c3 Set version to 3.0.1 2024-03-12 22:07:12 -07:00
Nick Gerleman
75e564d0d5 Fixup npm package README
Summary: Remove some outdated information and make sure that npmjs shows the builtin TypeScript typings (now that we no longer have clever resolution schemes).

Reviewed By: javache

Differential Revision: D54788636

fbshipit-source-id: 76e7663924189fd68ac62b27730f44213b13ad85
2024-03-12 22:06:24 -07:00
Joe Vilches
e82479e4ca Fix bug where absolute nodes were not insetted correctly in certain cases (#1593)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1593

X-link: https://github.com/facebook/react-native/pull/43417

There was a bug where we did not position absolute nodes correctly if the static node had a different main/cross axis from the containing node. This fixes that. The change is somewhat complicated unfortunately but I tried to add sufficient comments to explain what is happening

Reviewed By: NickGerleman

Differential Revision: D54703955

fbshipit-source-id: 096c643f61d4f9bb3ee6278d675ebd69b57350d7
2024-03-12 22:06:06 -07:00
Nick Gerleman
508b9a57fe Reduce warning level of distributed Yoga builds (#1592)
Summary:
X-link: https://github.com/facebook/react-native/pull/43405
Pull Request resolved: https://github.com/facebook/yoga/pull/1592

Fixes https://github.com/facebook/yoga/issues/1590

Yoga may be built with a high warning level. This is helpful in letting Yoga be used in more places, and finding defects. We currently set these in the internal BUCK build, the CMake reference build, and the Yoga Standalone (not RN) CocoaPods build.

Yoga's reference CMake build and spec are consumed today by users of Yoga, instead of just Yoga developers. Here, it makes more sense to avoid anything that could break compiler-to-compiler compatibility.

We default these to a less intense (`-Wall -Werror`). I kept `/W4`, for pragmatic reasons, and since it is relatively standard for MSVC.

We continue to build with strict flags on Buck build on Clang.

Reviewed By: cortinico

Differential Revision: D54735661

fbshipit-source-id: 130e35ac9dcffa2f7e70e48d18770f1275864e2a
2024-03-12 22:05:47 -07:00
Nick Gerleman
749b6b2bf8 Run CocoaPods publish on Mac VM 2024-03-05 16:06:47 -08:00
Nick Gerleman
fe7dc21eb1 Set version to 3.0.0 2024-03-05 15:59:59 -08:00
Riccardo Cipolleschi
59fb251edc Fix MacCatalyst archiving for stand-alone pod (#1585)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1585

When used alone, yoga has no visibility over the USE_FRAMEWORKS variable set by React Native.

In this case, there is no other way to know whether the current target that will install the spec is using use_framewors or not. As a failsafe, condition, let's always add the `"$(PODS_TARGET_SRCROOT)"` to the search paths to make sure that archiving won't fail.

Whe used in other context, that should not be a problem: search paths are just directory Xcode uses to find headers that are needed.

Reviewed By: NickGerleman

Differential Revision: D54417386

fbshipit-source-id: aa2ae41c077e4346c0417c73291a37c992a06b58
2024-03-05 15:56:01 -08:00
Kesha Antonov
efbebb4a97 Update Yoga.podspec: fixes archiving for Mac Catalyst
Summary:
Hi

When I tried to archive macos catalyst app in Xcode I got errors:

<img width="977" alt="Screenshot 2024-02-05 at 00 03 32" src="https://github.com/kesha-antonov/react-native/assets/11584712/b83f75a5-b42f-42e4-9afa-1e2527501baa">

This PR fixes archiving by linking PrivateHeaders in yoga.framework

<img width="399" alt="Screenshot 2024-02-05 at 01 03 48" src="https://github.com/kesha-antonov/react-native/assets/11584712/089080ad-b1dc-4703-9273-d8aa3253205e">

<img width="1404" alt="Screenshot 2024-02-05 at 01 05 18" src="https://github.com/kesha-antonov/react-native/assets/11584712/5263cb80-8a53-4a51-bcfc-9d3a2ba739b4">

Prev PR here https://github.com/facebook/react-native/pull/42159

## Changelog:

[IOS] [FIXED] - fixed archiving for Mac Catalyst

X-link: https://github.com/facebook/react-native/pull/42847

Reviewed By: NickGerleman

Differential Revision: D53920474

Pulled By: cipolleschi

fbshipit-source-id: 0534d9aa9d249e4e0c35ada0464c38c291be7f84
2024-02-22 21:27:59 -08:00
Joe Vilches
b35456b93c Error message when measure functions mismatch
Summary:
To sanity check that a capture is working as expected when running benchmark

We could maybe even throw here as a bad measure function will throw off the rest of the benchmark.

Reviewed By: NickGerleman

Differential Revision: D53681506

fbshipit-source-id: f5ab7e00e76df0ac899d62c3f6b4535b3780d45d
2024-02-13 17:22:08 -08:00
Joe Vilches
e679327904 Add small desktop capture
Summary: A much smaller tree than the previous one. It only has 100 or so nodes

Reviewed By: NickGerleman

Differential Revision: D53632451

fbshipit-source-id: 1268499fa768f3b6673ff8bcedac23cf6d9395ac
2024-02-13 17:22:08 -08:00
Joe Vilches
cae7ef924a Convience script to run benchmark
Summary: You need to provide the benchmark binary with a path to the captures. This is annoying and there is not a great way to do this in c++ that is cross-plat. So I just made this bash script to ease it. It can do buck and cmake.

Reviewed By: NickGerleman

Differential Revision: D53632438

fbshipit-source-id: 98b0ad52f91f2581e09f787da24f2ec2fff58bf4
2024-02-13 17:22:08 -08:00
Joe Vilches
e2ed3f031d Support for (de)serializing measure funcs
Summary:
In addition to all the state that gets set on the node that is easy to serialize - like floats, enums, bools, etc - we also need to serialize measure functions. This is because these functions take a nontrivial amount of time up during layout and we should capture that. Also, they are important to the ability to truly replay layout as it was captured as the results of the measure functions determine many of the steps the layout algorithm takes.

Capturing this is rather tricky however, but I think I found a solution that is relatively simple and non-error prone. Essentially, since we are capturing the entire tree and virtually every input that goes into the flexbox algorithm, we *should* be able to replay layout exactly as it was captured. This means that the order in which measure functions are called *should* be the same. If this is the case, then all we need to do to capture the measure functions is store their input, output, and duration in a big array. During deserialization we just keep track of an index and use that to determine which measure function we should call. That is the premise behind what happens in this diff. In theory the algorithm could change and the capture would be wrong but it is easy enough to recapture again. Additionally we need to dirty the tree so that we get rid of caching which might omit some measure func calls

In order to capture you need to insert a method exposed by CaptureTree.h into the client measure func, which is kind of annoying but not that bad. In future diffs I will put a macro in place to make this even easier.

I also add our first capture! Which is of a large react native desktop app

Reviewed By: NickGerleman

Differential Revision: D53581121

fbshipit-source-id: 876a230208d67f0ecf76844a4f1b80048353aae2
2024-02-13 17:22:08 -08:00
Joe Vilches
cc66362a28 Expose replacement wrapper of CalculateLayout + (de)serialize layout inputs (#1575)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1575

If we want to replay layouts for benchmark, we should also capture the inputs. This diff does that as well as changing the API in CaptureTree.h. We now expose YGCalculateLayoutWithCapture designed to be a drop-in replacement for YGCalculateLayout. This allows us to have a bit more control on the order of everything and lets us capture measure functions in the next diff much easier.

Reviewed By: NickGerleman

Differential Revision: D53444261

fbshipit-source-id: 616e39153c21e7b472911502b6a717e92c88a4d1
2024-02-09 16:44:32 -08:00
Joe Vilches
753b319977 Support for (de)serializing node state (#1570)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1570

X-link: https://github.com/facebook/react-native/pull/42751

tsia. Need node state

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D53206323

fbshipit-source-id: eb48c3873536eb52c8ffcce8005725da274e5373
2024-02-09 16:44:32 -08:00
Joe Vilches
f90ad378ff Support for (de)serializing config values (#1571)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1571

X-link: https://github.com/facebook/react-native/pull/42750

tsia. This is state we need to capture as it can drastically affect the benchmark times

Reviewed By: NickGerleman

Differential Revision: D53203385

fbshipit-source-id: 47178458d039df90fb15d8a420f9e0f17e4fe6ca
2024-02-09 16:44:32 -08:00
Joe Vilches
a37565f70d Fix failing MVSC benchmark builds (#1573)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1573

Noticed a recent stack of commits cause the MVSC builds of benchmark to fail. This was due to forgetting to call `.string()` of a path and trying to escape a character that cannot be escaped.

Reviewed By: philIip

Differential Revision: D53461723

fbshipit-source-id: b6cc034d53b3a61929012965e257a3984c3bff47
2024-02-06 11:16:21 -08:00
Joe Vilches
94960f123e main() function to run a benchmark (#1564)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1564

tsia

Reviewed By: NickGerleman

Differential Revision: D53028962

fbshipit-source-id: dee2670eaa4fe0ab8b6e2b9e1bbe4356bb2c0735
2024-02-05 11:48:07 -08:00
Joe Vilches
8dbf55f230 Add ability to time captured benchmarks (#1569)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1569

Added a `benchmark` function that takes a path representing a directory to read captures from. This is supplied by the caller due to annoyance with filesystem access in C++. This calls into timing code with <chrono> and prints it out to the console.

Reviewed By: NickGerleman

Differential Revision: D53104632

fbshipit-source-id: fe8bcb0a87198701865fb04193894591d2eff821
2024-02-05 11:48:07 -08:00
Joe Vilches
0ca15bdaaa Ability to recreate yoga trees from JSON captures (#1566)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1566

In the previous diffs we serialized the in-memory representation of a node into json. This diff exposes a `generateBenchmark` method that reads from that json executes the proper public Yoga API functions to recreate the same tree. It then calls calculate layout so that we can time that in the next diff.

This diff is really only focusing on the core aspects of a yoga tree like style, children, and calculating layout; there are still more things to add coming up:

* Support for configs, experiments, and errata
* Support for measure functions
* Support for general node state that is not style (like always forming a containing block)
* Actually running all of these benchmarks together
* Tests

Reviewed By: NickGerleman

Differential Revision: D52987588

fbshipit-source-id: 7f7c9ca9956f693be62bc5e3cebdf1aed6f58aec
2024-02-05 11:48:07 -08:00
Joe Vilches
efd27efd70 Ability to capture trees as JSON files (#1565)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1565

This adds a `captureTree` method intended to be injected in somewhere where you would like a JSON representation of a yoga tree. Right now it is very simple and just calls on `nodeToString` to serialize a node into JSON, and then saves that into a file in the given path. Some buck file changes needed to be done as well to use this in other files.

Reviewed By: NickGerleman

Differential Revision: D52972995

fbshipit-source-id: f4e09a815edef92ab959cfc76bacccbce225d940
2024-02-02 15:44:23 -08:00
Joe Vilches
bb83f45872 Change NodeToString.cpp to output JSON not html (#1563)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1563

X-link: https://github.com/facebook/react-native/pull/42645

We want to be able to capture Yoga trees in production. To do this we need to serialize and deserialize the in-memory representation of a tree. We have a way to turn a tree into html using NodeToString.cpp but that outputs html, which is going to be hard to deserialize. So, I added the [nlohmann json library](https://github.com/nlohmann/json/tree/develop?tab=readme-ov-file) so that we can serialize into JSON instead. Then we need to change the inner workings of NodeToString.cpp to use this library instead of its html.

One of the bigger structural changes I made was standardizing the checks need to append something to the string. What we want is to only add something if it is not the default style. The existing logic does that but bears the burden of knowing what the default of certain styles actually is. This just calls the getter on a new node to obtain that value, which should simplify things a bit.

Reviewed By: NickGerleman

Differential Revision: D52929268

fbshipit-source-id: 06eff1e10061bcb55fcdeb6f3ebe7e95155b4c86
2024-02-02 15:44:23 -08:00
Joe Vilches
19af9e6450 Move NodeToString.cpp to benchmark and remove interal usages (#1568)
Summary:
X-link: https://github.com/facebook/react-native/pull/42710

Pull Request resolved: https://github.com/facebook/yoga/pull/1568

This is no longer going to be built with normal yoga so we are going to switch to a public API

Reviewed By: NickGerleman

Differential Revision: D53141235

fbshipit-source-id: 259270a4cd91ef0dab91cefba9c41953b6340d78
2024-02-02 15:44:23 -08:00
Joe Vilches
28975a6053 Remove public APIs for YGNodePrint and YGConfigSetPrintTreeFlag (#1567)
Summary:
X-link: https://github.com/facebook/react-native/pull/42688

Pull Request resolved: https://github.com/facebook/yoga/pull/1567

We are planning on overhauling NodeToString to output JSON instead of HTML for the purposes of better benchmarking and capturing trees in JSON format to benchmark later. This gives us a bit of a headache as we have to revise several build files to ensure this new library works, ensure that it is only included in certain debug builds, and deal with the benchmark <-> internal cross boundary that arises as the benchmark code (which is a separate binary) tries to interact with it.

On top of it all this is really not used at all.

The plan is to rip out this functionality and just put it in a separate binary that one can include if they really want to debug. That means that it cannot exist in the public API, so I am removing it here.

Private internals come next

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D53137544

fbshipit-source-id: 7571d243b914cd9bf09ac2418d9a1b86d1bee64a
2024-02-02 15:44:23 -08:00
Nick Gerleman
58aa090774 Fix flooring of border (#1562)
Summary:
X-link: https://github.com/facebook/react-native/pull/42411

Pull Request resolved: https://github.com/facebook/yoga/pull/1562

I added a small regression D52605596, where negative border would not be correctly floored. This fixes that, and starts adding tests specifically targeting the computed style API, now decoupled from the yoga node.

Reviewed By: joevilches

Differential Revision: D52930827

fbshipit-source-id: e165dade705a8de54c92d65f3664c9081137788c
2024-01-22 22:07:49 -08:00
Joe Vilches
8fe38fc7a8 Fix mismatched cases of inlineStart/End and flexStart/End (#1561)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1561

Back when I introduced the inline functions that would get the edge according to the writing direction I swapped some instances of `setLayoutPosition` which wrote to the flexStart edge erroneously. We should basically never read from some inline style and write to the flex edge. This changes them all to use the flex values.

Reviewed By: NickGerleman

Differential Revision: D52921401

fbshipit-source-id: 92b74d652018596134c91827806272ed7418ef6c
2024-01-22 15:41:09 -08:00
Nick Gerleman
67154d47a3 Replace CompactValue with StyleValueHandle and StyleValuePool (#1534)
Summary:
X-link: https://github.com/facebook/react-native/pull/42131

Pull Request resolved: https://github.com/facebook/yoga/pull/1534

Now that the storage method is a hidden implementation detail, this changes the underlying data structure used to store styles, from `CompactValue` (a customized 32-bit float with tag bits), to `StyleValuePool`.

This new structure operates on 16-bit handles, and a shared small buffer. The vast majority of real-world values can be stored directly in the handle, but we allow arbitrary 32 bit (and soon 64-bit) values to be stored, where the handle then becomes an index into the styles buffer.

This results in a real-world memory usage win, while also letting us store the 64-bit values we are wanting to use for math function support (compared to doubling the storage requirements).

This does seem to make style reads slower, which due to their heavy frequency, does have a performance impact observable by synthetics. In an example laying out a tree of 10,000 nodes, we originally read from `StyleValuePool` 2.4 million times.

This originally resulted in a ~10% regression, but when combined with the changes in the last diff, most style reads become simple bitwise operations on the handle, and we are actually 14% faster than before.

| | Before | After | Δ |
| `sizeof(yoga::Style)` | 208B  | 144B | -64B/-31% |
| `sizeof(yoga::Node)` | 640B  | 576B | -64B/-10% |
| `sizeof(YogaLayoutableShadowNode) ` |  920B | 856B | -64B/-7% |
| `sizeof(YogaLayoutableShadowNode) + sizeof(YogaStylableProps)` | 1296B  | 1168B | -128B/-10% |
| `sizeof(ViewShadowNode)`  |  920B | 856B | -64B/-7% |
| `sizeof(ViewShadowNode) + sizeof(ViewShadowNodeProps)` | 2000B  | 1872B | -128B/-6% |
| "Huge nested layout" microbenchmark (M1 Ultra) | 11.5ms | 9.9ms | -1.6ms/-14% |
| Quest Store C++ heap usage (avg over 10 runs) | 86.2MB | 84.9MB | -1.3MB/-1.5% |

Reviewed By: joevilches

Differential Revision: D52223122

fbshipit-source-id: 990f4b7e991e8e22d198ce20f7da66d9c6ba637b
2024-01-19 18:22:29 -08:00
Nick Gerleman
395c596695 Add some tests for justification under row-reverse (#1560)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1560

I added these when I was trying to debug the Facepile break removing the row-reverse errata caused. Yoga is doing the right thing, and the tests pass. We didn't have this specific coverage before, so add it.

Reviewed By: joevilches

Differential Revision: D52909633

fbshipit-source-id: d1e8f55bb534d76bd7dfdc46a1e1cc6f0a3ca211
2024-01-19 16:21:59 -08:00
Nick Gerleman
8744792f41 Simplify Edge Resolution (#1550)
Summary:
X-link: https://github.com/facebook/react-native/pull/42254

Pull Request resolved: https://github.com/facebook/yoga/pull/1550

This change aims to simplify how we resolve edges. This operation happens many, many times, and has gotten complex and slow when paired with StyleValuePool.

This starts reshaping so that `yoga::Style` can resolve a style prop for a given edge. This is closer to the ideal computed style API to avoid recalcing this so many times, but doesn't address that.

This relies on removing the errata related to row-reverse, and cleans up the removal started in the last change.

This has no measurable perf effect under CompactValue, but has a >10% uplift in perf when using StyleValueHandle, where we can trivially check if a handle points to a defined value without resolving it, but only within `yoga::Style` since we don't expose the handle outside of it.

More quantifiably, we go from 2.35 million StyleValuePool reads to 993k. The rest are checks on the handle.

Reviewed By: joevilches

Differential Revision: D52605596

fbshipit-source-id: 0b366963a899e376f99ce3d75cd5f14a25d60cec
2024-01-19 11:28:06 -08:00
Nick Gerleman
35b9b5223e yoga::Node::getStyle() to yoga::Node::style() (#1555)
Summary:
X-link: https://github.com/facebook/react-native/pull/42314

Pull Request resolved: https://github.com/facebook/yoga/pull/1555

The next diff moves a bunch of methods to `yoga::Style`. This renames the function to be a tad bit shorter, for more readable callsites. It also makes it more consistent with style property getters.

Changelog: [Internal]

Reviewed By: rozele

Differential Revision: D52803393

fbshipit-source-id: 557df34a9f0fb0ee42ad23b1fda99c1e0eb1d4e3
2024-01-19 11:28:06 -08:00
Nick Gerleman
d6a3b71085 Fix missing assignment in Node move constructor (#1554)
Summary:
X-link: https://github.com/facebook/react-native/pull/42275

Pull Request resolved: https://github.com/facebook/yoga/pull/1554

Adds recently added field missing from move constructor

Reviewed By: joevilches

Differential Revision: D52767525

fbshipit-source-id: ec68452b058178967650bbef736562caafbf4a36
2024-01-19 09:46:08 -08:00
Nathaniel Brough
1a8b80a3d5 Add simple fuzz-harness (#1537)
Summary:
> This PR is based on the proposal in https://github.com/facebook/yoga/issues/1538 inlined below, to integrate yoga with `oss-fuzz`

Hey yoga team,

I've recently become interested in yoga. I'd like to suggest and champion an effort to set up some basic fuzz-testing and combine it with google/oss-fuzz for continuous fuzzing. I'm fully aware that you are very busy people and I don't want to overload your review/maintenance capacity. Is this a bad time to discuss potential security/reliability improvements?

If you're not familiar with fuzzing or oss-fuzz I've included a few brief notes below.

#### **Benefits of Fuzz-Testing**

- **Dynamic Code Testing**: Fuzz-testing challenges systems with unexpected data, aiming to identify vulnerabilities or bugs. It’s akin to an exhaustive stress-test for the code.
- **Detecting Hidden Vulnerabilities**: It can uncover potential weaknesses that may not be evident in routine tests.
- **Continuous and Automated Testing**: With tools like Google’s OSS-Fuzz, fuzz-testing can be automated, running continuously on distributed systems, ensuring daily resilience checks.

#### **Google/oss-fuzz for Continuous Fuzzing**

- **Automated Fuzzing**: OSS-Fuzz undertakes comprehensive fuzz-testing daily on a distributed cluster.
- **Detailed Reporting**: OSS-Fuzz offers exhaustive reports in case of detected anomalies, enabling effective action.

I’d be more than happy to lead the effort in integrating fuzz testing with the yoga and assist in any way required.

#### Prior integrations
There have been a number of previous integrations completed with facebook repositories and google/oss-fuzz including;
- facebook/time
- facebook/zstd
- facebookexperimental/starlark-rust (this was me)
- facebook/proxygen
- facebook/hermes
- facebook/rocksdb

As a proof of concept I created a couple of super simple fuzz harnesses in https://github.com/facebook/yoga/issues/1537.

NOTE: Adding fuzz-testing and integrating with google/oss-fuzz was previously suggested here https://github.com/facebook/yoga/pull/1055 and was rejected. I think I've addressed the concerns raised in the first PR. While the original PR contained what was probably a higher performance fuzzer, the new fuzzer should be easier to integrate and doesn't introduce multiple sources of truth.

Pull Request resolved: https://github.com/facebook/yoga/pull/1537

Reviewed By: yungsters

Differential Revision: D52800366

Pulled By: NickGerleman

fbshipit-source-id: 4957282456f3263e600d13ae6f3e983681bebda6
2024-01-19 09:00:35 -08:00
Joe Vilches
1541f9d528 Rename AbsolutePositioning errata (#1558)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1558

X-link: https://github.com/facebook/react-native/pull/42318

AbsolutePositioning -> AbsolutePositioningCatchAll

A bit more clear. This errata is for various issues with positioning absolute nodes. There really isn't a clear description as to what specifically this enables/disables, so I just opted to say "catch all" to indicate that this controls various bugs

Reviewed By: NickGerleman

Differential Revision: D52820117

fbshipit-source-id: 80b77832baf65e68e57ca523c418422dd346ef0f
2024-01-18 21:22:05 -08:00
Joe Vilches
06c26d7d46 Remove static-behaves-like-relative errata (#1556)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1556

X-link: https://github.com/facebook/react-native/pull/42315

Since we aim to ship static to all users of yoga (not just XPR), we need to remove the errata that is gating most of the features. This should be a non breaking change. To ensure that, I added a new errata which, if on, will use the inner size of the containing node as the containing block. This is how it has been for a while and resolving this is risky and time consuming so for the time being we will stick with that.

Reviewed By: NickGerleman

Differential Revision: D52706161

fbshipit-source-id: 30a93f29cb0d97b20b2947eaa21f36cdc78c4961
2024-01-18 21:22:05 -08:00
Joe Vilches
f69a1a43e5 Hardcode AbsolutePercentageAgainstPaddingEdge experimental feature to false (#1549)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1549

X-link: https://github.com/facebook/react-native/pull/42253

This experimental feature is always false, and with the next diff I will be deleting the branch that actually calls into this. Separating this diff out to simplify the review process.

Reviewed By: NickGerleman

Differential Revision: D52705765

fbshipit-source-id: 705f4aa297eae730af9b44753eb01c9dec385dcf
2024-01-18 21:22:05 -08:00
Nick Gerleman
0bbfe4503d Remove YGNodeSetPrintFunc and related (#1553)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1553

X-link: https://github.com/facebook/react-native/pull/42274

Separate from `YGConfigSetPrintTreeFlag` we have a public API `YGNodeSetPrintFunc` which sets a function called, if you manually change a constant in source code during debugging.

This is not debug-only, is exposed as part of the public API (without a way to turn it on from the public API), and takes up a pointer per node doing nothing.

I'm not aware of anyone recently using the capability, and the tracing/event related work done since then would be more powerful for this anyway.

Remove the API.

Changelog: [Internal]

Reviewed By: rozele

Differential Revision: D52767445

fbshipit-source-id: f72927b47cffa4fe6fe886b42f07cc1ba55f141e
2024-01-16 10:07:19 -08:00
Nick Gerleman
508df05f0d Android Gradle Plugin 8.2.1 (#1548)
Summary:
We technically didn't need to be on the Beta before for NDK 26, but this bumps to the latest, non-prerelease version.

Pull Request resolved: https://github.com/facebook/yoga/pull/1548

Reviewed By: christophpurrer

Differential Revision: D52749378

Pulled By: NickGerleman

fbshipit-source-id: c1940976e6b240aba2af5a2863f189280701ebd3
2024-01-12 22:49:14 -08:00
Nick Gerleman
8c3e01166b Remove row-reverse errata (#1547)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1547

X-link: https://github.com/facebook/react-native/pull/42251

Yoga has an odd behavior, where `start`/`end` edges under row-reverse are relative to flex-direction, instead of writing direction.

While Yoga doesn't actually document what this behavior is supposed to be, it goes against CK documentation, historic RN documentation, and the behavior valid on the web. It is also applied inconsistently (e.g. sometimes only on container, sometimes on child). It really is a bug, instead of an intended behavior.

We changed the default behavior for Yoga, but left the existing one behind an errata (so existing fbsource users got old behavior). We have previously seen this behavior show up in product code, including CK when running on FlexLayout.

`row-reverse` is surprisingly uncommon though:
1. Litho has <40 usages
2. RN has ~40 usages in `RKJSModules`,~30 in `arvr/js`, ~6 in `xplat/archon`
3. CK has ~80 usages
4. NT has ~40 usages

There are few enough, mostly simple components, that we can inspect through each of them, looking for signs they will hit the issue (at the potential chance of missing some).

CK accounts for 10/14 usages that I could tell would trigger the issue, since it only exposes start/end edge, and not left/right. It might make sense to make it preserve behavior instead, to reduce risk a bit.

FlexLayout is now separately powering Bloks, which wasn't surveyed, so I didn't touch CK behavior under Bloks.

There could also be other usages in other frameworks/bespoke usages, and this has implications for OSS users. But based on our own usage, of many, many components, this seems rare.

Changelog:
[General][Breaking] - Make `start/end` in styles always refer to writing direction

Reviewed By: pentiumao, joevilches

Differential Revision: D52698130

fbshipit-source-id: 2a9ac47e177469f30dc988d916b6c0ad95d53461
2024-01-12 13:49:53 -08:00
dependabot[bot]
7e70656e46 Bump follow-redirects from 1.15.3 to 1.15.4 (#1546)
Summary:
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
<details>
<summary>Commits</summary>
<ul>
<li><a href="65858205e5"><code>6585820</code></a> Release version 1.15.4 of the npm package.</li>
<li><a href="7a6567e16d"><code>7a6567e</code></a> Disallow bracketed hostnames.</li>
<li><a href="05629af696"><code>05629af</code></a> Prefer native URL instead of deprecated url.parse.</li>
<li><a href="1cba8e85fa"><code>1cba8e8</code></a> Prefer native URL instead of legacy url.resolve.</li>
<li><a href="72bc2a4229"><code>72bc2a4</code></a> Simplify _processResponse error handling.</li>
<li><a href="3d42aecdca"><code>3d42aec</code></a> Add bracket tests.</li>
<li><a href="bcbb096b32"><code>bcbb096</code></a> Do not directly set Error properties.</li>
<li>See full diff in <a href="https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.15.3&new-version=1.15.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

 ---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `dependabot rebase` will rebase this PR
- `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `dependabot merge` will merge this PR after your CI passes on it
- `dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `dependabot cancel merge` will cancel a previously requested merge and block automerging
- `dependabot reopen` will reopen this PR if it is closed
- `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/facebook/yoga/network/alerts).

</details>

Pull Request resolved: https://github.com/facebook/yoga/pull/1546

Reviewed By: cortinico

Differential Revision: D52689011

Pulled By: NickGerleman

fbshipit-source-id: 3a8b1d0802a03d262660bafd5998bedd8487c5b0
2024-01-11 04:28:37 -08:00
Joe Vilches
93b6c09eeb Fix error from misspelled function name (#1542)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1542

`YGNodeSetAlwaysFormContainingBlock` -> `YGNodeSetAlwaysFormsContainingBlock`

Started getting tasks telling me github CI was broken. Turns out I was just mispelling this.

Its not a big deal but we really should ensure that stuff will not land if it break github CI...not in our wheelhouse but it seems quite silly that this happens.

Also, a lot of the random files do not have auto complete or code checking which is why I didn't notice this to begin with.

Reviewed By: NickGerleman

Differential Revision: D52632257

fbshipit-source-id: 367ae5766ef9baecf55f7c227ff9fc8dece43af6
2024-01-09 11:46:05 -08:00
Joe Vilches
e4ab593686 JS bindings for setAlwaysFormsContainingBlock (#1541)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1541

tsia

Reviewed By: NickGerleman

Differential Revision: D52608426

fbshipit-source-id: 19fa01d87cbae3457adb9cdb9e0cb602b7afa619
2024-01-08 20:28:49 -08:00
Joe Vilches
47e9f33eb4 Java bindings for setAlwaysFormsContainingBlock (#1540)
Summary:
X-link: https://github.com/facebook/react-native/pull/42192

Pull Request resolved: https://github.com/facebook/yoga/pull/1540

tsia

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D52608259

fbshipit-source-id: 647ec4e2fe180ace8d6b641e17cd610fa53fe845
2024-01-08 20:28:49 -08:00
Joe Vilches
5425107246 Support transforms forming containing blocks (#1539)
Summary:
X-link: https://github.com/facebook/react-native/pull/42191

Pull Request resolved: https://github.com/facebook/yoga/pull/1539

React native supports transforms and if a node has a transform it will [form a containing block for absolute descendants regardless of position type](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block). So we need to pass that information into Yoga to ensure this happens.

The verbiage for the field "alwaysFormsContainingBlock" is very specific. In a vacuum a node cannot simply "form a containing block". It only forms a containing block in reference to a different node. This can be illustrated in a scenario where we have a static node that is a flex container which has 1 absolute child and 1 relative child. This static node will form a containing block for the relative child but not the absolute one. We could just pass the information on rather something has a transform or not but Yoga is not supposed to know about transforms in general. As a result we have a notion of "always" forming a containing block. Since Yoga is a flexbox spec, non-absolute nodes' containing blocks will ways be their parent. If we add something like a transform to a node then that will also apply to absolute nodes - hence we can say the node will **always** form a CB, no matter who is the descendant.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D52521160

fbshipit-source-id: bab9319ffddec617f5281823930f2a00cc2967f2
2024-01-08 20:28:49 -08:00
Nick Gerleman
94e7336394 Move trailing position functions (#1533)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1533

X-link: https://github.com/facebook/react-native/pull/42031

I have some reservations about  some of the conditional setting of trailing position in general, and some of the repeated transformations that neccesitates this, but these functions don't belong in `CalculateLayout.h`. For now, just move these to their own header.

Reviewed By: joevilches

Differential Revision: D52292121

fbshipit-source-id: 4a998a4390a8d045af45f5424adaf049ed635e7a
2023-12-21 13:48:11 -08:00
Nick Gerleman
c319c36b0d Remove duplicate declaration of calculateLayoutInternal (#1532)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1532

X-link: https://github.com/facebook/react-native/pull/42030

as per title

Reviewed By: joevilches

Differential Revision: D52289613

fbshipit-source-id: 51a810dda32e6fe44be05a42fe21d78aede52d30
2023-12-21 13:48:11 -08:00
Nick Gerleman
3b0545b15d Add tests for cycles with percentage dimensions (#1530)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1530

Yoga has a shortcut where if a min dimension and max dimension are the same, the value acts as a definite length.

I was curious how browsers handled this.

CSS 2.1 said:
> If the containing block's width depends on this element's width, then the resulting layout is undefined

This is superceded in the CSS box sizing spec. https://www.w3.org/TR/css-sizing-3/#sizing-values

> If, in a particular axis, the containing block’s size depends on the box’s size, see the relevant layout module for special rules on how to resolve percentages. Negative values are invalid.

And later:
https://www.w3.org/TR/css-sizing-3/#cyclic-percentage-contribution

> Sometimes the size of a percentage-sized box’s containing block depends on the intrinsic size contribution of the box itself, creating a cyclic dependency. When calculating the intrinsic size contribution of such a box (including any calculations for a content-based automatic minimum size), a percentage value that resolves against a size in the same axis as the intrinsic size contribution (a cyclic percentage size) is resolved specially:

> If the box is non-replaced, then the entire value of any max size property or preferred size property (width/max-width/height/max-height) specified as an expression containing a percentage (such as 10% or calc(10px + 0%)) that is cyclic is treated for the purpose of calculating the box’s intrinsic size contributions only as that property’s initial value. For example, given a box with width: calc(20px + 50%), its max-content contribution is calculated as if its width were auto. (The percentage is honored as usual, however, during the actual sizing of the box itself; see below.)

> Otherwise, the percentage is resolved against the containing block’s size. (The containing block’s size is not re-resolved based on the resulting size of the box; the contents might thus overflow or underflow the containing block).

So, for the purpose of sizing the parent, the child sized using a percentage does not contribute, but we should be sizing children based on that size.

Yoga does not really work like this right now, but gets the answer right answer for half of these tests.

Reviewed By: yungsters

Differential Revision: D52251601

fbshipit-source-id: 4978b90723130283b00e87bbf49795a4d209174c
2023-12-19 13:38:40 -08:00
Nick Gerleman
c5edcb3d3a Reorder members in Node.h (#1529)
Summary:
X-link: https://github.com/facebook/react-native/pull/41994

Pull Request resolved: https://github.com/facebook/yoga/pull/1529

Reorganizes the header according to common C++ convnetions. Public first, then private. Constructors, then functions, then member variables.

Reviewed By: joevilches

Differential Revision: D52106056

fbshipit-source-id: 0095cf7caa58dc79c1803b3b231911e4fc66ddaf
2023-12-19 13:38:40 -08:00
Nick Gerleman
1f391dfc50 Node::styleDefinesDimension() -> Node::hasDefiniteLength() (#1526)
Summary:
X-link: https://github.com/facebook/react-native/pull/41995

Pull Request resolved: https://github.com/facebook/yoga/pull/1526

This function has made quite the journey from something that originally made more sense. This renames, refactors, and adds documentation for what it actually does.

This should eventually make its way into `yoga::Style` once computed style is moved into that structure.

bypass-github-export-checks

Reviewed By: joevilches

Differential Revision: D52105718

fbshipit-source-id: 6492224dd2e10cef3c5fc6a139323ad189a0925c
2023-12-19 13:38:40 -08:00
Nick Gerleman
ca4ecc044d yoga::resolveValue -> Length::resolve (#1520)
Summary:
X-link: https://github.com/facebook/react-native/pull/41939

Pull Request resolved: https://github.com/facebook/yoga/pull/1520

This code originates as `YGValueResolve`, used to compute a YGValue to a length in points, using a reference for 100%.

This moves it to `Style::Length`, so we can encapsulate parts of it (for style value functions), and make the API more cohesive now that we can do C++ style OOP with it.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D51796973

fbshipit-source-id: a7c359c7544f4bd2066a80d976dde67a0d16f1dd
2023-12-19 13:38:40 -08:00
Nick Gerleman
192016a0a8 Make CompactValue internal detail of yoga::Style (#1492)
Summary:
X-link: https://github.com/facebook/react-native/pull/41776
Pull Request resolved: https://github.com/facebook/yoga/pull/1492

# Summary

In preparation to replace `CompactValue`, this fully encapsulates it as an implementation detail of `yoga::Style`.

The internal API now always operates on `Style::Length`, converted to `YGValue` at the public API boundary.

In the next step, we can plug in a new representation within `Style`, which should enable 64 bit values, and lower memory usage.

# Test Plan

1. Existing tests (inc for style, invalidation, CompactValue) pass
2. Check that constexpr `yoga::isinf()` produces same assembly under Clang as `std::isinf()`
3. Fabric Android builds
4. Yoga benchmark does style reads

# Performance

Checking whether a style is defined, then reading after, is a hot path, and we are doubling any space style lengths take in the stack (but not long-term on the node). After a naive move, on one system, the Yoga benchmark creating, laying out, and destroying a tree, ran about 8-10%  slower in the "Huge nested flex" example. We are converting in many more cases instead of doing undefined check, but operating on accessed style values no longer needs to do the conversion multiple times.

I changed the `CompactValue` conversion to YGValue/StyleLength path to check for undefined as the common case (since we always convert, instead of calling `isUndefined` directly on CompactValue. That seemed to get the difference down to ~5-6% when I was playing with it then. We can optimistically make some of this up with ValuePool giving better locality, and fix this more holistically if we reduce edge and value resolution.

On another machine where I tested this, the new revision went the opposite direction, and was about 5% faster, so this isn't really a cut and dry regression, but we see different characteristics than before.

# Changelog
[Internal]

Reviewed By: rozele

Differential Revision: D51775346

fbshipit-source-id: c618af41b4882b4a227c917fcad07375806faf78
2023-12-19 13:38:40 -08:00
Nick Gerleman
2caa8ac8cb Workaround Docusaurus prism highlighting hydration bug (#1519)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1519

See https://github.com/facebook/docusaurus/issues/9629

We use prism to render the code for the inline editor. Prism renders colors to style directly, and the color chosen is dependent on a setting that may not be available at SSR time.

This adds an SSR-specific representation of the code, missing some of the nuances in token colorization (similar to https://github.com/facebook/docusaurus/pull/7373). This adds a little bit of jank compared to perfect SSR, but fixes cases where the mode is incorrect, and is a lot less jank then the more generic solution used by theme-live-codeblock of keeping the rendering of the opposite color until rehydration.

Preview: https://yoga-website-next-git-fork-nickgerleman-exp-2f8171-fbopensource.vercel.app/

Reviewed By: yungsters

Differential Revision: D52163722

fbshipit-source-id: 312dc52134f0084d40f78147190151700ee10ff7
2023-12-19 12:46:26 -08:00
Nick Gerleman
3f6f04cb92 Don't include code in SSR generation of Playground endpoint (#1518)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1518

We allow arbitrary code in the query param, that static site generation doesn't know about. Current experience in production build is confusing, since you can see a flash of one set of code (playground default), quickly change to another. It is less confusing to have it go from blank to code showing.

Reviewed By: yungsters

Differential Revision: D52162928

fbshipit-source-id: fc7b51455682351a0616be8b9ecf557122d3a8db
2023-12-19 12:46:26 -08:00
Nick Gerleman
eeeb2cae49 Make playground links shorter (#1516)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1516

Right now playground links are URL encoded base64 of the code content. This leads to some pretty long links.

Running a service, or needing to auth to some other service, seems like a lot more headache than it is worth, so this change instead tries to make the URLs a bit more reasonable.

One minor saving is that we're URL encoding base64, instead of just representing using the url-safe variant of base64. But we can get more savings, even in small examples, using compression. This adds a popular, small, library to do that.

Reviewed By: yungsters

Differential Revision: D52161884

fbshipit-source-id: 9f5d131f27e25a611501c2e3bf3907e83c2e3da1
2023-12-19 12:46:26 -08:00
Nick Gerleman
7697be57a8 Consistent bracket spacing in main example (#1517)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1517

Accidentally using a combination of prettier default vs with `--no-bracket-spacing`

Reviewed By: yungsters

Differential Revision: D52162274

fbshipit-source-id: a0e629717060c17b63fa6144d775a590258580ac
2023-12-18 09:38:00 -08:00
Nick Gerleman
3f3b909086 Change Style::resolveColumnGap() return from Length to Style::Length (#1525)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1525

Accidentally left this inconsistent with some of the refactoring. Rename the lone usage of `Length` within Style class to `Style::Length` to match the rest of the code.

This is functionally identical as before.

Changelog: [Internal]

bypass-github-export-checks

Reviewed By: yungsters

Differential Revision: D52096820

fbshipit-source-id: d6c569a02fb27a6e7548a9c12ff764afb823a282
2023-12-17 02:58:54 -08:00
Nick Gerleman
a1751127ef Fix align-content of cross-stretched container (#1524)
Summary:
X-link: https://github.com/facebook/react-native/pull/41964

Pull Request resolved: https://github.com/facebook/yoga/pull/1524

D52087013 (#1513) fixed some issues, including where measuring under max-content or fit-content, align-content stretch would consume the entire available cross-dimensions, instead of only sizing to definite dimension, like the spec dicates.

I missed a case, where flexbox considers a container as having a definite cross-size if it is being stretched, even if it doesn't have a definite length.

https://www.w3.org/TR/css-flexbox-1/#definite-sizes

> 3. Once the cross size of a flex line has been determined, items in auto-sized flex containers are also considered definite for the purpose of layout;

> 1. If a single-line flex container has a definite cross size, the outer cross size of any stretched flex items is the flex container’s inner cross size (clamped to the flex item’s min and max cross size) and is considered definite.

We handle `align-items: stretch` of a flex container after cross-size determination by laying out the child under stretch-fit (previously YGMeasureModeExactly) constraint. This checks that case, and sizing the line container to specified cross-dim if we are told to stretch to it.

We could probably afford to merge this a bit with later with what is currently step 9, where we end up redoing some of this same math.

Reviewed By: yungsters

Differential Revision: D52234980

fbshipit-source-id: 475773a352fd01f63a4b21e93a55519726dc0da7
2023-12-17 01:13:36 -08:00
Cheng Zhao
2a8c7a4223 std::terminate belongs to <exception> header (#1511)
Summary:
X-link: https://github.com/facebook/react-native/pull/41917

See also https://en.cppreference.com/w/cpp/error/terminate.

Pull Request resolved: https://github.com/facebook/yoga/pull/1511

Reviewed By: christophpurrer

Differential Revision: D52072882

Pulled By: NickGerleman

fbshipit-source-id: 4d4d442b82d108d1d2ff5d241fbdc8df89045fed
2023-12-17 00:47:44 -08:00
Nick Gerleman
464d1668ba Fix sizing and alignment issues with multi-line containers (#1513)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1513

X-link: https://github.com/facebook/react-native/pull/41916

Fixes https://github.com/facebook/yoga/issues/1300
Fixes https://github.com/facebook/yoga/issues/1008

This fixes a smattering of issues related to both sizing and aligment of multi-line-containers:

1. We were previously incorrectly bounding the size of each flex line to the min/max of the entire container.
2. Per-line leads were sometimes incorrectly contributing to alignment within the line
3. The cross dim size used for multi-line alignment is not correct, or correctly clamped. If the available size comes from a max constraint, that was incorrectly used instead of a definite size, or size of content. Leads were entirely skipped for min constraint.

Need to test how breaking this is, to see if it might need to go behind an errata.

See related PRs:
1. https://github.com/facebook/yoga/pull/1491
2. https://github.com/facebook/yoga/pull/1493
3. https://github.com/facebook/yoga/pull/1013

Changelog:
[General][Fixed] - Fix Yoga sizing and alignment issues with multi-line containers

Reviewed By: joevilches

Differential Revision: D52087013

fbshipit-source-id: 8d95ad17e58c1fec1cceab9756413d0b3bd4cd8f
2023-12-16 01:12:30 -08:00
Nick Gerleman
baf95897cb Enable lints for React Components (#1515)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1515

The out-of-the-box docusaurus template doesn't enable linting for React components. This enables those, fixes the errors, and does dome cleanup around the area (e.g. autofocus is a lot more sane).

Reviewed By: vincentriemer

Differential Revision: D52156109

fbshipit-source-id: f32fede3ec4f8a42ecb7f9d77caa2a30581f35ee
2023-12-15 22:46:58 -08:00
Joe Vilches
bac80cafba Add minimist dep to package.json (#1522)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1522

I have the types but not the core

Reviewed By: NickGerleman

Differential Revision: D52216588

fbshipit-source-id: ec348537655213f6d8a3b35402fec03998744c78
2023-12-15 16:26:31 -08:00
Joe Vilches
7aee99567a Change readme yarn link (#1521)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1521

I linked the forked version of yarn when in reality it should be yarn classic

Reviewed By: NickGerleman

Differential Revision: D52215925

fbshipit-source-id: 5d5800d162f2cc250fcf54151de0de87a427abc2
2023-12-15 16:26:31 -08:00
Joe Vilches
43d09a3a94 GitHub workflow to ensure tests are up to date (#1505)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1505

This will ensure that all tests are not modified by hand and that gentest was run if needed (i.e. someone edited those files and forgot to update the tests). Not sure if this works right now, need to export to github and see what happens :)

Reviewed By: NickGerleman

Differential Revision: D52002920

fbshipit-source-id: 1ee033eda04eeba5d01885488bfe8078e7b2f386
2023-12-14 11:48:22 -08:00
Joe Vilches
06c792a407 Create gentest-validate script to validate test signatures (#1504)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1504

I aim to consume this via a github action or workflow. Separating out the CI configuration from the actual script. We could and should maybe change this script to take in arguments for the test dirs so it is more configurable, but getting the barebones down right now.

Reviewed By: NickGerleman

Differential Revision: D51999865

fbshipit-source-id: 5792c6eec5692943518fecefc6226d784c6f0ad8
2023-12-14 11:48:22 -08:00
Joe Vilches
4743040d62 Sign generated tests (#1503)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1503

This diff makes it so that our driver will sign all of the generated files to help ensure that they are not edited by hand. Next I will add CI to actually verify the signature

Reviewed By: NickGerleman

Differential Revision: D51966201

fbshipit-source-id: f7e3f4fde1c98832212a448b2dcc8e21be0560c4
2023-12-14 11:48:22 -08:00
Joe Vilches
e1b59d63a7 Update README with new instructions on how to run the gentest script (#1502)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1502

We should change this if we are moving away from using Ruby

Reviewed By: yungsters, NickGerleman

Differential Revision: D51957351

fbshipit-source-id: fddb0b3348db2b93c661c919a61150c81906a61a
2023-12-14 11:48:22 -08:00
Joe Vilches
e61eb0178c Remove ruby files and regen tests (#1501)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1501

Now that we have `gentest-driver.ts` we can delete the ruby gentest. I also regened all of the tests that have a comment with the wrong file name for where it was generated.

Reviewed By: yungsters, NickGerleman

Differential Revision: D51956567

fbshipit-source-id: d389492e54711cf161dff9e649396cc40f1e5073
2023-12-14 11:48:22 -08:00
Joe Vilches
fba32ebf14 Add command line options to new node-based gentest (#1499)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1499

This uses the minimist library to parse the command line easily for flags and options. I added over the same ones in the ruby script (-f and -s).

Reviewed By: yungsters, NickGerleman

Differential Revision: D51877810

fbshipit-source-id: 643606cad25609ed2567bb63470818b81ebef531
2023-12-14 11:48:22 -08:00
Joe Vilches
98552078b1 Create node version of gentest script (#1498)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1498

The only instance of ruby in this repository is `gentest.rb` used to generate test cases from html fixtures. This is quite annoying as ruby is not the most popular compared to something like Node and it does not integrate into the rest of our stack. I changed this to use Node.js instead. Instead of `watir` we now use `selenium-webdriver`. `watir` is backed by Selenium so I do not expect anything to change.

Next commits will add command line options, clean up gentest.rb and its references, and change the README

allow-large-files

Reviewed By: yungsters, NickGerleman

Differential Revision: D51874433

fbshipit-source-id: ef8588d48aa7f8b720b57df08738bbd01e9e74a3
2023-12-14 11:48:22 -08:00
Nick Gerleman
43cb24fdce Fix playground handling of visible scrollbars (#1514)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1514

On machines with scrollbars, we shouldn't show them unconditionally, and the toolbar should be in the area within them.

This also fixes a couple bugs:
1. Preview not rendering based on correct code when light/dark mode changes
2. Crash on start on mobile safari
3. Incorrect rendering of preview on mobile safari

This also fixes a bug where the playground re-rendering (e.g. on theme change) makes the preview snap back to the initial code passes.

https://yoga-website-next-git-fork-nickgerleman-exp-194d90-fbopensource.vercel.app/

Reviewed By: shwanton

Differential Revision: D52145666

fbshipit-source-id: 50184305987aab4cbcd066f37582997dfdc78c02
2023-12-13 20:05:29 -08:00
Nick Gerleman
738d04fcb0 Fix OSS JS Build (#1512)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1512

Latest revision either never got exported, or didn't block land? Fix the typo.

Reviewed By: joevilches

Differential Revision: D52087122

fbshipit-source-id: 2dfcff4925902bdd1dd210a219a78707186fd28e
2023-12-12 13:11:01 -08:00
Nick Gerleman
c01c4cbb82 Backfil Yoga 2.0 Release notes to blog (#1508)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1508

Backfilling the release notes already on GitHub before the next time we want to do a release, and use the Docusaurus blog.

Reviewed By: yungsters

Differential Revision: D52012362

fbshipit-source-id: 35de017017943ed21263a4a9aeaa0fd1418c2bc7
2023-12-12 09:06:58 -08:00
Nick Gerleman
77742af676 Replace Playground with JSX Editor (#1500)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1500

Inspired by the frequent usage of Expo snacks to run RN, to repro Yoga issues, this replaces the Playground port with a new ground up Playground UI. This UI right now is pretty simple, with a JSX editor which creates a Yoga tree, which is then rendered using the WebAssembly variant of Yoga.

There are a lot of ways we can continue to improve this, but this merges the foundation. Subjectively, I find this more useful as a tool to play with Yoga behavior than the GUI.

This also replaces some of the bits of the homepage, and adds a playground entrypoint (though it's pretty identical to the one I've been testing on the home page).

Reviewed By: yungsters

Differential Revision: D51963201

fbshipit-source-id: 1265cb1784151b685686e189d47ecd42cbacdf8f
2023-12-12 09:06:58 -08:00
Nick Gerleman
0d03d8a06d Manage native build toolchain (#1506)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1506

In order to build the website, you must build a Yoga binary. This usually requires installing native toolchains.

We have warning messages for these right now, but an even better solution is to just manage the dependencies ourselves. This does that, bringing in specific CMake and Ninja binaries from NPM, and caching a local copy of Emscripten during the build.

A downside is that the CMake packages are chunky, so we add 130MB to node_modules (for a repo total around 350MB). This also delays acquiring Emscripten (which is even chunkier) in CI builds until it is needed, and I added some caching for it as well.

The upside of JS users being able to run and test (inc the website) without installing and managing their own versions of toolchains is a real time-saver though, and is probably worth it.

allow-large-files

Reviewed By: yungsters

Differential Revision: D52013026

fbshipit-source-id: 3d307f751463a21c5e5d5b98b8e9e63db9d3d52e
2023-12-12 09:06:58 -08:00
Nick Gerleman
a43754266a <bit> and <concepts> (#1497)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1497

The lowest common denominator we have had for Yoga has been Clang 12 + MSVC 2017 stdlib. This has allowed Yoga to use C++ 20 language features, but not library features. React Native for mobile has not been bound to this restriction.

Builds using that toolchain are being updated to latest MSVC 2019 stdlib (which has good C++ 20 library support), along with Clang 17 (or maybe a stop at 15) pending projects using `-fcoroutines-ts` being migrated to C++ 20.

This tests out some C++ 20 standard library usages against the current Clang 12 + MSVC 2019 stdlib toolchain that didn't work before, and adds a couple concepts for better constraints/compiler error messages if misused.

This bumps min-tested XCode (and minimum required) version to 14.3, matching a similar change for React Native. This should probably be bumped to 15 sometime before Apple starts requiring 15+ to go out to the iOS app store.

We are approaching a practical support range of:
1. XCode >= 14.3
2. NDK >= 26
3. Clang/libc++ >= 14
4. GCC/libstdc++ >= 11
5. MSVC >= 16.11 (VS 2019)

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D51604487

fbshipit-source-id: d394d0d86672b69781b8ae071d87adcf944ddc72
2023-12-12 08:52:11 -08:00
Nick Gerleman
ab37ed70ae Add concurrency group for website publish (#1510)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1510

Make each workflow acquire a shared mutex so that if two commits happen close to each other, the publish order must correspond to commit order.

Reviewed By: cortinico

Differential Revision: D52031970

fbshipit-source-id: 960cd8e8f79b26ab3d9c131e0637b795618d898e
2023-12-12 07:07:56 -08:00
Joe Vilches
1ea575684d Enable previously broken absolute positioning tests (#1488)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1488

These were disabled when they were written because they were broken. The recent changes made them pass now so lets enable them. I also added another test that is already passing

Reviewed By: NickGerleman

Differential Revision: D51404875

fbshipit-source-id: ed10004968b871c1d033640d75138f00afc15968
2023-12-07 21:25:45 -08:00
Joe Vilches
bc5dc2d6bf Fix issues with aligning absolute nodes (#1490)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1490

X-link: https://github.com/facebook/react-native/pull/41692

In the previous diffs I fixed problems with justifying absolute nodes. The same issues plague aligning so I fixed them in the same way. Added tests that were failing before but now passing

Reviewed By: NickGerleman

Differential Revision: D51404489

fbshipit-source-id: 604495d651eb67cfdcca40df9d8d3a125c5741a8
2023-12-07 21:25:45 -08:00
Joe Vilches
b573f91a38 Fix issue where we were not applying flex end correctly when justifying (#1487)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1487

X-link: https://github.com/facebook/react-native/pull/41691

The code here was just wrong. I changed it to be the same logic as the Justify:FlexStart case, but with the flex end sides. Then I get the position for the opposite edge since we need to write to flex start side.

Reviewed By: NickGerleman

Differential Revision: D51383792

fbshipit-source-id: 372835a44edff361dbd84dd92ff9f2ec844b9f9c
2023-12-07 21:25:45 -08:00
Joe Vilches
897f9b7423 Fix issue where we were not centering absolute nodes correctly when justifying (#1489)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1489

X-link: https://github.com/facebook/react-native/pull/41690

Centering involves centering the margin box in the content box of the parent, and then getting the distance from the flex start edge of the parent to the child

Reviewed By: NickGerleman

Differential Revision: D51383625

fbshipit-source-id: 6bbbace95689ef39c35303bea4b99505952df457
2023-12-07 21:25:45 -08:00
Joe Vilches
f8d048bb1a Fix bug where we used border box for size of containing block in a certain case (#1486)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1486

X-link: https://github.com/facebook/react-native/pull/41688

Somehow missed this case. We never want to measure the CB as that gets border box but we want padding box

Reviewed By: NickGerleman

Differential Revision: D51376309

fbshipit-source-id: 2b5119c421ef92fadb28a70254cb7fe02aeb8c28
2023-12-07 21:25:45 -08:00
Joe Vilches
d1dda2185e Fix bug with align start not taking into account parent padding (#1484)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1484

X-link: https://github.com/facebook/react-native/pull/41687

Tsia. Added test and accounted for parent padding

Reviewed By: NickGerleman

Differential Revision: D51374086

fbshipit-source-id: ed9d79887aa1613ea93c10c639cd1465271d23d8
2023-12-07 21:25:45 -08:00
Joe Vilches
a5c955a579 Relayout test for containing block changing size (#1483)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1483

This test is for ensuring caching does not break layout when a CB changes. No changes were needed to get this passing it just works this way with the implementation we chose earlier.

Reviewed By: NickGerleman

Differential Revision: D51333812

fbshipit-source-id: b3b603fc641d470cb61e63c44c71f7544f3c7727
2023-12-07 21:25:45 -08:00
Joe Vilches
9b87d8b3f3 Fix issue where percentages were off of the border box, not padding box (#1485)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1485

X-link: https://github.com/facebook/react-native/pull/41686

The size of the containing block is the size of the padding box of the containing node for absolute nodes. We were looking at  `containingNode->getLayout().measuredDimension(Dimension::Width)` which is the border box. So we need to subtract the border from this.

Added a test that was failing before this change as well

Reviewed By: NickGerleman

Differential Revision: D51330526

fbshipit-source-id: adc448dfb71b54f1bbed0d9d61c5553bda4b106c
2023-12-07 21:25:45 -08:00
Joe Vilches
f6c4a8e8e4 Make position static behave like position static (#1482)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1482

X-link: https://github.com/facebook/react-native/pull/41685

This is the final step (that I know of) to get the core features of static working. Here we turn on all of the tests and pass down the correct owner size for the call to `calculateLayoutInternal` that is in `layoutAbsoluteChild`

Reviewed By: NickGerleman

Differential Revision: D51293606

fbshipit-source-id: 972259e7ebecb19b55aef2ef866bd7cb57aaf0ca
2023-12-07 21:25:45 -08:00
Joe Vilches
1b146cd8a8 Make positionAbsoluteChild the sole place that matters when determining absolute node's position (#1481)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1481

X-link: https://github.com/facebook/react-native/pull/41684

Absolute nodes can be laid out by themselves and do not have to care about what is happening to their siblings. Because of this we can make `positionAbsoluteChild` the sole place where we handle this logic. Right now that is scattered around algorithm with many `if (child is absolute)` cases everywhere. This makes implementing position static a lot harder since we are relying on the CB to do all this work, not the parent.

With this change the only time we set position for an absolute node and it matter (i.e. not overwritten) is in `positionAbsoluteChild`

Reviewed By: NickGerleman

Differential Revision: D51290723

fbshipit-source-id: 405d81b1d28826cbb0323dc117c406a44d381dff
2023-12-07 21:25:45 -08:00
Joe Vilches
c93734f579 Fix issue in gentest where border-<edge> would add a border to test (#1496)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1496

Gentest code has a problem where we try to apply a border in our test when the web browser is not actually adding one. This happens when we do something like `border-top: 10px`. This will actually set the style of the border to `initial` which is just `none`, so nothing renders. This is causing at least 1 test to pass when it actually fails.

I changed it so we ignore setting this value if the style is one of these values. I then re-ran the gentest code and excluded the now failing test (which gets fixed in my static stack).

Reviewed By: NickGerleman

Differential Revision: D51831754

fbshipit-source-id: a325e4a42b2d7cd6f19efc6cd5a2445574467fb7
2023-12-05 13:30:03 -08:00
Nicola Corti
7b3b66d288 Re-enabled disabled tests ReactPropForShadowNodeSpecTest and ReactPropForShadowNodeSetterTest (#1494)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1494

X-link: https://github.com/facebook/react-native/pull/41788

Those tests are currently disabled due to Yoga attempting to do JNI calls.
I've added infra to bypass .so loading during tests, and we should be good to re-enable those tests by now.

Changelog:
[Internal] [Changed] - Re-enabled disabled tests ReactPropForShadowNodeSpecTest and ReactPropForShadowNodeSetterTest

Reviewed By: NickGerleman

Differential Revision: D51814491

fbshipit-source-id: adbbace19c94a0c6d8947f61221fafafd7797ac8
2023-12-05 07:14:20 -08:00
Joe Vilches
dde0fda9f5 New file for layout methods related to absolute children (#1495)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1495

X-link: https://github.com/facebook/react-native/pull/41794

This is a copy of D51369722 to make it so that it preserves the file history

CalculateLayout.cpp is massive and approaching 3k lines. I added a few large functions dealing with layout of absolute nodes and was thinking it would be nice if that logic was just in its own file so it was more isolated and easier to reason about. So I made AbsoluteLayout.cpp and AbsoluteLayout.h to house this logic. In order for this to work I had to expose calculateLayoutInternal in CalculateLayout.h as layoutAbsoluteChild calls it. This is unideal and I would like to find a better way...

I also make LayoutUtils.h to house misc small helper methods as they are called in AbsoluteLayout.cpp and CalculateLayout.cpp

Reviewed By: NickGerleman

Differential Revision: D51824115

fbshipit-source-id: 9b27449e3c1516492c01e6167a6b2c4568a33807
2023-12-04 19:35:30 -08:00
Joe Vilches
7893c4bd85 Fix issue where start/end would not be respected in flex edge getters (#1479)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1479

X-link: https://github.com/facebook/react-native/pull/41682

There are two ways to get the value of a style for a specific edge right now:

1) From the inline start/end edge which is determined via the writing direction (ltr or rtl), assuming you do not have errata on
2) From the flex start/end edge which is determined via the flex direction (row, row-reverse, column, column-reverse)

There is a weird curiosity in the second case: you can define a style to be on the "start" or "end" edge when writing the stylex/css. The physical edge that this refers to is dependent on the writing direction. So `start` would be `left` in `ltr` and `right` in `rtl`, with `end` the opposite. It is **never** determined via the flex direction. Additionally, `start`/`end` takes precedence over the physical edge it corresponds to in the case both are defined.

So, all of this means that to actually get the value of a style from the flex start/end edges, we need to account for the case that one of these relative edges was defined and would overwrite any physical edge. Since this mapping is solely determined by the writing direction, we need to pass that in to all the flex start/end getters and do that logic. This is done in  `flexStartRelativeEdge`/`flexEndRelativeEdge` which was added earlier but for some reason only being used on border.

Reviewed By: NickGerleman

Differential Revision: D51293315

fbshipit-source-id: 26fafff54827134e7c5b10354ff9bfdf67096f5b
2023-12-04 19:35:30 -08:00
Joe Vilches
4ad9330fb9 Introduce positionAbsoluteChild (#1473)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1473

X-link: https://github.com/facebook/react-native/pull/41491

To simplify the logic a bit I introduce a new function called `positionAbsoluteChild`. This function will, eventually, be the **sole function that matters** when determining the layout position of an absolute node. Because [absolute nodes do not participate in flex layout](https://drafts.csswg.org/css-flexbox/#abspos-items), we can determine the position of said node independently of its siblings. The only information we need are the node itself, its parent, and its containing block - which we have all of in `layoutAbsoluteChild`.

Right now, however, this is purely a BE change with no functionality different. There was a big set of if statements at the end of `layoutAbsoluteChild` that would position the node on the main and cross axis for certain cases. The old code had it so that the main and cross axis had basically the same logic but the code was repeated. This puts that logic, as is, in `positionAbsoluteChild` and calls that from `layoutAbsoluteChild`.

I will soon edit this function to actually do what it is envisioned to do (i.e. be the sole place that position is set for absolute nodes).

Reviewed By: NickGerleman

Differential Revision: D51272855

fbshipit-source-id: 68fa1f0e0f4d595faf2af1d9eaceb467382ca406
2023-12-04 19:35:30 -08:00
Joe Vilches
e042cb102c Use containing block to adjust absolute child position (#1472)
Summary:
X-link: https://github.com/facebook/react-native/pull/41490

Pull Request resolved: https://github.com/facebook/yoga/pull/1472

This change has most of the logic needed for supporting `position: static`. We do two things here that fix a lot of the broken static test:

1) We pass in the containing node to `layoutAbsoluteChild` and use it to properly position the child in the case that insets are defined.
2) We rewrite the absolute child's position to be relative to it's parent in the event that insets are defined for that child (and thus it is positioned relative to its CB). Yoga's layout position has always be relative to parent, so I feel it is easier to just adjust the coordinates of a node to adhere to that design rather than change the consumers of yoga.

The "hard" part of this algorithm is determining how to iterate the offset from the containing block needed to do this translation described above. That is handled in `layoutAbsoluteDescendants`.

Reviewed By: NickGerleman

Differential Revision: D51224327

fbshipit-source-id: ae6dc54fe2a71bebb4090ba21a0afb0125264cbc
2023-12-04 19:35:30 -08:00
Joe Vilches
2222c2c475 Allow the containing block to set trailing position of absolute descendants (#1471)
Summary:
X-link: https://github.com/facebook/react-native/pull/41489

Pull Request resolved: https://github.com/facebook/yoga/pull/1471

If we are going to allow the containing block to layout its absolute descendants and NOT the direct parent then we need to change step 11 which is concerned with setting the trailing position in the case we are row or column reverse. This is the very last step in the function and is positioned that way because it operates on the assumption that all children have their position set by this time. That is no longer a valid assumption if CBs layout their absolute children. In that case the CB also needs to take care of setting the position here.

Because of this problem I moved some things around. It now works like:

* If errata is set, the direct parent will set trailing position for all non absolute children in step 11
* If errata is set the CB will set trailing position of absolute descendants after they are laid out inside of layoutAbsoluteDescendants

Reviewed By: NickGerleman

Differential Revision: D51217291

fbshipit-source-id: a7eea0d3623f9041b73d609a1de2bfb0f0343a26
2023-12-04 19:35:30 -08:00
Joe Vilches
5a24c81672 Let containing blocks layout their absolute descendants, not parents (#1470)
Summary:
X-link: https://github.com/facebook/react-native/pull/41488

Pull Request resolved: https://github.com/facebook/yoga/pull/1470

The way we plan on implementing `position: static` is by changing how we lay out absolutely positioned nodes. Instead of letting their direct parent lay them out we are going to let their containing block handle it. This is useful because by the time the containing block gets to this step it will already know its size, which is needed to ensure that absolute nodes can get the right value with percentage units. Additionally, it means that we can "translate" the position of the absolute nodes to be relative to their parent fairly easily, instead of some second pass that would not be possible with a different design.

This change just gets the core pieces of this process going. It makes it so that containing blocks will layout out absolute descendants that they contain. We also pass in the containing block size to the owner size args for `layoutAbsoluteChild`. This new path will only happen if we have the errata turned off. If there is no positioned ancestor for a given node we just assume the root is. This is not exactly how it works on the web - there is a notion of an initial containing block - but we are not implementing that as of right now.

Reviewed By: NickGerleman

Differential Revision: D51182593

fbshipit-source-id: 88b5730f7f4fec4f33ec64288618e23363091857
2023-12-04 19:35:30 -08:00
Joe Vilches
59bf902a17 Insets no longer apply to statically positioned nodes (#1454)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1454

X-link: https://github.com/facebook/react-native/pull/41369

One of the most basic aspects of statically positioned nodes is that [insets do not apply to them](https://developer.mozilla.org/en-US/docs/Web/CSS/position#static). So I put a guard inside `Node::relativePosition` where we take that into account when setting the position.

Reviewed By: NickGerleman

Differential Revision: D50507808

fbshipit-source-id: 7aab4138b06e60936db0ddb6019a9a30f1ded2db
2023-12-04 19:35:30 -08:00
Nick Gerleman
382faa3f44 Change default back to position: "relative" (#1469)
Summary:
X-link: https://github.com/facebook/react-native/pull/41480

Pull Request resolved: https://github.com/facebook/yoga/pull/1469

The previous version of static didn't do anything inside of Yoga. Now that we're making it do something, this changes the default back to relative so that users with no errata set don't see their deafult styles changing.

Reviewed By: joevilches

Differential Revision: D51182955

fbshipit-source-id: c0ea357694e1367fb6786f1907dfff784b19a4bc
2023-11-28 18:51:34 -08:00
Nick Gerleman
bb8fd593ff Remove NumericBitfield (#1463)
Summary:
X-link: https://github.com/facebook/react-native/pull/41394

Pull Request resolved: https://github.com/facebook/yoga/pull/1463

Now that are enums are unsigned, and we don't have BitfieldRef, we can convert the last remaining user of NumericBitfield to a plain old bitfield,  for better readability (e.g. the default values), debugability, and less complexity. We also break a cycle which lets us properly group public vs private members.

Reviewed By: joevilches

Differential Revision: D51159415

fbshipit-source-id: 7842a8330eed6061b863de3f175c761dcf4aa2be
2023-11-27 21:20:20 -08:00
Nick Gerleman
9b0fd09ec6 Remove yoga::Style::BitfieldRef (#1459)
Summary:
X-link: https://github.com/facebook/react-native/pull/41393

Pull Request resolved: https://github.com/facebook/yoga/pull/1459

Removes the last of the non setter-style style setters.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D51155925

fbshipit-source-id: 2921c87d95ad36495b7013e592d5169015321545
2023-11-27 21:20:20 -08:00
Nick Gerleman
ed6e91479c Remove yoga::Style::Ref (#1462)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1462

X-link: https://github.com/facebook/react-native/pull/41389

Moves the last usages of `yoga::Style::Ref` to setters.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D51154501

fbshipit-source-id: 52dbca7a76de500a8841387eb59fded463864de7
2023-11-27 21:20:20 -08:00
Nick Gerleman
325ccea068 YGEdge -> yoga::Edge (#1461)
Summary:
X-link: https://github.com/facebook/react-native/pull/41391

Pull Request resolved: https://github.com/facebook/yoga/pull/1461

Converts usages of `YGEdge` within internal APIs to `yoga::Edge` scoped enum.

With the exception of YGUnit which is in its own state of transition, this is the last public yoga enum to need to be moved to scoped enum form for usages internal to the Yoga public API.

Changelog: [internal]

Reviewed By: rshest

Differential Revision: D51152779

fbshipit-source-id: 06554f67bfd7709cbc24fdd9a5474e897e9e95d8
2023-11-25 20:41:22 -08:00
Nick Gerleman
a121483e81 Use CSS terminology for sizing rules (#1460)
Summary:
X-link: https://github.com/facebook/react-native/pull/41390

Pull Request resolved: https://github.com/facebook/yoga/pull/1460

Yoga passes `MeasureMode`/`YGMeasureMode` to express constraints in how a box should be measured, given definite or indefinite available space.

This is modeled after Android [MeasureSpec](https://developer.android.com/reference/android/view/View.MeasureSpec), with a table above `calculateLayoutImpl()` explaining the CSS terms they map to. This can be confusing when flipping between the spec, and code.

This switches internal usages to the CSS terms, but leaves around `YGMeasureMode` since it is the public API passed to measure functions.

Reviewed By: joevilches

Differential Revision: D51068417

fbshipit-source-id: 0a76266a4e7e0cc39996164607229c3c41de2818
2023-11-25 20:41:22 -08:00
Nick Gerleman
aca02406ef CompactValue -> Style::Length (#1458)
Summary:
X-link: https://github.com/facebook/react-native/pull/41392

Pull Request resolved: https://github.com/facebook/yoga/pull/1458

We're moving `CompactValue` to be an internal detail of `yoga::Style`, where users outside of the style will be dealing with a resolved/non-compact representation.

This change renames usages of `CompactValue` to `Style::Length`, which will be Yoga's representation for CSS input lengths. Right now one is just a type alias of the other, but this will let us change the internals of CompactValue with the rest of the world looking the same.

A few factory functions are added to `yoga::value` for creating CSS values. There are some shenanigans around how we want to represent CSS pixels (one YGUnitPoint), when we also end up adding CSS points (slightly larger than one YGUnitPoint). For now, I reused `point` until making other changes.

Changelog: [Internal]

Reviewed By: yungsters

Differential Revision: D51000389

fbshipit-source-id: 00f55e72bfb8aa291b53308f8a62ac8797be490f
2023-11-25 20:41:22 -08:00
Nick Gerleman
a822f2635e Do not expose aggregate style edges (#1477)
Summary:
X-link: https://github.com/facebook/react-native/pull/41610

Pull Request resolved: https://github.com/facebook/yoga/pull/1477

A reland of the main change in D50998164, moving away from exposing compound edge arrays directly.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D51512611

fbshipit-source-id: 2d4ceb89c9d76317feb9074aa271358a3abc0ee1
2023-11-22 22:43:41 -08:00
Nick Gerleman
0ebac779aa Remove YGStyleAccessorsTest (#1476)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1476

Up the stack, Style uses just plain getters and setters. These tests aren't particularly useful enough to update.

Previously landed as part of D50998164

Reviewed By: joevilches

Differential Revision: D51481328

fbshipit-source-id: b0413270fd5951c7c8d783269b08cca9f939ce25
2023-11-22 22:43:41 -08:00
Nick Gerleman
0eb861b459 Don't enable ordinalCount() or bitCount() for bitset enums
Summary:
Bitfield enums are not sequential, so use of these functions on these enums would be invalid.

I looked at whether we could trivially move `bitCount` to template based on `ordinalCount`. `bitCount` must be constexpr, since we use it directly as a bit-field size constant. `log2` and `ceil` to be constexpr, which isn't here until C++ 26.

Reviewed By: javache

Differential Revision: D51518899

fbshipit-source-id: 256f15bbed517be6f90bf43baa43ce96e9259a71
2023-11-22 11:06:31 -08:00
Nick Gerleman
c7c81e3c89 Remove composite border comparisons (#1475)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1475

X-link: https://github.com/facebook/react-native/pull/41568

Removes cases where we rely on comparing composite of Yoga edges, since we are removing that internal API (public API is already one at a time). Extracted from D50998164, with more sound facility for looping through edges.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D51478403

fbshipit-source-id: 162170b91345ff86db44a49a04a2345f0fbd0911
2023-11-21 23:17:32 -08:00
Nick Gerleman
a713a598c8 Fix modulemap typo (#1474)
Summary:
X-link: https://github.com/facebook/react-native/pull/41564

Pull Request resolved: https://github.com/facebook/yoga/pull/1474

Fixes https://github.com/facebook/yoga/issues/1468

We build with Swift, but don't build something consuming Yoga module, and don't use modulemap and CocoaPods. This is a gap in validation we should figure out, but in the meantime, we should fix the typo.

Reviewed By: christophpurrer

Differential Revision: D51472493

fbshipit-source-id: 60e05cc49969f51d5fc4856cf25ce2afede72f36
2023-11-20 14:10:56 -08:00
Nick Gerleman
27af596359 Revert D50998164: Allow lazy resolution of edge dimension values
Differential Revision:
D50998164

Original commit changeset: 248396f9587e

Original Phabricator Diff: D50998164

fbshipit-source-id: 4f592158324d758bb9e3731ced36b8e3587c459c
2023-11-15 18:34:47 -08:00
Nick Gerleman
f2c8acad2c Allow lazy resolution of edge dimension values (#1453)
Summary:
X-link: https://github.com/facebook/react-native/pull/41347

Pull Request resolved: https://github.com/facebook/yoga/pull/1453

This follows the previous patterns used for `Gutters` and `Dimension`, where we hide CompactValue array implementation from `yoga::Style` callers.

This allows a single read of a style to only need access to the resolved values of a single edge, vs all edges. This is cheap now because the interface is the representation, but gets expensive if `StyleValuePool` is the actual implementation.

This prevents us from needing to resolve nine dimensions, in order to read a single value like `marginLeft`. Doing this, in the new style, also lets us remove `IdxRef` from the API.

We unroll the structure dependent parts in the props parsing code, for something more verbose, but also a bit clearer.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D50998164

fbshipit-source-id: 248396f9587e29d62cde05ae7512d8194f60c809
2023-11-14 09:12:35 -08:00
Nick Gerleman
bb892af3a4 Make trunk builds "0.0.0" (#1466)
Summary:
X-link: https://github.com/facebook/react-native/pull/41423

Pull Request resolved: https://github.com/facebook/yoga/pull/1466

Right now Yoga's main branch says it's 2.0.0, and RN's dirsync says its 1.14.0, but the code is really closer to what will be Yoga 3.0.0.

This changes trunk builds to "0.0.0" for clarity, which will be assigned a real version number the first time publishing a new Yoga branch.

This is separately a good practice to prevent the chance of accidental publishes causing damage.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D51236778

fbshipit-source-id: 06cac89bcca1c707ce5c00f9c346f627eef6b4bc
2023-11-13 22:09:35 -08:00
Nick Gerleman
8c1a672f92 GitHub Actions Cleanup (#1467)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1467

1. Avoid reusing workflow names between validation and publish jobs, since they show up with the same UI when wanting to adhoc trigger a publish.
2. Merge SwiftPM checks into Validate Apple job
3. Merge remnants of website-next workflow into website workflow (and remove a redundant explicit build step already removed in one workflow)
4. Run more validation on push to release branch
5. Do not run website validation off of main, since it is only ever deployed from main branch

Reviewed By: cipolleschi

Differential Revision: D51236885

fbshipit-source-id: dd8165aa9871f173d8914f345569c6cde1edda72
2023-11-13 03:57:52 -08:00
Nick Gerleman
6f41a27e31 "yogalayout.com" to "yogalayout.dev" (#1465)
Summary:
X-link: https://github.com/facebook/react-native/pull/41420

Pull Request resolved: https://github.com/facebook/yoga/pull/1465

https://yogalayout.com now redirects to https://yogalayout.dev

This replaces references to "yogalayout.com" with "yogalayout.dev", the same website, with a new domain. This includes:
1. Code comments
2. Yoga website config (publish action CNAME, Docusaurus config)
3. Documentation URLs in Yoga packages

Changelog:
[General][Fixed] - "yogalayout.com" to "yogalayout.dev"

Reviewed By: christophpurrer

Differential Revision: D51229587

fbshipit-source-id: b1c336a52aab5e02565071b61430d5435381dc0a
2023-11-13 02:48:29 -08:00
Nick Gerleman
1b1df3a87e Simplify YGConfigSetPointScaleFactor (#1451)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1451

X-link: https://github.com/facebook/react-native/pull/41327

The special meaning of `0.0` is now explained in the function header, and we aren't doing any sort of insensitive compare here, so the code after should be equivalent and a bit simpler.

Reviewed By: yungsters

Differential Revision: D51014264

fbshipit-source-id: 60f4a2df039f74089d5c7fabd4b7d8ac6234ba72
2023-11-10 16:31:46 -08:00
Nick Gerleman
c46ea9c6f5 Modularize and document public API (#1449)
Summary:
X-link: https://github.com/facebook/react-native/pull/41317

Pull Request resolved: https://github.com/facebook/yoga/pull/1449

This aims to clean up the public Yoga C API, by:
1. Documenting public YGNode, YGValue, YGConfig APIs
2. Splitting APIs for specific objects into different header files (because Yoga.h was big enough without documentation)
3. Reordering headers and definitions for consistent grouping

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D50963424

fbshipit-source-id: 45124b7370256fc63aefd6d5b7641466e9a79d3b
2023-11-10 16:31:46 -08:00
Nick Gerleman
12a8d16b62 Remove Yoga-internal.h (#1452)
Summary:
X-link: https://github.com/facebook/react-native/pull/41346

Pull Request resolved: https://github.com/facebook/yoga/pull/1452

This removes the last remnant from `Yoga-interna.h`, `YGNodeDellocate()`. The API is renamed to `YGNodeFinalize` to give it the explicit purpose of freeing the node from a garbage collector, and made public with that documented contract.

With that, every top-level header is now a public API, and Yoga's JNI bindings do not need to rely on private headers anymore.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D51014340

fbshipit-source-id: 553f04b62c78b76f9102cd6197146650955aeec5
2023-11-07 21:27:59 -08:00
Nick Gerleman
9eb8a62739 Remove YGNodeMarkDirtyAndPropagateToDescendants (#1448)
Summary:
X-link: https://github.com/facebook/react-native/pull/41305

Pull Request resolved: https://github.com/facebook/yoga/pull/1448

This should not be part of Yoga's API. If benchmarks want to do this, they still can (though I don't know the ones we have for it are super valuable).

Reviewed By: javache

Differential Revision: D50963933

fbshipit-source-id: 6482bd269928188b6469a358ffde5c4f9f5f9527
2023-11-07 21:27:59 -08:00
Joe Vilches
283e3203f6 Fix issue where absolute children of row-reverse containers would inset on the wrong side (#1446)
Summary:
X-link: https://github.com/facebook/react-native/pull/41293

Pull Request resolved: https://github.com/facebook/yoga/pull/1446

NickGerleman pointed out that my recent changes to fix the slew of row-reverse problems in Yoga actually ended up regressing some parts. Specifically, absolute children of row-reverse containers would have their insets set to the wrong side. So if you set left: 10 it would apply it to the right.

Turns out, in `layoutAbsoluteChild` there were cases where we were applying inlineStart/End values to the flexStart/End edge, which can never be right. So I changed the values to also be flexStart/End as the fix here.

Reviewed By: NickGerleman

Differential Revision: D50945475

fbshipit-source-id: 290de06dcc04e8e644a3a32c127af12fdabb2f75
2023-11-07 11:02:20 -08:00
Joe Vilches
c09554056d Introduce isDefined() and remove cases of !isUndefined() (#1439)
Summary:
X-link: https://github.com/facebook/react-native/pull/41209

Pull Request resolved: https://github.com/facebook/yoga/pull/1439

There are so many instances in this code base where we use the double negative of `!yoga::isUndefined(<something>)`. This is not as easy to read since because of this double negative imo. Additionally, sometimes we have really long chains like `!longVariableName.longFunctionName(longArgumentName).isUndefined()` and it is hard to see that this undefined is inverted.

This just replaces all instances of inverted `isUndefined()` with `isDefined()` so its easier to read.

Reviewed By: NickGerleman

Differential Revision: D50705523

fbshipit-source-id: edc7d3f2cbbae38ddaeb2030a419320caf73feff
2023-11-07 11:02:20 -08:00
Joe Vilches
e90f2a8e2c Simplify getting padding + border for cross axis in algorithm (#1437)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1437

X-link: https://github.com/facebook/react-native/pull/41208

Reading through the sizing logic and this seemed a bit redundant/confusing. Lets use the same function we just used for the main axis for the cross axis as well so people do not think its special. Also we will need one less variable. The reason this was done it seems is because we need the leading padding + border elsewhere so this is technically a few less steps but this is cleaner

Reviewed By: NickGerleman

Differential Revision: D50704177

fbshipit-source-id: 1a091edbfee6482a2bf472aca2980702bd75ad94
2023-11-07 11:02:20 -08:00
Joe Vilches
484118c89b Change gentest to only write position relative to parent
Summary:
`child.offsetLeft/Top` calculates the offset from child to its nearest positioned ancestor, not its direct parent. These are often the same and have not mattered in the past since we have not supported position static. Since are are in the process of supporting that, we would like our tests to be usable so this adjusts the gentest methodology to only speak the same language as Yoga - that is left/top are always relative to direct parents.

It works by using `getBoundingClientRect().left/top` instead. Then we pass that down to children and subtract it from the childs `getBoundingClientRect()` to get the position relative to the parent. Note we have to round the final result as `child.offsetLeft/Top` is rounded.

Reviewed By: NickGerleman

Differential Revision: D51053629

fbshipit-source-id: 8809588d12953565228ae50fdf38197213c46182
2023-11-07 09:57:04 -08:00
Joe Vilches
301ed20296 Remove unused arg in calculateTree in gentest.js
Summary: tsia

Reviewed By: NickGerleman

Differential Revision: D51050456

fbshipit-source-id: bd3ef113bd612f6d257b9adce5501f8e8bb32d26
2023-11-07 09:57:04 -08:00
Nick Gerleman
92860077f9 Port more content to website-next (#1435)
Summary:
This builds upon https://github.com/facebook/yoga/pull/1433 and starts porting over and fixing some of the code in website-next.

1. Create a hero similar to current https://yogalayout.com hero
2. Start moving from `antd` and harcoded colors to [Infima](https://infima.dev/docs/getting-started/introduction/) primitives provided by Docusaurus
3. Replaced some more stock docusaurus assets, links, and text with the ones for Yoga.

There is still a lot to do here (not the least, adding real content), but it's beginning to look like a website, and is already pretty snappy. Eventually I want to get SSR working correctly with Playground, which is still a little broken in the port.

Pull Request resolved: https://github.com/facebook/yoga/pull/1435

Test Plan:
**Gatsby Original**
<img width="1795" alt="image" src="https://github.com/facebook/yoga/assets/835219/7670d53a-00a8-4146-a100-e4a05dd77488">

**New (light mode)**
<img width="800" alt="image" src="https://github.com/facebook/yoga/assets/835219/ebe11d15-5f6f-445f-bcc8-9ec51ecfac62">

**New (dark mode)**
<img width="800" alt="image" src="https://github.com/facebook/yoga/assets/835219/ca44a492-46df-410a-8303-baec3029ec49">

Reviewed By: yungsters

Differential Revision: D50523462

Pulled By: NickGerleman

fbshipit-source-id: 61b4610104f695a4e38a7d4bb6a0c2488bd6f89e
2023-11-03 05:23:56 -07:00
Joe Vilches
a20559063e Regenerate YGStaticPositionTest.test.ts that was failing github CI checks (#1447)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1447

Just ran

```
ruby xplat/yoga/gentest/gentest.rb
```

It seems that D50453324 changed a bunch of the import directories in these test files but for some reason it did not get picked up for this test. As a result we are seeing failing github checks like https://github.com/facebook/yoga/actions/runs/6738747977/job/18318958442. Should be good now.

Reviewed By: yungsters

Differential Revision: D50951161

fbshipit-source-id: a2472022782f64f840a64ed991f5f35c3ef556dc
2023-11-02 16:52:18 -07:00
Nick Gerleman
ef1d772447 Consolidate JavaScript Flavors (#1433)
Summary:
Fixes https://github.com/facebook/yoga/issues/1417

This dramatically simplifies the matrix of Node vs web, ASM vs WASM, sync vs async compilation, or CommonJS vs ES Modules. We have one variant, using wasm, with ESModule top-level await to do async compilation. Web/node share the same binary, and we base64 encode the WASM into a wrapper JS file for compatibility with Node and bundlers.

This has some downsides, like requiring an environment with top level await, but also has upsides, like a consistent, sync looking API compatible with older Yoga, and mitigating TypeScript issues with package exports and typings resolution.

As part of this work I also removed `ts-node` from the toolchain (at the cost of a couple of config files needing to be vanilla JS).

Pull Request resolved: https://github.com/facebook/yoga/pull/1433

Test Plan:
1. `yarn test`
2. `yarn lint`
3. `yarn tsc`
4. `yarn benchmark`
5. `yarn build` website-next
6. `yarn lint` website-next
7. Locally test website-next
8. Examine package artifact created by GitHub
9. All Automation passes

Reviewed By: yungsters

Differential Revision: D50453324

Pulled By: NickGerleman

fbshipit-source-id: fe1192acc69e57fa69a1ff056dd7b5844d2198d5
2023-10-31 20:41:38 -07:00
Nick Gerleman
b19a07cff9 Use libc++ in Ubuntu Clang Build (#1440)
Summary:
An ubuntu-latest image update caused build failures, where GTest importing chrono headers caused an error inside of stdlib++ when being compiled by Clang.

This switches to explicitly asking for libc++ (LLVM stdlib) in the reference Ubuntu Clang build instead of stdlibc++ (GCC stdlib).

We don’t force this in CMake logic, to not force a specific stdlib for users compiling Yoga alongside other libraries. E.g. https://github.com/facebookexperimental/libunifex/issues/86

Pull Request resolved: https://github.com/facebook/yoga/pull/1440

Reviewed By: joevilches

Differential Revision: D50790965

Pulled By: NickGerleman

fbshipit-source-id: fe525cfb4385a9a5ae6a02fcbc6decad0e07ff6e
2023-10-30 14:52:45 -07:00
Joe Vilches
4f98bfe40a Add tests for absolute positioning of children with padding in the parent
Summary:
I was playing around with absolute children and padding and noticed an issue so adding tests to track.

Made a github issue: https://github.com/facebook/yoga/issues/1436

Reviewed By: yungsters

Differential Revision: D50670457

fbshipit-source-id: 4672d1e8b831a0a42509d95e91178944fc0f5c06
2023-10-26 10:12:04 -07:00
Joe Vilches
52ae53a7c8 Add errata supporting changes to position: static (#1434)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1434

X-link: https://github.com/facebook/react-native/pull/41130

I will use this errata to gate my changes that actually make position: static behave like the web. We have future plans to make position: relative the default again but users could still have declared certain nodes as position: static, so I think this is needed regardless.

Reviewed By: NickGerleman

Differential Revision: D50506915

fbshipit-source-id: b0d9e6883167de6ff002352c9288053324464cb9
2023-10-23 18:20:24 -07:00
Joe Vilches
6cc9e58246 Add more tests to eventually test position: static
Summary:
Doing some test-driven-development to support this feature, so I will start by adding a ton of tests to ensure the nuance of position: static is captured in Yoga. Specifically I have a slew of tests to capture:

* Insets have no effect on static elements
* Insets are relative to the nearest non-static ancestor
* Percentage values for insets, padding, and margin of absolute children respect the correct dimension of the nearest non-static ancestor
  * Also added similar ones for static and relative children which should just respect their ancestor (static only because it is a flexbox by default)
  * This rule does NOT apply to border
* The containing block for absolute children is the padding box of their nearest non-static ancestor
* The containing block for static children is the content box of their parent (because all elements are flex containers in yoga, at least right now)

Reviewed By: NickGerleman

Differential Revision: D50475939

fbshipit-source-id: 7988ffc9bea3317875128dd1908d787b9b714a45
2023-10-23 18:20:24 -07:00
Joe Vilches
2ea4c043fd gentest support for position: static + initial test
Summary:
I am about to embark on supporting `position: static` in Yoga. The enum exists already (and is the default position type, lol) but does not actually do anything and just behaves like `position: relative`.

My approach here is to write a bunch of tests to test for the various behaviors of static positions and then develop on Yoga afterwards to get those tests passing. To do this, we need to make a few changes to the gentest files as there is not support for adding `position: static` at the moment:

* Make it so that the gentest code can physically write `YGPositionTypeStatic` if it encounters `position: static` in the style
* Make it so that gentest.js knows that Yoga's default is actually static. This way the code generated in the tests will actually label nodes for non default values
* Explicitly label the position type even when it is not declared in the style prop (with the exception of the default)
* Regenerate all the tests

Additionally I added the first, basic test: making sure insets do nothing on a statically positioned element.

Reviewed By: NickGerleman

Differential Revision: D50437855

fbshipit-source-id: 0e8bbf1c224d477ea4592b7563d0b70d2ffa79c8
2023-10-23 18:20:24 -07:00
Joe Vilches
7e91004b90 Remove skips on passing position tests and add errata tests
Summary: Now that the tests are passing let's not skip it anymore. Also adding errata tests to make sure most prod builds are still protected.

Reviewed By: NickGerleman

Differential Revision: D50390993

fbshipit-source-id: cb91a7a377e919eaca24fb25e3d73d3c92eb8931
2023-10-18 17:30:18 -07:00
Joe Vilches
b6f85a1c76 Fix issue where position insets were not working with row reverse (#1431)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1431

X-link: https://github.com/facebook/react-native/pull/41041

The last of the row-reverse issues hurray!

The position insets were broken with row-reverse since we were using the main-start/main-end edges to inset from and NOT the inline-start/inline-end edges as we should. This made it so that inset in left and right were swapped and same with top and bottom (with column-reverse). The solution here is the same as the previous ones were we are migrating to using inline-start/end as the leading/trailing edge now.

Reviewed By: NickGerleman

Differential Revision: D50390543

fbshipit-source-id: b714deab8489fbe11f7f6db21e4aad3b3aa314b3
2023-10-18 17:30:18 -07:00
Joe Vilches
2668e8e70c Fix row-reverse flex direction fixtures
Summary:
These tests were a bit weird for testing something with position. The gentest setup makes it so that the fixtures are wrapped in a absolutely positioned container with height and width bot 0. However, the generated yoga tests do NOT do this and instead have the root node as the fixture itself with no wrapping container.

This causes a problem when testing left/right/top/bottom position insets. Because left/right/top/bottom will position the element relative to its containing block when position is absolute, we will get different values on yoga and chrome even if the implementation is correct: https://developer.mozilla.org/en-US/docs/Web/CSS/right#description

To fix this, we just wrap the fixture in a set size div that is also absolutely positioned.

The file was also formatted.

Reviewed By: NickGerleman

Differential Revision: D50389229

fbshipit-source-id: ecd23939b973225cfb0611dc87f30c262952c5fc
2023-10-18 17:30:18 -07:00
Nick Gerleman
f1f869b955 Build with MSVC /W4 (#1432)
Summary:
X-link: https://github.com/facebook/react-native/pull/41044

The reference Clang/GCC build has a pretty strict set of warnings enabled. The reference MSVC build has less strict warnings, which can be a problem for MSVC users building at higher warning levels (e.g. React Native for Windows in OSS uses `/W4` as its baseline warning level).

This bumps up the MSVC warning level to `/W4`, since we are nearly clean already.

There are some limitations. E.g. we don't test binary with MSVC (some issues I didn't work out), and only test building statically linked. But but we do have a minimal C benchmark we compile with MSVC.

Pull Request resolved: https://github.com/facebook/yoga/pull/1432

Test Plan: GitHub Actions running benchmark MSVC build.

Reviewed By: yungsters

Differential Revision: D50398443

Pulled By: NickGerleman

fbshipit-source-id: 6616034d79b1a308b32d5d3387bae70f40b7b5ab
2023-10-18 17:24:52 -07:00
Nico Burns
0d28b283e2 Support "align-content: space-evenly" (#1422)
Summary:
X-link: https://github.com/facebook/react-native/pull/41019

### Changes made
- Regenerated tests (as some aspect ratio tests seem to be out of date compared to the fixtures)
- Added SpaceEvenly variant to the "Align" enums (via enums.py)
- Implemented `align-content: space-evenly` alignment in CalculateLayout.cpp
- Added generated tests `align-content: space-evenly`
- Updated NumericBitfield test to account for the fact that the Align enum now requires more bits (this bit could do with being reviewed as I am not 100% certain that it's valid to just update the test like this).

### Changes not made
- Any attempt to improve the spec-compliance of content alignment in general (e.g. I think https://github.com/facebook/yoga/pull/1013 probably still needs to happen)

Pull Request resolved: https://github.com/facebook/yoga/pull/1422

Reviewed By: yungsters

Differential Revision: D50305438

Pulled By: NickGerleman

fbshipit-source-id: ef9f6f14220a0db066bc30db8dd690a4a82a0b00
2023-10-17 20:59:51 -07:00
Joe Vilches
2e2c124c28 Revamp tests for row reverse + padding after fix
Summary: no longer skip passing tests + add errata tests

Reviewed By: NickGerleman

Differential Revision: D50282417

fbshipit-source-id: 515600111c1ad7b15e40cfe5a3894e40c241c559
2023-10-17 20:30:16 -07:00
Joe Vilches
7502595bef Fix issue where paddingStart and paddingEnd were swapped with row reverse (#1426)
Summary:
X-link: https://github.com/facebook/react-native/pull/41023

Pull Request resolved: https://github.com/facebook/yoga/pull/1426

Just like D50140503 where marginStart and marginEnd were not working with row reverse, paddingStart and paddingEnd are not working either with row reverse either. The solution is similar - we were checking the flex item layout starting/ending edges and not the general layout starting/ending edges. This change makes it so that we look at the proper edge according to what direction is set.

One caveat is that in the case of padding (and also border) there is a callsite that actually wants to get the flex item layout's leading/trailing padding and not the one dictated by direction. So, I made a new function to accommodate this and just swapped that callsite out.

Reviewed By: NickGerleman

Differential Revision: D50348995

fbshipit-source-id: 85717df23de7cf5f66b38d3ff28435b053a4e68e
2023-10-17 20:30:16 -07:00
Joe Vilches
b558059b74 Revamp tests for row reverse + border after fix
Summary: this is fixed now so we can turn it on

Reviewed By: NickGerleman

Differential Revision: D50348206

fbshipit-source-id: 61c2a72164c6f0ee91b1b5b576d3f129e8cfbe40
2023-10-17 20:30:16 -07:00
Joe Vilches
b52d6ce7f2 Fix issue where borderStart and borderEnd were swapped with row reverse (#1425)
Summary:
X-link: https://github.com/facebook/react-native/pull/41022

Pull Request resolved: https://github.com/facebook/yoga/pull/1425

Just like D50140503 where marginStart and marginEnd were not working with row reverse, borderStart and borderEnd are not working either with row reverse either. The solution is similar - we were checking the flex item layout starting/ending edges and not the general layout starting/ending edges. This change makes it so that we look at the proper edge according to what direction is set.

One caveat is that in the case of border (and also padding) there is a callsite that actually wants to get the flex item layout's leading/trailing border and not the one dictated by direction. So, I made a new function to accommodate this and just swapped that callsite out.

Reviewed By: NickGerleman

Differential Revision: D50348085

fbshipit-source-id: eca2702c1753dbebb503034e2f0732684ad6c56e
2023-10-17 20:30:16 -07:00
Joe Vilches
e17005960f Rename ambiguous getLeading/Trailing... functions in Node.cpp (#1424)
Summary:
X-link: https://github.com/facebook/react-native/pull/41018

Pull Request resolved: https://github.com/facebook/yoga/pull/1424

See D50344874

Reviewed By: NickGerleman

Differential Revision: D50344874

fbshipit-source-id: 32fc35df674eb854c682a5e387c031a94c1c4f68
2023-10-17 20:30:16 -07:00
Joe Vilches
bab6b43683 Rename ambiguous leading/trailingEdge functions (#1423)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1423

X-link: https://github.com/facebook/react-native/pull/41017

Before resolving https://github.com/facebook/yoga/issues/1208 yoga was in a state where "leading" and "trailing" only referred to the main-start and main-end directions ([definition in spec](https://drafts.csswg.org/css-flexbox/#box-model)). That is, the start/end of the layout of flex items in a container. This is distinct from something like inline-start/inline-end which is the [start of text layout as defined by direction](https://drafts.csswg.org/css-writing-modes-3/#inline-start).

The bug linked above happened because "leading" and "trailing" functions are referring to the wrong directions in certain cases. So in order to fix this we added a new set of functions to get the "leading" and "trailing" edges according to what inline-start/inline-end would refer to - i.e. those defined by the direction (ltr | rtl). In this state I think it is confusing to understand which function refers to which direction and more specific names could help that.

This diff just renames the following 4 FlexDirection.h functions:

* **leadingEdge** -> **flexStartEdge**
* **trailingEdge** -> **flexEndEdge**
* **leadingLayoutEdge** -> **inlineStartEdge**
* **trailingLayoutEdge** -> **inlineEndEdge**

The spec calls the start/end directions as dictated by the flex-direction attribute "main-start" and "main-end" respectively, but mainStartEdge might be a bit confusing given it will be compared to a non-flexbox-specific name in inlineStartEdge. As a result I landed on flexStart/flexEnd similar to what values are used with alignment attributes (justify-content, align-content).

I chose to get rid of the "leading" and "trailing" descriptors to be more in line with what terminology the spec uses.

Next diff will be to rename the functions in Node.cpp to adhere to the above patterns.

Reviewed By: NickGerleman

Differential Revision: D50342254

fbshipit-source-id: 1e83a885876af9cf363822ebdbb64537f4784520
2023-10-17 20:30:16 -07:00
dependabot[bot]
b40e44c1ab Bump @babel/traverse from 7.21.5 to 7.23.2 (#1428)
Summary:
Bumps [babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.5 to 7.23.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/babel/babel/releases"><code>@​babel/traverse</code>'s releases</a>.</em></p>
<blockquote>
<h2>v7.23.2 (2023-10-11)</h2>
<p><strong>NOTE</strong>: This release also re-publishes <code>babel/core</code>, even if it does not appear in the linked release commit.</p>
<p>Thanks <a href="https://github.com/jimmydief"><code>@​jimmydief</code></a> for your first PR!</p>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-traverse</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16033">#16033</a> Only evaluate own String/Number/Math methods (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-preset-typescript</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16022">#16022</a> Rewrite <code>.tsx</code> extension when using <code>rewriteImportExtensions</code> (<a href="https://github.com/jimmydief"><code>@​jimmydief</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16017">#16017</a> Fix: fallback to typeof when toString is applied to incompatible object (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16025">#16025</a> Avoid override mistake in namespace imports (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
</ul>
<h4>Committers: 5</h4>
<ul>
<li>Babel Bot (<a href="https://github.com/babel-bot"><code>@​babel-bot</code></a>)</li>
<li>Huáng Jùnliàng (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
<li>James Diefenderfer (<a href="https://github.com/jimmydief"><code>@​jimmydief</code></a>)</li>
<li>Nicolò Ribaudo (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
<li><a href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a></li>
</ul>
<h2>v7.23.1 (2023-09-25)</h2>
<p>Re-publishing <code>babel/helpers</code> due to a publishing error in 7.23.0.</p>
<h2>v7.23.0 (2023-09-25)</h2>
<p>Thanks <a href="https://github.com/lorenzoferre"><code>@​lorenzoferre</code></a> and <a href="https://github.com/RajShukla1"><code>@​RajShukla1</code></a> for your first PRs!</p>
<h4>🚀 New Feature</h4>
<ul>
<li><code>babel-plugin-proposal-import-wasm-source</code>, <code>babel-plugin-syntax-import-source</code>, <code>babel-plugin-transform-dynamic-import</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15870">#15870</a> Support transforming <code>import source</code> for wasm (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-helper-module-transforms</code>, <code>babel-helpers</code>, <code>babel-plugin-proposal-import-defer</code>, <code>babel-plugin-syntax-import-defer</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>, <code>babel-standalone</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15878">#15878</a> Implement <code>import defer</code> proposal transform support (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>, <code>babel-parser</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15845">#15845</a> Implement <code>import defer</code> parsing support (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
<li><a href="https://redirect.github.com/babel/babel/pull/15829">#15829</a> Add parsing support for the &quot;source phase imports&quot; proposal (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>, <code>babel-helper-module-transforms</code>, <code>babel-parser</code>, <code>babel-plugin-transform-dynamic-import</code>, <code>babel-plugin-transform-modules-amd</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-plugin-transform-modules-systemjs</code>, <code>babel-traverse</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15682">#15682</a> Add <code>createImportExpressions</code> parser option (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-standalone</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15671">#15671</a> Pass through nonce to the transformed script element (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helper-function-name</code>, <code>babel-helper-member-expression-to-functions</code>, <code>babel-helpers</code>, <code>babel-parser</code>, <code>babel-plugin-proposal-destructuring-private</code>, <code>babel-plugin-proposal-optional-chaining-assign</code>, <code>babel-plugin-syntax-optional-chaining-assign</code>, <code>babel-plugin-transform-destructuring</code>, <code>babel-plugin-transform-optional-chaining</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>, <code>babel-standalone</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15751">#15751</a> Add support for optional chain in assignments (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-plugin-proposal-decorators</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15895">#15895</a> Implement the &quot;decorator metadata&quot; proposal (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15893">#15893</a> Add <code>t.buildUndefinedNode</code> (<a href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-preset-typescript</code></li>
</ul>

</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/babel/babel/blob/main/CHANGELOG.md"><code>@​babel/traverse</code>'s changelog</a>.</em></p>
<blockquote>
<h2>v7.23.2 (2023-10-11)</h2>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-traverse</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16033">#16033</a> Only evaluate own String/Number/Math methods (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-preset-typescript</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16022">#16022</a> Rewrite <code>.tsx</code> extension when using <code>rewriteImportExtensions</code> (<a href="https://github.com/jimmydief"><code>@​jimmydief</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16017">#16017</a> Fix: fallback to typeof when toString is applied to incompatible object (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/16025">#16025</a> Avoid override mistake in namespace imports (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
</ul>
<h2>v7.23.0 (2023-09-25)</h2>
<h4>🚀 New Feature</h4>
<ul>
<li><code>babel-plugin-proposal-import-wasm-source</code>, <code>babel-plugin-syntax-import-source</code>, <code>babel-plugin-transform-dynamic-import</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15870">#15870</a> Support transforming <code>import source</code> for wasm (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-helper-module-transforms</code>, <code>babel-helpers</code>, <code>babel-plugin-proposal-import-defer</code>, <code>babel-plugin-syntax-import-defer</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>, <code>babel-standalone</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15878">#15878</a> Implement <code>import defer</code> proposal transform support (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>, <code>babel-parser</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15845">#15845</a> Implement <code>import defer</code> parsing support (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
<li><a href="https://redirect.github.com/babel/babel/pull/15829">#15829</a> Add parsing support for the &quot;source phase imports&quot; proposal (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-generator</code>, <code>babel-helper-module-transforms</code>, <code>babel-parser</code>, <code>babel-plugin-transform-dynamic-import</code>, <code>babel-plugin-transform-modules-amd</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-plugin-transform-modules-systemjs</code>, <code>babel-traverse</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15682">#15682</a> Add <code>createImportExpressions</code> parser option (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-standalone</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15671">#15671</a> Pass through nonce to the transformed script element (<a href="https://github.com/JLHwung"><code>@​JLHwung</code></a>)</li>
</ul>
</li>
<li><code>babel-helper-function-name</code>, <code>babel-helper-member-expression-to-functions</code>, <code>babel-helpers</code>, <code>babel-parser</code>, <code>babel-plugin-proposal-destructuring-private</code>, <code>babel-plugin-proposal-optional-chaining-assign</code>, <code>babel-plugin-syntax-optional-chaining-assign</code>, <code>babel-plugin-transform-destructuring</code>, <code>babel-plugin-transform-optional-chaining</code>, <code>babel-runtime-corejs2</code>, <code>babel-runtime-corejs3</code>, <code>babel-runtime</code>, <code>babel-standalone</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15751">#15751</a> Add support for optional chain in assignments (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-helpers</code>, <code>babel-plugin-proposal-decorators</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15895">#15895</a> Implement the &quot;decorator metadata&quot; proposal (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-traverse</code>, <code>babel-types</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15893">#15893</a> Add <code>t.buildUndefinedNode</code> (<a href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
<li><code>babel-preset-typescript</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15913">#15913</a> Add <code>rewriteImportExtensions</code> option to TS preset (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
<li><code>babel-parser</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15896">#15896</a> Allow TS tuples to have both labeled and unlabeled elements (<a href="https://github.com/yukukotani"><code>@​yukukotani</code></a>)</li>
</ul>
</li>
</ul>
<h4>🐛 Bug Fix</h4>
<ul>
<li><code>babel-plugin-transform-block-scoping</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15962">#15962</a> fix: <code>transform-block-scoping</code> captures the variables of the method in the loop (<a href="https://github.com/liuxingbaoyu"><code>@​liuxingbaoyu</code></a>)</li>
</ul>
</li>
</ul>
<h4>💅 Polish</h4>
<ul>
<li><code>babel-traverse</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15797">#15797</a> Expand evaluation of global built-ins in <code>babel/traverse</code> (<a href="https://github.com/lorenzoferre"><code>@​lorenzoferre</code></a>)</li>
</ul>
</li>
<li><code>babel-plugin-proposal-explicit-resource-management</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15985">#15985</a> Improve source maps for blocks with <code>using</code> declarations (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
</ul>
<h4>🔬 Output optimization</h4>
<ul>
<li><code>babel-core</code>, <code>babel-helper-module-transforms</code>, <code>babel-plugin-transform-async-to-generator</code>, <code>babel-plugin-transform-classes</code>, <code>babel-plugin-transform-dynamic-import</code>, <code>babel-plugin-transform-function-name</code>, <code>babel-plugin-transform-modules-amd</code>, <code>babel-plugin-transform-modules-commonjs</code>, <code>babel-plugin-transform-modules-umd</code>, <code>babel-plugin-transform-parameters</code>, <code>babel-plugin-transform-react-constant-elements</code>, <code>babel-plugin-transform-react-inline-elements</code>, <code>babel-plugin-transform-runtime</code>, <code>babel-plugin-transform-typescript</code>, <code>babel-preset-env</code>
<ul>
<li><a href="https://redirect.github.com/babel/babel/pull/15984">#15984</a> Inline <code>exports.XXX =</code> update in simple variable declarations (<a href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>)</li>
</ul>
</li>
</ul>
<h2>v7.22.20 (2023-09-16)</h2>

</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="b4b9942a6c"><code>b4b9942</code></a> v7.23.2</li>
<li><a href="b13376b346"><code>b13376b</code></a> Only evaluate own String/Number/Math methods (<a href="https://github.com/babel/babel/tree/HEAD/packages/babel-traverse/issues/16033">#16033</a>)</li>
<li><a href="ca58ec15cb"><code>ca58ec1</code></a> v7.23.0</li>
<li><a href="0f333dafcf"><code>0f333da</code></a> Add <code>createImportExpressions</code> parser option (<a href="https://github.com/babel/babel/tree/HEAD/packages/babel-traverse/issues/15682">#15682</a>)</li>
<li><a href="3744545649"><code>3744545</code></a> Fix linting</li>
<li><a href="c7e6806e21"><code>c7e6806</code></a> Add <code>t.buildUndefinedNode</code> (<a href="https://github.com/babel/babel/tree/HEAD/packages/babel-traverse/issues/15893">#15893</a>)</li>
<li><a href="38ee8b4dd6"><code>38ee8b4</code></a> Expand evaluation of global built-ins in <code>babel/traverse</code> (<a href="https://github.com/babel/babel/tree/HEAD/packages/babel-traverse/issues/15797">#15797</a>)</li>
<li><a href="9f3dfd9021"><code>9f3dfd9</code></a> v7.22.20</li>
<li><a href="3ed28b29c1"><code>3ed28b2</code></a> Fully support <code>||</code> and <code>&amp;&amp;</code> in <code>pluginToggleBooleanFlag</code> (<a href="https://github.com/babel/babel/tree/HEAD/packages/babel-traverse/issues/15961">#15961</a>)</li>
<li><a href="77b0d73599"><code>77b0d73</code></a> v7.22.19</li>
<li>Additional commits viewable in <a href="https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@babel/traverse&package-manager=npm_and_yarn&previous-version=7.21.5&new-version=7.23.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

 ---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `dependabot rebase` will rebase this PR
- `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `dependabot merge` will merge this PR after your CI passes on it
- `dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `dependabot cancel merge` will cancel a previously requested merge and block automerging
- `dependabot reopen` will reopen this PR if it is closed
- `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/facebook/yoga/network/alerts).

</details>

Pull Request resolved: https://github.com/facebook/yoga/pull/1428

Reviewed By: yungsters

Differential Revision: D50373481

Pulled By: NickGerleman

fbshipit-source-id: 8ee1ef3f180c92e7858f4b748a96a30b7fea6317
2023-10-17 16:43:09 -07:00
Nick Gerleman
40279a8d4a Bump 'postcss' and 'word-wrap' (#1427)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1427

Fixes https://github.com/facebook/yoga/pull/1336
Fixes https://github.com/facebook/yoga/pull/1416

Reviewed By: yungsters

Differential Revision: D50349988

fbshipit-source-id: 6ab938b5914b26928ab627022e8a6e93310d65d5
2023-10-16 20:33:34 -07:00
Joe Vilches
656830e595 Add test for errata behavior for margin start/end
Summary: see D50200030

Reviewed By: NickGerleman

Differential Revision: D50231227

fbshipit-source-id: 5c1c51d8d885a36a0a51573f36a8ccc4c3b2e125
2023-10-12 17:51:24 -07:00
Joe Vilches
3fbd92bc3b No longer skip margin_start and margin_end row reverse tests
Summary: These are fixed now!

Reviewed By: NickGerleman

Differential Revision: D50200030

fbshipit-source-id: ac3e80e33d8c35440e342a10cfd3246161ee9018
2023-10-12 16:22:27 -07:00
Joe Vilches
f4337fbb07 Fix issue where marginStart and marginEnd were not working with rowReverse flex direction (#1420)
Summary:
X-link: https://github.com/facebook/litho/pull/962

X-link: https://github.com/facebook/react-native/pull/40804

Pull Request resolved: https://github.com/facebook/yoga/pull/1420

This stack is ultimately aiming to solve https://github.com/facebook/yoga/issues/1208

**The problem**
Turns out that we do not even check direction when determining which edge is the leading (start) and trailing (end) edges. This is not how web does it as the start/end is based on the writing direction NOT the flex direction: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox#start_and_end_lines. While web does not have marginStart and marginEnd, they do have margin-inline-start/end which relies on the writing mode to determine the "start"/"end": https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline-start.

This means that if you do something like
```
export default function Playground(props: Props): React.Node {
  return (
    <View style={styles.container}>
      <View style={styles.item} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    marginEnd: 100,
    flexDirection: 'row-reverse',
    backgroundColor: 'red',
    display: 'flex',
    width: 100,
    height: 100,
  },
  item: {
    backgroundColor: 'blue',
    width: 10,
  },
});
```

You get  {F1116264350}
As you can see the margin gets applied to the left edge even thought the direction is ltr and it should be applied to the right edge.

**The solution**
I ended up fixing this by creating a new `leadingLayoutEdge` and `trailingLayoutEdge` function that take the flex direction as well as the direction. Based on the errata, the a few functions will use these new functions to determine which `YGEdge` is the starting/ending.

You might be wondering why I did not put this logic inside of `leadingEdge(flexDirection)` / `trailingEdge(flexDirection)` since other areas could potentially have the same bug like `getLeadingPadding`. These functions are a bit overloaded and there are cases where we actually want to use the flexDirection to get the edge in question. For example, many of the calls to `setLayoutPosition` in `CalculateLayout.cpp` call `leadingEdge()` / `trailingEdge()` to set the proper position for cases like row-reverse where items need to line up in a different direction.

Reviewed By: NickGerleman

Differential Revision: D50140503

fbshipit-source-id: 5b580c7570f6ae1e2d031971926ac4e8f52dd362
2023-10-12 16:22:27 -07:00
Joe Vilches
50dc5ae2d1 Add errata for fix to marginStart/End for row-reverse flex direction (#1419)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1419

X-link: https://github.com/facebook/litho/pull/961

X-link: https://github.com/facebook/react-native/pull/40803

This stack is ultimately aiming to solve https://github.com/facebook/yoga/issues/1208

This adds an value to the Errata enum. I will use this to gate this fix as there is potential for users to rely on this bug or have a hack in place to fix it and this would be a breaking change.

Reviewed By: NickGerleman

Differential Revision: D50145273

fbshipit-source-id: 913d2103cd31c1fa94cb39fc15d05b0c0b255920
2023-10-12 16:22:27 -07:00
Eric Rozell
50ecd98141 Add test fixture to reproduce bug in aspect-ratio (#1421)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1421

Adds repro of inconsistent implementation of aspect-ratio behavior across Web and React Native.

Reviewed By: NickGerleman

Differential Revision: D50225804

fbshipit-source-id: 0494e0373dcdebc789715b2ec19c002a47d69ce0
2023-10-12 15:02:00 -07:00
Eric Rozell
a876372357 Add option to use aspect-ratio in test fixtures
Summary: Wires up codegen for aspect-ratio styles in JS, Java and CPP gentest fixtures.

Reviewed By: NickGerleman

Differential Revision: D50225805

fbshipit-source-id: 660a3999eab24611b7c2d2e073b1d55180a5d4f0
2023-10-12 15:02:00 -07:00
Eric Rozell
fad4d50b8e Add option to use overflow: scroll in text fixtures
Summary: Wires up the option to use overflow: scroll in JS, Java, and CPP gentest fixtures.

Reviewed By: NickGerleman

Differential Revision: D50225803

fbshipit-source-id: f71c000b7143ad9e0e34d99e2d5ba2c6186266d1
2023-10-12 15:02:00 -07:00
Joe Vilches
bac658b4f5 Add row reverse tests for position
Summary: after looking into the issue described in https://github.com/facebook/yoga/issues/1208 it seems to apply to position too, so adding tests to confirm

Reviewed By: NickGerleman

Differential Revision: D50154056

fbshipit-source-id: 64dd04ce3ad765526a547fe60b699b664f251c06
2023-10-11 14:02:39 -07:00
Joe Vilches
799624b9a5 Add row reverse tests for border
Summary: after looking into the issue described in https://github.com/facebook/yoga/issues/1208 it seems to apply to border too, so adding tests to confirm

Reviewed By: NickGerleman

Differential Revision: D50153472

fbshipit-source-id: a50f3e040153086b6a573924b513919dbb94f3c0
2023-10-11 14:02:39 -07:00
Joe Vilches
9d9b1f0874 Add row reverse tests for padding
Summary: after looking into the issue described in https://github.com/facebook/yoga/issues/1208 it seems to apply to padding too, so adding tests to confirm

Reviewed By: NickGerleman

Differential Revision: D50153085

fbshipit-source-id: bad0ef50389a71a45ec3a58d87c1dea0c2b26024
2023-10-11 14:02:39 -07:00
Chiara Mooney
81de77af8d Fix Type Casting Warnings in ReactCommon
Summary:
Casting warnings are treated as errors in React Native Windows. Adjusting casting to fix warnings.

## Changelog:
[GENERAL] [FIXED] - Fixes type casting warnings that are treated as errors downstream in React Native Windows.

X-link: https://github.com/facebook/react-native/pull/39818

Reviewed By: rshest

Differential Revision: D49940422

Pulled By: NickGerleman

fbshipit-source-id: e4d44326806a2b1974c7e50770e61807a007e39f
2023-10-09 11:07:46 -07:00
Nick Gerleman
7fe6e345a7 FloatOptional GCC build fix and more constexpr (#1411)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1411

X-link: https://github.com/facebook/react-native/pull/39796

Pull Request resolved: https://github.com/facebook/yoga/pull/1414

GCC flags that `isUndefined()` is not declared `constexpr` but that `unwrapOrDefault()` is. `std::isnan` is not constexpr until C++ 23 (because we cannot have nice things), so I made `yoga::isUndefined()` constexpr, using the same code `std::isnan()` boils down to. I then made `FloatOptional` depend on `Comparison.h` (instead of the other way around), so we can use it.

Note that the use of the `std::floating_point` concept here requires the libc++ bump in the previous diff in the stack.

Reviewed By: yungsters

Differential Revision: D49896837

fbshipit-source-id: 61e2bbbfedecffd007a12d42d998e43d3cf5119c
2023-10-06 13:04:39 -07:00
Nick Gerleman
f700e1335c C++ style enums 17/N: Gutter (#1407)
Summary:
X-link: https://github.com/facebook/react-native/pull/39599

Pull Request resolved: https://github.com/facebook/yoga/pull/1407

Replaces internal usages of YGGutter with Gutter.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49532100

fbshipit-source-id: 53c1d23e23a9db7294c66b6dc0eaff4e62ff278c
2023-10-04 20:34:42 -07:00
Nick Gerleman
98d2172e05 Use modern Android libc++ in Yoga and React Native OSS (#1412)
Summary:
X-link: https://github.com/facebook/react-native/pull/39795
Pull Request resolved: https://github.com/facebook/yoga/pull/1412

Android NDK 25 uses a version of libc++ that is more than three years old, missing a lot of basic features of C++ 20. This is rectified in NDK 26 (latest LTS NDK), which brings us up to date with latest Clang (17, released this year), and adds a new policy where future NDK versions will bump libc++ as part of bumping LLVM/Clang.

This requires an a beta AGP version (and corresponding Android Studio Preview). Based on how far we are historically, it wouldn't be a surprise if we see the stable release this month (well before the RN 0.74/Yoga 3.0 cut, even in the worse case).

Changelog:
[Android][Changed] - Use NDK 26

Reviewed By: yungsters

Differential Revision: D49895949

fbshipit-source-id: 37bb4d1fdf81137be7f14f6675b4e079c6f861e4
2023-10-04 19:57:14 -07:00
Nick Gerleman
2734784ddb Fix style resolution functions returning FloatOptional (#1404)
Summary:
X-link: https://github.com/facebook/react-native/pull/39595

Pull Request resolved: https://github.com/facebook/yoga/pull/1404

These functions all ensure their returns are defined, but return FloatOptional anyway, making their callers have to deal with that possibility. Return `float` instead of `FloatOptional`, and do some additional cleanup.

Reviewed By: rshest

Differential Revision: D49531421

fbshipit-source-id: 95b21cade74e501dd54c7b6ca667c8c3859c5dae
2023-10-03 15:36:01 -07:00
Joe Vilches
ce8fe99bf9 Fix typo on aspect ratio page
Summary: Was reading https://yogalayout.com/docs/aspect-ratio and noticed a typo. "Flexbox does has" -> "Flexbox does have"

Reviewed By: NickGerleman

Differential Revision: D49885138

fbshipit-source-id: 847b40eda257bd0726c3992a7285ac6fa267d418
2023-10-03 14:44:00 -07:00
Joe Vilches
e099449c37 Adding optional QOL flags to gentest
Summary:
This diff adds the ability to add 2 flags to gentest.rb that can help with debugging that I found myself wanting to use:

* The ability to suspend the script. Meaning the chrome browser will pause after running each fixture so the user can inspect elements to see if their html is as expected. Normally, the browser just redirects to the next fixture
* The ability to run a specific fixture instead of all fixtures. I found myself wanting to do this as I was changing one in the previous diff. If the user input is incorrect it just ignores it.

Hopefully this will help make debugging this script a bit easier for noobs like me :P

Reviewed By: NickGerleman

Differential Revision: D49775228

fbshipit-source-id: 29933029119ee5afc0195213df70d4d2cf881a9e
2023-10-03 11:31:34 -07:00
Joe Vilches
e60ae290ea Add fixtures to test proper margins with reverse flex directions
Summary:
Yoga has a known bug where marginStart and marginEnd will swap with row-reverse flex direction. This is not the intended behavior. On Paper this is also an issue with marginLeft and marginRight (at least we think Paper is the culprit, not exactly clear yet).

margin-start (and end) is not actually valid css. The gentest.rb script will just turn this into margin-left, but the cpp generated will properly test marginStart. This seems a bit weird to be since marginStart != marginLeft AFAIK. Things like RTL and LTR modes might make this test not exactly right. But given how many other tests depend on this quirk I think it is fine to add as is - the end result is the same after all. If not, a followup would be to add support for mapping margin-inline-start (valid css) to marginStart.

Anyway, this diff is to add test coverage for this scenario. Next stop is to actually try to fix this problem, which may be a bit harder :P

See https://github.com/facebook/yoga/issues/1208 for more info.

Reviewed By: NickGerleman

Differential Revision: D49744271

fbshipit-source-id: 75b8dd0cc5c53b2f338476fb70b60006aaa89054
2023-10-03 11:31:34 -07:00
Nick Gerleman
4ef28bce24 Remove usage of Gutters arrays and YGGutter as index (#1406)
Summary:
X-link: https://github.com/facebook/react-native/pull/39597

Pull Request resolved: https://github.com/facebook/yoga/pull/1406

Similar in vain to D49362819, we want to stop exposing pre-resolved CompactValue, and allow enum class usage without becoming annoying.

This also simplifies gap resolution a bit. I moved this to Style, to make it clear we aren't relying on any node state. I plan to do some similar cleanup for other resolution later.

Reviewed By: rshest

Differential Revision: D49530923

fbshipit-source-id: 47b06a7301fb283acc493dba159f496159d59580
2023-10-03 10:08:10 -07:00
jlmip
07cabca526 Fixed issue with first line element gap handling. (#1408)
Summary:
If the first element of a line is not contributing (e.g. position absolute), an additional gap will be added to the line, because the first gap element of the line is never identified (wrong start index).
Fix: raise the index of the first line element until we find an element that is contributing to the line.

Pull Request resolved: https://github.com/facebook/yoga/pull/1408

Reviewed By: yungsters

Differential Revision: D49722065

Pulled By: NickGerleman

fbshipit-source-id: 1068cb0b11ae4b04ec8d063e70540cce06181d5a
2023-10-03 06:24:48 -07:00
Nick Gerleman
b03a821884 Fix handling of negative flex gap (#1405)
Summary:
X-link: https://github.com/facebook/react-native/pull/39596

Pull Request resolved: https://github.com/facebook/yoga/pull/1405

I noticed that we weren't clamping negative flex gap values to zero. This fixes that bug.

Reviewed By: rshest

Differential Revision: D49530494

fbshipit-source-id: 069db7312f72a085c5c4b01ead7bc66a353a07e5
2023-09-30 21:09:13 -07:00
Nick Gerleman
a8566a0150 C++ style enums 16/N: Dimension (#1403)
Summary:
X-link: https://github.com/facebook/react-native/pull/39598

Pull Request resolved: https://github.com/facebook/yoga/pull/1403

Replaces all usages of YGDimension with Dimension.

Adds `yoga::to_underlying` to act like `std::to_underlying`, added in C++ 23.

This enum is oddly only used internally, and is never an input to the public API, but it handled as any other public generated enum. Potentially some more cleanup to do there.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49475409

fbshipit-source-id: 7d4c31e8a84485baea0dab50b5cf16b86769fa07
2023-09-29 00:06:34 -07:00
Nick Gerleman
8d24fcd90f Remove FloatOptional related TODOs
Summary:
These were added quite a while ago, and the proprosed change doesn't really make sense to pursue, since FloatOptional is a C++ wrapper around a Float, and the public API is entirely C.

bypass-github-export-checks

Reviewed By: rshest

Differential Revision: D49476343

fbshipit-source-id: f83cc99adda75fc0dba96e063cca92510c3d2ef0
2023-09-21 18:14:46 -07:00
Nick Gerleman
83705c2942 Remove usage of Dimension arrays and YGDimension as index (#1402)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1402

X-link: https://github.com/facebook/react-native/pull/39567

This change hides away most usages of YGDimension as an index. We do this for a couple reasons:

1. Right now the style interface may return a full array of resolved edge or dimension values, as a CompactValue. As we abstract away from CompactValue, and move towards ValuePool, this will no longer be the internal interface, and cheap to return. We instead change the interface to return a single value at once, which lets us resolve values lazily.

2. As we move internal usage to scoped enums, enums are not implicitly convertible to intergers (broadly a good thing). Hiding the enum as index prevents the need for callers to cast or convert to underlying.

Instead of making a new version of `IdxRef` for this, I converted to a more traditional setter. I will be making similar changes later for other styles, when I hide CompactValue from the public interface.

To review I would recommend filtering to changes in `xplat`, or viewing this in a single one of the OSS PRs exported. Everything apart from the below 20 files is a mirror.

{F1096792573}

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D49362819

fbshipit-source-id: 30d730d78e62f36597d43f477120f65694e51ea3
2023-09-20 16:19:59 -07:00
hetan thakkar
81754d8cb2 Fixed minor syntax error (#1363)
Summary:
I've corrected the operator syntax error. This error disrupts the execution of command: 'npx react-native run-ios.'

<img width="1440" alt="Screenshot 2023-09-08 at 8 04 49 PM" src="https://github.com/facebook/yoga/assets/38756320/b6a51270-8ae3-4d3f-be22-1fd938af42f8">

bypass-github-export-checks

Pull Request resolved: https://github.com/facebook/yoga/pull/1363

Reviewed By: yungsters

Differential Revision: D49118992

Pulled By: NickGerleman

fbshipit-source-id: 55c3ec7890a9682887d2d88e66400eb7f6613792
2023-09-20 08:34:18 -07:00
Nick Gerleman
6bc896e549 C++ style enums 15/N: Display (#1397)
Summary:
X-link: https://github.com/facebook/react-native/pull/39541

Pull Request resolved: https://github.com/facebook/yoga/pull/1397

Moves internal usages of YGDisplay to Display

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: sammy-SC

Differential Revision: D49361952

fbshipit-source-id: a961efaa35a3fed01659d23783bf90e0b47656f0
2023-09-19 16:30:02 -07:00
Nick Gerleman
75bbfb0b71 C++ style enums 14/N: Overflow (#1398)
Summary:
X-link: https://github.com/facebook/react-native/pull/39537

Pull Request resolved: https://github.com/facebook/yoga/pull/1398

Moves internal usages of YGOverflow to Overflow

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49361843

fbshipit-source-id: 42161aa8a26f64f052587b861120cdad0290ae46
2023-09-19 16:30:02 -07:00
Nick Gerleman
03d0523996 C++ style enums 13/N: Wrap (#1400)
Summary:
X-link: https://github.com/facebook/react-native/pull/39539

Pull Request resolved: https://github.com/facebook/yoga/pull/1400

Moves internal usages of YGPositionType to PositionType

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49361746

fbshipit-source-id: ccc77b4c77753b5f41e11f1849d4c02153c190b7
2023-09-19 16:30:02 -07:00
Nick Gerleman
4ea6b4c4f9 C++ Style enums 12/N: PositionType (#1399)
Summary:
X-link: https://github.com/facebook/react-native/pull/39538

Pull Request resolved: https://github.com/facebook/yoga/pull/1399

Moves internal usages of YGPositionType to PositionType

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49361677

fbshipit-source-id: 526222d6cf9f3dc26eddfbfb8a04de4ba28e14a9
2023-09-19 16:30:02 -07:00
Nick Gerleman
5bf81b1ef8 C++ style enums 11/N: Align (#1395)
Summary:
X-link: https://github.com/facebook/react-native/pull/39497

Pull Request resolved: https://github.com/facebook/yoga/pull/1395

Moves internal usages of YGAlign to Align

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49337511

fbshipit-source-id: bb9906fcd22780d2cfd8d1360ef69f66b9701bb6
2023-09-19 16:30:02 -07:00
Nick Gerleman
61763e7d0a C++ style enums 10/N: Justify (#1396)
Summary:
X-link: https://github.com/facebook/react-native/pull/39498

Pull Request resolved: https://github.com/facebook/yoga/pull/1396

Moves internal usages of YGJustify to Justify.

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49336538

fbshipit-source-id: 6deb2438e3cd2989c8212ee294fd0fe4819f40ab
2023-09-19 16:30:02 -07:00
Nick Gerleman
6ec790dd1b C++ style enums 9/N: FlexDirection (#1394)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1394

X-link: https://github.com/facebook/react-native/pull/39484

Moves internal usages of YGDirection to Direction.

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49336066

fbshipit-source-id: b49e1ffcd79a427e36ea8d2c26debaa98ef3e4c9
2023-09-19 16:30:02 -07:00
Nick Gerleman
9b99e4fc22 C++ style enums 8/N: Direction (#1392)
Summary:
X-link: https://github.com/facebook/react-native/pull/39483

Pull Request resolved: https://github.com/facebook/yoga/pull/1392

Moves internal usages of YGDirection to Direction.

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49335231

fbshipit-source-id: 459fe820c91be88734cebaa8655cd3f0afda83bf
2023-09-19 16:30:02 -07:00
Nick Gerleman
ea3869fe9f Simplify bitfields (#1393)
Summary:
X-link: https://github.com/facebook/react-native/pull/39485

Pull Request resolved: https://github.com/facebook/yoga/pull/1393

These were previously packed into structs to allow zero initializing all the flags at once without needing a ctor. C++ 20 has in-class member initializer support for bitfields, which makes these look more like normal member variables.

Setting enum values is a bit jank right now, due to relying on C enums which are effectively int32_t, along with GCC `-Wconversion` being a bit aggressive in needing to explicitly mask. I have some ideas to fix this later (e.g. using scoped enums internally).

bypass-github-export-checks

Changelog:
[General][Breaking] - Require C++ 20 when including renderer headers

Reviewed By: sammy-SC

Differential Revision: D49265967

fbshipit-source-id: 6ab935a866196df06e742c821f3af88eb4d18e1a
2023-09-19 01:28:35 -07:00
Nick Gerleman
557d2a76fe Breaking: Use C++ 20 (#1382)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1382

X-link: https://github.com/facebook/react-native/pull/39437

Have been running into places where C++ 20 makes life easier for use like `std::bit_cast` (that one is easy to polyfill), in-class member initializer support for bitfields, designated initializers, defaulted comparison operator, concepts instead of SFINAE, and probably more.

Our other infra is in the process of making this jump, or already has. This tests it out everywhere, across the various reference builds, to see if we have any issues.

This is a bit more aggressive than I had previously communicated, but n - 1 is going to be a better long term place than n - 2.

If we wanted to use `std::bit_cast` we would need one of:
1. GCC 11+ (~2.5 years old)
1. Clang 14 (~2.5 years old)
1. VS 16.11 (~2 years old)

For mobile this means:
1. NDK 26 (still in Beta 😭)
1. XCode 14.3.0 (~6 months old)

https://en.cppreference.com/w/cpp/compiler_support/20

That isn't quite doable yet, but we can start taking advantage of language features in the meantime. More of these will be supported in older toolchains.

Anyone needing support for older C++ versions can lag behind on more recent changes. E.g. Yoga 2.0 supports C++ 14.

bypass-github-export-checks

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D49261607

fbshipit-source-id: ceb06eac20dfe93352d7b796d6847a7314069cf3
2023-09-19 01:28:35 -07:00
Nick Gerleman
ed406f0b55 C++ style enums 7/N: MeasureMode (#1389)
Summary:
X-link: https://github.com/facebook/react-native/pull/39452

Pull Request resolved: https://github.com/facebook/yoga/pull/1389

This converts usages of YGMeasureMode to MeasureMode

Reviewed By: rozele

Differential Revision: D49271165

fbshipit-source-id: 273c9ed0a61c3965e469548d29d37e4566c974dc
2023-09-14 23:06:34 -07:00
Nick Gerleman
383b325d06 C++ style enums 6/N: PrintOptions (#1385)
Summary:
X-link: https://github.com/facebook/react-native/pull/39449

Pull Request resolved: https://github.com/facebook/yoga/pull/1385

This converts usages of YGPrintOptions to PrintOptions

Reviewed By: rozele

Differential Revision: D49270929

fbshipit-source-id: ad98dd25865138808d25b48eff22fbe81ccdd352
2023-09-14 23:06:34 -07:00
Nick Gerleman
70954bbda5 C++ style enums 5/N: LogLevel (#1387)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1387

X-link: https://github.com/facebook/react-native/pull/39447

This converts usages of YGLogLevel to LogLevel

Reviewed By: rozele

Differential Revision: D49270695

fbshipit-source-id: 2ba5b4f2b0af93fef89dbbb2ce54c2f486670aac
2023-09-14 23:06:34 -07:00
Nick Gerleman
6f5eaefc51 C++ style enums 4/N: Errata (#1388)
Summary:
X-link: https://github.com/facebook/react-native/pull/39451

Pull Request resolved: https://github.com/facebook/yoga/pull/1388

This converts usages of YGErrata to Errata

Reviewed By: rozele

Differential Revision: D49270354

fbshipit-source-id: 39c0d26a1609cca0a96da843796ab41c81e3af93
2023-09-14 23:06:34 -07:00
Nick Gerleman
42e1f2c737 C++ style enums 3/N: ExperimentalFeature (#1386)
Summary:
X-link: https://github.com/facebook/react-native/pull/39448

Pull Request resolved: https://github.com/facebook/yoga/pull/1386

This converts usages of YGExperimentalFeature to ExperimentalFeature

Reviewed By: rozele

Differential Revision: D49269440

fbshipit-source-id: 0fcb4f380e214a6aadcac457df5a989789bb05d2
2023-09-14 23:06:34 -07:00
Nick Gerleman
9129a0af87 C++ style enums 2/N: NodeType (#1383)
Summary:
X-link: https://github.com/facebook/react-native/pull/39450

Pull Request resolved: https://github.com/facebook/yoga/pull/1383

This converts usages of YGNodeType to NodeType

Reviewed By: rozele

Differential Revision: D49269117

fbshipit-source-id: 27318279fe555c28c605625a160d5be781b662b8
2023-09-14 23:06:34 -07:00
Nick Gerleman
4cd45ac5d5 C++ style enums 1/N: Generator (#1384)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1384

X-link: https://github.com/facebook/react-native/pull/39446

This adds logic to the enum generator to generate C++ style scoped enums.

This gives us a few nicities over C enums, even if both must exist:
1. We can add types and keep unsgined enums directly in bitfields
2. Style/readability
3. Avoiding implicit int conversion

Reviewed By: rozele

Differential Revision: D49267996

fbshipit-source-id: 1c41164c377b317c1fef97811c46cbc00b5a837e
2023-09-14 23:06:34 -07:00
Nick Gerleman
c60050d0cb Fixup hack for flex line size calculation (#1380)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1380

X-link: https://github.com/facebook/react-native/pull/39433

Back when rolling out flex gap, we encountered a bug where gap was added to the end of the main axis when a size was not specified.

During flex line justification/sizing, we calculate the amount of space that should be in between children. We erroneously add this, even after the last child element.

For `justify-content`, this space between children is derived from free space along the axis. The only time we have free space is if we had a dimension/dimension constraint already set on the parent. In this case, the extra space added to the end of the flex line is usually never noticed, because we bound `maxLineMainDim` to container dimension constraints at the end of layout, and the error doesn't effect how any children are positioned or sized.

There was at least one screenshot test where this issue showed up though, and I was able to add a slightly different repro where we may have free space without a definite dimension by enforcing a min dimension and not stretching.

{F1091401183}

The new reference is correct, and looking back at diffs, is what this seemed to originally look like when added three years ago. Seems like there may have been a potential regression, but I didn't spot anything suspicious when I looked around the code history.

`betweenMainDim` may still be set for `gap` even if we don't have a sized parent, which makes the extra space propagated to `maxLineMainDim` effect parent size.

Because we were in a code freeze, I opted to have us go with a solution just effecting flex gap, instead of the right one, in case there were any side effects. This cleans up the code to use the right calculation everywhere, and fixes a separate bug, where `endOfLineIndex` and `startOfLineIndex` may not be the last/first in the line if they are out of the layout flow (absolutely positioned, or display: none_

See the original conversation on https://github.com/facebook/yoga/pull/1188

Reviewed By: javache

Differential Revision: D49260049

fbshipit-source-id: 218552c5ff938668b9f257df7a1493e13ded4d0d
2023-09-14 20:26:31 -07:00
Nick Gerleman
f9c2c27d33 Use fbsource clang-format config
Summary:
This mirrors the clang-format config used by fbsource to Yoga.

They are pretty similar, except for an annoying habit where Yoga's previous forced small functions in headers to be a a single line, so you would get a combination of multiline and single line functions next to each other which are hard to read. That is what motivated this change.

It also enforces header ordering (yay). I don't think we have any side-effect causing headers, so this should be safe.

Reviewed By: yungsters

Differential Revision: D49248994

fbshipit-source-id: 66998395e7c0158ff9d9fb1bee44e8401bdd8f21
2023-09-13 20:12:55 -07:00
Nick Gerleman
9d21e3e300 Remove config drilling (#1378)
Summary:
X-link: https://github.com/facebook/react-native/pull/39404

Pull Request resolved: https://github.com/facebook/yoga/pull/1378

We thread a config through the root node, to every function, that we don't actually use (with the exception of `canUseCachedMeasurement` in the previous diff which was passing root `pointScaleFactor`).

Since the model is currently per-node config, this only creates redundancy/confusion.

I do think we might want to make some more global/layout-pass scoped configuration in the future, but likely not with this sort of drilling.

Reviewed By: yungsters

Differential Revision: D49180385

fbshipit-source-id: 91248042df7d3cea1fc316b47b8137fcb790b521
2023-09-13 20:12:55 -07:00
Nick Gerleman
66cc95f932 Breaking: per-node pointScaleFactor (#1379)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1379

X-link: https://github.com/facebook/react-native/pull/39403

Right now we have a `pointScaleFactor` per-node, but only ever read the one off the root node. In most cases where config is global, these will be the same, but it is possible for these to differ.

This... doesn't make much sense from an API perspective, and there are edge cases where we may want to allow laying out a subtree with a different DPI then the rest of the tree (though I think there might be other solutions to that).

We should rethink some of what is currently on config being allowed per-node (do we really need each node to be able to have a separate logger?), but this makes the model consistent in the meantime.

This change is breaking to any users relying on setting `pointScaleFactor` on the config of the root node, but not other nodes.

Reviewed By: yungsters

Differential Revision: D49181131

fbshipit-source-id: f1363ca242094f04b995fd50c1e56834d5003425
2023-09-13 14:11:25 -07:00
Nick Gerleman
0a90b16ac6 Remove layoutContext Drilling (#1376)
Summary:
X-link: https://github.com/facebook/react-native/pull/39401

Pull Request resolved: https://github.com/facebook/yoga/pull/1376

kill_with_fire_flamethrower

Reviewed By: rshest

Differential Revision: D49179244

fbshipit-source-id: 9a827e1bd29205254fee5725449191726d6bcf5a
2023-09-12 19:08:55 -07:00
Nick Gerleman
b1e0140aaa Remove JNI Binding usage of layoutContext (#1377)
Summary:
X-link: https://github.com/facebook/react-native/pull/39402

Pull Request resolved: https://github.com/facebook/yoga/pull/1377

To avoid keeping a per-node mapping on native Yoga nodes to Java nodes, a per-layout context was added, to be able to pass information from the start of the layout, to measure functions, log functions, etc.

The way this was done was super invasive, and added quite a few private APIs used only by the JNI functions.

This change removes the context-using functions from the JNI bindings in favor of it managing its own context. Next diff removes all the cruft.

Reviewed By: javache

Differential Revision: D49179243

fbshipit-source-id: 7e4944bead864e6b73fd2208a47c5725c18ff2b0
2023-09-12 19:08:55 -07:00
Nick Gerleman
700be8c8ad Extract isBaselineLayout() (#1375)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1375

X-link: https://github.com/facebook/react-native/pull/39400

Moves `isBaselineLayout` out of `CalculateLayout` into `Baseline.h`. This function is called by flex line justification code, which I have been looking at extracting.

Reviewed By: yungsters

Differential Revision: D49177937

fbshipit-source-id: 02c13aa0b02b26cb60ef197473b90e06d53d5f8d
2023-09-12 19:08:55 -07:00
Nick Gerleman
62ba8ebb3d Replace dim and pos arrays (#1373)
Summary:
X-link: https://github.com/facebook/react-native/pull/39397

Pull Request resolved: https://github.com/facebook/yoga/pull/1373

These are used to get the position origin edge from axis (same as leading edge), and dimension from axis.

Replace them with function usage, so that we can call into them from other files than `CalculateLayout.cpp`, and so that we can later use scoped enums not implicitly convertible to ints.

Reviewed By: rshest

Differential Revision: D49134566

fbshipit-source-id: cb806539ba0733a5773c594713720d465987e469
2023-09-11 19:51:40 -07:00
Nick Gerleman
a4f36bdb51 Separate FlexLine functionality (#1374)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1374

X-link: https://github.com/facebook/react-native/pull/39396

Yoga today has a struct `CollectFlexItemsRowValues`, and function `calculateFlexItemsRowValues()`. These names have evolved over time into something not making much sense.

The job of `calculateFlexItemsRowValues()` is a flex-wrap container into lines (i.e. line-breaking main-axis content, which may be row or column). It returns line-breaking results, but some other fields on `calculateFlexItemsRowValues()` are set much later in the process, and the struct is acting effectivelty as a holder for the line-specific values.

This change:
1. Does some renaming (mainly to FlexLine)
2. Reconciles the count `itemsOnLine` and list `relativeChildren` to list `itemsInFlow` (`relativeChildren` is a lie, as it can include elements with `YGPositionTypeStatic` and exclude relative elements which have `display: "none"`. It really just means children which are included in the layout flow for the line)
3. Makes non-changing algorithm outputs const for clarity of what is a running value, and what is a result of line-breaking values with flex basis.
4. Moves working layout values to a substructure `flexLine.layout`
5. Replaces some dishonest documentation about `endOfLineIndex`.
6. Extracts this logic out of `CalculateLayout()` to a separate file
7. Extracts `boundAxis` wholesale into a separate file, to be usable outside of `CalculateLayout.cpp`

Reviewed By: rshest

Differential Revision: D49133837

fbshipit-source-id: ec68c5a3d2f01e7c9bd8d26e28298331a3fe2475
2023-09-11 19:51:40 -07:00
Nick Gerleman
241c5e4baf Cleanup visibility macros (#1372)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1372

X-link: https://github.com/facebook/react-native/pull/39375

D18029030 added `-fvisibility-hidden`, and a corresponding `YOGA_EXPORT` macro for defining shared library visibility. This is used inline next to function and class definitions that should be exported out of the binary.

There was already a `WIN_EXPORT` macro doing the same thing when building a DLL, defined in the headers instead of CPP files, and it seems like sometimes folks forgot to add it to new public APIs after?

This reconciles the redundant macros into a single visibility macro, that we always place with declaration instead of definition. We also rename `YOGA_EXPORT` to `YG_EXPORT` to match the naming convention of other Yoga macros.

Reviewed By: rshest

Differential Revision: D49132643

fbshipit-source-id: cafa6de0c300788a72d9a446ce07c5ac89a20a8e
2023-09-11 19:51:40 -07:00
Nick Gerleman
0720e0b22a Breaking: YGConfigRef related const-correctness fixes (#1371)
Summary:
X-link: https://github.com/facebook/react-native/pull/39374

Pull Request resolved: https://github.com/facebook/yoga/pull/1371

Right now `YGConfigGetDefault` and `YGNodeGetConfig` both return mutable, freeable, configs, which is bad, since the former points to a global singleton config, and the latter usually does too. Mutating this is not thread safe, and it should never be freed.

This change makes these functions return `YGConfigConstRef` to prevent mutation, and also lets us allow `YGConfigNewWithConfig` to accept a const config. If a caller does want to mutate a config (such as to free it), it must be tracked manually.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D49132476

fbshipit-source-id: ac9ce61149e69c6c25cadb99711435b0a5b9f38a
2023-09-11 19:51:40 -07:00
Nick Gerleman
a003c09a4c Cleanup Android logger code (#1367)
Summary:
X-link: https://github.com/facebook/react-native/pull/39373

Pull Request resolved: https://github.com/facebook/yoga/pull/1367

Moves some messiness around conditionally using Android's logger to `Log.cpp`, isolated to within a single function.

Reviewed By: rshest

Differential Revision: D49131964

fbshipit-source-id: cdff8af1d4df6ae28f00eecfed1920c71eec24d0
2023-09-11 19:51:40 -07:00
Nick Gerleman
c35f8819ae Breaking: Remove "UseLegacyStretchBehaviour" functions (#1368)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1368

X-link: https://github.com/facebook/react-native/pull/39372

These were marked as deprecated as part of the public Yoga 2.0 release, and were alredy emitting deprecation warnings. Remove them.

Reviewed By: javache

Differential Revision: D49131250

fbshipit-source-id: cc1d4e8b179697b9a11a685f4fc4e9d36e1a26a0
2023-09-11 19:51:40 -07:00
Nick Gerleman
a2d3fc6a3c Breaking: Remove YGConfigGetInstanceCount (#1370)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1370

X-link: https://github.com/facebook/react-native/pull/39369

This was added in https://github.com/facebook/yoga/pull/497 specifically for tests related to memory leaks in the C# bindings to count how often YGConfigFree.

This is the wrong layer for this check, we don't have officially supported C# bindings anymore, and this API is not safe when Yoga runs on multiple threads. This removes it, similar to a global node instance count that was also previously removed.

Reviewed By: rshest

Differential Revision: D49131207

fbshipit-source-id: 58537ed635ed455ff065471bdf77061a4bf826f4
2023-09-11 19:51:40 -07:00
Nick Gerleman
776065d7c7 Breaking: size_t indices (#1366)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1366

X-link: https://github.com/facebook/react-native/pull/39371

Yoga's public API exposes indices most often as `uint32_t`, with exception of clone callbacks which are `int32_t`. Yoga internally represents these indices as `size_t` when dealing with the child vector, and this is the true index.

This changes the API to consistently be `size_t`. This should not be breaking for most users, but will cause breaks where:

1. Users set a clone node callback (I think this should be rare. RN uses it, but only because it relies on a separate private API).
2. Callers of `YGNodeGetChildCount()` are assigning to an int with less width than `size_t` and have strong warnings enabled.
3. Using a newer Yoga binary with older source, since we are not preserving ABI compatibility (Yoga in general does not aim to be ABI stable between major versions, only ABI safe for a given set of sources).

Changelog: [Internal]

Reviewed By: sammy-SC

Differential Revision: D49130914

fbshipit-source-id: 6a004c160c4c50f68047b108508fd437156f5fac
2023-09-11 19:51:40 -07:00
Nick Gerleman
26f2b28eca Breaking: Fix callback const-correctness (#1369)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1369

X-link: https://github.com/facebook/react-native/pull/39370

This fixes const-correctness of callbacks (e.g. not letting a logger function modify nodes during layout). This helps us to continue to fix const-correctness issues inside of Yoga.

This change is breaking to the public API, since it requires a change in signature passed to Yoga.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D49130714

fbshipit-source-id: 4305f8882d89f296e45b78497a51716a0dbb3b2d
2023-09-11 19:51:40 -07:00
Nick Gerleman
b12a6a340c Non-breaking const-correctness fixes (#1365)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1365

X-link: https://github.com/facebook/react-native/pull/39368

This changes public Yoga API to in more places accept const structures where before they required mutable ones.

`resolveRef` is added as a quick way to resolve overloaded opaque refs for different types which is a bit easier to read than static_casting, and which will propagate const-ness. We also add `YGConfigConstRef`, similar to `YGNodeConstRef`. I was a bit iffy on whether we should add something to make it easier to convert to private interface,  but this doesn't seem any easier to misuse than someone who looks at the internals to find the `static_cast`.

This tries to avoid more breaking changes yet, e.g. changing callbacks to require clients do not modify nodes when they are passed for logging. We also don't have const variants for returning child structures which would allow mutation of dependencies of the const object. These would need new names under the public API, since we do not have operator overloading in C.

Reviewed By: rshest

Differential Revision: D49130412

fbshipit-source-id: ee6b31b47f4622031c63dd52d8ac133d21bf29b7
2023-09-11 19:51:40 -07:00
Nick Gerleman
3cb29e60a8 Fix Running Java UTs with CXX Platform
Reviewed By: rshest

Differential Revision: D49131492

fbshipit-source-id: 80b2f108b2a5d80b92c1a11c573a61520b345ac2
2023-09-11 16:15:46 -07:00
Nick Gerleman
26d2a2682f yoga::bit_cast
Summary:
X-link: https://github.com/facebook/react-native/pull/39358

This adds a function polyfilling C++ 20's `std::bit_cast`, using `memcpy()` to be safe with strict aliasing rules.

This replaces the conditional code in CompactValue for type punning, an unsafe place in YGJNI where we do it unsafely, and is used in ValuePool. The polyfill can be switched to `std::bit_cast` whenever we adopt C++ 20.

Note that this doesn't actually call into `memcpy()`, as verified by Godbolt. Compilers are aware of the memcpy type punning pattern and optimize it, but it's ugly and confusing to folks who haven't seen it before.

Reviewed By: javache

Differential Revision: D49082997

fbshipit-source-id: b848775a68286bdb11b2a3a95bef8069364ac9b5
2023-09-08 13:03:48 -07:00
Nick Gerleman
f8e2bc0875 Add copyright header to ld version script (#1360)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1360

Fixes a ShipIt warning about this.

Reviewed By: yungsters

Differential Revision: D48992315

fbshipit-source-id: 20b6ba86abc27599e5f7dc12471344295151db66
2023-09-06 09:50:43 -07:00
Nick Gerleman
aee43a53bc Enable -Wconversion (#1359)
Summary:
X-link: https://github.com/facebook/react-native/pull/39291

Pull Request resolved: https://github.com/facebook/yoga/pull/1359

This enables clang warnings around potentially unsafe conversions, such as those with mismatched signedness, or ones which may lead to truncation.

This should catch issues in local development which create errors for MSVC (e.g. Dash), who's default `/W3` includes warnings akin to `-Wshorten-64-to-32`.

This full set of warnings here is a tad spammy, but probably more useful than not.

Changelog: [Internal]

Reviewed By: yungsters

Differential Revision: D48954777

fbshipit-source-id: 1ccc07b99d09d1c2d428158149698ffd04025605
2023-09-06 08:16:42 -07:00
Nick Gerleman
95a7b4497e Enable GitHub Actions Yarn Caching (#1332)
Summary:
We sometimes see workflows fail due to network flakiness, almost always being Windows agents doing a `yarn install`. This change enables storing and reusing Yarn's package cache, and makes `yarn install` more permissive when it does hit the network. https://stackoverflow.com/questions/51508364/yarn-there-appears-to-be-trouble-with-your-network-connection-retrying

Pull Request resolved: https://github.com/facebook/yoga/pull/1332

Test Plan: Workflows succeed, and we see cache being restored before yarn install step.

Reviewed By: lunaleaps

Differential Revision: D48967590

Pulled By: NickGerleman

fbshipit-source-id: 55ce6f05496f014512e0153b6646a7ca0ab964d9
2023-09-05 10:27:27 -07:00
Nick Gerleman
65ae809d5d C++ Cleanup 10/N: YGNodeCalculateLayout (#1352)
Summary:
X-link: https://github.com/facebook/react-native/pull/39195

Pull Request resolved: https://github.com/facebook/yoga/pull/1352

## This diff

This splits out all of the logic under `YGNodeCalculateLayout` to a couple of different files, does some mechanical renaming, and starts to split up the implementation a tiny bit. After this, core layout functions are all C++ convention and namespaced.

Each new file is marked as a move for the sake of blame history. It means Phabricator has a very inaccurate count of lines removed though.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48770478

fbshipit-source-id: 2a74b86441c3352de03ae193c98fc3a3573047ed
2023-09-05 05:24:54 -07:00
Nick Gerleman
7d8b9176fd C++ Cleanup 9/N: YGAssert (#1353)
Summary:
X-link: https://github.com/facebook/react-native/pull/39201

Pull Request resolved: https://github.com/facebook/yoga/pull/1353

## This diff

This moves and renames `YGAssert`, and removes it from the public API, since external users should not need to call into internal Yoga assert functions, and the current API prevents us from making this a macro later to include the condition in the message.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48769809

fbshipit-source-id: b5480ac54781bc01b00c158b07d2d751fac87d37
2023-09-04 11:20:17 -07:00
Nick Gerleman
7be985d97c C++ Cleanup 8/N: Yoga-internal (#1355)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1355

X-link: https://github.com/facebook/react-native/pull/39198

## This diff

This splits up `Yoga-internal.h` which has become a grab bag. The actual header is left, with the purpose of being a private C ABI for bindings, but everything else is moved to a place more appropriate or removed.

A few notes:
1. `yoga::isUndefined` is replaced with `std::isnan` to avoid a layer of indirection (we will never be able to change its representation anyway). Internal usages of `YGFloatIsUndefined` are also replaced with `std::isnan` since the previous being at a library boundary means I'm not sure it can be inlined/.
2. `leading`, `trailing` arrays are factored into proper functions
3. `Values` is replaced entirely with `std::array`, since most of it was unused.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48769241

fbshipit-source-id: 5b8e2192309539e7c133c3b3b29b445b59dd5835
2023-09-04 11:20:17 -07:00
Nick Gerleman
31b6c0ddc9 C++ Cleanup 7/N: BitUtils (#1351)
Summary:
X-link: https://github.com/facebook/react-native/pull/39223

X-link: https://github.com/facebook/react-native/pull/39200

Pull Request resolved: https://github.com/facebook/yoga/pull/1351

## This diff

This splits up `BitUtils.h`, does some minor renaming, and namespace consistency fixes.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

bypass-github-export-checks

Reviewed By: shwanton

Differential Revision: D48847255

fbshipit-source-id: 4b9722303372f43e936118f8187c0127bceeb1d4
2023-08-31 01:17:39 -07:00
Nick Gerleman
989f352e03 C++ Cleanup 6/N: YGFloatOptional (#1356)
Summary:
X-link: https://github.com/facebook/react-native/pull/39224

Pull Request resolved: https://github.com/facebook/yoga/pull/1356

X-link: https://github.com/facebook/react-native/pull/39196

## This diff

This renames YGFloatOptional to FloatOptional, adds it to a namespace, and moves it to a subdirectory. This needs Fabric updates because Fabric uses Yoga internals for props storage.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

bypass-github-export-checks

Reviewed By: shwanton

Differential Revision: D48847256

fbshipit-source-id: ab9729a4a02ab90d974183425935f4d274db5732
2023-08-31 01:17:39 -07:00
Nick Gerleman
c029041707 C++ Cleanup 5/N: Reorganize Utils (#1357)
Summary:
X-link: https://github.com/facebook/react-native/pull/39222

Pull Request resolved: https://github.com/facebook/yoga/pull/1357

X-link: https://github.com/facebook/react-native/pull/39199

## This diff

This splits `Utils.h` and `Utils.cpp`, and tweaks naming and namespaces.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

bypass-github-export-checks

Reviewed By: shwanton

Differential Revision: D48847260

fbshipit-source-id: b99df3029cd66257a7ae64de28c13e8751ceb20c
2023-08-31 01:17:39 -07:00
Nick Gerleman
b959774af7 C++ Cleanup 4/N: Reorganize Log and YGNodePrint (#1354)
Summary:
X-link: https://github.com/facebook/react-native/pull/39220

Pull Request resolved: https://github.com/facebook/yoga/pull/1354

X-link: https://github.com/facebook/react-native/pull/39197

## This diff

Moves these files to a `yoga/debug` subdirectory and does some mild renaming, namespace adjustment, and removes Yoga internal log function from list of library exports.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

bypass-github-export-checks

Reviewed By: shwanton

Differential Revision: D48847259

fbshipit-source-id: c1607b1c6457d5a47039c735cdb7c365a301ebc0
2023-08-31 01:17:39 -07:00
Nick Gerleman
992f073746 C++ Cleanup 3/N: Reorganize YGNode (#1350)
Summary:
X-link: https://github.com/facebook/react-native/pull/39219

Pull Request resolved: https://github.com/facebook/yoga/pull/1350

X-link: https://github.com/facebook/react-native/pull/39170

## This diff

This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`).

The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it.

This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

bypass-github-export-checks

Reviewed By: shwanton

Differential Revision: D48847258

fbshipit-source-id: fc560893533b55a5c2d52c37d8e9a59f7369f174
2023-08-30 19:57:16 -07:00
Nick Gerleman
f82babba8a C++ Cleanup 2/N: Reorganize YGConfig (#1348)
Summary:
X-link: https://github.com/facebook/react-native/pull/39218

X-link: https://github.com/facebook/react-native/pull/39169

Pull Request resolved: https://github.com/facebook/yoga/pull/1348

## This diff

This diff adds a top level `config` directory for code related to configuring Yoga and Yoga Nodes.

The public API for config handles is `YGConfigRef`, which is forward declared to be a pointer to a struct named `YGConfig`. The existing `YGConfig` is split into `yoga::Config`, as the private C++ implementation, inheriting from `YGConfig`, a marker type represented as an empty struct. The public API continues to accept `YGConfigRef`, which continues to be `YGConfig *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: shwanton

Differential Revision: D48847257

fbshipit-source-id: 7a2157d169ba80a6f79620693ae45bb10dfca5a3
2023-08-30 16:27:32 -07:00
Nick Gerleman
65d7f95901 C++ Cleanup 1/N: Reorganize YGStyle (#1349)
Summary:
X-link: https://github.com/facebook/react-native/pull/39221

Pull Request resolved: https://github.com/facebook/yoga/pull/1349

X-link: https://github.com/facebook/react-native/pull/39171

## This diff

This diff adds a `style` directory for code related to storing and manipulating styles. `YGStyle`, which is not a public API, is renamed to `yoga::Style` and moved into this folder, alongside `CompactValue`. We will eventually add `ValuePool` alongside this for the next generation style representation.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: shwanton

Differential Revision: D48847261

fbshipit-source-id: 0fc8c6991e19079f3f0d55d368574757e453fe93
2023-08-30 16:27:32 -07:00
Zhiyao Zhou
2a32637c56 Revert D48710084: C++ Cleanup 1/N: Reorganize YGStyle
Differential Revision:
D48710084

Original commit changeset: 20961aee30d5

Original Phabricator Diff: D48710084

fbshipit-source-id: 79cda4f13979b8d0cdf87dabbfc13cbd17abe488
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
13c5ce2234 Revert D48710796: C++ Cleanup 2/N: Reorganize YGConfig
Differential Revision:
D48710796

Original commit changeset: d548553f7ce8

Original Phabricator Diff: D48710796

fbshipit-source-id: c8b2de245f3894f6a87c262ec70d313020aa228e
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
ea7f61a3db Revert D48712710: C++ Cleanup 3/N: Reorganize YGNode
Differential Revision:
D48712710

Original commit changeset: d28eae38469a

Original Phabricator Diff: D48712710

fbshipit-source-id: 7a10b071edcf045ce98bbf8f9deca0d0e2e80a14
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
6ca56e87ce Revert D48763820: C++ Cleanup 4/N: Reorganize Log and YGNodePrint
Differential Revision:
D48763820

Original commit changeset: 7e3ed7354497

Original Phabricator Diff: D48763820

fbshipit-source-id: 1bea996374f14481160aba8f87940c00e229aa69
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
4c0e89e492 Revert D48767465: C++ Cleanup 5/N: Reorganize Utils
Differential Revision:
D48767465

Original commit changeset: da7157953292

Original Phabricator Diff: D48767465

fbshipit-source-id: 0dd948e2c4e6b3aaeb6e197b28b565c0b385d033
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
7cf0483b17 Revert D48767992: C++ Cleanup 6/N: YGFloatOptional
Differential Revision:
D48767992

Original commit changeset: afaff0234359

Original Phabricator Diff: D48767992

fbshipit-source-id: 4666bdbb83aebbf2f7373b3a10a8c1dd0a03f92c
2023-08-29 23:27:25 -07:00
Zhiyao Zhou
8a95b785a8 Revert D48768374: C++ Cleanup 7/N: BitUtils
Differential Revision:
D48768374

Original commit changeset: 921a22ec88bd

Original Phabricator Diff: D48768374

fbshipit-source-id: 59106ab3d03619940023dac1c2af62fd88566773
2023-08-29 23:27:25 -07:00
Nick Gerleman
866b4f7d62 C++ Cleanup 7/N: BitUtils (#1351)
Summary:
X-link: https://github.com/facebook/react-native/pull/39200

Pull Request resolved: https://github.com/facebook/yoga/pull/1351

## This diff

This splits up `BitUtils.h`, does some minor renaming, and namespace consistency fixes.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48768374

fbshipit-source-id: 921a22ec88bd470da1ef9b51f8954afc073d327d
2023-08-29 21:32:56 -07:00
Nick Gerleman
20fd7695e1 C++ Cleanup 6/N: YGFloatOptional (#1356)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1356

X-link: https://github.com/facebook/react-native/pull/39196

## This diff

This renames YGFloatOptional to FloatOptional, adds it to a namespace, and moves it to a subdirectory. This needs Fabric updates because Fabric uses Yoga internals for props storage.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D48767992

fbshipit-source-id: afaff023435915dbd5e571fd1ee2e695e4f59a5c
2023-08-29 21:32:56 -07:00
Nick Gerleman
8fd4d290e6 C++ Cleanup 5/N: Reorganize Utils (#1357)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1357

X-link: https://github.com/facebook/react-native/pull/39199

## This diff

This splits `Utils.h` and `Utils.cpp`, and tweaks naming and namespaces.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48767465

fbshipit-source-id: da71579532924b3a912454163fe281c481770a77
2023-08-29 21:32:56 -07:00
Nick Gerleman
9ba8aa409c C++ Cleanup 4/N: Reorganize Log and YGNodePrint (#1354)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1354

X-link: https://github.com/facebook/react-native/pull/39197

## This diff

Moves these files to a `yoga/debug` subdirectory and does some mild renaming, namespace adjustment, and removes Yoga internal log function from list of library exports.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Reviewed By: rshest

Differential Revision: D48763820

fbshipit-source-id: 7e3ed7354497a7e85b2a1f20ab50b0e2baeb4862
2023-08-29 21:32:56 -07:00
Nick Gerleman
59f75b0194 C++ Cleanup 3/N: Reorganize YGNode (#1350)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1350

X-link: https://github.com/facebook/react-native/pull/39170

## This diff

This diff adds a top level `node` directory for code related to Yoga nodes and data structures on them (inc moving `YGLayout` to `LayoutResults`).

The public API for config handles is `YGNodeRef`, which is forward declared to be a pointer to a struct named `YGNode`. The existing `YGNode` is split into `yoga::Node`, as the private C++ implementation, inheriting from `YGNode`, a marker type represented as an empty struct. The public API continues to accept `YGNodeRef`, which continues to be `YGNode *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it.

This change ends up needing to touch quite a bit, due to the amount of code that mixed and matched private and public APIs. Don't be scared though, because these changes are very mechanical, and Phabricator's line-count is 3x the actual amount due to mirrors and dirsyncs.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D48712710

fbshipit-source-id: d28eae38469afa24a8cb03e4e75eeb8e431173c5
2023-08-29 21:32:56 -07:00
Nick Gerleman
5af3a5d1d9 C++ Cleanup 2/N: Reorganize YGConfig (#1348)
Summary:
X-link: https://github.com/facebook/react-native/pull/39169

Pull Request resolved: https://github.com/facebook/yoga/pull/1348

## This diff

This diff adds a top level `config` directory for code related to configuring Yoga and Yoga Nodes.

The public API for config handles is `YGConfigRef`, which is forward declared to be a pointer to a struct named `YGConfig`. The existing `YGConfig` is split into `yoga::Config`, as the private C++ implementation, inheriting from `YGConfig`, a marker type represented as an empty struct. The public API continues to accept `YGConfigRef`, which continues to be `YGConfig *`, but it must be cast to its concrete internal representation at the API boundary before doing work on it.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D48710796

fbshipit-source-id: d548553f7ce872488ebdd697e0aceaa9a625df62
2023-08-29 21:32:56 -07:00
Nick Gerleman
b289d12248 C++ Cleanup 1/N: Reorganize YGStyle (#1349)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1349

X-link: https://github.com/facebook/react-native/pull/39171

## This diff

This diff adds a `style` directory for code related to storing and manipulating styles. `YGStyle`, which is not a public API, is renamed to `yoga::Style` and moved into this folder, alongside `CompactValue`. We will eventually add `ValuePool` alongside this for the next generation style representation.

## This stack

The organization of the C++ internals of Yoga are in need of attention.
1. Some of the C++ internals are namespaced, but others not.
2. Some of the namespaces include `detail`, but are meant to be used outside of the translation unit (FB Clang Tidy rules warn on any usage of these)
2. Most of the files are in a flat hierarchy, except for event tracing in its own folder
3. Some files and functions begin with YG, others don’t
4. Some functions are uppercase, others are not
5. Almost all of the interesting logic is in Yoga.cpp, and the file is too large to reason about
6. There are multiple grab bag files where folks put random functions they need in (Utils, BitUtils, Yoga-Internal.h)
7. There is no clear indication from file structure or type naming what is private vs not
8. Handles like `YGNodeRef` and `YGConfigRef` can be used to access internals just by importing headers

This stack does some much needed spring cleaning:
1. All non-public headers and C++ implementation details are in separate folders from the root level `yoga`. This will give us room to split up logic and add more files without too large a flat hierarchy
3. All private C++ internals are under the `facebook::yoga` namespace. Details namespaces are only ever used within the same header, as they are intended
4. Utils files are split
5. Most C++ internals drop the YG prefix
6. Most C++ internal function names are all lower camel case
7. We start to split up Yoga.cpp
8. Every header beginning with YG or at the top-level directory is public and C only, with the exception of Yoga-Internal.h which has non-public functions for bindings
9. It is not possible to use private APIs without static casting handles to internal classes

This will give us more leeway to continue splitting monolithic files, and consistent guidelines for style in new files as well.

These changes should not be breaking to any project using only public Yoga headers. This includes every usage of Yoga in fbsource except for RN Fabric which is currently tied to internals. This refactor should make that boundary clearer.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D48710084

fbshipit-source-id: 20961aee30d54a6b0d8c1cc2976df09b9b6d486a
2023-08-29 21:32:56 -07:00
Andrew Wang
49fbd406b6 Ship the fix for local reference overflow in Yoga (#1347)
Summary:
X-link: https://github.com/facebook/litho/pull/954

X-link: https://github.com/facebook/react-native/pull/39132

Pull Request resolved: https://github.com/facebook/yoga/pull/1347

# Context

Reviewed By: NickGerleman, astreet

Differential Revision: D48607502

fbshipit-source-id: 79552bc76879d1fc15341423ae6fbadeab2fb7af
2023-08-24 12:48:56 -07:00
Nick Gerleman
38ad93c87b Fix segfault calling YGJNILogFunc (#1344)
Summary:
X-link: https://github.com/facebook/react-native/pull/39051

Pull Request resolved: https://github.com/facebook/yoga/pull/1344

`YGJNILogFunc` has a bug where it uses a `va_list` to determine the length of a printf string, then reuses the same `va_list` later after it has already been iterated through. Even if no arguments are present, this may cause a crash looking something like:

```
C  [libsystem_platform.dylib+0xf12]  _platform_strlen+0x12
C  [libsystem_c.dylib+0x31bf]  __vfprintf+0x1339
C  [libsystem_c.dylib+0x307ce]  _vsnprintf+0x100
C  [libsystem_c.dylib+0x6965]  vsnprintf+0x44
C  [libyoga.dylib+0x5161]  YGJNILogFunc(YGConfig*, YGNode*, YGLogLevel, void*, char const*, __va_list_tag*)+0x59
```

Fixing this fixes crashing unit tests which are not explicitly disabled.

Reviewed By: yungsters

Differential Revision: D48388548

fbshipit-source-id: 492e7a89aeb5f9d15485ce31641875a295356bef
2023-08-18 00:07:51 -07:00
Roland Crosby
910adaa5dc Update standalone JS docs (#1342)
Summary:
Excited to see some activity around this project! The move from Flow to TypeScript broke one of the links in the documentation, which this PR fixes. I went ahead and updated the example code as well, since it looks like some things moved around since the switch to the async loader.

Pull Request resolved: https://github.com/facebook/yoga/pull/1342

Reviewed By: yungsters

Differential Revision: D48337554

Pulled By: NickGerleman

fbshipit-source-id: 8397cab60cf2104e271029a235f287a179ece2f4
2023-08-14 21:20:59 -07:00
Lvv.me
ef5784aca6 Add SwiftPM support (#1339)
Summary:
Using yoga in Swift

```swift
import UIKit
import yoga

let YGUndefined = Float.nan

UIGraphicsBeginImageContextWithOptions(CGSize(width: 1, height: 1), true, 0)
let scale = UIGraphicsGetCurrentContext()?.ctm.a ?? 1
UIGraphicsEndImageContext()

let config = YGConfigNew()
YGConfigSetUseWebDefaults(config, true)
YGConfigSetPointScaleFactor(config, Float(scale))

let root = YGNodeNewWithConfig(config)
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow)
YGNodeStyleSetFlexWrap(root, YGWrapWrap)
YGNodeStyleSetWidth(root, 200)

for i in 0..<4 {
    let leaf = YGNodeNewWithConfig(config)
    YGNodeStyleSetAspectRatio(leaf, 1)
    YGNodeStyleSetWidthPercent(leaf, 50)
    YGNodeInsertChild(root, leaf, UInt32(i))
}

YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGNodeStyleGetDirection(root))

for i in 0..<4 {
    let leaf = YGNodeGetChild(root, UInt32(i))
    let left = YGNodeLayoutGetLeft(leaf)
    let top = YGNodeLayoutGetTop(leaf)
    let width = YGNodeLayoutGetWidth(leaf)
    let height = YGNodeLayoutGetHeight(leaf)

    print("leaf \(i): origin: \(left), \(top), size: \(width), \(height)")
}

let width = YGNodeLayoutGetWidth(root)
let height = YGNodeLayoutGetHeight(root)

print("root size: \(width), \(height)")

YGNodeFreeRecursive(root)
YGConfigFree(config)
```

Pull Request resolved: https://github.com/facebook/yoga/pull/1339

Reviewed By: cipolleschi

Differential Revision: D48166958

Pulled By: NickGerleman

fbshipit-source-id: 72fef99729d01c7ba291b11f047cf75fd39f7630
2023-08-10 17:40:10 -07:00
Nick Gerleman
c18e2566a0 Fix set-version script corrupting files when new version is shorter than old one
Summary: We were leaving the last bits of the previous string around if writing a new file which is shorter. See 30b697d3fe for an example.

Reviewed By: cortinico

Differential Revision: D47824696

fbshipit-source-id: 82ebafd9cd1720752cbc62ea93c5b9362395d5c8
2023-07-28 09:48:07 -07:00
Nick Gerleman
44507ec62e Unify ESLint and Prettier across xplat/yoga (#1335)
Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1335

Reviewed By: christophpurrer

Differential Revision: D47459866

fbshipit-source-id: fd6b4e4e2518d91968ac7180b32129b3f70edf88
2023-07-17 14:27:32 -07:00
Nick Gerleman
05b2edfb85 Explicit C++ toolchain on ubuntu-latest (#1334)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1334

Last change made me discover CMake on Ubuntu defaults to GCC. This change makes it so that we explicitly configure whether to use Clang/LLVM or GCC when building on machines with the `ubuntu-latest` image.

Reviewed By: christophpurrer

Differential Revision: D47462132

fbshipit-source-id: e81fb6d4b1740ab2913d39b6e90c52d674e5a4f2
2023-07-14 20:54:41 -07:00
Nick Gerleman
61bf4d15ab Fixup Yoga UT compilation on non-clang compilers (#1333)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1333

Yoga is compiled on more than clang, both internally, and in OSS. D47345669 added unguarded usage of `
#pragma clang diagnostic push` to Yoga UTs which will cause unrecognized pragma errors if building the tests on other platforms.

The code here is flagged for implicitly converting enum to float. This is test code which is using the enum as an arbitrary differentiating input, so I just changed this to an explicit cast, which I assume should silence the warning.

Reviewed By: NuriAmari

Differential Revision: D47460301

fbshipit-source-id: 952ff61e0c26deb8bbff8b860faf30896753a3bd
2023-07-14 09:49:50 -07:00
dependabot[bot]
45b0697960 Bump semver from 5.7.1 to 5.7.2 (#1328)
Summary:
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/npm/node-semver/releases">semver's releases</a>.</em></p>
<blockquote>
<h2>v5.7.2</h2>
<h2><a href="https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2">5.7.2</a> (2023-07-10)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a href="2f8fd41487"><code>2f8fd41</code></a> <a href="https://redirect.github.com/npm/node-semver/pull/585">https://github.com/facebook/yoga/issues/585</a> better handling of whitespace (<a href="https://redirect.github.com/npm/node-semver/issues/585">https://github.com/facebook/yoga/issues/585</a>) (<a href="https://github.com/joaomoreno"><code>@​joaomoreno</code></a>, <a href="https://github.com/lukekarrys"><code>@​lukekarrys</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md">semver's changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2">5.7.2</a> (2023-07-10)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><a href="2f8fd41487"><code>2f8fd41</code></a> <a href="https://redirect.github.com/npm/node-semver/pull/585">https://github.com/facebook/yoga/issues/585</a> better handling of whitespace (<a href="https://redirect.github.com/npm/node-semver/issues/585">https://github.com/facebook/yoga/issues/585</a>) (<a href="https://github.com/joaomoreno"><code>@​joaomoreno</code></a>, <a href="https://github.com/lukekarrys"><code>@​lukekarrys</code></a>)</li>
</ul>
<h2>5.7</h2>
<ul>
<li>Add <code>minVersion</code> method</li>
</ul>
<h2>5.6</h2>
<ul>
<li>Move boolean <code>loose</code> param to an options object, with
backwards-compatibility protection.</li>
<li>Add ability to opt out of special prerelease version handling with
the <code>includePrerelease</code> option flag.</li>
</ul>
<h2>5.5</h2>
<ul>
<li>Add version coercion capabilities</li>
</ul>
<h2>5.4</h2>
<ul>
<li>Add intersection checking</li>
</ul>
<h2>5.3</h2>
<ul>
<li>Add <code>minSatisfying</code> method</li>
</ul>
<h2>5.2</h2>
<ul>
<li>Add <code>prerelease(v)</code> that returns prerelease components</li>
</ul>
<h2>5.1</h2>
<ul>
<li>Add Backus-Naur for ranges</li>
<li>Remove excessively cute inspection methods</li>
</ul>
<h2>5.0</h2>
<ul>
<li>Remove AMD/Browserified build artifacts</li>
<li>Fix ltr and gtr when using the <code>*</code> range</li>
<li>Fix for range <code>*</code> with a prerelease identifier</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="f8cc313550"><code>f8cc313</code></a> chore: release 5.7.2</li>
<li><a href="2f8fd41487"><code>2f8fd41</code></a> fix: better handling of whitespace (<a href="https://redirect.github.com/npm/node-semver/issues/585">https://github.com/facebook/yoga/issues/585</a>)</li>
<li><a href="deb5ad51bf"><code>deb5ad5</code></a> chore: <code>@​npmcli/template-oss</code><a href="https://github.com/4"><code>@​4</code></a>.16.0</li>
<li>See full diff in <a href="https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2">compare view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a href="https://www.npmjs.com/~lukekarrys">lukekarrys</a>, a new releaser for semver since your current version.</p>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=semver&package-manager=npm_and_yarn&previous-version=5.7.1&new-version=5.7.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

 ---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `dependabot rebase` will rebase this PR
- `dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `dependabot merge` will merge this PR after your CI passes on it
- `dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `dependabot cancel merge` will cancel a previously requested merge and block automerging
- `dependabot reopen` will reopen this PR if it is closed
- `dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/facebook/yoga/network/alerts).

</details>

Pull Request resolved: https://github.com/facebook/yoga/pull/1328

Reviewed By: yungsters

Differential Revision: D47414316

Pulled By: NickGerleman

fbshipit-source-id: 9a38149c18cfc0fef714ffc9ce73f92a463224a1
2023-07-13 18:13:35 -07:00
Nick Gerleman
aa6a5d8888 Docusarus: Build validation (#1329)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1329

Add build validation for `website-next`, also adding validation that `yoga-layout/sync` may be bundled by Webpack out-of-the-box.

Reviewed By: christophpurrer

Differential Revision: D47414176

fbshipit-source-id: 9adc4f9673333f4f79c91352f445489d6da05a07
2023-07-13 14:08:07 -07:00
Nick Gerleman
45088187a7 Docusaurus: Replant Playground (#1331)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1331

This lifts and copies code from the Yoga Playground component of the old website, to the new one, using a live workspace version of JS Yoga. This may eventually be used if the new website is fleshed out, but this also gives us real-world validation of the Yoga bindings in a web scenario, compared to the Node test runner. There is still some cleanup here needed, but we are able to bundle, typecheck, and render the playground now.

The code here is mostly the same as before, but I removed some of the cruftier bits (e.g. URL shortener that goes to some private Heroku instance), and needed to do some tweaks for the new Yoga package, and TypeScript.

This is currently using `yoga-layout/sync`, the asmjs bindings. But I had previously checked bundling against the wasm ones.

Reviewed By: cortinico

Differential Revision: D46884439

fbshipit-source-id: f53f0855c131cd2b81975bf05f71c43713600616
2023-07-13 14:08:07 -07:00
Nuri Amari
6ae890dccd Pragma guard remaining -Wenum-conversion failures
Summary:
These incorrect enum conversions constitute compile errors
with Pika / Xcode 15. Pragma guard the current offenders so
we can make the warning fatal again.

Reviewed By: drodriguez

Differential Revision: D47345669

fbshipit-source-id: bb85a93e9f48236c9f8c54d59d55a86648c1c6fd
2023-07-13 09:53:47 -07:00
Nick Gerleman
5bbaadfa54 Add blurb about configuring TypeScript for ESM resolution (#1330)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1330

This has been tripping folks up a bit. Add a blurb to the package README to call it out.

Reviewed By: cortinico

Differential Revision: D47370333

fbshipit-source-id: f07b2e4d0e23865e3554c7aaf6ec77dff71a7423
2023-07-12 15:43:10 -07:00
Nick Gerleman
5310eb3cf7 Separate Node and Web Binaries (#1325)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1325

Fixes https://github.com/facebook/yoga/issues/1321

Reviewed By: cortinico

Differential Revision: D47368830

fbshipit-source-id: 570a45ad7fd182ef82e6edda4037ae2f6faa0c75
2023-07-12 15:43:10 -07:00
Nick Gerleman
7097b12b92 Remove YogaKit
Summary: We deprecated this as part of the Yoga 2.0 release. The last version is still present as part of the release-v2.0 branch, and was still published, but we are not carrying the code forward. This removes it.

Reviewed By: mdvacca

Differential Revision: D47136781

fbshipit-source-id: ac60939efb2372db04e33ed26456bad2f3b5852b
2023-07-12 12:19:27 -07:00
Nick Gerleman
660edcec20 C++ 17 style nested namespaces (#1326)
Summary:
X-link: https://github.com/facebook/react-native/pull/38304

Pull Request resolved: https://github.com/facebook/yoga/pull/1326

For better readability

Reviewed By: christophpurrer

Differential Revision: D47384926

fbshipit-source-id: 2f60d50a185331b3624d45d1fc45f98d504b3034
2023-07-12 09:38:40 -07:00
Nick Gerleman
423dc155d8 Target C++ 17 (#1327)
Summary:
X-link: https://github.com/facebook/react-native/pull/38303

Pull Request resolved: https://github.com/facebook/yoga/pull/1327

This bumps Yoga to C++ 17 for a few reasons:
1. New versions of C++ may introduce behavior changes (e.g. evaluation order) and deprecations. Keeping the version closer to the version of large users helps avoid that.
2. C++ 17 unblocks some new bits I have wanted to use at times, like `std::optional`, `std::variant`, `if constexpr`, `[[nodiscard]]`.
3. There are already changes in C++ 20 that would be directly useful to Yoga, like `std::bit_cast` to avoid `memcpy` style type punning.

There has been some contention around C++ versions before, but by the time the next stable version of Yoga is out, it will have been more than 6 years (~2 C++ versions) since a stable version of Clang/LLVM with C++ 17 support. I would not like to go back further than n-2.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D47383922

fbshipit-source-id: eb95d4853f2168b68d6df5fddb797236eac55870
2023-07-12 09:38:40 -07:00
Nicola Corti
dbd8e915d5 Remove the build-logic module
Summary:
As now we have a single module to publish, I'm removing the build-logic folder and moving everything inside the java/build.gradle file.
I've also converted it to Kotlin,

Reviewed By: NickGerleman

Differential Revision: D47259204

fbshipit-source-id: 2378d9e9598d7816f230db5f763f2b0f4cdf01d0
2023-07-11 04:30:02 -07:00
Nick Gerleman
f7324fb71e Remove YogaLayout ViewGroup (#1318)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1318

We deprecated this as part of the Yoga 2.0 release. The last version is still present as part of the `release-v2.0` branch, and was still published, but we are not carrying the code forward. This removes it.

There are a couple followups here:
1. The top-level organization (e.g. a gradle-specific directory called `build-logic` in a multi-language repo) can and should be cleaned up. Yoga Java bindings should be condensed to the `java` folder.
2. We no longer have a sample app excersizing the bindings. We should resolve this by getting the JNI binding UTs working in OSS again.

Reviewed By: cortinico

Differential Revision: D47136243

fbshipit-source-id: 72f74914effde2c895934ac1100adfd305044d46
2023-07-05 22:00:16 -07:00
Nick Gerleman
1b40f05b8c Fixes for publish workflows (#1319)
Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1319

This fixes a few issues encountered during publishing Yoga `2.0.0-beta.1`.

1. The tag trigger was missing quotes needed to be valid syntax
2. `pod trunk publish` must be run with `--synchronous` if we are publishing a package that relies on another just published package. There does not seem to be a way to just publish evertything at once.
3. `yarn publish` was not reading the NPM auth token from the environment, so we write it to a `.npmrc` before publishing.
4. The root `.gitignore` was not updated when moving to yarn workspaces to ignore `node_modules`, so the OSS Yoga repo (not internal) will, try to add its contents after `yarn install`.

Reviewed By: cortinico

Differential Revision: D47135994

fbshipit-source-id: d8c9b05176a98443be1ebc7cf74996f22520d20d
2023-06-30 11:44:15 -07:00
Nicola Corti
cdc3328ca5 Actually hit Maven Central when publishing tags
Summary:
Due to a copypasta error, the tags will keep on publishing to the snapshot repo.
I've updated the config to actually hit Maven Central.

Reviewed By: NickGerleman

Differential Revision: D47150549

fbshipit-source-id: 314b931c1c9203ef53e1433a88bcc00609abd3d0
2023-06-30 11:39:09 -07:00
442 changed files with 117522 additions and 22638 deletions

View File

@@ -1,61 +1,3 @@
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: false
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyReturnTypeOnItsOwnLine: 2000
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
Standard: Cpp11
UseTab: Never
---
Language: ObjC
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveMacros: false
@@ -165,4 +107,3 @@ Standard: Latest
TabWidth: 8
UseCRLF: false
UseTab: Never
...

View File

@@ -1 +0,0 @@
^lib/.*

89
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
module.exports = {
root: true,
ignorePatterns: [
'/website',
'**/binaries/**',
'**/build/**',
'**/generated/**',
],
overrides: [
// Catch-all
{
files: ['**/*.?(m|c){j,t}s?(x)'],
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
plugins: ['prettier'],
rules: {
'no-unused-vars': ['error', {argsIgnorePattern: '^_'}],
'no-var': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'prefer-object-spread': 'error',
'prefer-spread': 'error',
'require-await': 'error',
},
env: {
es2020: true,
'shared-node-browser': true,
},
parserOptions: {
ecmaVersion: 'latest',
},
},
// ES Modules
{
files: ['**/*.?(m){j,t}s?(x)'],
parserOptions: {
sourceType: 'module',
},
},
// CommonJS Modules
{
files: ['**/*.c{j,t}s?(x)'],
env: {
commonjs: true,
},
parserOptions: {
sourceType: 'commonjs',
},
},
// TypeScript
{
files: ['**/*.?(m|c)ts?(x)'],
extends: ['plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: true,
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{argsIgnorePattern: '^_'},
],
'@typescript-eslint/no-var-requires': 'off',
},
},
// Node
{
files: ['**/.*rc.(c){j,t}s', '**/*.config.?(c){j,t}s'],
env: {
node: true,
},
},
// Jest
{
files: ['**/tests/**'],
extends: ['plugin:jest/recommended'],
},
],
};

10
.github/actions/cache-emsdk/action.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
name: Cache the installed copy of emsdk and its build artifacts
runs:
using: "composite"
steps:
- name: Cache emsdk
uses: actions/cache@v3
with:
path: javascript/.emsdk
key: emsdk-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', 'javascript/package.json', 'javascript/just.config.cjs')}}

View File

@@ -1,23 +0,0 @@
name: Install emsdk (including emcc)
inputs:
version:
description: EMCC Version to install
required: false
default: 3.1.28
runs:
using: "composite"
steps:
- name: Clone emsdk repo
working-directory: ${{ runner.temp }}
shell: bash
run: git clone https://github.com/emscripten-core/emsdk.git
- name: emdsk install
working-directory: ${{ runner.temp }}/emsdk
shell: bash
run: |
./emsdk install ${{ inputs.version }}
./emsdk activate ${{ inputs.version }}
echo $RUNNER_TEMP/emsdk >> $GITHUB_PATH
echo $RUNNER_TEMP/emsdk/upstream/emscripten >> $GITHUB_PATH

View File

@@ -7,3 +7,7 @@ runs:
- name: Install Cocoapods
shell: bash
run: sudo gem install cocoapods
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 14.3.1

View File

@@ -1,4 +1,9 @@
name: Setup C++ envirionment
inputs:
toolchain:
description: Compiler toolchain to use (Clang, GCC, or MSVC)
required: false
default: 'Clang'
runs:
using: "composite"
@@ -7,6 +12,22 @@ runs:
if: ${{ runner.os != 'Windows' }}
uses: ./.github/actions/install-ninja
- name: Set Clang as compiler
if: ${{ inputs.toolchain == 'Clang' }}
shell: bash
run: |
echo "CC=/usr/bin/clang" >> $GITHUB_ENV
echo "CXX=/usr/bin/clang++" >> $GITHUB_ENV
echo "CXXFLAGS=-stdlib=libc++" >> $GITHUB_ENV
echo "LDFLAGS=-stdlib=libc++" >> $GITHUB_ENV
- name: Set GCC as compiler
if: ${{ inputs.toolchain == 'GCC' }}
shell: bash
run: |
echo "CC=/usr/bin/gcc" >> $GITHUB_ENV
echo "CXX=/usr/bin/g++" >> $GITHUB_ENV
- name: Setup VS Developer Command Prompt
if: ${{ runner.os == 'Windows' }}
uses: ilammy/msvc-dev-cmd@v1

View File

@@ -3,18 +3,16 @@ name: Setup JavaScript envirionment
runs:
using: "composite"
steps:
- name: Install Node
uses: actions/setup-node@v1
- name: Setup Node environment
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Install emsdk
uses: ./.github/actions/install-emsdk
- name: Install Ninja
uses: ./.github/actions/install-ninja
cache: yarn
cache-dependency-path: yarn.lock
env:
# https://github.com/actions/setup-node/issues/317
FORCE_COLOR: 0
- name: yarn install
shell: bash
run: yarn install --frozen-lockfile
working-directory: javascript

View File

@@ -5,14 +5,19 @@ runs:
steps:
# TODO: Update to latest when website is moved to the workspace version of
# yoga-layout
- name: Install Node
uses: actions/setup-node@v1
- name: Setup Node environment
uses: actions/setup-node@v3
with:
node-version: 12.x
cache: yarn
cache-dependency-path: website/yarn.lock
env:
# https://github.com/actions/setup-node/issues/317
FORCE_COLOR: 0
# TODO: the website should be in a yarn workspace with the library, but the
# current version of gatsby is incompatible with hoisting.
- name: yarn install
shell: bash
run: yarn install --frozen-lockfile
run: yarn install --frozen-lockfile --network-timeout 1000000
working-directory: website

View File

@@ -3,7 +3,7 @@ name: Publish Android Release
on:
push:
tags:
- *
- '*'
workflow_dispatch:
jobs:
@@ -20,7 +20,6 @@ jobs:
- name: Publish to Maven Local
run: ./gradlew publishToMavenLocal
env:
ORG_GRADLE_PROJECT_USE_SNAPSHOT: true
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
@@ -30,11 +29,10 @@ jobs:
name: 'snapshot-artifacts'
path: '~/.m2/repository/'
- name: Publish to the Snapshot Repository
run: ./gradlew publishToSonatype
- name: Publish to the Maven Central
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
env:
ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }}
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
ORG_GRADLE_PROJECT_USE_SNAPSHOT: true

View File

@@ -3,13 +3,13 @@ name: Publish CocoaPods Release
on:
push:
tags:
- *
- '*'
workflow_dispatch:
jobs:
publish:
name: Publish to CocoaPods trunk
runs-on: ubuntu-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v3
@@ -21,8 +21,3 @@ jobs:
run: pod trunk push Yoga.podspec
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
- name: Publish YogaKit
run: pod trunk push YogaKit.podspec
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}

View File

@@ -3,7 +3,7 @@ name: Publish NPM Release
on:
push:
tags:
- *
- '*'
workflow_dispatch:
jobs:
@@ -17,8 +17,9 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-js
- name: Store auth token in config file
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
- name: yarn publish
run: yarn publish
working-directory: javascript
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,4 +1,4 @@
name: Website
name: Publish Website
on:
push:
@@ -6,6 +6,9 @@ on:
- main
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
jobs:
publish:
name: Publish to GitHub Pages
@@ -26,7 +29,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: gh-pages
publish_dir: website/public
cname: yogalayout.com
cname: yogalayout.dev
keep_files: true
user_name: 'Yoga-bot'
user_email: 'yogabot@fb.com'

View File

@@ -1,10 +1,11 @@
name: Android
name: Validate Android
on:
pull_request:
push:
branches:
- main
- 'release-*'
workflow_dispatch:
jobs:

View File

@@ -1,16 +1,17 @@
name: Apple
name: Validate Apple
on:
pull_request:
push:
branches:
- main
- 'release-*'
workflow_dispatch:
jobs:
lint-pods:
name: Lint pod
runs-on: macos-latest
name: Build [CocoaPods]
runs-on: macos-13
steps:
- uses: actions/checkout@v3
@@ -21,12 +22,9 @@ jobs:
- name: pod lib lint
run: pod lib lint --verbose --include-podspecs=**/*.podspec
build-sample:
name: Build sample [${{ matrix.mode }}]
runs-on: macos-latest
strategy:
matrix:
mode: [Debug, Release]
test:
name: Build [SwiftPM]
runs-on: macos-13
steps:
- uses: actions/checkout@v3
@@ -34,22 +32,8 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-apple
- name: pod install
run: pod install
working-directory: ./YogaKit/YogaKitSample
- name: Build Debug
run: swift build -c debug
- name: xcodebuild YogaKitSample.xcworkspace
run: xcodebuild -workspace YogaKitSample.xcworkspace -scheme YogaKitSample
working-directory: ./YogaKit/YogaKitSample
clang-format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: clang-format
uses: ./.github/actions/clang-format
with:
directory: ./YogaKit
- name: Build Release
run: swift build -c release

View File

@@ -1,10 +1,11 @@
name: C++
name: Validate C++
on:
pull_request:
push:
branches:
- main
- 'release-*'
workflow_dispatch:
env:
@@ -12,45 +13,91 @@ env:
jobs:
test:
name: Build and Test [${{ matrix.os }}][${{ matrix.mode }}]
runs-on: ${{ matrix.os }}
name: Build and Test [${{ matrix.toolchain }}][${{ matrix.mode }}]
runs-on: ${{ (matrix.toolchain == 'MSVC') && 'windows-latest' || 'ubuntu-latest' }}
strategy:
matrix:
mode: [Debug, Release]
os: [ubuntu-latest] # TODO: fix issues building GTest Binary with MSVC in GitHub Actions
toolchain: [Clang, GCC] # TODO: fix issues building GTest Binary with MSVC in GitHub Actions
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-cpp
with:
toolchain: ${{ matrix.toolchain }}
- name: Unit tests
run: ./unit_tests ${{ matrix.mode }}
benchmark:
name: Benchmark [${{ matrix.os }}]
runs-on: ${{ matrix.os }}
build_fuzzers:
name: Build fuzzers [${{ matrix.toolchain }}][${{ matrix.mode }}]
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
mode: [Debug, Release]
toolchain: [Clang]
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-cpp
with:
toolchain: ${{ matrix.toolchain }}
- name: Build benchmark
- name: Unit tests
run: ./build_fuzz_tests ${{ matrix.mode }}
benchmark:
name: Benchmark [${{ matrix.toolchain }}]
runs-on: ${{ (matrix.toolchain == 'MSVC') && 'windows-latest' || 'ubuntu-latest' }}
strategy:
matrix:
toolchain: [Clang, GCC, MSVC]
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-cpp
with:
toolchain: ${{ matrix.toolchain }}
- name: Build and run benchmark
run: |
cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release
cmake --build build
./build/benchmark ./captures
working-directory: benchmark
- name: Run benchmark
run: ./build/benchmark
- name: Build and run benchmarklegacy
run: ./build/benchmarklegacy
working-directory: benchmark
capture:
name: Capture [${{ matrix.toolchain }}]
runs-on: ${{ (matrix.toolchain == 'MSVC') && 'windows-latest' || 'ubuntu-latest' }}
strategy:
matrix:
toolchain: [Clang, GCC, MSVC]
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-cpp
with:
toolchain: ${{ matrix.toolchain }}
- name: Build capture
run: |
cmake -S . -B build -G Ninja -D CMAKE_BUILD_TYPE=Release
cmake --build build
working-directory: capture
clang-format:
name: Format
runs-on: ubuntu-latest
@@ -60,5 +107,3 @@ jobs:
- name: clang-format
uses: ./.github/actions/clang-format
with:
directory: ./yoga

View File

@@ -1,10 +1,11 @@
name: JavaScript
name: Validate JavaScript
on:
pull_request:
push:
branches:
- main
- 'release-*'
workflow_dispatch:
env:
@@ -23,6 +24,9 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn benchmark
run: yarn benchmark
working-directory: javascript
@@ -39,8 +43,11 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn build
run: yarn build
run: yarn build --verbose
working-directory: javascript
test:
@@ -55,22 +62,36 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn test
run: yarn test
working-directory: javascript
lint:
name: Lint
name: ESLint (All Packages)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: yarn install
run: yarn install --frozen-lockfile
working-directory: javascript
- name: Setup
uses: ./.github/actions/setup-js
- name: yarn lint
run: yarn lint
typecheck:
name: Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-js
- name: yarn tsc
run: yarn tsc
working-directory: javascript
pack:
@@ -82,6 +103,9 @@ jobs:
- name: Setup
uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: yarn pack
run: yarn pack --filename yoga-layout.tar.gz
working-directory: javascript

View File

@@ -1,21 +0,0 @@
name: Python
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: black --check
uses: ./.github/actions/black
with:
directory: ${{ github.workspace }}

32
.github/workflows/validate-tests.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Validate Tests
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
validate:
name: Validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup JS
uses: ./.github/actions/setup-js
- name: yarn gentest-validate
run: yarn gentest-validate
- name: yarn gentest
run: yarn gentest -h
- name: Check for modified tests
run: |
if [[ -n $(git status -s) ]]; then
git status -s
echo "yarn gentest modified these tests. Please run yarn gentest to resolve."
exit 1
fi

View File

@@ -1,7 +1,9 @@
name: Website
name: Validate Website
on:
pull_request:
branches:
- main
push:
branches:
- main
@@ -9,7 +11,7 @@ on:
jobs:
build:
name: Build
name: Build [Gatsby]
runs-on: ubuntu-20.04
steps:
@@ -21,3 +23,34 @@ jobs:
- name: yarn build
run: yarn build
working-directory: website
build_next:
name: Build [Docusaurus]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-js
- name: Restore emsdk
uses: ./.github/actions/cache-emsdk
- name: Build Website
run: yarn build
working-directory: website-next
typecheck:
name: Typecheck [Docusaurus]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup-js
- name: yarn tsc
run: yarn tsc
working-directory: website-next

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
/.buckd
/gentest/test.html
.buck-java11
node_modules
# Jekyll
/.sass-cache/

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
**/binaries/**
**/build/**
**/generated/**

View File

@@ -12,6 +12,12 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/project-defaults.cmake)
add_subdirectory(yoga)
add_subdirectory(tests)
option(BUILD_FUZZ_TESTS "Build fuzz tests" OFF)
if ('${CMAKE_CXX_COMPILER_ID}' MATCHES 'Clang' AND BUILD_FUZZ_TESTS)
add_subdirectory(fuzz)
endif()
# cmake install config
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

32
Package.swift Normal file
View File

@@ -0,0 +1,32 @@
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import PackageDescription
let package = Package(
name: "yoga",
products: [
.library(name: "yoga", targets: [ "core" ])
],
targets: [
.target(
name: "core",
path: ".",
sources: [
"yoga"
],
publicHeadersPath: ".",
cxxSettings: [
.headerSearchPath(".")
]
)
],
cxxLanguageStandard: CXXLanguageStandard(rawValue: "c++20")
)

View File

@@ -1,10 +1,10 @@
# Yoga [![Support Ukraine](https://img.shields.io/badge/Support-Ukraine-FFD500?style=flat&labelColor=005BBB)](https://opensource.fb.com/support-ukraine) [![CocoaPods](https://img.shields.io/cocoapods/v/YogaKit.svg)](http://cocoapods.org/pods/YogaKit) [![npm](https://img.shields.io/npm/v/yoga-layout.svg)](https://www.npmjs.com/package/yoga-layout) [![Maven Central](https://img.shields.io/maven-central/v/com.facebook.yoga/yoga)](https://search.maven.org/artifact/com.facebook.yoga/yoga)
# Yoga [![Support Ukraine](https://img.shields.io/badge/Support-Ukraine-FFD500?style=flat&labelColor=005BBB)](https://opensource.fb.com/support-ukraine) [![CocoaPods](https://img.shields.io/cocoapods/v/Yoga.svg)](http://cocoapods.org/pods/Yoga) [![npm](https://img.shields.io/npm/v/yoga-layout.svg)](https://www.npmjs.com/package/yoga-layout) [![Maven Central](https://img.shields.io/maven-central/v/com.facebook.yoga/yoga)](https://search.maven.org/artifact/com.facebook.yoga/yoga)
Yoga is an embeddable and performant flexbox layout engine with bindings for multiple languages.
## Building
Yoga's main implementation targets C++ 14 with accompanying build logic in CMake. A wrapper is provided to build the main library and run unit tests.
Yoga's main implementation targets C++ 20 with accompanying build logic in CMake. A wrapper is provided to build the main library and run unit tests.
```sh
./unit_tests <Debug|Release>
@@ -14,8 +14,6 @@ While not required, this script will use [ninja](https://ninja-build.org/) if it
Yoga is additionally part of the [vcpkg](https://github.com/Microsoft/vcpkg/) collection of ports maintained by Microsoft and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Adding Tests
Many of Yoga's tests are automatically generated, using HTML fixtures describing node structure. These are rendered in Chrome to generate an expected layout result for the tree. New fixtures can be added to `gentest/fixtures`.
@@ -27,9 +25,10 @@ Many of Yoga's tests are automatically generated, using HTML fixtures describing
```
To generate new tests from added fixtures:
1. Run `bundle install` in the `gentest` directory to install dependencies of the test generator.
2. Run `ruby gentest.rb` in the `gentest` directory.
1. Ensure you have [yarn classic](https://classic.yarnpkg.com) installed.
2. Run `yarn install` to install dependencies for the test generator.
3. Run `yarn gentest` in the `yoga` directory.
## Debugging

View File

@@ -6,10 +6,10 @@
Pod::Spec.new do |spec|
spec.name = 'Yoga'
spec.version = '2.0.0'
spec.version = '3.0.4'
spec.license = { :type => 'MIT', :file => "LICENSE" }
spec.homepage = 'https://yogalayout.com/'
spec.documentation_url = 'https://yogalayout.com/docs'
spec.homepage = 'https://yogalayout.dev/'
spec.documentation_url = 'https://yogalayout.dev/docs'
spec.summary = 'An embeddable and performant flexbox layout engine with bindings for multiple languages'
@@ -24,18 +24,27 @@ Pod::Spec.new do |spec|
spec.module_name = 'yoga'
spec.requires_arc = false
spec.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES'
'DEFINES_MODULE' => 'YES',
'HEADER_SEARCH_PATHS' => '"$(PODS_TARGET_SRCROOT)"',
}
spec.compiler_flags = [
'-fno-omit-frame-pointer',
'-fexceptions',
'-Wall',
'-Werror',
'-Wextra',
'-std=c++14',
'-std=c++20',
'-fPIC'
]
spec.source_files = 'yoga/**/*.{h,cpp}'
spec.public_header_files = 'yoga/{Yoga,YGEnums,YGMacros,YGValue}.h'
spec.swift_version = '5.1'
spec.source_files = 'yoga/**/*.{h,cpp}'
spec.header_mappings_dir = 'yoga'
public_header_files = 'yoga/*.h'
spec.public_header_files = public_header_files
all_header_files = 'yoga/**/*.h'
spec.private_header_files = Dir.glob(all_header_files) - Dir.glob(public_header_files)
spec.preserve_paths = [all_header_files]
end

View File

@@ -1,34 +0,0 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
podspec = Pod::Spec.new do |spec|
spec.name = 'YogaKit'
spec.version = '2.0.0'
spec.license = { :type => 'MIT', :file => "LICENSE" }
spec.homepage = 'https://yogalayout.com/'
spec.documentation_url = 'https://yogalayout.com/docs'
spec.deprecated = true
spec.summary = 'YogaKit allows using the Yoga layout engine in combination with UIKit'
spec.authors = {'Meta Open Source' => 'opensource@meta.com'}
spec.source = {
:git => 'https://github.com/facebook/yoga.git',
:tag => "v#{spec.version.to_s}",
}
spec.platforms = { :ios => "13.4" }
spec.ios.frameworks = 'UIKit'
spec.module_name = 'YogaKit'
spec.dependency 'Yoga', "~> #{spec.version.to_s}"
spec.source_files = 'YogaKit/Source/*.{h,m,swift}'
spec.public_header_files = 'YogaKit/Source/{YGLayout,UIView+Yoga}.h'
spec.private_header_files = 'YogaKit/Source/YGLayout+Private.h'
spec.swift_version = '5.1'
end
# See https://github.com/facebook/yoga/pull/366
podspec.attributes_hash["readme"] = "YogaKit/README.md"
podspec

View File

@@ -1,21 +0,0 @@
# YogaKit
[![CocoaPods](https://img.shields.io/cocoapods/v/YogaKit.svg?style=flat)](https://cocoapods.org/pods/YogaKit)
[![Platform](https://img.shields.io/badge/platforms-iOS-orange.svg)](https://facebook.github.io/yoga/docs/api/yogakit/)
[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](https://facebook.github.io/yoga/docs/api/yogakit/)
## Installation
YogaKit is available to install via [CocoaPods](https://cocoapods.org/).
```
pod 'YogaKit', '~> 1.7'
```
## Getting Started
We have a sample project. To try it out, clone this repo and open `YogaKitSample.xcodeproj` in the [YogaKitSample](https://github.com/facebook/yoga/tree/main/YogaKit/YogaKitSample) directory.
## Contributing
We welcome all pull-requests! At Facebook we sync the open source version of `YogaKit` daily, so we're always testing the latest changes.
See the [CONTRIBUTING.md](https://github.com/facebook/yoga/blob/main/CONTRIBUTING.md) file for how to help out.

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "YGLayout.h"
NS_ASSUME_NONNULL_BEGIN
typedef void (^YGLayoutConfigurationBlock)(YGLayout* layout);
@interface UIView (Yoga)
/**
The YGLayout that is attached to this view. It is lazily created.
*/
@property(nonatomic, readonly, strong) YGLayout* yoga;
/**
Indicates whether or not Yoga is enabled
*/
@property(nonatomic, readonly, assign) BOOL isYogaEnabled;
/**
In ObjC land, every time you access `view.yoga.*` you are adding another
`objc_msgSend` to your code. If you plan on making multiple changes to
YGLayout, it's more performant to use this method, which uses a single
objc_msgSend call.
*/
- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block
NS_SWIFT_NAME(configureLayout(block:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,37 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <objc/runtime.h>
#import "UIView+Yoga.h"
#import "YGLayout+Private.h"
static const void* kYGYogaAssociatedKey = &kYGYogaAssociatedKey;
@implementation UIView (YogaKit)
- (YGLayout*)yoga {
YGLayout* yoga = objc_getAssociatedObject(self, kYGYogaAssociatedKey);
if (!yoga) {
yoga = [[YGLayout alloc] initWithView:self];
objc_setAssociatedObject(
self, kYGYogaAssociatedKey, yoga, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return yoga;
}
- (BOOL)isYogaEnabled {
return objc_getAssociatedObject(self, kYGYogaAssociatedKey) != nil;
}
- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block {
if (block != nil) {
block(self.yoga);
}
}
@end

View File

@@ -1,17 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <yoga/Yoga.h>
#import "YGLayout.h"
@interface YGLayout ()
@property(nonatomic, assign, readonly) YGNodeRef node;
- (instancetype)initWithView:(UIView*)view;
@end

View File

@@ -1,174 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <yoga/YGEnums.h>
#import <yoga/YGMacros.h>
#import <yoga/Yoga.h>
YG_EXTERN_C_BEGIN
extern YGValue YGPointValue(CGFloat value) NS_SWIFT_UNAVAILABLE(
"Use the swift Int and FloatingPoint extensions instead");
extern YGValue YGPercentValue(CGFloat value) NS_SWIFT_UNAVAILABLE(
"Use the swift Int and FloatingPoint extensions instead");
YG_EXTERN_C_END
typedef NS_OPTIONS(NSInteger, YGDimensionFlexibility) {
YGDimensionFlexibilityFlexibleWidth = 1 << 0,
YGDimensionFlexibilityFlexibleHeight = 1 << 1,
};
@interface YGLayout : NSObject
/**
Make default init unavailable, as it will not initialise YGNode which is
required for the setters and getters of YGLayout's properties to work properly.
*/
- (instancetype)init
__attribute__((unavailable("you are not meant to initialise YGLayout")));
/**
Make default init unavailable, as it will not initialise YGNode which is
required for the setters and getters of YGLayout's properties to work properly.
*/
+ (instancetype)new
__attribute__((unavailable("you are not meant to initialise YGLayout")));
/**
The property that decides if we should include this view when calculating
layout. Defaults to YES.
*/
@property(nonatomic, readwrite, assign, setter=setIncludedInLayout:)
BOOL isIncludedInLayout;
/**
The property that decides during layout/sizing whether or not styling
properties should be applied. Defaults to NO.
*/
@property(nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled;
@property(nonatomic, readwrite, assign) YGDirection direction;
@property(nonatomic, readwrite, assign) YGFlexDirection flexDirection;
@property(nonatomic, readwrite, assign) YGJustify justifyContent;
@property(nonatomic, readwrite, assign) YGAlign alignContent;
@property(nonatomic, readwrite, assign) YGAlign alignItems;
@property(nonatomic, readwrite, assign) YGAlign alignSelf;
@property(nonatomic, readwrite, assign) YGPositionType position;
@property(nonatomic, readwrite, assign) YGWrap flexWrap;
@property(nonatomic, readwrite, assign) YGOverflow overflow;
@property(nonatomic, readwrite, assign) YGDisplay display;
@property(nonatomic, readwrite, assign) CGFloat flex;
@property(nonatomic, readwrite, assign) CGFloat flexGrow;
@property(nonatomic, readwrite, assign) CGFloat flexShrink;
@property(nonatomic, readwrite, assign) YGValue flexBasis;
@property(nonatomic, readwrite, assign) YGValue left;
@property(nonatomic, readwrite, assign) YGValue top;
@property(nonatomic, readwrite, assign) YGValue right;
@property(nonatomic, readwrite, assign) YGValue bottom;
@property(nonatomic, readwrite, assign) YGValue start;
@property(nonatomic, readwrite, assign) YGValue end;
@property(nonatomic, readwrite, assign) YGValue marginLeft;
@property(nonatomic, readwrite, assign) YGValue marginTop;
@property(nonatomic, readwrite, assign) YGValue marginRight;
@property(nonatomic, readwrite, assign) YGValue marginBottom;
@property(nonatomic, readwrite, assign) YGValue marginStart;
@property(nonatomic, readwrite, assign) YGValue marginEnd;
@property(nonatomic, readwrite, assign) YGValue marginHorizontal;
@property(nonatomic, readwrite, assign) YGValue marginVertical;
@property(nonatomic, readwrite, assign) YGValue margin;
@property(nonatomic, readwrite, assign) YGValue paddingLeft;
@property(nonatomic, readwrite, assign) YGValue paddingTop;
@property(nonatomic, readwrite, assign) YGValue paddingRight;
@property(nonatomic, readwrite, assign) YGValue paddingBottom;
@property(nonatomic, readwrite, assign) YGValue paddingStart;
@property(nonatomic, readwrite, assign) YGValue paddingEnd;
@property(nonatomic, readwrite, assign) YGValue paddingHorizontal;
@property(nonatomic, readwrite, assign) YGValue paddingVertical;
@property(nonatomic, readwrite, assign) YGValue padding;
@property(nonatomic, readwrite, assign) CGFloat borderLeftWidth;
@property(nonatomic, readwrite, assign) CGFloat borderTopWidth;
@property(nonatomic, readwrite, assign) CGFloat borderRightWidth;
@property(nonatomic, readwrite, assign) CGFloat borderBottomWidth;
@property(nonatomic, readwrite, assign) CGFloat borderStartWidth;
@property(nonatomic, readwrite, assign) CGFloat borderEndWidth;
@property(nonatomic, readwrite, assign) CGFloat borderWidth;
@property(nonatomic, readwrite, assign) YGValue width;
@property(nonatomic, readwrite, assign) YGValue height;
@property(nonatomic, readwrite, assign) YGValue minWidth;
@property(nonatomic, readwrite, assign) YGValue minHeight;
@property(nonatomic, readwrite, assign) YGValue maxWidth;
@property(nonatomic, readwrite, assign) YGValue maxHeight;
// Yoga specific properties, not compatible with flexbox specification
@property(nonatomic, readwrite, assign) CGFloat aspectRatio;
/**
Get the resolved direction of this node. This won't be YGDirectionInherit
*/
@property(nonatomic, readonly, assign) YGDirection resolvedDirection;
/**
Perform a layout calculation and update the frames of the views in the
hierarchy with the results. If the origin is not preserved, the root view's
layout results will applied from {0,0}.
*/
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
NS_SWIFT_NAME(applyLayout(preservingOrigin:));
/**
Perform a layout calculation and update the frames of the views in the
hierarchy with the results. If the origin is not preserved, the root view's
layout results will applied from {0,0}.
*/
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
dimensionFlexibility:(YGDimensionFlexibility)dimensionFlexibility
NS_SWIFT_NAME(applyLayout(preservingOrigin:dimensionFlexibility:));
/**
Returns the size of the view if no constraints were given. This could
equivalent to calling [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
*/
@property(nonatomic, readonly, assign) CGSize intrinsicSize;
/**
Returns the size of the view based on provided constraints. Pass NaN for an
unconstrained dimension.
*/
- (CGSize)calculateLayoutWithSize:(CGSize)size
NS_SWIFT_NAME(calculateLayout(with:));
/**
Returns the number of children that are using Flexbox.
*/
@property(nonatomic, readonly, assign) NSUInteger numberOfChildren;
/**
Return a BOOL indiciating whether or not we this node contains any subviews
that are included in Yoga's layout.
*/
@property(nonatomic, readonly, assign) BOOL isLeaf;
/**
Return's a BOOL indicating if a view is dirty. When a node is dirty
it usually indicates that it will be remeasured on the next layout pass.
*/
@property(nonatomic, readonly, assign) BOOL isDirty;
/**
Mark that a view's layout needs to be recalculated. Only works for leaf views.
*/
- (void)markDirty;
@end

View File

@@ -1,511 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "UIView+Yoga.h"
#import "YGLayout+Private.h"
#define YG_PROPERTY(type, lowercased_name, capitalized_name) \
-(type)lowercased_name { \
return YGNodeStyleGet##capitalized_name(self.node); \
} \
\
-(void)set##capitalized_name : (type)lowercased_name { \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \
}
#define YG_VALUE_PROPERTY(lowercased_name, capitalized_name) \
-(YGValue)lowercased_name { \
return YGNodeStyleGet##capitalized_name(self.node); \
} \
\
-(void)set##capitalized_name : (YGValue)lowercased_name { \
switch (lowercased_name.unit) { \
case YGUnitUndefined: \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
break; \
case YGUnitPoint: \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
break; \
case YGUnitPercent: \
YGNodeStyleSet##capitalized_name##Percent( \
self.node, lowercased_name.value); \
break; \
default: \
NSAssert(NO, @"Not implemented"); \
} \
}
#define YG_AUTO_VALUE_PROPERTY(lowercased_name, capitalized_name) \
-(YGValue)lowercased_name { \
return YGNodeStyleGet##capitalized_name(self.node); \
} \
\
-(void)set##capitalized_name : (YGValue)lowercased_name { \
switch (lowercased_name.unit) { \
case YGUnitPoint: \
YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \
break; \
case YGUnitPercent: \
YGNodeStyleSet##capitalized_name##Percent( \
self.node, lowercased_name.value); \
break; \
case YGUnitAuto: \
YGNodeStyleSet##capitalized_name##Auto(self.node); \
break; \
default: \
NSAssert(NO, @"Not implemented"); \
} \
}
#define YG_EDGE_PROPERTY_GETTER( \
type, lowercased_name, capitalized_name, property, edge) \
-(type)lowercased_name { \
return YGNodeStyleGet##property(self.node, edge); \
}
#define YG_EDGE_PROPERTY_SETTER( \
lowercased_name, capitalized_name, property, edge) \
-(void)set##capitalized_name : (CGFloat)lowercased_name { \
YGNodeStyleSet##property(self.node, edge, lowercased_name); \
}
#define YG_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \
YG_EDGE_PROPERTY_GETTER( \
CGFloat, lowercased_name, capitalized_name, property, edge) \
YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge)
#define YG_VALUE_EDGE_PROPERTY_SETTER( \
objc_lowercased_name, objc_capitalized_name, c_name, edge) \
-(void)set##objc_capitalized_name : (YGValue)objc_lowercased_name { \
switch (objc_lowercased_name.unit) { \
case YGUnitUndefined: \
YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \
break; \
case YGUnitPoint: \
YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \
break; \
case YGUnitPercent: \
YGNodeStyleSet##c_name##Percent( \
self.node, edge, objc_lowercased_name.value); \
break; \
default: \
NSAssert(NO, @"Not implemented"); \
} \
}
#define YG_VALUE_EDGE_PROPERTY( \
lowercased_name, capitalized_name, property, edge) \
YG_EDGE_PROPERTY_GETTER( \
YGValue, lowercased_name, capitalized_name, property, edge) \
YG_VALUE_EDGE_PROPERTY_SETTER( \
lowercased_name, capitalized_name, property, edge)
#define YG_VALUE_EDGES_PROPERTIES(lowercased_name, capitalized_name) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Left, \
capitalized_name##Left, \
capitalized_name, \
YGEdgeLeft) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Top, \
capitalized_name##Top, \
capitalized_name, \
YGEdgeTop) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Right, \
capitalized_name##Right, \
capitalized_name, \
YGEdgeRight) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Bottom, \
capitalized_name##Bottom, \
capitalized_name, \
YGEdgeBottom) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Start, \
capitalized_name##Start, \
capitalized_name, \
YGEdgeStart) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##End, \
capitalized_name##End, \
capitalized_name, \
YGEdgeEnd) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Horizontal, \
capitalized_name##Horizontal, \
capitalized_name, \
YGEdgeHorizontal) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name##Vertical, \
capitalized_name##Vertical, \
capitalized_name, \
YGEdgeVertical) \
YG_VALUE_EDGE_PROPERTY( \
lowercased_name, capitalized_name, capitalized_name, YGEdgeAll)
YGValue YGPointValue(CGFloat value) {
return (YGValue){.value = value, .unit = YGUnitPoint};
}
YGValue YGPercentValue(CGFloat value) {
return (YGValue){.value = value, .unit = YGUnitPercent};
}
static YGConfigRef globalConfig;
@interface YGLayout ()
@property(nonatomic, weak, readonly) UIView* view;
@property(nonatomic, assign, readonly) BOOL isUIView;
@end
@implementation YGLayout
@synthesize isEnabled = _isEnabled;
@synthesize isIncludedInLayout = _isIncludedInLayout;
@synthesize node = _node;
+ (void)initialize {
globalConfig = YGConfigNew();
YGConfigSetExperimentalFeatureEnabled(
globalConfig, YGExperimentalFeatureWebFlexBasis, true);
YGConfigSetErrata(globalConfig, YGErrataClassic);
YGConfigSetPointScaleFactor(globalConfig, [UIScreen mainScreen].scale);
}
- (instancetype)initWithView:(UIView*)view {
if (self = [super init]) {
_view = view;
_node = YGNodeNewWithConfig(globalConfig);
YGNodeSetContext(_node, (__bridge void*)view);
_isEnabled = NO;
_isIncludedInLayout = YES;
_isUIView = [view isMemberOfClass:[UIView class]];
}
return self;
}
- (void)dealloc {
YGNodeFree(self.node);
}
- (BOOL)isDirty {
return YGNodeIsDirty(self.node);
}
- (void)markDirty {
if (self.isDirty || !self.isLeaf) {
return;
}
// Yoga is not happy if we try to mark a node as "dirty" before we have set
// the measure function. Since we already know that this is a leaf,
// this *should* be fine. Forgive me Hack Gods.
const YGNodeRef node = self.node;
if (!YGNodeHasMeasureFunc(node)) {
YGNodeSetMeasureFunc(node, YGMeasureView);
}
YGNodeMarkDirty(node);
}
- (NSUInteger)numberOfChildren {
return YGNodeGetChildCount(self.node);
}
- (BOOL)isLeaf {
NSAssert(
[NSThread isMainThread],
@"This method must be called on the main thread.");
if (self.isEnabled) {
for (UIView* subview in self.view.subviews) {
YGLayout* const yoga = subview.yoga;
if (yoga.isEnabled && yoga.isIncludedInLayout) {
return NO;
}
}
}
return YES;
}
#pragma mark - Style
- (YGPositionType)position {
return YGNodeStyleGetPositionType(self.node);
}
- (void)setPosition:(YGPositionType)position {
YGNodeStyleSetPositionType(self.node, position);
}
YG_PROPERTY(YGDirection, direction, Direction)
YG_PROPERTY(YGFlexDirection, flexDirection, FlexDirection)
YG_PROPERTY(YGJustify, justifyContent, JustifyContent)
YG_PROPERTY(YGAlign, alignContent, AlignContent)
YG_PROPERTY(YGAlign, alignItems, AlignItems)
YG_PROPERTY(YGAlign, alignSelf, AlignSelf)
YG_PROPERTY(YGWrap, flexWrap, FlexWrap)
YG_PROPERTY(YGOverflow, overflow, Overflow)
YG_PROPERTY(YGDisplay, display, Display)
YG_PROPERTY(CGFloat, flex, Flex)
YG_PROPERTY(CGFloat, flexGrow, FlexGrow)
YG_PROPERTY(CGFloat, flexShrink, FlexShrink)
YG_AUTO_VALUE_PROPERTY(flexBasis, FlexBasis)
YG_VALUE_EDGE_PROPERTY(left, Left, Position, YGEdgeLeft)
YG_VALUE_EDGE_PROPERTY(top, Top, Position, YGEdgeTop)
YG_VALUE_EDGE_PROPERTY(right, Right, Position, YGEdgeRight)
YG_VALUE_EDGE_PROPERTY(bottom, Bottom, Position, YGEdgeBottom)
YG_VALUE_EDGE_PROPERTY(start, Start, Position, YGEdgeStart)
YG_VALUE_EDGE_PROPERTY(end, End, Position, YGEdgeEnd)
YG_VALUE_EDGES_PROPERTIES(margin, Margin)
YG_VALUE_EDGES_PROPERTIES(padding, Padding)
YG_EDGE_PROPERTY(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft)
YG_EDGE_PROPERTY(borderTopWidth, BorderTopWidth, Border, YGEdgeTop)
YG_EDGE_PROPERTY(borderRightWidth, BorderRightWidth, Border, YGEdgeRight)
YG_EDGE_PROPERTY(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom)
YG_EDGE_PROPERTY(borderStartWidth, BorderStartWidth, Border, YGEdgeStart)
YG_EDGE_PROPERTY(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd)
YG_EDGE_PROPERTY(borderWidth, BorderWidth, Border, YGEdgeAll)
YG_AUTO_VALUE_PROPERTY(width, Width)
YG_AUTO_VALUE_PROPERTY(height, Height)
YG_VALUE_PROPERTY(minWidth, MinWidth)
YG_VALUE_PROPERTY(minHeight, MinHeight)
YG_VALUE_PROPERTY(maxWidth, MaxWidth)
YG_VALUE_PROPERTY(maxHeight, MaxHeight)
YG_PROPERTY(CGFloat, aspectRatio, AspectRatio)
YG_EDGE_PROPERTY(columnGap, ColumnGap, Gap, YGGutterColumn)
YG_EDGE_PROPERTY(rowGap, RowGap, Gap, YGGutterRow)
YG_EDGE_PROPERTY(gap, Gap, Gap, YGGutterAll)
#pragma mark - Layout and Sizing
- (YGDirection)resolvedDirection {
return YGNodeLayoutGetDirection(self.node);
}
- (void)applyLayout {
[self calculateLayoutWithSize:self.view.bounds.size];
YGApplyLayoutToViewHierarchy(self.view, NO);
}
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin {
[self calculateLayoutWithSize:self.view.bounds.size];
YGApplyLayoutToViewHierarchy(self.view, preserveOrigin);
}
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
dimensionFlexibility:
(YGDimensionFlexibility)dimensionFlexibility {
CGSize size = self.view.bounds.size;
if (dimensionFlexibility & YGDimensionFlexibilityFlexibleWidth) {
size.width = YGUndefined;
}
if (dimensionFlexibility & YGDimensionFlexibilityFlexibleHeight) {
size.height = YGUndefined;
}
[self calculateLayoutWithSize:size];
YGApplyLayoutToViewHierarchy(self.view, preserveOrigin);
}
- (CGSize)intrinsicSize {
const CGSize constrainedSize = {
.width = YGUndefined,
.height = YGUndefined,
};
return [self calculateLayoutWithSize:constrainedSize];
}
- (CGSize)calculateLayoutWithSize:(CGSize)size {
NSAssert([NSThread isMainThread], @"Yoga calculation must be done on main.");
NSAssert(self.isEnabled, @"Yoga is not enabled for this view.");
YGAttachNodesFromViewHierachy(self.view);
const YGNodeRef node = self.node;
YGNodeCalculateLayout(
node, size.width, size.height, YGNodeStyleGetDirection(node));
return (CGSize){
.width = YGNodeLayoutGetWidth(node),
.height = YGNodeLayoutGetHeight(node),
};
}
#pragma mark - Private
static YGSize YGMeasureView(
YGNodeRef node,
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode) {
const CGFloat constrainedWidth =
(widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width;
const CGFloat constrainedHeight =
(heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : height;
UIView* view = (__bridge UIView*)YGNodeGetContext(node);
CGSize sizeThatFits = CGSizeZero;
// The default implementation of sizeThatFits: returns the existing size of
// the view. That means that if we want to layout an empty UIView, which
// already has got a frame set, its measured size should be CGSizeZero, but
// UIKit returns the existing size.
//
// See https://github.com/facebook/yoga/issues/606 for more information.
if (!view.yoga.isUIView || [view.subviews count] > 0) {
sizeThatFits = [view sizeThatFits:(CGSize){
.width = constrainedWidth,
.height = constrainedHeight,
}];
}
return (YGSize){
.width = YGSanitizeMeasurement(
constrainedWidth, sizeThatFits.width, widthMode),
.height = YGSanitizeMeasurement(
constrainedHeight, sizeThatFits.height, heightMode),
};
}
static CGFloat YGSanitizeMeasurement(
CGFloat constrainedSize,
CGFloat measuredSize,
YGMeasureMode measureMode) {
CGFloat result;
if (measureMode == YGMeasureModeExactly) {
result = constrainedSize;
} else if (measureMode == YGMeasureModeAtMost) {
result = MIN(constrainedSize, measuredSize);
} else {
result = measuredSize;
}
return result;
}
static BOOL YGNodeHasExactSameChildren(
const YGNodeRef node,
NSArray<UIView*>* subviews) {
if (YGNodeGetChildCount(node) != subviews.count) {
return NO;
}
for (int i = 0; i < subviews.count; i++) {
if (YGNodeGetChild(node, i) != subviews[i].yoga.node) {
return NO;
}
}
return YES;
}
static void YGAttachNodesFromViewHierachy(UIView* const view) {
YGLayout* const yoga = view.yoga;
const YGNodeRef node = yoga.node;
// Only leaf nodes should have a measure function
if (yoga.isLeaf) {
YGRemoveAllChildren(node);
YGNodeSetMeasureFunc(node, YGMeasureView);
} else {
YGNodeSetMeasureFunc(node, NULL);
NSMutableArray<UIView*>* subviewsToInclude =
[[NSMutableArray alloc] initWithCapacity:view.subviews.count];
for (UIView* subview in view.subviews) {
if (subview.yoga.isEnabled && subview.yoga.isIncludedInLayout) {
[subviewsToInclude addObject:subview];
}
}
if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) {
YGRemoveAllChildren(node);
for (int i = 0; i < subviewsToInclude.count; i++) {
YGNodeInsertChild(node, subviewsToInclude[i].yoga.node, i);
}
}
for (UIView* const subview in subviewsToInclude) {
YGAttachNodesFromViewHierachy(subview);
}
}
}
static void YGRemoveAllChildren(const YGNodeRef node) {
if (node == NULL) {
return;
}
YGNodeRemoveAllChildren(node);
}
static CGFloat YGRoundPixelValue(CGFloat value) {
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^() {
scale = [UIScreen mainScreen].scale;
});
return roundf(value * scale) / scale;
}
static void YGApplyLayoutToViewHierarchy(UIView* view, BOOL preserveOrigin) {
NSCAssert(
[NSThread isMainThread],
@"Framesetting should only be done on the main thread.");
const YGLayout* yoga = view.yoga;
if (!yoga.isIncludedInLayout) {
return;
}
YGNodeRef node = yoga.node;
const CGPoint topLeft = {
YGNodeLayoutGetLeft(node),
YGNodeLayoutGetTop(node),
};
const CGPoint bottomRight = {
topLeft.x + YGNodeLayoutGetWidth(node),
topLeft.y + YGNodeLayoutGetHeight(node),
};
const CGPoint origin = preserveOrigin ? view.frame.origin : CGPointZero;
view.frame = (CGRect){
.origin =
{
.x = YGRoundPixelValue(topLeft.x + origin.x),
.y = YGRoundPixelValue(topLeft.y + origin.y),
},
.size =
{
.width = YGRoundPixelValue(bottomRight.x) -
YGRoundPixelValue(topLeft.x),
.height = YGRoundPixelValue(bottomRight.y) -
YGRoundPixelValue(topLeft.y),
},
};
if (!yoga.isLeaf) {
for (NSUInteger i = 0; i < view.subviews.count; i++) {
YGApplyLayoutToViewHierarchy(view.subviews[i], NO);
}
}
}
@end

View File

@@ -1,45 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import yoga;
postfix operator %
extension Int {
public static postfix func %(value: Int) -> YGValue {
return YGValue(value: Float(value), unit: .percent)
}
}
extension Float {
public static postfix func %(value: Float) -> YGValue {
return YGValue(value: value, unit: .percent)
}
}
extension CGFloat {
public static postfix func %(value: CGFloat) -> YGValue {
return YGValue(value: Float(value), unit: .percent)
}
}
extension YGValue : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
public init(integerLiteral value: Int) {
self = YGValue(value: Float(value), unit: .point)
}
public init(floatLiteral value: Float) {
self = YGValue(value: value, unit: .point)
}
public init(_ value: Float) {
self = YGValue(value: value, unit: .point)
}
public init(_ value: CGFloat) {
self = YGValue(value: Float(value), unit: .point)
}
}

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.facebook.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -1,753 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <XCTest/XCTest.h>
#import <YogaKit/UIView+Yoga.h>
#import <YogaKit/YGLayout+Private.h>
#import <yoga/Yoga.h>
@interface YogaKitTests : XCTestCase
@end
@implementation YogaKitTests
- (void)testConfigureLayoutIsNoOpWithNilBlock {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
id block = nil;
XCTAssertNoThrow([view configureLayoutWithBlock:block]);
}
- (void)testConfigureLayoutBlockWorksWithValidBlock {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
[view configureLayoutWithBlock:^(YGLayout* layout) {
XCTAssertNotNil(layout);
layout.isEnabled = YES;
layout.width = YGPointValue(25);
}];
XCTAssertTrue(view.yoga.isEnabled);
XCTAssertEqual(view.yoga.width.value, 25);
}
- (void)testNodesAreDeallocedWithSingleView {
__weak YGLayout* layoutRef = nil;
@autoreleasepool {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.flexBasis = YGPointValue(1);
layoutRef = view.yoga;
XCTAssertNotNil(layoutRef);
view = nil;
}
XCTAssertNil(layoutRef);
}
- (void)testNodesAreDeallocedCascade {
__weak YGLayout* topLayout = nil;
__weak YGLayout* subviewLayout = nil;
@autoreleasepool {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
topLayout = view.yoga;
topLayout.flexBasis = YGPointValue(1);
UIView* subview = [[UIView alloc] initWithFrame:CGRectZero];
subviewLayout = subview.yoga;
subviewLayout.flexBasis = YGPointValue(1);
view = nil;
}
XCTAssertNil(topLayout);
XCTAssertNil(subviewLayout);
}
- (void)testIsEnabled {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertFalse(view.yoga.isEnabled);
view.yoga.isEnabled = YES;
XCTAssertTrue(view.yoga.isEnabled);
view.yoga.isEnabled = NO;
XCTAssertFalse(view.yoga.isEnabled);
}
- (void)testSizeThatFitsAsserts {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
dispatch_sync(
dispatch_queue_create("com.facebook.Yoga.testing", DISPATCH_QUEUE_SERIAL),
^(void) {
XCTAssertThrows(view.yoga.intrinsicSize);
});
}
- (void)testSizeThatFitsSmoke {
UIView* container = [[UIView alloc] initWithFrame:CGRectZero];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
container.yoga.alignItems = YGAlignFlexStart;
UILabel* longTextLabel = [[UILabel alloc] initWithFrame:CGRectZero];
longTextLabel.text =
@"This is a very very very very very very very very long piece of text.";
longTextLabel.lineBreakMode = NSLineBreakByTruncatingTail;
longTextLabel.numberOfLines = 1;
longTextLabel.yoga.isEnabled = YES;
longTextLabel.yoga.flexShrink = 1;
[container addSubview:longTextLabel];
UIView* textBadgeView = [[UIView alloc] initWithFrame:CGRectZero];
textBadgeView.yoga.isEnabled = YES;
textBadgeView.yoga.margin = YGPointValue(0);
textBadgeView.yoga.width = YGPointValue(10);
textBadgeView.yoga.height = YGPointValue(10);
[container addSubview:textBadgeView];
const CGSize textBadgeViewSize = textBadgeView.yoga.intrinsicSize;
XCTAssertEqual(textBadgeViewSize.height, 10);
XCTAssertEqual(textBadgeViewSize.width, 10);
const CGSize containerSize = container.yoga.intrinsicSize;
const CGSize longTextLabelSize = longTextLabel.yoga.intrinsicSize;
XCTAssertEqual(longTextLabelSize.height, containerSize.height);
XCTAssertEqual(
longTextLabelSize.width + textBadgeView.yoga.intrinsicSize.width,
containerSize.width);
}
- (void)testSizeThatFitsEmptyView {
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
view.yoga.isEnabled = YES;
const CGSize viewSize = view.yoga.intrinsicSize;
XCTAssertEqual(viewSize.height, 0);
XCTAssertEqual(viewSize.width, 0);
}
- (void)testPreservingOrigin {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 75)];
container.yoga.isEnabled = YES;
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.isEnabled = YES;
view.yoga.flexBasis = YGPointValue(0);
view.yoga.flexGrow = 1;
[container addSubview:view];
UIView* view2 = [[UIView alloc] initWithFrame:CGRectZero];
view2.yoga.isEnabled = YES;
view2.yoga.marginTop = YGPointValue(25);
view2.yoga.flexBasis = YGPointValue(0);
view2.yoga.flexGrow = 1;
[container addSubview:view2];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(50, view2.frame.origin.y);
[view2.yoga applyLayoutPreservingOrigin:NO];
XCTAssertEqual(25, view2.frame.origin.y);
}
- (void)testContainerWithFlexibleWidthGetsCorrectlySized {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
container.yoga.isEnabled = YES;
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view.yoga.isEnabled = YES;
view.yoga.width = YGPointValue(100);
view.yoga.height = YGPointValue(100);
[container addSubview:view];
[container.yoga
applyLayoutPreservingOrigin:YES
dimensionFlexibility:YGDimensionFlexibilityFlexibleWidth];
XCTAssertEqual(100, container.frame.size.width);
XCTAssertEqual(200, container.frame.size.height);
}
- (void)testContainerWithFlexibleHeightGetsCorrectlySized {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
container.yoga.isEnabled = YES;
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view.yoga.isEnabled = YES;
view.yoga.width = YGPointValue(100);
view.yoga.height = YGPointValue(100);
[container addSubview:view];
[container.yoga
applyLayoutPreservingOrigin:YES
dimensionFlexibility:YGDimensionFlexibilityFlexibleHeight];
XCTAssertEqual(200, container.frame.size.width);
XCTAssertEqual(100, container.frame.size.height);
}
- (void)testContainerWithFlexibleWidthAndHeightGetsCorrectlySized {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
container.yoga.isEnabled = YES;
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
view.yoga.isEnabled = YES;
view.yoga.width = YGPointValue(100);
view.yoga.height = YGPointValue(100);
[container addSubview:view];
[container.yoga
applyLayoutPreservingOrigin:YES
dimensionFlexibility:YGDimensionFlexibilityFlexibleWidth |
YGDimensionFlexibilityFlexibleHeight];
XCTAssertEqual(100, container.frame.size.width);
XCTAssertEqual(100, container.frame.size.height);
}
- (void)testMarkingDirtyOnlyWorksOnLeafNodes {
UIView* container = [[UIView alloc] initWithFrame:CGRectZero];
container.yoga.isEnabled = YES;
UIView* subview = [[UIView alloc] initWithFrame:CGRectZero];
subview.yoga.isEnabled = YES;
[container addSubview:subview];
XCTAssertFalse(container.yoga.isDirty);
[container.yoga markDirty];
XCTAssertFalse(container.yoga.isDirty);
XCTAssertFalse(subview.yoga.isDirty);
[subview.yoga markDirty];
XCTAssertTrue(subview.yoga.isDirty);
}
- (void)testThatMarkingLeafsAsDirtyWillTriggerASizeRecalculation {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 50)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
container.yoga.alignItems = YGAlignFlexStart;
UILabel* view = [[UILabel alloc] initWithFrame:CGRectZero];
view.text = @"This is a short text.";
view.numberOfLines = 1;
view.yoga.isEnabled = YES;
[container addSubview:view];
[container.yoga applyLayoutPreservingOrigin:YES];
CGSize const viewSizeAfterFirstPass = view.frame.size;
view.text = @"This is a slightly longer text.";
XCTAssertTrue(CGSizeEqualToSize(view.frame.size, viewSizeAfterFirstPass));
[view.yoga markDirty];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertFalse(CGSizeEqualToSize(view.frame.size, viewSizeAfterFirstPass));
}
- (void)testFrameAndOriginPlacement {
// https://www.internalfb.com/intern/test/562950017690956
XCTSkip(
@"TODO: this test fails with actual dimensions off by 1px from expected");
const CGSize containerSize = CGSizeMake(320, 50);
UIView* container = [[UIView alloc]
initWithFrame:CGRectMake(
0, 0, containerSize.width, containerSize.height)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.flexGrow = 1;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.flexGrow = 1;
[container addSubview:subview2];
UIView* subview3 = [[UIView alloc] initWithFrame:CGRectZero];
subview3.yoga.isEnabled = YES;
subview3.yoga.flexGrow = 1;
[container addSubview:subview3];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqualWithAccuracy(
subview2.frame.origin.x, CGRectGetMaxX(subview1.frame), FLT_EPSILON);
XCTAssertEqualWithAccuracy(
subview3.frame.origin.x, CGRectGetMaxX(subview2.frame), FLT_EPSILON);
CGFloat totalWidth = 0;
for (UIView* view in container.subviews) {
totalWidth += view.bounds.size.width;
}
XCTAssertEqual(
containerSize.width,
totalWidth,
@"The container's width is %.6f, the subviews take up %.6f",
containerSize.width,
totalWidth);
}
- (void)testThatLayoutIsCorrectWhenWeSwapViewOrder {
const CGSize containerSize = CGSizeMake(300, 50);
UIView* container = [[UIView alloc]
initWithFrame:CGRectMake(
0, 0, containerSize.width, containerSize.height)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.flexGrow = 1;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.flexGrow = 1;
[container addSubview:subview2];
UIView* subview3 = [[UIView alloc] initWithFrame:CGRectZero];
subview3.yoga.isEnabled = YES;
subview3.yoga.flexGrow = 1;
[container addSubview:subview3];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(0, 0, 100, 50)));
XCTAssertTrue(CGRectEqualToRect(subview2.frame, CGRectMake(100, 0, 100, 50)));
XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(200, 0, 100, 50)));
[container exchangeSubviewAtIndex:2 withSubviewAtIndex:0];
subview2.yoga.isIncludedInLayout = NO;
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(0, 0, 150, 50)));
XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(150, 0, 150, 50)));
// this frame shouldn't have been modified since last time.
XCTAssertTrue(CGRectEqualToRect(subview2.frame, CGRectMake(100, 0, 100, 50)));
}
- (void)testThatWeRespectIncludeInLayoutFlag {
const CGSize containerSize = CGSizeMake(300, 50);
UIView* container = [[UIView alloc]
initWithFrame:CGRectMake(
0, 0, containerSize.width, containerSize.height)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.flexGrow = 1;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.flexGrow = 1;
[container addSubview:subview2];
UIView* subview3 = [[UIView alloc] initWithFrame:CGRectZero];
subview3.yoga.isEnabled = YES;
subview3.yoga.flexGrow = 1;
[container addSubview:subview3];
[container.yoga applyLayoutPreservingOrigin:YES];
for (UIView* subview in container.subviews) {
XCTAssertEqual(subview.bounds.size.width, 100);
}
subview3.yoga.isIncludedInLayout = NO;
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(subview1.bounds.size.width, 150);
XCTAssertEqual(subview2.bounds.size.width, 150);
// We don't set the frame to zero, so, it should be set to what it was
// previously at.
XCTAssertEqual(subview3.bounds.size.width, 100);
}
- (void)testThatNumberOfChildrenIsCorrectWhenWeIgnoreSubviews {
UIView* container = [[UIView alloc] initWithFrame:CGRectZero];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.isIncludedInLayout = NO;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.isIncludedInLayout = NO;
[container addSubview:subview2];
UIView* subview3 = [[UIView alloc] initWithFrame:CGRectZero];
subview3.yoga.isEnabled = YES;
subview3.yoga.isIncludedInLayout = YES;
[container addSubview:subview3];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(container.yoga.numberOfChildren, 1);
subview2.yoga.isIncludedInLayout = YES;
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(container.yoga.numberOfChildren, 2);
}
- (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionRow;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.flexGrow = 1;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.flexGrow = 1;
[container addSubview:subview2];
UIView* subview3 = [[UIView alloc] initWithFrame:CGRectZero];
subview3.yoga.isEnabled = YES;
subview3.yoga.flexGrow = 1;
subview3.yoga.isIncludedInLayout = NO;
[container addSubview:subview3];
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(subview1.bounds.size.width, 150);
XCTAssertEqual(subview2.bounds.size.width, 150);
XCTAssertEqual(subview3.bounds.size.width, 0);
subview3.yoga.isIncludedInLayout = YES;
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(subview1.bounds.size.width, 100);
XCTAssertEqual(subview2.bounds.size.width, 100);
XCTAssertEqual(subview3.bounds.size.width, 100);
}
- (void)testIsLeafFlag {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
XCTAssertTrue(view.yoga.isLeaf);
for (int i = 0; i < 10; i++) {
UIView* subview = [[UIView alloc] initWithFrame:CGRectZero];
[view addSubview:subview];
}
XCTAssertTrue(view.yoga.isLeaf);
view.yoga.isEnabled = YES;
view.yoga.width = YGPointValue(50);
XCTAssertTrue(view.yoga.isLeaf);
UIView* const subview = view.subviews[0];
subview.yoga.isEnabled = YES;
subview.yoga.width = YGPointValue(50);
XCTAssertFalse(view.yoga.isLeaf);
}
- (void)testThatWeCorrectlyAttachNestedViews {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
container.yoga.isEnabled = YES;
container.yoga.flexDirection = YGFlexDirectionColumn;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
subview1.yoga.width = YGPointValue(100);
subview1.yoga.flexGrow = 1;
subview1.yoga.flexDirection = YGFlexDirectionColumn;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
subview2.yoga.width = YGPointValue(150);
subview2.yoga.flexGrow = 1;
subview2.yoga.flexDirection = YGFlexDirectionColumn;
[container addSubview:subview2];
for (UIView* view in @[ subview1, subview2 ]) {
UIView* someView = [[UIView alloc] initWithFrame:CGRectZero];
someView.yoga.isEnabled = YES;
someView.yoga.flexGrow = 1;
[view addSubview:someView];
}
[container.yoga applyLayoutPreservingOrigin:YES];
// Add the same amount of new views, reapply layout.
for (UIView* view in @[ subview1, subview2 ]) {
UIView* someView = [[UIView alloc] initWithFrame:CGRectZero];
someView.yoga.isEnabled = YES;
someView.yoga.flexGrow = 1;
[view addSubview:someView];
}
[container.yoga applyLayoutPreservingOrigin:YES];
XCTAssertEqual(subview1.bounds.size.width, 100);
XCTAssertEqual(subview1.bounds.size.height, 25);
for (UIView* subview in subview1.subviews) {
const CGSize subviewSize = subview.bounds.size;
XCTAssertNotEqual(subviewSize.width, 0);
XCTAssertNotEqual(subviewSize.height, 0);
XCTAssertFalse(isnan(subviewSize.height));
XCTAssertFalse(isnan(subviewSize.width));
}
XCTAssertEqual(subview2.bounds.size.width, 150);
XCTAssertEqual(subview2.bounds.size.height, 25);
for (UIView* subview in subview2.subviews) {
const CGSize subviewSize = subview.bounds.size;
XCTAssertNotEqual(subviewSize.width, 0);
XCTAssertNotEqual(subviewSize.height, 0);
XCTAssertFalse(isnan(subviewSize.height));
XCTAssertFalse(isnan(subviewSize.width));
}
}
- (void)testThatANonLeafNodeCanBecomeALeafNode {
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
container.yoga.isEnabled = YES;
UIView* subview1 = [[UIView alloc] initWithFrame:CGRectZero];
subview1.yoga.isEnabled = YES;
[container addSubview:subview1];
UIView* subview2 = [[UIView alloc] initWithFrame:CGRectZero];
subview2.yoga.isEnabled = YES;
[subview1 addSubview:subview2];
[container.yoga applyLayoutPreservingOrigin:YES];
[subview2 removeFromSuperview];
[container.yoga applyLayoutPreservingOrigin:YES];
}
- (void)testPointPercent {
XCTAssertEqual(YGPointValue(1).value, 1);
XCTAssertEqual(YGPointValue(1).unit, YGUnitPoint);
XCTAssertEqual(YGPercentValue(2).value, 2);
XCTAssertEqual(YGPercentValue(2).unit, YGUnitPercent);
}
- (void)testPositionalPropertiesWork {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.left = YGPointValue(1);
XCTAssertEqual(view.yoga.left.value, 1);
XCTAssertEqual(view.yoga.left.unit, YGUnitPoint);
view.yoga.left = YGPercentValue(2);
XCTAssertEqual(view.yoga.left.value, 2);
XCTAssertEqual(view.yoga.left.unit, YGUnitPercent);
view.yoga.right = YGPointValue(3);
XCTAssertEqual(view.yoga.right.value, 3);
XCTAssertEqual(view.yoga.right.unit, YGUnitPoint);
view.yoga.right = YGPercentValue(4);
XCTAssertEqual(view.yoga.right.value, 4);
XCTAssertEqual(view.yoga.right.unit, YGUnitPercent);
view.yoga.top = YGPointValue(5);
XCTAssertEqual(view.yoga.top.value, 5);
XCTAssertEqual(view.yoga.top.unit, YGUnitPoint);
view.yoga.top = YGPercentValue(6);
XCTAssertEqual(view.yoga.top.value, 6);
XCTAssertEqual(view.yoga.top.unit, YGUnitPercent);
view.yoga.bottom = YGPointValue(7);
XCTAssertEqual(view.yoga.bottom.value, 7);
XCTAssertEqual(view.yoga.bottom.unit, YGUnitPoint);
view.yoga.bottom = YGPercentValue(8);
XCTAssertEqual(view.yoga.bottom.value, 8);
XCTAssertEqual(view.yoga.bottom.unit, YGUnitPercent);
view.yoga.start = YGPointValue(9);
XCTAssertEqual(view.yoga.start.value, 9);
XCTAssertEqual(view.yoga.start.unit, YGUnitPoint);
view.yoga.start = YGPercentValue(10);
XCTAssertEqual(view.yoga.start.value, 10);
XCTAssertEqual(view.yoga.start.unit, YGUnitPercent);
view.yoga.end = YGPointValue(11);
XCTAssertEqual(view.yoga.end.value, 11);
XCTAssertEqual(view.yoga.end.unit, YGUnitPoint);
view.yoga.end = YGPercentValue(12);
XCTAssertEqual(view.yoga.end.value, 12);
XCTAssertEqual(view.yoga.end.unit, YGUnitPercent);
}
- (void)testMarginPropertiesWork {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.margin = YGPointValue(1);
XCTAssertEqual(view.yoga.margin.value, 1);
XCTAssertEqual(view.yoga.margin.unit, YGUnitPoint);
view.yoga.margin = YGPercentValue(2);
XCTAssertEqual(view.yoga.margin.value, 2);
XCTAssertEqual(view.yoga.margin.unit, YGUnitPercent);
view.yoga.marginHorizontal = YGPointValue(3);
XCTAssertEqual(view.yoga.marginHorizontal.value, 3);
XCTAssertEqual(view.yoga.marginHorizontal.unit, YGUnitPoint);
view.yoga.marginHorizontal = YGPercentValue(4);
XCTAssertEqual(view.yoga.marginHorizontal.value, 4);
XCTAssertEqual(view.yoga.marginHorizontal.unit, YGUnitPercent);
view.yoga.marginVertical = YGPointValue(5);
XCTAssertEqual(view.yoga.marginVertical.value, 5);
XCTAssertEqual(view.yoga.marginVertical.unit, YGUnitPoint);
view.yoga.marginVertical = YGPercentValue(6);
XCTAssertEqual(view.yoga.marginVertical.value, 6);
XCTAssertEqual(view.yoga.marginVertical.unit, YGUnitPercent);
view.yoga.marginLeft = YGPointValue(7);
XCTAssertEqual(view.yoga.marginLeft.value, 7);
XCTAssertEqual(view.yoga.marginLeft.unit, YGUnitPoint);
view.yoga.marginLeft = YGPercentValue(8);
XCTAssertEqual(view.yoga.marginLeft.value, 8);
XCTAssertEqual(view.yoga.marginLeft.unit, YGUnitPercent);
view.yoga.marginRight = YGPointValue(9);
XCTAssertEqual(view.yoga.marginRight.value, 9);
XCTAssertEqual(view.yoga.marginRight.unit, YGUnitPoint);
view.yoga.marginRight = YGPercentValue(10);
XCTAssertEqual(view.yoga.marginRight.value, 10);
XCTAssertEqual(view.yoga.marginRight.unit, YGUnitPercent);
view.yoga.marginTop = YGPointValue(11);
XCTAssertEqual(view.yoga.marginTop.value, 11);
XCTAssertEqual(view.yoga.marginTop.unit, YGUnitPoint);
view.yoga.marginTop = YGPercentValue(12);
XCTAssertEqual(view.yoga.marginTop.value, 12);
XCTAssertEqual(view.yoga.marginTop.unit, YGUnitPercent);
view.yoga.marginBottom = YGPointValue(13);
XCTAssertEqual(view.yoga.marginBottom.value, 13);
XCTAssertEqual(view.yoga.marginBottom.unit, YGUnitPoint);
view.yoga.marginBottom = YGPercentValue(14);
XCTAssertEqual(view.yoga.marginBottom.value, 14);
XCTAssertEqual(view.yoga.marginBottom.unit, YGUnitPercent);
view.yoga.marginStart = YGPointValue(15);
XCTAssertEqual(view.yoga.marginStart.value, 15);
XCTAssertEqual(view.yoga.marginStart.unit, YGUnitPoint);
view.yoga.marginStart = YGPercentValue(16);
XCTAssertEqual(view.yoga.marginStart.value, 16);
XCTAssertEqual(view.yoga.marginStart.unit, YGUnitPercent);
view.yoga.marginEnd = YGPointValue(17);
XCTAssertEqual(view.yoga.marginEnd.value, 17);
XCTAssertEqual(view.yoga.marginEnd.unit, YGUnitPoint);
view.yoga.marginEnd = YGPercentValue(18);
XCTAssertEqual(view.yoga.marginEnd.value, 18);
XCTAssertEqual(view.yoga.marginEnd.unit, YGUnitPercent);
}
- (void)testPaddingPropertiesWork {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.padding = YGPointValue(1);
XCTAssertEqual(view.yoga.padding.value, 1);
XCTAssertEqual(view.yoga.padding.unit, YGUnitPoint);
view.yoga.padding = YGPercentValue(2);
XCTAssertEqual(view.yoga.padding.value, 2);
XCTAssertEqual(view.yoga.padding.unit, YGUnitPercent);
view.yoga.paddingHorizontal = YGPointValue(3);
XCTAssertEqual(view.yoga.paddingHorizontal.value, 3);
XCTAssertEqual(view.yoga.paddingHorizontal.unit, YGUnitPoint);
view.yoga.paddingHorizontal = YGPercentValue(4);
XCTAssertEqual(view.yoga.paddingHorizontal.value, 4);
XCTAssertEqual(view.yoga.paddingHorizontal.unit, YGUnitPercent);
view.yoga.paddingVertical = YGPointValue(5);
XCTAssertEqual(view.yoga.paddingVertical.value, 5);
XCTAssertEqual(view.yoga.paddingVertical.unit, YGUnitPoint);
view.yoga.paddingVertical = YGPercentValue(6);
XCTAssertEqual(view.yoga.paddingVertical.value, 6);
XCTAssertEqual(view.yoga.paddingVertical.unit, YGUnitPercent);
view.yoga.paddingLeft = YGPointValue(7);
XCTAssertEqual(view.yoga.paddingLeft.value, 7);
XCTAssertEqual(view.yoga.paddingLeft.unit, YGUnitPoint);
view.yoga.paddingLeft = YGPercentValue(8);
XCTAssertEqual(view.yoga.paddingLeft.value, 8);
XCTAssertEqual(view.yoga.paddingLeft.unit, YGUnitPercent);
view.yoga.paddingRight = YGPointValue(9);
XCTAssertEqual(view.yoga.paddingRight.value, 9);
XCTAssertEqual(view.yoga.paddingRight.unit, YGUnitPoint);
view.yoga.paddingRight = YGPercentValue(10);
XCTAssertEqual(view.yoga.paddingRight.value, 10);
XCTAssertEqual(view.yoga.paddingRight.unit, YGUnitPercent);
view.yoga.paddingTop = YGPointValue(11);
XCTAssertEqual(view.yoga.paddingTop.value, 11);
XCTAssertEqual(view.yoga.paddingTop.unit, YGUnitPoint);
view.yoga.paddingTop = YGPercentValue(12);
XCTAssertEqual(view.yoga.paddingTop.value, 12);
XCTAssertEqual(view.yoga.paddingTop.unit, YGUnitPercent);
view.yoga.paddingBottom = YGPointValue(13);
XCTAssertEqual(view.yoga.paddingBottom.value, 13);
XCTAssertEqual(view.yoga.paddingBottom.unit, YGUnitPoint);
view.yoga.paddingBottom = YGPercentValue(14);
XCTAssertEqual(view.yoga.paddingBottom.value, 14);
XCTAssertEqual(view.yoga.paddingBottom.unit, YGUnitPercent);
view.yoga.paddingStart = YGPointValue(15);
XCTAssertEqual(view.yoga.paddingStart.value, 15);
XCTAssertEqual(view.yoga.paddingStart.unit, YGUnitPoint);
view.yoga.paddingStart = YGPercentValue(16);
XCTAssertEqual(view.yoga.paddingStart.value, 16);
XCTAssertEqual(view.yoga.paddingStart.unit, YGUnitPercent);
view.yoga.paddingEnd = YGPointValue(17);
XCTAssertEqual(view.yoga.paddingEnd.value, 17);
XCTAssertEqual(view.yoga.paddingEnd.unit, YGUnitPoint);
view.yoga.paddingEnd = YGPercentValue(18);
XCTAssertEqual(view.yoga.paddingEnd.value, 18);
XCTAssertEqual(view.yoga.paddingEnd.unit, YGUnitPercent);
}
- (void)testBorderWidthPropertiesWork {
UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
view.yoga.borderWidth = 1;
XCTAssertEqual(view.yoga.borderWidth, 1);
view.yoga.borderLeftWidth = 2;
XCTAssertEqual(view.yoga.borderLeftWidth, 2);
view.yoga.borderRightWidth = 3;
XCTAssertEqual(view.yoga.borderRightWidth, 3);
view.yoga.borderTopWidth = 4;
XCTAssertEqual(view.yoga.borderTopWidth, 4);
view.yoga.borderBottomWidth = 5;
XCTAssertEqual(view.yoga.borderBottomWidth, 5);
view.yoga.borderStartWidth = 6;
XCTAssertEqual(view.yoga.borderStartWidth, 6);
view.yoga.borderEndWidth = 7;
XCTAssertEqual(view.yoga.borderEndWidth, 7);
}
@end

View File

@@ -1,17 +0,0 @@
use_frameworks!
platform :ios, "13.4"
target 'YogaKitSample' do
pod 'YogaKit', :path => '../../YogaKit.podspec'
pod 'Yoga', :path => '../../Yoga.podspec'
pod 'IGListKit', '~> 4.0.0'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.4'
end
end
end

View File

@@ -1,33 +0,0 @@
PODS:
- IGListDiffKit (4.0.0)
- IGListKit (4.0.0):
- IGListDiffKit (= 4.0.0)
- Yoga (2.0.0-beta.1)
- YogaKit (2.0.0-beta.1):
- Yoga (~> 2.0.0-beta.1)
DEPENDENCIES:
- IGListKit (~> 4.0.0)
- Yoga (from `../../Yoga.podspec`)
- YogaKit (from `../../YogaKit.podspec`)
SPEC REPOS:
trunk:
- IGListDiffKit
- IGListKit
EXTERNAL SOURCES:
Yoga:
:path: "../../Yoga.podspec"
YogaKit:
:path: "../../YogaKit.podspec"
SPEC CHECKSUMS:
IGListDiffKit: 665d6cf43ce726e676013db9c7d6c4294259b6b2
IGListKit: fd5a5d21935298f5849fa49d426843cff97b77c7
Yoga: 090c6851e548d085d6211b5466b57ba8f4013a90
YogaKit: 5ae1939cd7516fdf6c343abb4b4aa381c9911ccf
PODFILE CHECKSUM: e5d9841ef739884db00a29f2d529d16bf247a4ca
COCOAPODS: 1.12.1

View File

@@ -1,518 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
13687D531DF8748400E7C260 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13687D521DF8748400E7C260 /* Assets.xcassets */; };
13687D851DF87D1E00E7C260 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D841DF87D1E00E7C260 /* UIKit.framework */; };
13687D871DF87D2400E7C260 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13687D861DF87D2400E7C260 /* Foundation.framework */; };
15A7CB5995C9DAB1C8803834 /* Pods_YogaKitSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C80A931E90C7F3088CB86822 /* Pods_YogaKitSample.framework */; };
40BD9F461E477A09002790A9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BD9F451E477A09002790A9 /* AppDelegate.swift */; };
40BD9F4B1E47850C002790A9 /* BasicViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BD9F4A1E47850C002790A9 /* BasicViewController.swift */; };
40BD9F501E479079002790A9 /* SingleLabelCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BD9F4F1E479079002790A9 /* SingleLabelCollectionCell.swift */; };
40BD9F521E479173002790A9 /* LayoutInclusionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40BD9F511E479173002790A9 /* LayoutInclusionViewController.swift */; };
638A94481E1F06D100A726AD /* ExamplesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A94471E1F06D100A726AD /* ExamplesViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
638A94541E215CC800A726AD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 13687D3B1DF8748300E7C260 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13687D421DF8748300E7C260;
remoteInfo = YogaKitSample;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
13687D771DF878A000E7C260 /* yoga */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/yoga;
dstSubfolderSpec = 16;
files = (
);
name = yoga;
runOnlyForDeploymentPostprocessing = 0;
};
13687D7B1DF878CE00E7C260 /* YogaKit */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = include/YogaKit;
dstSubfolderSpec = 16;
files = (
);
name = YogaKit;
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
13687D431DF8748400E7C260 /* YogaKitSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YogaKitSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
13687D521DF8748400E7C260 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
13687D571DF8748400E7C260 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13687D841DF87D1E00E7C260 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
13687D861DF87D2400E7C260 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
1D2FF4D5FCA6A8C54A4074A3 /* Pods-YogaKitSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YogaKitSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-YogaKitSample/Pods-YogaKitSample.debug.xcconfig"; sourceTree = "<group>"; };
40BD9F451E477A09002790A9 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
40BD9F4A1E47850C002790A9 /* BasicViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BasicViewController.swift; path = ViewControllers/BasicViewController.swift; sourceTree = "<group>"; };
40BD9F4F1E479079002790A9 /* SingleLabelCollectionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SingleLabelCollectionCell.swift; path = Views/SingleLabelCollectionCell.swift; sourceTree = "<group>"; };
40BD9F511E479173002790A9 /* LayoutInclusionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LayoutInclusionViewController.swift; path = ViewControllers/LayoutInclusionViewController.swift; sourceTree = "<group>"; };
638A94471E1F06D100A726AD /* ExamplesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExamplesViewController.swift; sourceTree = "<group>"; };
638A944F1E215CC800A726AD /* YogaKitSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YogaKitSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
82F0896A88112E957EF37C7F /* Pods-YogaKitSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YogaKitSample.release.xcconfig"; path = "Pods/Target Support Files/Pods-YogaKitSample/Pods-YogaKitSample.release.xcconfig"; sourceTree = "<group>"; };
C80A931E90C7F3088CB86822 /* Pods_YogaKitSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YogaKitSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
13687D401DF8748300E7C260 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
13687D871DF87D2400E7C260 /* Foundation.framework in Frameworks */,
13687D851DF87D1E00E7C260 /* UIKit.framework in Frameworks */,
15A7CB5995C9DAB1C8803834 /* Pods_YogaKitSample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
638A944C1E215CC800A726AD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
13687D3A1DF8748300E7C260 = {
isa = PBXGroup;
children = (
13687D451DF8748400E7C260 /* YogaKitSample */,
13687D441DF8748400E7C260 /* Products */,
13687D831DF87D1E00E7C260 /* Frameworks */,
E1C759E3C8E84821213ECE8D /* Pods */,
);
sourceTree = "<group>";
};
13687D441DF8748400E7C260 /* Products */ = {
isa = PBXGroup;
children = (
13687D431DF8748400E7C260 /* YogaKitSample.app */,
638A944F1E215CC800A726AD /* YogaKitSampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
13687D451DF8748400E7C260 /* YogaKitSample */ = {
isa = PBXGroup;
children = (
40BD9F4E1E47902F002790A9 /* Views */,
40BD9F481E4784B3002790A9 /* ViewControllers */,
638A94471E1F06D100A726AD /* ExamplesViewController.swift */,
13687D521DF8748400E7C260 /* Assets.xcassets */,
13687D571DF8748400E7C260 /* Info.plist */,
40BD9F451E477A09002790A9 /* AppDelegate.swift */,
);
path = YogaKitSample;
sourceTree = "<group>";
};
13687D831DF87D1E00E7C260 /* Frameworks */ = {
isa = PBXGroup;
children = (
13687D861DF87D2400E7C260 /* Foundation.framework */,
13687D841DF87D1E00E7C260 /* UIKit.framework */,
C80A931E90C7F3088CB86822 /* Pods_YogaKitSample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
40BD9F481E4784B3002790A9 /* ViewControllers */ = {
isa = PBXGroup;
children = (
40BD9F4A1E47850C002790A9 /* BasicViewController.swift */,
40BD9F511E479173002790A9 /* LayoutInclusionViewController.swift */,
);
name = ViewControllers;
sourceTree = "<group>";
};
40BD9F4E1E47902F002790A9 /* Views */ = {
isa = PBXGroup;
children = (
40BD9F4F1E479079002790A9 /* SingleLabelCollectionCell.swift */,
);
name = Views;
sourceTree = "<group>";
};
E1C759E3C8E84821213ECE8D /* Pods */ = {
isa = PBXGroup;
children = (
1D2FF4D5FCA6A8C54A4074A3 /* Pods-YogaKitSample.debug.xcconfig */,
82F0896A88112E957EF37C7F /* Pods-YogaKitSample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
13687D421DF8748300E7C260 /* YogaKitSample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13687D5A1DF8748400E7C260 /* Build configuration list for PBXNativeTarget "YogaKitSample" */;
buildPhases = (
513B543F92B2E4F4D1EE1CE7 /* [CP] Check Pods Manifest.lock */,
13687D771DF878A000E7C260 /* yoga */,
13687D7B1DF878CE00E7C260 /* YogaKit */,
13687D3F1DF8748300E7C260 /* Sources */,
13687D401DF8748300E7C260 /* Frameworks */,
13687D411DF8748300E7C260 /* Resources */,
FA2FB9DD6471BDD3FBCE503B /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = YogaKitSample;
productName = YogaKitSample;
productReference = 13687D431DF8748400E7C260 /* YogaKitSample.app */;
productType = "com.apple.product-type.application";
};
638A944E1E215CC800A726AD /* YogaKitSampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 638A94561E215CC800A726AD /* Build configuration list for PBXNativeTarget "YogaKitSampleTests" */;
buildPhases = (
638A944B1E215CC800A726AD /* Sources */,
638A944C1E215CC800A726AD /* Frameworks */,
638A944D1E215CC800A726AD /* Resources */,
);
buildRules = (
);
dependencies = (
638A94551E215CC800A726AD /* PBXTargetDependency */,
);
name = YogaKitSampleTests;
productName = YogaKitSampleTests;
productReference = 638A944F1E215CC800A726AD /* YogaKitSampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
13687D3B1DF8748300E7C260 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = facebook;
TargetAttributes = {
13687D421DF8748300E7C260 = {
CreatedOnToolsVersion = 8.1;
LastSwiftMigration = 0820;
ProvisioningStyle = Automatic;
};
638A944E1E215CC800A726AD = {
CreatedOnToolsVersion = 8.2.1;
ProvisioningStyle = Automatic;
TestTargetID = 13687D421DF8748300E7C260;
};
};
};
buildConfigurationList = 13687D3E1DF8748300E7C260 /* Build configuration list for PBXProject "YogaKitSample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 13687D3A1DF8748300E7C260;
productRefGroup = 13687D441DF8748400E7C260 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
13687D421DF8748300E7C260 /* YogaKitSample */,
638A944E1E215CC800A726AD /* YogaKitSampleTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
13687D411DF8748300E7C260 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13687D531DF8748400E7C260 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
638A944D1E215CC800A726AD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
513B543F92B2E4F4D1EE1CE7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-YogaKitSample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FA2FB9DD6471BDD3FBCE503B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-YogaKitSample/Pods-YogaKitSample-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/IGListDiffKit/IGListDiffKit.framework",
"${BUILT_PRODUCTS_DIR}/IGListKit/IGListKit.framework",
"${BUILT_PRODUCTS_DIR}/Yoga/yoga.framework",
"${BUILT_PRODUCTS_DIR}/YogaKit/YogaKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IGListDiffKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IGListKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YogaKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-YogaKitSample/Pods-YogaKitSample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
13687D3F1DF8748300E7C260 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
40BD9F501E479079002790A9 /* SingleLabelCollectionCell.swift in Sources */,
40BD9F521E479173002790A9 /* LayoutInclusionViewController.swift in Sources */,
638A94481E1F06D100A726AD /* ExamplesViewController.swift in Sources */,
40BD9F4B1E47850C002790A9 /* BasicViewController.swift in Sources */,
40BD9F461E477A09002790A9 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
638A944B1E215CC800A726AD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
638A94551E215CC800A726AD /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13687D421DF8748300E7C260 /* YogaKitSample */;
targetProxy = 638A94541E215CC800A726AD /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
13687D581DF8748400E7C260 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGNING_ALLOWED = NO;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
13687D591DF8748400E7C260 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGNING_ALLOWED = NO;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
13687D5B1DF8748400E7C260 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1D2FF4D5FCA6A8C54A4074A3 /* Pods-YogaKitSample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = YogaKitSample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.1;
};
name = Debug;
};
13687D5C1DF8748400E7C260 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 82F0896A88112E957EF37C7F /* Pods-YogaKitSample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = YogaKitSample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_VERSION = 5.1;
};
name = Release;
};
638A94571E215CC800A726AD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = YogaKitSampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YogaKitSample.app/YogaKitSample";
};
name = Debug;
};
638A94581E215CC800A726AD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = YogaKitSampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.YogaKitSampleTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YogaKitSample.app/YogaKitSample";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
13687D3E1DF8748300E7C260 /* Build configuration list for PBXProject "YogaKitSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13687D581DF8748400E7C260 /* Debug */,
13687D591DF8748400E7C260 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13687D5A1DF8748400E7C260 /* Build configuration list for PBXNativeTarget "YogaKitSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13687D5B1DF8748400E7C260 /* Debug */,
13687D5C1DF8748400E7C260 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
638A94561E215CC800A726AD /* Build configuration list for PBXNativeTarget "YogaKitSampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
638A94571E215CC800A726AD /* Debug */,
638A94581E215CC800A726AD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 13687D3B1DF8748300E7C260 /* Project object */;
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:YogaKitSample.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13687D421DF8748300E7C260"
BuildableName = "YogaKitSample.app"
BlueprintName = "YogaKitSample"
ReferencedContainer = "container:YogaKitSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "638A944E1E215CC800A726AD"
BuildableName = "YogaKitSampleTests.xctest"
BlueprintName = "YogaKitSampleTests"
ReferencedContainer = "container:YogaKitSample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13687D421DF8748300E7C260"
BuildableName = "YogaKitSample.app"
BlueprintName = "YogaKitSample"
ReferencedContainer = "container:YogaKitSample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13687D421DF8748300E7C260"
BuildableName = "YogaKitSample.app"
BlueprintName = "YogaKitSample"
ReferencedContainer = "container:YogaKitSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13687D421DF8748300E7C260"
BuildableName = "YogaKitSample.app"
BlueprintName = "YogaKitSample"
ReferencedContainer = "container:YogaKitSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:YogaKitSample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -1,26 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import Foundation
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func applicationDidFinishLaunching(_ application: UIApplication) {
self.window = UIWindow(frame: UIScreen.main.bounds)
if let window = self.window {
let navigationController = UINavigationController(rootViewController: ExamplesViewController())
navigationController.navigationBar.isTranslucent = false
window.rootViewController = navigationController
window.backgroundColor = .white
window.makeKeyAndVisible()
}
}
}

View File

@@ -1,74 +0,0 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Yoga-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Yoga-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Yoga-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Yoga-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Yoga-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Yoga-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Yoga-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Yoga-57x57@1x.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Yoga-57x57@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Yoga-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Yoga-60x60@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import UIKit
import IGListKit
private final class ExampleModel {
let title: String
let controllerClass: UIViewController.Type
init(title: String, controllerClass: UIViewController.Type) {
self.title = title
self.controllerClass = controllerClass
}
}
extension ExampleModel: ListDiffable {
fileprivate func diffIdentifier() -> NSObjectProtocol {
return title as NSString
}
fileprivate func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
guard let otherObj = object as? ExampleModel else { return false }
return (title == otherObj.title) &&
(controllerClass == otherObj.controllerClass)
}
}
final class ExamplesViewController: UIViewController, ListAdapterDataSource, ListSingleSectionControllerDelegate {
private lazy var adapter: ListAdapter = {
return ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()
private lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = UIColor.systemBackground
return collectionView
}()
// Update this to array to create more examples.
private let models: [ExampleModel] = [ExampleModel(title: "Basic Layout", controllerClass: BasicViewController.self),
ExampleModel(title: "Exclude Views in Layout", controllerClass: LayoutInclusionViewController.self)]
//MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
title = "Examples"
view.addSubview(collectionView)
adapter.collectionView = collectionView
adapter.dataSource = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
//MARK: IGListAdapterDataSource
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
return models as [ListDiffable]
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
let sizeBlock: ListSingleSectionCellSizeBlock = { (model, context) in
return CGSize(width: (context?.containerSize.width)!, height: 75.0)
}
let configureBlock: ListSingleSectionCellConfigureBlock = { (model, cell) in
guard let m = model as? ExampleModel, let c = cell as? SingleLabelCollectionCell else {
return
}
c.label.text = m.title
}
let sectionController = ListSingleSectionController(cellClass: SingleLabelCollectionCell.self,
configureBlock: configureBlock,
sizeBlock: sizeBlock)
sectionController.selectionDelegate = self
return sectionController
}
func emptyView(for listAdapter: ListAdapter) -> UIView? { return nil }
//MARK: IGListSingleSectionControllerDelegate
func didSelect(_ sectionController: ListSingleSectionController, with object: Any) {
let section = adapter.section(for: sectionController)
let model = models[section]
let controller = model.controllerClass.init()
controller.title = model.title
self.navigationController?.pushViewController(controller, animated: true)
}
}

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string></string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -1,70 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import UIKit
import IGListKit
import YogaKit
struct DemoItem {
let name: String
root.backgroundColor = .red
root.yoga.isEnabled = true
root.yoga.width = YGValue(self.view.bounds.size.width)
root.yoga.height = YGValue(self.view.bounds.size.height)
root.yoga.alignItems = .center
}
final class SwiftViewController: UIViewController, IGListAdapterDataSource {
lazy var adapter: IGListAdapter = {
return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()
let collectionView = IGListCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
//MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
title = "YogaKit Examples"
view.addSubview(collectionView)
adapter.collectionView = collectionView
adapter.dataSource = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
//MARK: IGListAdapterDataSource
func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
return ["Dustin" as IGListDiffable, "Ryan" as IGListDiffable]
}
func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
let sizeBlock: IGListSingleSectionCellSizeBlock = { (model, context) in
return CGSize(width: (context?.containerSize.width)!, height: 100.0)
}
let configureBlock: IGListSingleSectionCellConfigureBlock = { (model, cell) in
guard let m = model as? String else {
return
}
cell.backgroundColor = (m == "Dustin") ? .blue : .red
}
return IGListSingleSectionController(cellClass: UICollectionViewCell.self,
configureBlock: configureBlock,
sizeBlock: sizeBlock)
}
func emptyView(for listAdapter: IGListAdapter) -> UIView? {
return nil
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ViewController.h"
#import <YogaKit/UIView+Yoga.h>
@implementation ViewController
- (void)viewDidLoad {
UIView* root = self.view;
root.backgroundColor = [UIColor redColor];
root.yoga.isEnabled = YES;
root.yoga.width = YGPointValue(self.view.bounds.size.width);
root.yoga.height = YGPointValue(self.view.bounds.size.height);
root.yoga.alignItems = YGAlignCenter;
root.yoga.justifyContent = YGJustifyCenter;
UIView* child1 = [UIView new];
child1.backgroundColor = [UIColor blueColor];
child1.yoga.isEnabled = YES;
child1.yoga.width = YGPointValue(100);
child1.yoga.height = YGPointValue(100);
UIView* child2 = [UIView new];
child2.backgroundColor = [UIColor greenColor];
child2.frame = (CGRect){
.size = {
.width = 200,
.height = 100,
}};
UIView* child3 = [UIView new];
child3.backgroundColor = [UIColor yellowColor];
child3.frame = (CGRect){
.size = {
.width = 100,
.height = 100,
}};
[child2 addSubview:child3];
[root addSubview:child1];
[root addSubview:child2];
[root.yoga applyLayoutPreservingOrigin:NO];
}
@end

View File

@@ -1,58 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import UIKit
import YogaKit
final class BasicViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let containerSize = self.view.bounds.size
view.backgroundColor = UIColor.systemBackground
view.configureLayout { (layout) in
layout.isEnabled = true
layout.width = YGValue(containerSize.width)
layout.height = YGValue(containerSize.height)
layout.alignItems = .center
layout.justifyContent = .center
}
let child1 = UIView()
child1.backgroundColor = .blue
child1.configureLayout { (layout) in
layout.isEnabled = true
layout.width = 100
layout.height = 10
layout.marginBottom = 25
}
view.addSubview(child1)
let child2 = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
child2.backgroundColor = .green
child2.configureLayout { (layout) in
layout.isEnabled = true
layout.alignSelf = .flexEnd
layout.width = 200
layout.height = 200
}
view.addSubview(child2)
let child3 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
child3.backgroundColor = .yellow
child3.configureLayout { (layout) in
layout.isEnabled = true
layout.alignSelf = .flexStart
layout.width = 100
layout.height = 100
}
view.addSubview(child3)
view.yoga.applyLayout(preservingOrigin: true)
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import UIKit
import YogaKit
final class LayoutInclusionViewController: UIViewController {
private let button: UIButton = UIButton(type: .system)
private let disappearingView: UIView = UIView(frame: .zero)
private let contentView: UIView = UIView(frame: .zero)
override func viewDidLoad() {
super.viewDidLoad()
let root = self.view!
root.backgroundColor = .white
root.configureLayout { (layout) in
layout.isEnabled = true
layout.flexDirection = .column
layout.justifyContent = .spaceAround
}
contentView.backgroundColor = .clear
contentView.layer.borderColor = UIColor.lightGray.cgColor
contentView.layer.borderWidth = 1.0
contentView.configureLayout { (layout) in
layout.isEnabled = true
layout.height = 300
layout.width = YGValue(self.view.bounds.size.width)
layout.flexDirection = .row
layout.justifyContent = .center
layout.paddingHorizontal = 25
}
self.view.addSubview(contentView)
let redView = UIView(frame: .zero)
redView.backgroundColor = .red
redView.configureLayout { (layout) in
layout.isEnabled = true
layout.flexGrow = 1
layout.flexShrink = 1
}
contentView.addSubview(redView)
disappearingView.backgroundColor = .blue
disappearingView.configureLayout { (layout) in
layout.isEnabled = true
layout.flexGrow = 1
}
contentView.addSubview(disappearingView)
button.setTitle("Add Blue View", for: .selected)
button.setTitle("Remove Blue View", for: .normal)
button.addTarget(self, action: #selector(buttonWasTapped), for: .touchUpInside)
button.configureLayout { (layout) in
layout.isEnabled = true
layout.height = 300
layout.width = 300
layout.alignSelf = .center
}
root.addSubview(button)
root.yoga.applyLayout(preservingOrigin: false)
}
// MARK - UIButton Action
@objc func buttonWasTapped() {
button.isSelected = !button.isSelected
button.isUserInteractionEnabled = false
disappearingView.yoga.isIncludedInLayout = !disappearingView.yoga.isIncludedInLayout
disappearingView.isHidden = !disappearingView.isHidden
contentView.yoga.applyLayout(preservingOrigin: true)
button.isUserInteractionEnabled = true
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import UIKit
import YogaKit
final class SingleLabelCollectionCell: UICollectionViewCell {
let label: UILabel = UILabel(frame: .zero)
override init(frame: CGRect) {
super.init(frame: frame)
contentView.configureLayout { (layout) in
layout.isEnabled = true
layout.flexDirection = .column
layout.justifyContent = .flexEnd
}
label.textAlignment = .center
label.numberOfLines = 1
label.yoga.isIncludedInLayout = false
contentView.addSubview(label)
let border = UIView(frame: .zero)
border.backgroundColor = .lightGray
border.configureLayout { (layout) in
layout.isEnabled = true
layout.height = 0.5
layout.marginHorizontal = 25
}
contentView.addSubview(border)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.yoga.applyLayout(preservingOrigin: false)
label.frame = contentView.bounds
}
}

View File

@@ -1,21 +0,0 @@
# YogaLayout [![Platform](https://img.shields.io/badge/platforms-Android-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Languages](https://img.shields.io/badge/languages-Java-orange.svg)](https://facebook.github.io/yoga/docs/api/android/) [![Download](https://img.shields.io/bintray/v/facebook/maven/com.facebook.yoga.android:yoga-layout.svg)](https://bintray.com/facebook/maven/com.facebook.yoga.android%3Ayoga-layout/_latestVersion)
## Installation
YogaLayout is available via jcenter:
implementation 'com.facebook.yoga.android:yoga-layout:1.16.0'
## Getting Started
Check out the docs [here](https://yogalayout.com/getting-started/standalone/).
We also have a sample project. To try it, clone the repo and run (with a device attached)
buck install -r android/sample
## Contributing
We welcome all pull-requests! At Facebook we sync the open source version of YogaKit daily, so we're always testing the latest changes.
See the CONTRIBUTING file for how to help out.

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
plugins {
id("com.android.library")
id("publish")
}
android {
namespace 'com.facebook.yoga.android'
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
ndkVersion rootProject.ndkVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
}
compileOptions {
targetCompatibility rootProject.targetCompatibilityVersion
sourceCompatibility rootProject.sourceCompatibilityVersion
}
publishing {
multipleVariants {
withSourcesJar()
withJavadocJar()
includeBuildTypeValues('debug', 'release')
}
}
}
dependencies {
api project(':yoga')
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application/>
</manifest>

View File

@@ -1,149 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeFactory;
/**
* Much like a {@link YogaLayout}, except this class does not render itself (the container) to the
* screen. As a result, <i>do not use this if you wish the container to have a background or
* foreground</i>. However, all of its children will still render as expected.
*
* <p>
* In practice, this class never added to the View tree, and all its children become children of its
* parent. As a result, all the layout (such as the traversal of the tree) is performed by Yoga
* (and so natively) increasing performance.
*/
public class VirtualYogaLayout extends ViewGroup {
final private List<View> mChildren = new LinkedList<>();
final private Map<View, YogaNode> mYogaNodes = new HashMap<>();
final private YogaNode mYogaNode = YogaNodeFactory.create();
public VirtualYogaLayout(Context context) {
super(context);
}
public VirtualYogaLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VirtualYogaLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(context, attrs);
YogaLayout.applyLayoutParams(lp, mYogaNode, this);
}
public YogaNode getYogaNode() {
return mYogaNode;
}
/**
* Called to add a view, creating a new yoga node for it and adding that yoga node to the parent.
* If the child is a {@link VirtualYogaLayout}, we simply transfer all its children to this one
* in a manner that maintains the tree, and add its root to the tree.
*
* @param child the View to add
* @param index the position at which to add it (ignored)
* @param params the layout parameters to apply
*/
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof VirtualYogaLayout) {
((VirtualYogaLayout) child).transferChildren(this);
final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode();
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
return;
}
YogaNode node = YogaNodeFactory.create();
YogaLayout.LayoutParams lp = new YogaLayout.LayoutParams(params);
YogaLayout.applyLayoutParams(lp, node, child);
node.setData(child);
node.setMeasureFunction(new YogaLayout.ViewMeasureFunction());
mYogaNode.addChildAt(node, mYogaNode.getChildCount());
addView(child, node);
}
/**
* Called to add a view with a corresponding node, but not to change the Yoga tree in any way.
*
* @param child the View to add
* @param node the corresponding yoga node
*/
public void addView(View child, YogaNode node) {
mChildren.add(child);
mYogaNodes.put(child, node);
}
/**
* Gives up children {@code View}s to the parent, maintaining the Yoga tree. This function calls
* {@link YogaLayout#addView(View, YogaNode)} or {@link VirtualYogaLayout#addView(View, YogaNode)}
* on the parent to add the {@code View} without generating new yoga nodes.
*
* @param parent the parent to pass children to (must be a YogaLayout or a VirtualYogaLayout)
*/
protected void transferChildren(ViewGroup parent) {
if (parent instanceof VirtualYogaLayout) {
for (View child : mChildren) {
((VirtualYogaLayout) parent).addView(child, mYogaNodes.get(child));
}
} else if (parent instanceof YogaLayout) {
for (View child : mChildren) {
((YogaLayout) parent).addView(child, mYogaNodes.get(child));
}
} else {
throw new RuntimeException("VirtualYogaLayout cannot transfer children to ViewGroup of type "
+parent.getClass().getCanonicalName()+". Must either be a VirtualYogaLayout or a " +
"YogaLayout.");
}
mChildren.clear();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
throw new RuntimeException("Attempting to layout a VirtualYogaLayout");
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new YogaLayout.LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new YogaLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new YogaLayout.LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof YogaLayout.LayoutParams;
}
}

View File

@@ -1,816 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import com.facebook.yoga.YogaAlign;
import com.facebook.yoga.YogaConstants;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaDisplay;
import com.facebook.yoga.YogaEdge;
import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaNodeFactory;
import com.facebook.yoga.YogaOverflow;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaWrap;
import java.util.HashMap;
import java.util.Map;
/**
* A {@code ViewGroup} based on the Yoga layout engine.
*
* <p>
* This class is designed to be as "plug and play" as possible. That is, you can use it in XML
* like this (note: to use {@code YogaLayout} you need to use the {@link YogaViewLayoutFactory}):
* <p>
* <pre>{@code
* <YogaLayout
* xmlns:android="http://schemas.android.com/apk/res/android"
* xmlns:yoga="http://schemas.android.com/apk/com.facebook.yoga.android"
* android:layout_width="match_owner"
* android:layout_height="match_owner"
* yoga:flex_direction="row"
* yoga:padding_all="10dp"
* >
* <TextView
* android:layout_width="match_owner"
* android:layout_height="match_owner"
* android:text="Hello, World!"
* yoga:flex="1"
* />
* </YogaLayout>
* }</pre>
*
* Under the hood, all views added to this {@code ViewGroup} are laid out using flexbox rules
* and the Yoga engine.
*/
public class YogaLayout extends ViewGroup {
private final Map<View, YogaNode> mYogaNodes;
private final YogaNode mYogaNode;
public YogaLayout(Context context) {
this(context, null, 0);
}
public YogaLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public YogaLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mYogaNode = YogaNodeFactory.create();
mYogaNodes = new HashMap<>();
mYogaNode.setData(this);
mYogaNode.setMeasureFunction(new ViewMeasureFunction());
LayoutParams layoutParams = null;
if (attrs != null) {
layoutParams = new LayoutParams(context, attrs);
} else {
layoutParams = (LayoutParams) generateDefaultLayoutParams();
}
applyLayoutParams(layoutParams, mYogaNode, this);
}
public YogaNode getYogaNode() {
return mYogaNode;
}
public YogaNode getYogaNodeForView(View view) {
return mYogaNodes.get(view);
}
/**
* Adds a child view with the specified layout parameters.
*
* In the typical View is added, this constructs a {@code YogaNode} for this child and applies all
* the {@code yoga:*} attributes. The Yoga node is added to the Yoga tree and the child is added
* to this ViewGroup.
*
* If the child is a {@link YogaLayout} itself, we do not construct a new Yoga node for that
* child, but use its root node instead.
*
* If the child is a {@link VirtualYogaLayout}, we also use its Yoga node, but we also instruct it
* to transfer all of its children to this {@link YogaLayout} while preserving the Yoga tree (so
* that the layout of its children is correct). The {@link VirtualYogaLayout} is then not added
* to the View hierarchy.
*
* <p><strong>Note:</strong> do not invoke this method from
* {@code #draw(android.graphics.Canvas)}, {@code onDraw(android.graphics.Canvas)},
* {@code #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
*
* @param child the child view to add
* @param index the position at which to add the child or -1 to add last
* @param params the layout parameters to set on the child
*/
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
// Internal nodes (which this is now) cannot have measure functions
mYogaNode.setMeasureFunction(null);
if (child instanceof VirtualYogaLayout) {
((VirtualYogaLayout) child).transferChildren(this);
final YogaNode childNode = ((VirtualYogaLayout) child).getYogaNode();
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
return;
}
super.addView(child, index, params);
// It is possible that addView is being called as part of a transferal of children, in which
// case we already know about the YogaNode and only need the Android View tree to be aware
// that we now own this child. If so, we don't need to do anything further
if (mYogaNodes.containsKey(child)) {
return;
}
YogaNode childNode;
if (child instanceof YogaLayout) {
childNode = ((YogaLayout) child).getYogaNode();
} else {
if(mYogaNodes.containsKey(child)) {
childNode = mYogaNodes.get(child);
} else {
childNode = YogaNodeFactory.create();
}
childNode.setData(child);
childNode.setMeasureFunction(new ViewMeasureFunction());
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
applyLayoutParams(lp, childNode, child);
mYogaNodes.put(child, childNode);
mYogaNode.addChildAt(childNode, mYogaNode.getChildCount());
}
/**
* Adds a view to this {@code ViewGroup} with an already given {@code YogaNode}. Use
* this function if you already have a Yoga node (and perhaps tree) associated with the view you
* are adding, that you would like to preserve.
*
* @param child The view to add
* @param node The Yoga node belonging to the view
*/
public void addView(View child, YogaNode node) {
mYogaNodes.put(child, node);
addView(child);
}
@Override
public void removeView(View view) {
removeViewFromYogaTree(view, false);
super.removeView(view);
}
@Override
public void removeViewAt(int index) {
removeViewFromYogaTree(getChildAt(index), false);
super.removeViewAt(index);
}
@Override
public void removeViewInLayout(View view) {
removeViewFromYogaTree(view, true);
super.removeViewInLayout(view);
}
@Override
public void removeViews(int start, int count) {
for (int i = start; i < start + count; i++) {
removeViewFromYogaTree(getChildAt(i), false);
}
super.removeViews(start, count);
}
@Override
public void removeViewsInLayout(int start, int count) {
for (int i = start; i < start + count; i++) {
removeViewFromYogaTree(getChildAt(i), true);
}
super.removeViewsInLayout(start, count);
}
@Override
public void removeAllViews() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
removeViewFromYogaTree(getChildAt(i), false);
}
super.removeAllViews();
}
@Override
public void removeAllViewsInLayout() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
removeViewFromYogaTree(getChildAt(i), true);
}
super.removeAllViewsInLayout();
}
/**
* Marks a particular view as "dirty" and to be relaid out. If the view is not a child of this
* {@link YogaLayout}, the entire tree is traversed to find it.
*
* @param view the view to mark as dirty
*/
public void invalidate(View view) {
if (mYogaNodes.containsKey(view)) {
mYogaNodes.get(view).dirty();
return;
}
final int childCount = mYogaNode.getChildCount();
for (int i = 0; i < childCount; i++) {
final YogaNode yogaNode = mYogaNode.getChildAt(i);
if (yogaNode.getData() instanceof YogaLayout) {
((YogaLayout) yogaNode.getData()).invalidate(view);
}
}
invalidate();
}
private void removeViewFromYogaTree(View view, boolean inLayout) {
final YogaNode node = mYogaNodes.get(view);
if (node == null) {
return;
}
final YogaNode owner = node.getOwner();
for (int i = 0; i < owner.getChildCount(); i++) {
if (owner.getChildAt(i).equals(node)) {
owner.removeChildAt(i);
break;
}
}
node.setData(null);
mYogaNodes.remove(view);
if (inLayout) {
mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
}
private void applyLayoutRecursive(YogaNode node, float xOffset, float yOffset) {
View view = (View) node.getData();
if (view != null && view != this) {
if (view.getVisibility() == GONE) {
return;
}
int left = Math.round(xOffset + node.getLayoutX());
int top = Math.round(yOffset + node.getLayoutY());
view.measure(
View.MeasureSpec.makeMeasureSpec(
Math.round(node.getLayoutWidth()),
View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(
Math.round(node.getLayoutHeight()),
View.MeasureSpec.EXACTLY));
view.layout(left, top, left + view.getMeasuredWidth(), top + view.getMeasuredHeight());
}
final int childrenCount = node.getChildCount();
for (int i = 0; i < childrenCount; i++) {
if (this.equals(view)) {
applyLayoutRecursive(node.getChildAt(i), xOffset, yOffset);
} else if (view instanceof YogaLayout) {
continue;
} else {
applyLayoutRecursive(
node.getChildAt(i),
xOffset + node.getLayoutX(),
yOffset + node.getLayoutY());
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Either we are a root of a tree, or this function is called by our owner's onLayout, in which
// case our r-l and b-t are the size of our node.
if (!(getParent() instanceof YogaLayout)) {
createLayout(
MeasureSpec.makeMeasureSpec(r - l, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(b - t, MeasureSpec.EXACTLY));
}
applyLayoutRecursive(mYogaNode, 0, 0);
}
/**
* This function is mostly unneeded, because Yoga is doing the measuring. Hence we only need to
* return accurate results if we are the root.
*
* @param widthMeasureSpec the suggested specification for the width
* @param heightMeasureSpec the suggested specification for the height
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!(getParent() instanceof YogaLayout)) {
createLayout(widthMeasureSpec, heightMeasureSpec);
}
setMeasuredDimension(
Math.round(mYogaNode.getLayoutWidth()),
Math.round(mYogaNode.getLayoutHeight()));
}
private void createLayout(int widthMeasureSpec, int heightMeasureSpec) {
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
mYogaNode.setHeight(heightSize);
}
if (widthMode == MeasureSpec.EXACTLY) {
mYogaNode.setWidth(widthSize);
}
if (heightMode == MeasureSpec.AT_MOST) {
mYogaNode.setMaxHeight(heightSize);
}
if (widthMode == MeasureSpec.AT_MOST) {
mYogaNode.setMaxWidth(widthSize);
}
mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
/**
* Applies the layout parameters to the YogaNode. That is, this function is a translator from
* {@code yoga:X="Y"} to {@code yogaNode.setX(Y);}, with some reasonable defaults.
*
* <p>
* If the SDK version is high enough, and the {@code yoga:direction} is not set on
* the component, the direction (LTR or RTL) is set according to the locale.
*
* <p>
* The attributes {@code padding_top}, {@code padding_right} etc. default to those of the view's
* drawable background, if it has one.
*
* @param layoutParameters The source set of params
* @param node The destination node
*/
protected static void applyLayoutParams(LayoutParams layoutParameters, YogaNode node, View view) {
// JELLY_BEAN_MR1 (17) is the first version supporting getLayoutDirection()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Configuration configuration = view.getResources().getConfiguration();
if (configuration.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
node.setDirection(YogaDirection.RTL);
}
}
Drawable background = view.getBackground();
if (background != null) {
final Rect backgroundPadding = new Rect();
if (background.getPadding(backgroundPadding)) {
node.setPadding(YogaEdge.LEFT, backgroundPadding.left);
node.setPadding(YogaEdge.TOP, backgroundPadding.top);
node.setPadding(YogaEdge.RIGHT, backgroundPadding.right);
node.setPadding(YogaEdge.BOTTOM, backgroundPadding.bottom);
}
}
for (int i = 0; i < layoutParameters.numericAttributes.size(); i++) {
final int attribute = layoutParameters.numericAttributes.keyAt(i);
final float value = layoutParameters.numericAttributes.valueAt(i);
if (attribute == R.styleable.yoga_yg_alignContent) {
node.setAlignContent(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_alignItems) {
node.setAlignItems(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_alignSelf) {
node.setAlignSelf(YogaAlign.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_aspectRatio) {
node.setAspectRatio(value);
} else if (attribute == R.styleable.yoga_yg_borderLeft) {
node.setBorder(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_borderTop) {
node.setBorder(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_borderRight) {
node.setBorder(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_borderBottom) {
node.setBorder(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_borderStart) {
node.setBorder(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_borderEnd) {
node.setBorder(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_borderHorizontal) {
node.setBorder(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_borderVertical) {
node.setBorder(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_borderAll) {
node.setBorder(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_direction) {
node.setDirection(YogaDirection.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_display) {
node.setDisplay(YogaDisplay.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_flex) {
node.setFlex(value);
} else if (attribute == R.styleable.yoga_yg_flexBasis) {
node.setFlexBasis(value);
} else if (attribute == R.styleable.yoga_yg_flexDirection) {
node.setFlexDirection(YogaFlexDirection.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_flexGrow) {
node.setFlexGrow(value);
} else if (attribute == R.styleable.yoga_yg_flexShrink) {
node.setFlexShrink(value);
} else if (attribute == R.styleable.yoga_yg_height) {
node.setHeight(value);
} else if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMargin(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_justifyContent) {
node.setJustifyContent(YogaJustify.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMargin(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMargin(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMargin(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMargin(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMargin(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMargin(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMargin(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMargin(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_maxHeight) {
node.setMaxHeight(value);
} else if (attribute == R.styleable.yoga_yg_maxWidth) {
node.setMaxWidth(value);
} else if (attribute == R.styleable.yoga_yg_minHeight) {
node.setMinHeight(value);
} else if (attribute == R.styleable.yoga_yg_minWidth) {
node.setMinWidth(value);
} else if (attribute == R.styleable.yoga_yg_overflow) {
node.setOverflow(YogaOverflow.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_paddingLeft) {
node.setPadding(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_paddingTop) {
node.setPadding(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_paddingRight) {
node.setPadding(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_paddingBottom) {
node.setPadding(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_paddingStart) {
node.setPadding(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_paddingEnd) {
node.setPadding(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_paddingHorizontal) {
node.setPadding(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_paddingVertical) {
node.setPadding(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_paddingAll) {
node.setPadding(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_positionLeft) {
node.setPosition(YogaEdge.LEFT, value);
} else if (attribute == R.styleable.yoga_yg_positionTop) {
node.setPosition(YogaEdge.TOP, value);
} else if (attribute == R.styleable.yoga_yg_positionRight) {
node.setPosition(YogaEdge.RIGHT, value);
} else if (attribute == R.styleable.yoga_yg_positionBottom) {
node.setPosition(YogaEdge.BOTTOM, value);
} else if (attribute == R.styleable.yoga_yg_positionStart) {
node.setPosition(YogaEdge.START, value);
} else if (attribute == R.styleable.yoga_yg_positionEnd) {
node.setPosition(YogaEdge.END, value);
} else if (attribute == R.styleable.yoga_yg_positionHorizontal) {
node.setPosition(YogaEdge.HORIZONTAL, value);
} else if (attribute == R.styleable.yoga_yg_positionVertical) {
node.setPosition(YogaEdge.VERTICAL, value);
} else if (attribute == R.styleable.yoga_yg_positionAll) {
node.setPosition(YogaEdge.ALL, value);
} else if (attribute == R.styleable.yoga_yg_positionType) {
node.setPositionType(YogaPositionType.fromInt(Math.round(value)));
} else if (attribute == R.styleable.yoga_yg_width) {
node.setWidth(value);
} else if (attribute == R.styleable.yoga_yg_wrap) {
node.setWrap(YogaWrap.fromInt(Math.round(value)));
}
}
for (int i = 0; i < layoutParameters.stringAttributes.size(); i++) {
final int attribute = layoutParameters.stringAttributes.keyAt(i);
final String value = layoutParameters.stringAttributes.valueAt(i);
if (value.equals("auto")) {
if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMarginAuto(YogaEdge.LEFT);
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMarginAuto(YogaEdge.TOP);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMarginAuto(YogaEdge.RIGHT);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMarginAuto(YogaEdge.BOTTOM);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMarginAuto(YogaEdge.START);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMarginAuto(YogaEdge.END);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMarginAuto(YogaEdge.HORIZONTAL);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMarginAuto(YogaEdge.VERTICAL);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMarginAuto(YogaEdge.ALL);
}
}
if (value.endsWith("%")) {
final float numericValue = Float.parseFloat(value.substring(0, value.length()-1));
if (attribute == R.styleable.yoga_yg_flexBasis) {
node.setFlexBasisPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_height) {
node.setHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_marginLeft) {
node.setMarginPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginTop) {
node.setMarginPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginRight) {
node.setMarginPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginBottom) {
node.setMarginPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginStart) {
node.setMarginPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginEnd) {
node.setMarginPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginHorizontal) {
node.setMarginPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginVertical) {
node.setMarginPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_marginAll) {
node.setMarginPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_maxHeight) {
node.setMaxHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_maxWidth) {
node.setMaxWidthPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_minHeight) {
node.setMinHeightPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_minWidth) {
node.setMinWidthPercent(numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingLeft) {
node.setPaddingPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingTop) {
node.setPaddingPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingRight) {
node.setPaddingPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingBottom) {
node.setPaddingPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingStart) {
node.setPaddingPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingEnd) {
node.setPaddingPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingHorizontal) {
node.setPaddingPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingVertical) {
node.setPaddingPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_paddingAll) {
node.setPaddingPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionLeft) {
node.setPositionPercent(YogaEdge.LEFT, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionTop) {
node.setPositionPercent(YogaEdge.TOP, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionRight) {
node.setPositionPercent(YogaEdge.RIGHT, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionBottom) {
node.setPositionPercent(YogaEdge.BOTTOM, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionStart) {
node.setPositionPercent(YogaEdge.START, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionEnd) {
node.setPositionPercent(YogaEdge.END, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionHorizontal) {
node.setPositionPercent(YogaEdge.HORIZONTAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionVertical) {
node.setPositionPercent(YogaEdge.VERTICAL, numericValue);
} else if (attribute == R.styleable.yoga_yg_positionAll) {
node.setPositionPercent(YogaEdge.ALL, numericValue);
} else if (attribute == R.styleable.yoga_yg_width) {
node.setWidthPercent(numericValue);
}
}
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new YogaLayout.LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new YogaLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new YogaLayout.LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
/**
* {@code YogaLayout.LayoutParams} are used by views to tell {@link YogaLayout} how they want to
* be laid out. More precisely, the specify the yoga parameters of the view.
*
* <p>
* This is actually mostly a wrapper around a {@code SparseArray} that holds a mapping between
* styleable id's ({@code R.styleable.yoga_yg_*}) and the float of their values. In cases where
* the value is an enum or an integer, they should first be cast to int (with rounding) before
* using.
*/
public static class LayoutParams extends ViewGroup.LayoutParams {
/**
* A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) to the float of their values.
* For attributes like position_percent_left (float), this is the native type. For attributes
* like align_self (enums), the integer enum value is cast (rounding is used on the other side
* to prevent precision errors). Dimension attributes are stored as float pixels.
*/
SparseArray<Float> numericAttributes;
/**
* A mapping from attribute keys ({@code R.styleable.yoga_yg_*}) with string values to those
* strings. This is used for values such as "auto".
*/
SparseArray<String> stringAttributes;
/**
* Constructs a set of layout params from a source set. In the case that the source set is
* actually a {@link YogaLayout.LayoutParams}, we can copy all the yoga attributes. Otherwise
* we start with a blank slate.
*
* @param source The layout params to copy from
*/
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
if (source instanceof LayoutParams) {
numericAttributes = ((LayoutParams) source).numericAttributes.clone();
stringAttributes = ((LayoutParams) source).stringAttributes.clone();
} else {
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (source.width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (source.height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
}
}
/**
* Constructs a set of layout params, given width and height specs. In this case, we can set
* the {@code yoga:width} and {@code yoga:height} if we are given them explicitly. If other
* options (such as {@code match_owner} or {@code wrap_content} are given, then the owner
* LayoutParams will store them, and we deal with them during layout. (see
* {@link YogaLayout#createLayout})
*
* @param width the requested width, either a pixel size, {@code WRAP_CONTENT} or
* {@code MATCH_PARENT}.
* @param height the requested height, either a pixel size, {@code WRAP_CONTENT} or
* {@code MATCH_PARENT}.
*/
public LayoutParams(int width, int height) {
super(width, height);
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
}
/**
* Constructs a set of layout params, given attributes. Grabs all the {@code yoga:*}
* defined in {@code ALL_YOGA_ATTRIBUTES} and collects the ones that are set in {@code attrs}.
*
* @param context the application environment
* @param attrs the set of attributes from which to extract the yoga specific attributes
*/
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
numericAttributes = new SparseArray<>();
stringAttributes = new SparseArray<>();
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.yoga);
// Negative values include MATCH_PARENT and WRAP_CONTENT
if (width >= 0) {
numericAttributes.put(R.styleable.yoga_yg_width, (float) width);
}
if (height >= 0) {
numericAttributes.put(R.styleable.yoga_yg_height, (float) height);
}
final int attributeCount = a.getIndexCount();
for (int i = 0; i < attributeCount; i++) {
final int attribute = a.getIndex(i);
final TypedValue val = new TypedValue();
a.getValue(attribute, val);
if (val.type == TypedValue.TYPE_DIMENSION) {
numericAttributes.put(
attribute,
(float) a.getDimensionPixelSize(attribute, 0));
} else if (val.type == TypedValue.TYPE_STRING) {
stringAttributes.put(attribute, a.getString(attribute));
} else {
numericAttributes.put(attribute, a.getFloat(attribute, 0));
}
}
a.recycle();
}
}
/**
* Wrapper around measure function for yoga leaves.
*/
public static class ViewMeasureFunction implements YogaMeasureFunction {
/**
* A function to measure leaves of the Yoga tree. Yoga needs some way to know how large
* elements want to be. This function passes that question directly through to the relevant
* {@code View}'s measure function.
*
* @param node The yoga node to measure
* @param width The suggested width from the owner
* @param widthMode The type of suggestion for the width
* @param height The suggested height from the owner
* @param heightMode The type of suggestion for the height
* @return A measurement output ({@code YogaMeasureOutput}) for the node
*/
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
final View view = (View) node.getData();
if (view == null || view instanceof YogaLayout) {
return YogaMeasureOutput.make(0, 0);
}
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) width,
viewMeasureSpecFromYogaMeasureMode(widthMode));
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
(int) height,
viewMeasureSpecFromYogaMeasureMode(heightMode));
view.measure(widthMeasureSpec, heightMeasureSpec);
return YogaMeasureOutput.make(view.getMeasuredWidth(), view.getMeasuredHeight());
}
private int viewMeasureSpecFromYogaMeasureMode(YogaMeasureMode mode) {
if (mode == YogaMeasureMode.AT_MOST) {
return MeasureSpec.AT_MOST;
} else if (mode == YogaMeasureMode.EXACTLY) {
return MeasureSpec.EXACTLY;
} else {
return MeasureSpec.UNSPECIFIED;
}
}
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga.android;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
/**
* A layout inflater factory. This provides our custom {@link YogaViewLayoutFactory#onCreateView}
* to the XML inflation system, allowing us to replace XML tags.
*/
public class YogaViewLayoutFactory implements LayoutInflater.Factory {
private static YogaViewLayoutFactory sYogaViewLayoutFactory;
/**
* Obtains (and initialises if necessary) the singleton {@link YogaViewLayoutFactory}.
*
* @return The singleton instance
*/
public static YogaViewLayoutFactory getInstance() {
if (sYogaViewLayoutFactory == null) {
sYogaViewLayoutFactory = new YogaViewLayoutFactory();
}
return sYogaViewLayoutFactory;
}
YogaViewLayoutFactory() {}
/**
* Hook for inflating from a LayoutInflater. This hook replaces the cumbersome
* {@code com.facebook.etc.YogaLayout} with simply {@code YogaLayout} in your XML and the same
* with {@code VirtualYogaLayout}.
*
* @param name Tag name to be inflated.
* @param context The context the view is being created in.
* @param attrs Inflation attributes as specified in XML file.
*
* @return View Newly created view. Return null for the default behavior.
*/
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (YogaLayout.class.getSimpleName().equals(name)) {
return new YogaLayout(context, attrs);
}
if (VirtualYogaLayout.class.getSimpleName().equals(name)) {
return new VirtualYogaLayout(context, attrs);
}
return null;
}
}

View File

@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
-->
<resources>
<declare-styleable name="yoga">
<attr name="yg_alignContent" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_alignItems" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_alignSelf" format="enum">
<enum name="auto" value="0"/>
<enum name="flex_start" value="1"/>
<enum name="center" value="2"/>
<enum name="flex_end" value="3"/>
<enum name="stretch" value="4"/>
<enum name="baseline" value="5"/>
</attr>
<attr name="yg_aspectRatio" format="float"/>
<attr name="yg_borderLeft" format="dimension"/>
<attr name="yg_borderTop" format="dimension"/>
<attr name="yg_borderRight" format="dimension"/>
<attr name="yg_borderBottom" format="dimension"/>
<attr name="yg_borderStart" format="dimension"/>
<attr name="yg_borderEnd" format="dimension"/>
<attr name="yg_borderHorizontal" format="dimension"/>
<attr name="yg_borderVertical" format="dimension"/>
<attr name="yg_borderAll" format="dimension"/>
<attr name="yg_direction" format="enum">
<enum name="inherit" value="0"/>
<enum name="ltr" value="1"/>
<enum name="rtl" value="2"/>
</attr>
<attr name="yg_display" format="enum">
<enum name="flex" value="0"/>
<enum name="none" value="1"/>
</attr>
<attr name="yg_flex" format="float"/>
<attr name="yg_flexBasis" format="float|string"/>
<attr name="yg_flexDirection" format="enum">
<enum name="column" value="0"/>
<enum name="column_reverse" value="1"/>
<enum name="row" value="2"/>
<enum name="row_reverse" value="3"/>
</attr>
<attr name="yg_flexGrow" format="float"/>
<attr name="yg_flexShrink" format="float"/>
<attr name="yg_height" format="dimension|string"/>
<attr name="yg_justifyContent" format="enum">
<enum name="flex_start" value="0"/>
<enum name="center" value="1"/>
<enum name="flex_end" value="2"/>
<enum name="space_between" value="3"/>
<enum name="space_around" value="4"/>
</attr>
<attr name="yg_marginLeft" format="dimension|string"/>
<attr name="yg_marginTop" format="dimension|string"/>
<attr name="yg_marginRight" format="dimension|string"/>
<attr name="yg_marginBottom" format="dimension|string"/>
<attr name="yg_marginStart" format="dimension|string"/>
<attr name="yg_marginEnd" format="dimension|string"/>
<attr name="yg_marginHorizontal" format="dimension|string"/>
<attr name="yg_marginVertical" format="dimension|string"/>
<attr name="yg_marginAll" format="dimension|string"/>
<attr name="yg_maxHeight" format="dimension|string"/>
<attr name="yg_maxWidth" format="dimension|string"/>
<attr name="yg_minHeight" format="dimension|string"/>
<attr name="yg_minWidth" format="dimension|string"/>
<attr name="yg_overflow" format="enum">
<enum name="visible" value="0"/>
<enum name="hidden" value="1"/>
<enum name="scroll" value="2"/>
</attr>
<attr name="yg_paddingLeft" format="dimension|string"/>
<attr name="yg_paddingTop" format="dimension|string"/>
<attr name="yg_paddingRight" format="dimension|string"/>
<attr name="yg_paddingBottom" format="dimension|string"/>
<attr name="yg_paddingStart" format="dimension|string"/>
<attr name="yg_paddingEnd" format="dimension|string"/>
<attr name="yg_paddingHorizontal" format="dimension|string"/>
<attr name="yg_paddingVertical" format="dimension|string"/>
<attr name="yg_paddingAll" format="dimension|string"/>
<attr name="yg_positionLeft" format="dimension|string"/>
<attr name="yg_positionTop" format="dimension|string"/>
<attr name="yg_positionRight" format="dimension|string"/>
<attr name="yg_positionBottom" format="dimension|string"/>
<attr name="yg_positionStart" format="dimension|string"/>
<attr name="yg_positionEnd" format="dimension|string"/>
<attr name="yg_positionHorizontal" format="dimension|string"/>
<attr name="yg_positionVertical" format="dimension|string"/>
<attr name="yg_positionAll" format="dimension|string"/>
<attr name="yg_positionType" format="enum">
<!-- "static" is a reserved keyword
<enum name="static" value="0"/>
-->
<enum name="relative" value="1"/>
<enum name="absolute" value="2"/>
<enum name="position_static" value="0"/>
<enum name="position_relative" value="1"/>
<enum name="position_absolute" value="2"/>
</attr>
<attr name="yg_width" format="dimension|string"/>
<attr name="yg_wrap" format="enum">
<enum name="no_wrap" value="0"/>
<enum name="wrap" value="1"/>
</attr>
</declare-styleable>
</resources>

399
benchmark/Benchmark.cpp Normal file
View File

@@ -0,0 +1,399 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
#include <benchmark/Benchmark.h>
#include <benchmark/TreeDeserialization.h>
#include <capture/CaptureTree.h>
#include <nlohmann/json.hpp>
namespace facebook::yoga {
using namespace nlohmann;
using namespace std::chrono;
constexpr uint32_t kNumRepititions = 100;
using SteadyClockDurations =
std::array<steady_clock::duration, kNumRepititions>;
static bool inputsMatch(
float actualWidth,
float expectedWidth,
float actualHeight,
float expectedHeight,
YGMeasureMode actualWidthMode,
YGMeasureMode expectedWidthMode,
YGMeasureMode actualHeightMode,
YGMeasureMode expectedHeightMode) {
bool widthsAreUndefined =
YGFloatIsUndefined(actualWidth) && YGFloatIsUndefined(expectedWidth);
bool widthsAreEqual = widthsAreUndefined || actualWidth == expectedWidth;
bool heightsAreUndefined =
YGFloatIsUndefined(actualHeight) && YGFloatIsUndefined(expectedHeight);
bool heightsAreEqual = heightsAreUndefined || actualHeight == expectedHeight;
return widthsAreEqual && heightsAreEqual &&
actualWidthMode == expectedWidthMode &&
actualHeightMode == expectedHeightMode;
}
YGSize mockMeasureFunc(
YGNodeConstRef node,
float availableWidth,
YGMeasureMode widthMode,
float availableHeight,
YGMeasureMode heightMode) {
(void)node;
MeasureFuncVecWithIndex* fns =
static_cast<MeasureFuncVecWithIndex*>(YGNodeGetContext(node));
if (fns->index >= fns->vec.size()) {
std::cout << "Extra measure function call made" << std::endl;
return {10.0, 10.0};
}
auto values = fns->vec.at(fns->index);
if (!inputsMatch(
availableWidth,
values.inputWidth,
availableHeight,
values.inputHeight,
widthMode,
values.widthMode,
heightMode,
values.heightMode)) {
std::cout << "Measure function input mismatch." << std::endl
<< "Expected width: " << values.inputWidth
<< ", actual width: " << availableWidth << std::endl
<< "Expected height: " << values.inputHeight
<< ", actual height: " << availableHeight << std::endl
<< "Expected width mode: " << values.widthMode
<< ", actual width mode: " << widthMode << std::endl
<< "Expected height mode: " << values.heightMode
<< ", actual height mode: " << heightMode << std::endl;
return {10.0, 10.0};
}
fns->index++;
std::this_thread::sleep_for(std::chrono::nanoseconds(values.durationNs));
return {values.outputWidth, values.outputHeight};
}
std::shared_ptr<const YGConfig> buildConfigFromJson(const json& j) {
json jsonConfig = j["config"];
std::shared_ptr<YGConfig> config(YGConfigNew(), YGConfigFree);
for (json::iterator it = jsonConfig.begin(); it != jsonConfig.end(); it++) {
if (it.key() == "use-web-defaults") {
YGConfigSetUseWebDefaults(config.get(), it.value());
} else if (it.key() == "point-scale-factor") {
YGConfigSetPointScaleFactor(config.get(), it.value());
} else if (it.key() == "errata") {
YGConfigSetErrata(config.get(), errataFromString(it.value()));
} else if (it.key() == "experimental-features") {
// Experimental features is serialized into an array where the values
// present indicate that that feature is enabled
for (json::iterator efIt = it.value().begin(); efIt != it.value().end();
efIt++) {
YGConfigSetExperimentalFeatureEnabled(
config.get(), experimentalFeatureFromString(efIt.value()), true);
}
}
}
return config;
}
std::string edgeStringFromPropertyName(
std::string key,
std::string propertyName) {
return key.substr(propertyName.length() + 1);
}
void setStylesFromJson(const json& j, YGNodeRef node) {
json style = j["style"];
for (const auto& [key, value] : style.items()) {
if (key == "flex-direction") {
YGNodeStyleSetFlexDirection(node, flexDirectionFromString(value));
} else if (key == "justify-content") {
YGNodeStyleSetJustifyContent(node, justifyContentFromString(value));
} else if (key == "align-items") {
YGNodeStyleSetAlignItems(node, alignFromString(value));
} else if (key == "align-content") {
YGNodeStyleSetAlignContent(node, alignFromString(value));
} else if (key == "align-self") {
YGNodeStyleSetAlignSelf(node, alignFromString(value));
} else if (key == "flex-wrap") {
YGNodeStyleSetFlexWrap(node, wrapFromString(value));
} else if (key == "overflow") {
YGNodeStyleSetOverflow(node, overflowFromString(value));
} else if (key == "display") {
YGNodeStyleSetDisplay(node, displayFromString(value));
} else if (key == "position-type") {
YGNodeStyleSetPositionType(node, positionTypeFromString(value));
} else if (key == "flex-grow") {
YGNodeStyleSetFlexGrow(node, value);
} else if (key == "flex-shrink") {
YGNodeStyleSetFlexShrink(node, value);
} else if (key == "flex") {
YGNodeStyleSetFlex(node, value);
} else if (key == "flex-basis") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitAuto) {
YGNodeStyleSetFlexBasisAuto(node);
} else if (unit == YGUnitPoint) {
YGNodeStyleSetFlexBasis(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetFlexBasisPercent(node, value["value"]);
}
} else if (key.starts_with("position")) {
YGEdge edge = edgeFromString(edgeStringFromPropertyName(key, "position"));
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetPosition(node, edge, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetPositionPercent(node, edge, value["value"]);
}
} else if (key.starts_with("padding")) {
YGEdge edge = edgeFromString(edgeStringFromPropertyName(key, "padding"));
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetPadding(node, edge, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetPaddingPercent(node, edge, value["value"]);
}
} else if (key.starts_with("border")) {
YGEdge edge = edgeFromString(edgeStringFromPropertyName(key, "border"));
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetBorder(node, edge, value["value"]);
}
} else if (key.starts_with("margin")) {
YGEdge edge = edgeFromString(edgeStringFromPropertyName(key, "margin"));
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetMargin(node, edge, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetMarginPercent(node, edge, value["value"]);
} else if (unit == YGUnitAuto) {
YGNodeStyleSetMarginAuto(node, edge);
}
} else if (key == "gap") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetGap(node, YGGutterAll, value["value"]);
}
} else if (key == "column-gap") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetGap(node, YGGutterColumn, value["value"]);
}
} else if (key == "row-gap") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetGap(node, YGGutterRow, value["value"]);
}
} else if (key == "height") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitAuto) {
YGNodeStyleSetHeightAuto(node);
} else if (unit == YGUnitPoint) {
YGNodeStyleSetHeight(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetHeightPercent(node, value["value"]);
}
} else if (key == "width") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitAuto) {
YGNodeStyleSetWidthAuto(node);
} else if (unit == YGUnitPoint) {
YGNodeStyleSetWidth(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetWidthPercent(node, value["value"]);
}
} else if (key == "min-height") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetMinHeight(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetMinHeightPercent(node, value["value"]);
}
} else if (key == "min-width") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetMinWidth(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetMinWidthPercent(node, value["value"]);
}
} else if (key == "max-height") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetMaxHeight(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetMaxHeightPercent(node, value["value"]);
}
} else if (key == "max-width") {
YGUnit unit = unitFromJson(value);
if (unit == YGUnitPoint) {
YGNodeStyleSetMaxWidth(node, value["value"]);
} else if (unit == YGUnitPercent) {
YGNodeStyleSetMaxWidthPercent(node, value["value"]);
}
}
}
}
std::shared_ptr<YGNode> buildNodeFromJson(
const json& j,
std::shared_ptr<const YGConfig> config,
std::shared_ptr<MeasureFuncVecWithIndex> fns) {
std::shared_ptr<YGNode> node(YGNodeNewWithConfig(config.get()), YGNodeFree);
if (!j.contains("node") || j["node"].is_null()) {
return node;
}
json nodeState = j["node"];
for (json::iterator it = nodeState.begin(); it != nodeState.end(); it++) {
if (it.key() == "always-forms-containing-block") {
YGNodeSetAlwaysFormsContainingBlock(node.get(), it.value());
} else if (it.key() == "has-custom-measure" && it.value()) {
YGNodeSetContext(node.get(), fns.get());
YGNodeSetMeasureFunc(node.get(), mockMeasureFunc);
}
}
return node;
}
std::shared_ptr<YogaNodeAndConfig> buildTreeFromJson(
const json& j,
std::shared_ptr<MeasureFuncVecWithIndex> fns,
std::shared_ptr<YogaNodeAndConfig> parent,
size_t index) {
auto config = buildConfigFromJson(j);
auto node = buildNodeFromJson(j, config, fns);
auto wrapper = std::make_shared<YogaNodeAndConfig>(
node, config, std::vector<std::shared_ptr<YogaNodeAndConfig>>{});
if (parent != nullptr) {
YGNodeInsertChild(parent->node_.get(), node.get(), index);
parent->children_.push_back(wrapper);
}
setStylesFromJson(j, node.get());
if (j.contains("children")) {
json children = j["children"];
size_t childIndex = 0;
for (json child : children) {
buildTreeFromJson(child, fns, wrapper, childIndex);
childIndex++;
}
}
return wrapper;
}
BenchmarkResult generateBenchmark(json& capture) {
auto fns = std::make_shared<MeasureFuncVecWithIndex>();
populateMeasureFuncVec(capture["measure-funcs"], fns);
auto treeCreationBegin = steady_clock::now();
std::shared_ptr<YogaNodeAndConfig> root =
buildTreeFromJson(capture["tree"], fns, nullptr, 0 /*index*/);
auto treeCreationEnd = steady_clock::now();
json layoutInputs = capture["layout-inputs"];
float availableWidth = layoutInputs["available-width"];
float availableHeight = layoutInputs["available-height"];
YGDirection direction = directionFromString(layoutInputs["owner-direction"]);
auto layoutBegin = steady_clock::now();
YGNodeCalculateLayout(
root->node_.get(), availableWidth, availableHeight, direction);
auto layoutEnd = steady_clock::now();
return BenchmarkResult{
treeCreationEnd - treeCreationBegin, layoutEnd - layoutBegin};
}
static void printBenchmarkResult(
std::string name,
SteadyClockDurations& durations) {
std::array<double, kNumRepititions> timesInMs;
double mean = 0;
for (uint32_t i = 0; i < kNumRepititions; i++) {
auto ms = duration<double, std::milli>(durations[i]).count();
timesInMs[i] = ms;
mean += ms;
}
mean /= kNumRepititions;
std::sort(timesInMs.begin(), timesInMs.end());
double median = timesInMs[kNumRepititions / 2];
double variance = 0;
for (uint32_t i = 0; i < kNumRepititions; i++) {
variance += std::pow(timesInMs[i] - mean, 2);
}
variance /= kNumRepititions;
double stddev = std::sqrt(variance);
printf("%s: median: %lf ms, stddev: %lf ms\n", name.c_str(), median, stddev);
}
void benchmark(std::filesystem::path& capturesDir) {
for (auto& capture : std::filesystem::directory_iterator(capturesDir)) {
if (capture.is_directory() || capture.path().extension() != ".json") {
continue;
}
SteadyClockDurations treeCreationDurations;
SteadyClockDurations layoutDurations;
SteadyClockDurations totalDurations;
std::ifstream captureFile(capture.path());
json j = json::parse(captureFile);
std::string captureName = capture.path().stem().string();
std::cout << "Starting benchmark for " << captureName << std::endl;
for (uint32_t i = 0; i < kNumRepititions; i++) {
BenchmarkResult result = generateBenchmark(j);
treeCreationDurations[i] = result.treeCreationDuration;
layoutDurations[i] = result.layoutDuration;
totalDurations[i] = result.treeCreationDuration + result.layoutDuration;
}
printBenchmarkResult(captureName + " tree creation", treeCreationDurations);
printBenchmarkResult(captureName + " layout", layoutDurations);
printBenchmarkResult(captureName + " total", totalDurations);
std::cout << std::endl;
}
}
} // namespace facebook::yoga
int main(int argc, char* argv[]) {
if (argc == 2) {
std::filesystem::path capturesDir = argv[argc - 1];
facebook::yoga::benchmark(capturesDir);
} else {
throw std::invalid_argument("Expecting a path as an argument");
return 1;
}
return 0;
}

36
benchmark/Benchmark.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <chrono>
#include <memory>
#include <string>
#include <vector>
#include <yoga/Yoga.h>
namespace facebook::yoga {
struct YogaNodeAndConfig {
YogaNodeAndConfig(
std::shared_ptr<YGNode> node,
std::shared_ptr<const YGConfig> config,
std::vector<std::shared_ptr<YogaNodeAndConfig>> children)
: node_(node), config_(config), children_(children) {}
std::shared_ptr<YGNode> node_;
std::shared_ptr<const YGConfig> config_;
std::vector<std::shared_ptr<YogaNodeAndConfig>> children_;
};
struct BenchmarkResult {
std::chrono::steady_clock::duration treeCreationDuration;
std::chrono::steady_clock::duration layoutDuration;
};
} // namespace facebook::yoga

View File

@@ -3,7 +3,6 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13...3.26)
project(benchmark)
set(CMAKE_VERBOSE_MAKEFILE on)
@@ -13,7 +12,16 @@ include(${YOGA_ROOT}/cmake/project-defaults.cmake)
add_subdirectory(${YOGA_ROOT}/yoga ${CMAKE_CURRENT_BINARY_DIR}/yoga)
file(GLOB SOURCES CONFIGURE_DEPENDS
file(GLOB SOURCES_LEGACY CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.c)
file(GLOB SOURCES CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_executable(benchmark ${SOURCES})
add_executable(benchmarklegacy ${SOURCES_LEGACY})
target_link_libraries(benchmark yogacore)
target_link_libraries(benchmarklegacy yogacore)
target_include_directories(benchmark
PRIVATE
$<BUILD_INTERFACE:${YOGA_ROOT}/lib>)

View File

@@ -0,0 +1,245 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <vector>
#include <benchmark/TreeDeserialization.h>
#include <capture/CaptureTree.h>
#include <nlohmann/json.hpp>
#include <yoga/Yoga.h>
namespace facebook::yoga {
using namespace nlohmann;
static inline bool isAuto(json& j) {
return j.is_string() && j == "auto";
}
static inline std::string invalidArgumentMessage(
std::string arg,
std::string enumName) {
return arg + " does not represent any " + enumName + " values";
}
static inline float floatFromJson(json& j) {
float result = YGUndefined;
if (!j.is_null()) {
result = j;
}
return result;
}
YGFlexDirection flexDirectionFromString(std::string str) {
if (str == "row") {
return YGFlexDirectionRow;
} else if (str == "row-reverse") {
return YGFlexDirectionRowReverse;
} else if (str == "column") {
return YGFlexDirectionColumn;
} else if (str == "column-reverse") {
return YGFlexDirectionColumnReverse;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGFlexDirection"));
}
}
YGJustify justifyContentFromString(std::string str) {
if (str == "flex-start") {
return YGJustifyFlexStart;
} else if (str == "center") {
return YGJustifyCenter;
} else if (str == "flex-end") {
return YGJustifyFlexEnd;
} else if (str == "space-between") {
return YGJustifySpaceBetween;
} else if (str == "space-around") {
return YGJustifySpaceAround;
} else if (str == "space-evenly") {
return YGJustifySpaceEvenly;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGJustify"));
}
}
YGAlign alignFromString(std::string str) {
if (str == "auto") {
return YGAlignAuto;
} else if (str == "flex-start") {
return YGAlignFlexStart;
} else if (str == "center") {
return YGAlignCenter;
} else if (str == "flex-end") {
return YGAlignFlexEnd;
} else if (str == "stretch") {
return YGAlignStretch;
} else if (str == "baseline") {
return YGAlignBaseline;
} else if (str == "space-between") {
return YGAlignSpaceBetween;
} else if (str == "space-around") {
return YGAlignSpaceAround;
} else if (str == "space-evenly") {
return YGAlignSpaceEvenly;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGAlign"));
}
}
YGWrap wrapFromString(std::string str) {
if (str == "no-wrap") {
return YGWrapNoWrap;
} else if (str == "wrap") {
return YGWrapWrap;
} else if (str == "wrap-reverse") {
return YGWrapWrapReverse;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGAlign"));
}
}
YGOverflow overflowFromString(std::string str) {
if (str == "visible") {
return YGOverflowVisible;
} else if (str == "hidden") {
return YGOverflowHidden;
} else if (str == "scroll") {
return YGOverflowScroll;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGAlign"));
}
}
YGDisplay displayFromString(std::string str) {
if (str == "flex") {
return YGDisplayFlex;
} else if (str == "none") {
return YGDisplayNone;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGAlign"));
}
}
YGPositionType positionTypeFromString(std::string str) {
if (str == "static") {
return YGPositionTypeStatic;
} else if (str == "relative") {
return YGPositionTypeRelative;
} else if (str == "absolute") {
return YGPositionTypeAbsolute;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGAlign"));
}
}
YGUnit unitFromJson(json& j) {
if (isAuto(j)) {
return YGUnitAuto;
}
std::string unit = j["unit"];
if (unit == "px") {
return YGUnitPoint;
} else if (unit == "pct") {
return YGUnitPercent;
} else {
throw std::invalid_argument(invalidArgumentMessage(unit, "YGUnit"));
}
}
YGEdge edgeFromString(std::string str) {
if (str == "left") {
return YGEdgeLeft;
} else if (str == "top") {
return YGEdgeTop;
} else if (str == "right") {
return YGEdgeRight;
} else if (str == "bottom") {
return YGEdgeBottom;
} else if (str == "start") {
return YGEdgeStart;
} else if (str == "end") {
return YGEdgeEnd;
} else if (str == "horizontal") {
return YGEdgeHorizontal;
} else if (str == "vertical") {
return YGEdgeVertical;
} else if (str == "all") {
return YGEdgeAll;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGEdge"));
}
}
YGErrata errataFromString(std::string str) {
if (str == "none") {
return YGErrataNone;
} else if (str == "all") {
return YGErrataAll;
} else if (str == "classic") {
return YGErrataClassic;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGErrata"));
}
}
YGExperimentalFeature experimentalFeatureFromString(std::string str) {
if (str == "web-flex-basis") {
return YGExperimentalFeatureWebFlexBasis;
} else {
throw std::invalid_argument(
invalidArgumentMessage(str, "YGExperimentalFeature"));
}
}
std::string edgeStringFromPropertyName(
json::iterator it,
std::string propertyName) {
return it.key().substr(propertyName.length() + 1);
}
YGDirection directionFromString(std::string str) {
if (str == "ltr") {
return YGDirectionLTR;
} else if (str == "rtl") {
return YGDirectionRTL;
} else if (str == "inherit") {
return YGDirectionInherit;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGDirection"));
}
}
YGMeasureMode measureModeFromString(std::string str) {
if (str == "at-most") {
return YGMeasureModeAtMost;
} else if (str == "exactly") {
return YGMeasureModeExactly;
} else if (str == "undefined") {
return YGMeasureModeUndefined;
} else {
throw std::invalid_argument(invalidArgumentMessage(str, "YGMeasureMode"));
}
}
void populateMeasureFuncVec(
json& j,
std::shared_ptr<MeasureFuncVecWithIndex> fns) {
for (auto measureFuncJson : j) {
fns->vec.push_back(SerializedMeasureFunc{
floatFromJson(measureFuncJson["width"]),
measureModeFromString(measureFuncJson["width-mode"]),
floatFromJson(measureFuncJson["height"]),
measureModeFromString(measureFuncJson["height-mode"]),
floatFromJson(measureFuncJson["output-width"]),
floatFromJson(measureFuncJson["output-height"]),
measureFuncJson["duration-ns"]});
}
}
} // namespace facebook::yoga

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <memory>
#include <vector>
#include <capture/CaptureTree.h>
#include <nlohmann/json.hpp>
#include <yoga/Yoga.h>
namespace facebook::yoga {
using namespace nlohmann;
struct MeasureFuncVecWithIndex {
std::vector<SerializedMeasureFunc> vec;
size_t index;
};
YGFlexDirection flexDirectionFromString(std::string str);
YGJustify justifyContentFromString(std::string str);
YGAlign alignFromString(std::string str);
YGWrap wrapFromString(std::string str);
YGOverflow overflowFromString(std::string str);
YGDisplay displayFromString(std::string str);
YGPositionType positionTypeFromString(std::string str);
YGUnit unitFromJson(json& j);
YGEdge edgeFromString(std::string str);
YGErrata errataFromString(std::string str);
YGExperimentalFeature experimentalFeatureFromString(std::string str);
std::string edgeStringFromPropertyName(
json::iterator it,
std::string propertyName);
YGDirection directionFromString(std::string str);
YGMeasureMode measureModeFromString(std::string str);
void populateMeasureFuncVec(
json& j,
std::shared_ptr<MeasureFuncVecWithIndex> fns);
} // namespace facebook::yoga

View File

@@ -17,8 +17,8 @@
#define YGBENCHMARKS(BLOCK) \
int main(int argc, char const* argv[]) { \
(void) argc; \
(void) argv; \
(void)argc; \
(void)argv; \
clock_t __start; \
clock_t __endTimes[NUM_REPETITIONS]; \
{ BLOCK } \
@@ -33,8 +33,8 @@
__printBenchmarkResult(NAME, __start, __endTimes);
static int __compareDoubles(const void* a, const void* b) {
double arg1 = *(const double*) a;
double arg2 = *(const double*) b;
double arg1 = *(const double*)a;
double arg2 = *(const double*)b;
if (arg1 < arg2) {
return -1;
@@ -47,15 +47,14 @@ static int __compareDoubles(const void* a, const void* b) {
return 0;
}
static void __printBenchmarkResult(
char* name,
clock_t start,
clock_t* endTimes) {
static void
__printBenchmarkResult(char* name, clock_t start, clock_t* endTimes) {
double timesInMs[NUM_REPETITIONS];
double mean = 0;
clock_t lastEnd = start;
for (uint32_t i = 0; i < NUM_REPETITIONS; i++) {
timesInMs[i] = (endTimes[i] - lastEnd) / (double) CLOCKS_PER_SEC * 1000;
timesInMs[i] =
((double)(endTimes[i] - lastEnd)) / (double)CLOCKS_PER_SEC * 1000;
lastEnd = endTimes[i];
mean += timesInMs[i];
}
@@ -75,12 +74,12 @@ static void __printBenchmarkResult(
}
static YGSize _measure(
YGNodeRef node,
YGNodeConstRef node,
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode) {
(void) node;
(void)node;
return (YGSize){
.width = widthMode == YGMeasureModeUndefined ? 10 : width,
.height = heightMode == YGMeasureModeUndefined ? 10 : height,

15
benchmark/benchmark Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cd "$(dirname "$0")" || exit
CAPTURES_PATH="$(dirname "$(realpath "$0")")""/captures"
if [ "$1" = "buck" ]; then
buck run @fbcode/mode/opt :benchmarkCXX "${CAPTURES_PATH}"
else
cmake -B build -S . -D CMAKE_BUILD_TYPE=Release
cmake --build build
build/benchmark "${CAPTURES_PATH}"
fi

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
*/
plugins {
id("com.android.library") version "8.0.1" apply false
id("com.android.application") version "8.0.1" apply false
id("com.android.library") version "8.2.1" apply false
id("com.android.application") version "8.2.1" apply false
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
}
@@ -38,16 +38,6 @@ nexusPublishing {
}
}
ext {
buildToolsVersion = "33.0.0"
ndkVersion = "23.1.7779620"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
sourceCompatibilityVersion = JavaVersion.VERSION_1_8
targetCompatibilityVersion = JavaVersion.VERSION_1_8
}
task clean(type: Delete) {
delete rootProject.buildDir
}

28
build_fuzz_tests Executable file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env sh
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
if [ "$#" -eq 0 ]; then
build_type="Debug"
else
build_type="$1"
fi
export CC=clang
export CXX=clang++
# Make sure libc++ isn't used, as libfuzzer is linked against stdlibc++ which causes conflicts
unset CXXFLAGS
export LDFLAGS=-lstdc++
if which ninja; then
set -e
cmake -B build -S . -D BUILD_FUZZ_TESTS=ON -D CMAKE_BUILD_TYPE="$build_type" -G Ninja
else
set -e
cmake -B build -S . -D BUILD_FUZZ_TESTS=ON -D CMAKE_BUILD_TYPE="$build_type"
fi
cmake --build build --target fuzz_layout

23
capture/CMakeLists.txt Normal file
View File

@@ -0,0 +1,23 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13...3.26)
project(capture)
set(CMAKE_VERBOSE_MAKEFILE on)
set(YOGA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
include(${YOGA_ROOT}/cmake/project-defaults.cmake)
add_subdirectory(${YOGA_ROOT}/yoga ${CMAKE_CURRENT_BINARY_DIR}/yoga)
file(GLOB SOURCES CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_library(capture STATIC ${SOURCES})
target_link_libraries(capture yogacore)
target_include_directories(capture
PUBLIC
$<BUILD_INTERFACE:${YOGA_ROOT}/lib>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include/yoga/lib>)

94
capture/CaptureTree.cpp Normal file
View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fstream>
#include <vector>
#include <capture/CaptureTree.h>
#include <capture/NodeToString.h>
#include <nlohmann/json.hpp>
namespace facebook::yoga {
using namespace nlohmann;
static void captureTree(
std::string_view serializedTree,
const std::filesystem::path& path) {
std::ofstream file(path);
file << serializedTree;
}
static std::vector<SerializedMeasureFunc>& currentSerializedMeasureFuncVec() {
static thread_local std::vector<SerializedMeasureFunc>
currentSerializedMeasureFuncVec;
return currentSerializedMeasureFuncVec;
}
/*
* Capturing a tree often means that we capturing multiple serial layouts over
* the course of the capture. Because of this, we need to make sure that we do
* a full layout pass with no caching. If we do not do this there is a chance
* we do not capture measure functions that were called and cached in previous
* layouts. Missing these captures would lead to inaccurate benchmarking where
* we do not have cached state.
*
* TODO: Dirty entire tree not just measure function nodes
*/
static void dirtyTree(YGNodeRef node) {
if (YGNodeHasMeasureFunc(node)) {
YGNodeMarkDirty(node);
}
const size_t childCount = YGNodeGetChildCount(node);
for (size_t i = 0; i < childCount; i++) {
dirtyTree(YGNodeGetChild(node, i));
}
}
void YGNodeCalculateLayoutWithCapture(
YGNodeRef node,
float availableWidth,
float availableHeight,
YGDirection ownerDirection,
const std::filesystem::path& path) {
dirtyTree(node);
json j;
serializeLayoutInputs(j, availableWidth, availableHeight, ownerDirection);
serializeTree(
j,
node,
PrintOptions::Style | PrintOptions::Children | PrintOptions::Config |
PrintOptions::Node);
YGNodeCalculateLayout(node, availableWidth, availableHeight, ownerDirection);
serializeMeasureFuncResults(j, currentSerializedMeasureFuncVec());
// TODO: It is possible to have a measure function call layout again if, e.g.,
// views are nested in text. Need to be able to resolve this special case.
currentSerializedMeasureFuncVec().clear();
captureTree(j.dump(2), path);
}
void captureMeasureFunc(
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode,
YGSize output,
std::chrono::steady_clock::duration durationNs) {
currentSerializedMeasureFuncVec().push_back(SerializedMeasureFunc{
width,
widthMode,
height,
heightMode,
output.width,
output.height,
durationNs.count()});
}
} // namespace facebook::yoga

41
capture/CaptureTree.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <filesystem>
#include <yoga/Yoga.h>
namespace facebook::yoga {
struct SerializedMeasureFunc {
float inputWidth{0.0f};
YGMeasureMode widthMode{YGMeasureModeUndefined};
float inputHeight{0.0};
YGMeasureMode heightMode{YGMeasureModeUndefined};
float outputWidth{0.0f};
float outputHeight{0.0f};
std::chrono::steady_clock::duration::rep durationNs;
};
void YGNodeCalculateLayoutWithCapture(
YGNodeRef node,
float availableWidth,
float availableHeight,
YGDirection ownerDirection,
const std::filesystem::path& path);
void captureMeasureFunc(
float width,
YGMeasureMode widthMode,
float height,
YGMeasureMode heightMode,
YGSize output,
std::chrono::steady_clock::duration durationNs);
} // namespace facebook::yoga

341
capture/NodeToString.cpp Normal file
View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <capture/NodeToString.h>
namespace facebook::yoga {
using namespace nlohmann;
static void appendFloatIfNotDefault(
json& j,
std::string_view key,
float num,
float defaultNum) {
if (num != defaultNum && !YGFloatIsUndefined(num)) {
j[key] = num;
}
}
static void appendYGValueIfNotDefault(
json& j,
std::string_view key,
const YGValue& value,
const YGValue& defaultValue) {
if (value != defaultValue) {
if (value.unit == YGUnitAuto) {
j[key] = "auto";
} else if (value.unit == YGUnitUndefined) {
j[key] = "undefined";
} else {
std::string unit = value.unit == YGUnitPoint ? "px" : "pct";
j[key]["value"] = value.value;
j[key]["unit"] = unit;
}
}
}
static void appendEnumValueIfNotDefault(
json& j,
std::string_view key,
std::string_view value,
std::string_view defaultValue) {
if (value != defaultValue) {
j[key] = value;
}
}
static void appendBoolIfNotDefault(
json& j,
std::string_view key,
bool value,
bool defaultValue) {
if (value != defaultValue) {
j[key] = value;
}
}
template <auto Field>
static void appendEdges(
json& j,
const std::string& key,
YGNodeRef node,
YGNodeRef defaultNode) {
appendYGValueIfNotDefault(
j["style"],
key + "-left",
(*Field)(node, YGEdgeLeft),
(*Field)(defaultNode, YGEdgeLeft));
appendYGValueIfNotDefault(
j["style"],
key + "-right",
(*Field)(node, YGEdgeRight),
(*Field)(defaultNode, YGEdgeRight));
appendYGValueIfNotDefault(
j["style"],
key + "-top",
(*Field)(node, YGEdgeTop),
(*Field)(defaultNode, YGEdgeTop));
appendYGValueIfNotDefault(
j["style"],
key + "-bottom",
(*Field)(node, YGEdgeBottom),
(*Field)(defaultNode, YGEdgeBottom));
appendYGValueIfNotDefault(
j["style"],
key + "-all",
(*Field)(node, YGEdgeAll),
(*Field)(defaultNode, YGEdgeAll));
appendYGValueIfNotDefault(
j["style"],
key + "-start",
(*Field)(node, YGEdgeStart),
(*Field)(defaultNode, YGEdgeStart));
appendYGValueIfNotDefault(
j["style"],
key + "-end",
(*Field)(node, YGEdgeEnd),
(*Field)(defaultNode, YGEdgeEnd));
appendYGValueIfNotDefault(
j["style"],
key + "-vertical",
(*Field)(node, YGEdgeVertical),
(*Field)(defaultNode, YGEdgeVertical));
appendYGValueIfNotDefault(
j["style"],
key + "-horizontal",
(*Field)(node, YGEdgeHorizontal),
(*Field)(defaultNode, YGEdgeHorizontal));
}
static YGValue borderFloatToYGValue(YGNodeRef node, YGEdge edge) {
float val = YGNodeStyleGetBorder(node, edge);
YGUnit unit = YGFloatIsUndefined(val) ? YGUnitUndefined : YGUnitPoint;
return YGValue{val, unit};
}
static void serializeTreeImpl(json& j, YGNodeRef node, PrintOptions options) {
if ((options & PrintOptions::Layout) == PrintOptions::Layout) {
j["layout"]["width"] = YGNodeStyleGetWidth(node).value;
j["layout"]["height"] = YGNodeStyleGetHeight(node).value;
j["layout"]["top"] = YGNodeStyleGetPosition(node, YGEdgeTop).value;
j["layout"]["left"] = YGNodeStyleGetPosition(node, YGEdgeLeft).value;
}
std::unique_ptr<YGNode, decltype(&YGNodeFree)> defaultNode(
YGNodeNew(), YGNodeFree);
if ((options & PrintOptions::Style) == PrintOptions::Style) {
appendEnumValueIfNotDefault(
j["style"],
"flex-direction",
YGFlexDirectionToString(YGNodeStyleGetFlexDirection(node)),
YGFlexDirectionToString(
YGNodeStyleGetFlexDirection(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"justify-content",
YGJustifyToString(YGNodeStyleGetJustifyContent(node)),
YGJustifyToString(YGNodeStyleGetJustifyContent(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"align-items",
YGAlignToString(YGNodeStyleGetAlignItems(node)),
YGAlignToString(YGNodeStyleGetAlignItems(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"align-content",
YGAlignToString(YGNodeStyleGetAlignContent(node)),
YGAlignToString(YGNodeStyleGetAlignContent(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"align-self",
YGAlignToString(YGNodeStyleGetAlignSelf(node)),
YGAlignToString(YGNodeStyleGetAlignSelf(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"flex-wrap",
YGWrapToString(YGNodeStyleGetFlexWrap(node)),
YGWrapToString(YGNodeStyleGetFlexWrap(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"overflow",
YGOverflowToString(YGNodeStyleGetOverflow(node)),
YGOverflowToString(YGNodeStyleGetOverflow(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"display",
YGDisplayToString(YGNodeStyleGetDisplay(node)),
YGDisplayToString(YGNodeStyleGetDisplay(defaultNode.get())));
appendEnumValueIfNotDefault(
j["style"],
"position-type",
YGPositionTypeToString(YGNodeStyleGetPositionType(node)),
YGPositionTypeToString(YGNodeStyleGetPositionType(defaultNode.get())));
appendFloatIfNotDefault(
j["style"],
"flex-grow",
YGNodeStyleGetFlexGrow(node),
YGNodeStyleGetFlexGrow(defaultNode.get()));
appendFloatIfNotDefault(
j["style"],
"flex-shrink",
YGNodeStyleGetFlexShrink(node),
YGNodeStyleGetFlexShrink(defaultNode.get()));
appendFloatIfNotDefault(
j["style"],
"flex",
YGNodeStyleGetFlex(node),
YGNodeStyleGetFlex(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"flex-basis",
YGNodeStyleGetFlexBasis(node),
YGNodeStyleGetFlexBasis(defaultNode.get()));
appendEdges<&YGNodeStyleGetMargin>(j, "margin", node, defaultNode.get());
appendEdges<&YGNodeStyleGetPadding>(j, "padding", node, defaultNode.get());
appendEdges<&borderFloatToYGValue>(j, "border", node, defaultNode.get());
appendEdges<&YGNodeStyleGetPosition>(
j, "position", node, defaultNode.get());
appendFloatIfNotDefault(
j["style"],
"gap",
YGNodeStyleGetGap(node, YGGutterAll),
YGNodeStyleGetGap(defaultNode.get(), YGGutterAll));
appendFloatIfNotDefault(
j["style"],
"column-gap",
YGNodeStyleGetGap(node, YGGutterColumn),
YGNodeStyleGetGap(defaultNode.get(), YGGutterColumn));
appendFloatIfNotDefault(
j["style"],
"row-gap",
YGNodeStyleGetGap(node, YGGutterRow),
YGNodeStyleGetGap(defaultNode.get(), YGGutterRow));
appendYGValueIfNotDefault(
j["style"],
"width",
YGNodeStyleGetWidth(node),
YGNodeStyleGetWidth(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"height",
YGNodeStyleGetHeight(node),
YGNodeStyleGetHeight(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"max-width",
YGNodeStyleGetMaxWidth(node),
YGNodeStyleGetMaxWidth(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"max-height",
YGNodeStyleGetMaxHeight(node),
YGNodeStyleGetMaxHeight(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"min-width",
YGNodeStyleGetMinWidth(node),
YGNodeStyleGetMinWidth(defaultNode.get()));
appendYGValueIfNotDefault(
j["style"],
"min-height",
YGNodeStyleGetMinHeight(node),
YGNodeStyleGetMinHeight(defaultNode.get()));
}
if ((options & PrintOptions::Config) == PrintOptions::Config) {
YGConfigConstRef config = YGNodeGetConfig(node);
YGConfigConstRef defaultConfig = YGConfigGetDefault();
appendBoolIfNotDefault(
j["config"],
"use-web-defaults",
YGConfigGetUseWebDefaults(config),
YGConfigGetUseWebDefaults(defaultConfig));
appendFloatIfNotDefault(
j["config"],
"point-scale-factor",
YGConfigGetPointScaleFactor(config),
YGConfigGetPointScaleFactor(defaultConfig));
YGErrata errata = YGConfigGetErrata(config);
if (errata == YGErrataNone || errata == YGErrataAll ||
errata == YGErrataClassic) {
appendEnumValueIfNotDefault(
j["config"],
"errata",
YGErrataToString(errata),
YGErrataToString(YGConfigGetErrata(defaultConfig)));
}
if (YGConfigIsExperimentalFeatureEnabled(
config, YGExperimentalFeatureWebFlexBasis) !=
YGConfigIsExperimentalFeatureEnabled(
defaultConfig, YGExperimentalFeatureWebFlexBasis)) {
j["config"]["experimental-features"].push_back(
YGExperimentalFeatureToString(YGExperimentalFeatureWebFlexBasis));
}
}
if ((options & PrintOptions::Node) == PrintOptions::Node) {
appendBoolIfNotDefault(
j["node"],
"always-forms-containing-block",
YGNodeGetAlwaysFormsContainingBlock(node),
YGNodeGetAlwaysFormsContainingBlock(defaultNode.get()));
if (YGNodeHasMeasureFunc(node)) {
j["node"]["has-custom-measure"] = true;
}
}
const size_t childCount = YGNodeGetChildCount(node);
if ((options & PrintOptions::Children) == PrintOptions::Children &&
childCount > 0) {
for (size_t i = 0; i < childCount; i++) {
j["children"].push_back({});
serializeTreeImpl(j["children"][i], YGNodeGetChild(node, i), options);
}
}
}
void serializeTree(json& j, YGNodeRef node, PrintOptions options) {
serializeTreeImpl(j["tree"], node, options);
}
void serializeLayoutInputs(
json& j,
float availableWidth,
float availableHeight,
YGDirection ownerDirection) {
j["layout-inputs"] = {
{"available-width", availableWidth},
{"available-height", availableHeight},
{"owner-direction", YGDirectionToString(ownerDirection)},
};
}
void serializeMeasureFuncResults(
json& j,
std::vector<SerializedMeasureFunc>& measureFuncs) {
for (auto measureFunc : measureFuncs) {
j["measure-funcs"].push_back(
{{"width", measureFunc.inputWidth},
{"width-mode", YGMeasureModeToString(measureFunc.widthMode)},
{"height", measureFunc.inputHeight},
{"height-mode", YGMeasureModeToString(measureFunc.heightMode)},
{"output-width", measureFunc.outputWidth},
{"output-height", measureFunc.outputHeight},
{"duration-ns", measureFunc.durationNs}});
}
}
} // namespace facebook::yoga

40
capture/NodeToString.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <chrono>
#include <string>
#include <capture/CaptureTree.h>
#include <nlohmann/json.hpp>
#include <yoga/Yoga.h>
namespace facebook::yoga {
enum class PrintOptions : uint8_t {
Layout = 1 << 0,
Children = 1 << 1,
Style = 1 << 2,
Config = 1 << 3,
Node = 1 << 4,
};
YG_DEFINE_ENUM_FLAG_OPERATORS(PrintOptions);
void serializeTree(nlohmann::json& j, YGNodeRef root, PrintOptions options);
void serializeLayoutInputs(
nlohmann::json& j,
float availableWidth,
float availableHeight,
YGDirection ownerDirection);
void serializeMeasureFuncResults(
nlohmann::json& j,
std::vector<SerializedMeasureFunc>& measureFuncs);
} // namespace facebook::yoga

View File

@@ -3,7 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@@ -17,7 +17,7 @@ add_compile_options(
# "Standard C++ exception handling" (C++ stack unwinding including extern c)
/EHsc
# Enable warnings and warnings as errors
/W3
/W4
/WX
# Disable RTTI
$<$<COMPILE_LANGUAGE:CXX>:/GR->
@@ -33,7 +33,6 @@ add_compile_options(
-fexceptions
# Enable warnings and warnings as errors
-Wall
-Wextra
-Werror
# Disable RTTI
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>

View File

@@ -4,6 +4,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import math
import os
ENUMS = {
@@ -28,6 +29,7 @@ ENUMS = {
"Baseline",
"SpaceBetween",
"SpaceAround",
"SpaceEvenly",
],
"PositionType": ["Static", "Relative", "Absolute"],
"Display": ["Flex", "None"],
@@ -50,15 +52,6 @@ ENUMS = {
"ExperimentalFeature": [
# Mimic web flex-basis behavior (experiment may be broken)
"WebFlexBasis",
# Conformance fix: https://github.com/facebook/yoga/pull/1028
"AbsolutePercentageAgainstPaddingEdge",
# fix JNI local ref overflows
"FixJNILocalRefOverflows",
],
"PrintOptions": [
("Layout", 1 << 0),
("Style", 1 << 1),
("Children", 1 << 2),
],
"Gutter": ["Column", "Row", "All"],
# Known incorrect behavior which can be enabled for compatibility
@@ -68,6 +61,12 @@ ENUMS = {
# Allows main-axis flex basis to be stretched without flexGrow being
# set (previously referred to as "UseLegacyStretchBehaviour")
("StretchFlexBasis", 1 << 0),
# Positioning of absolute nodes will have various bugs related to
# justification, alignment, and insets
("AbsolutePositioningIncorrect", 1 << 1),
# Absolute nodes will resolve percentages against the inner size of
# their containing node, not the padding box
("AbsolutePercentAgainstInnerSize", 1 << 2),
# Enable all incorrect behavior (preserve compatibility)
("All", 0x7FFFFFFF),
# Enable all errata except for "StretchFlexBasis" (Defaults behavior
@@ -78,7 +77,7 @@ ENUMS = {
DO_NOT_STRIP = ["LogLevel"]
BITSET_ENUMS = ["PrintOptions", "Errata"]
BITSET_ENUMS = ["Errata"]
def get_license(ext):
@@ -90,7 +89,7 @@ def get_license(ext):
*/
// @{"generated"} by enums.py
{"// clang-format off" if ext == "cpp" else ""}
"""
@@ -123,15 +122,11 @@ with open(root + "/yoga/YGEnums.h", "w") as f:
f.write(get_license("cpp"))
f.write("#pragma once\n")
f.write("#include <yoga/YGMacros.h>\n\n")
f.write("// clang-format off\n\n\n")
f.write("YG_EXTERN_C_BEGIN\n\n")
items = sorted(ENUMS.items())
for name, values in items:
if isinstance(values[0], tuple):
f.write("YG_ENUM_DECL(\n")
else:
f.write("YG_ENUM_SEQ_DECL(\n")
f.write("YG_ENUM_DECL(\n")
f.write(" YG%s,\n" % name)
for value in values:
@@ -148,6 +143,47 @@ with open(root + "/yoga/YGEnums.h", "w") as f:
f.write("\n")
f.write("YG_EXTERN_C_END\n")
# Write out C++ scoped enums
for name, values in sorted(ENUMS.items()):
with open(f"{root}/yoga/enums/{name}.h", "w") as f:
f.write(get_license("cpp"))
f.write("#pragma once\n\n")
f.write("#include <cstdint>\n")
f.write("#include <yoga/YGEnums.h>\n")
f.write("#include <yoga/enums/YogaEnums.h>\n\n")
f.write("namespace facebook::yoga {\n\n")
width = "uint32_t" if name in BITSET_ENUMS else "uint8_t"
f.write(f"enum class {name} : {width} {{\n")
for value in values:
ordinal = value[0] if isinstance(value, tuple) else value
f.write(f" {ordinal} = YG{name}{ordinal},\n")
f.write("};\n\n")
if name in BITSET_ENUMS:
f.write(f"YG_DEFINE_ENUM_FLAG_OPERATORS({name})\n\n")
else:
f.write("template <>\n")
f.write(f"constexpr int32_t ordinalCount<{name}>() {{\n")
f.write(f" return {len(values)};\n")
f.write("}\n\n")
f.write(f"constexpr {name} scopedEnum(YG{name} unscoped) {{\n")
f.write(f" return static_cast<{name}>(unscoped);\n")
f.write("}\n\n")
f.write(f"constexpr YG{name} unscopedEnum({name} scoped) {{\n")
f.write(f" return static_cast<YG{name}>(scoped);\n")
f.write("}\n\n")
f.write(f"inline const char* toString({name} e) {{\n")
f.write(f" return YG{name}ToString(unscopedEnum(e));\n")
f.write("}\n\n")
f.write("} // namespace facebook::yoga\n")
# write out C body for printing
with open(root + "/yoga/YGEnums.cpp", "w") as f:
f.write(get_license("cpp"))

21
fuzz/CMakeLists.txt Normal file
View File

@@ -0,0 +1,21 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# If google/oss-fuzz has set the fuzzing engine
if(DEFINED ENV{LIB_FUZZING_ENGINE})
set(FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE})
set(FUZZING_COMPILE_FLAGS "")
set(FUZZING_LINK_FLAGS "${FUZZING_ENGINE}")
else()
set(FUZZING_COMPILE_FLAGS "-fsanitize=fuzzer")
set(FUZZING_LINK_FLAGS "-fsanitize=fuzzer")
endif()
add_executable(fuzz_layout FuzzLayout.cpp)
set_target_properties(fuzz_layout PROPERTIES
COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}"
LINK_FLAGS "${FUZZING_LINK_FLAGS}"
)
target_link_libraries(fuzz_layout yogacore)

57
fuzz/FuzzLayout.cpp Normal file
View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <fuzzer/FuzzedDataProvider.h>
#include <yoga/Yoga.h>
#include <cstdint>
YGFlexDirection fuzzedFlexDirection(FuzzedDataProvider& fdp) {
return fdp.PickValueInArray({
YGFlexDirectionColumn,
YGFlexDirectionColumnReverse,
YGFlexDirectionRow,
YGFlexDirectionRowReverse,
});
}
void fillFuzzedTree(
FuzzedDataProvider& fdp,
YGConfigConstRef config,
YGNodeRef root,
size_t depth = 0) {
constexpr size_t kMaxDepth = 20;
constexpr size_t kMaxChildren = 20;
if (depth > kMaxDepth) {
return;
}
size_t children = fdp.ConsumeIntegralInRange<size_t>(0, kMaxChildren);
for (size_t i = 0; i < children; i++) {
YGNodeRef child = YGNodeNewWithConfig(config);
YGNodeStyleSetFlexDirection(root, fuzzedFlexDirection(fdp));
YGNodeStyleSetWidth(child, fdp.ConsumeFloatingPoint<float>());
YGNodeStyleSetGap(
child, YGGutterAll, fdp.ConsumeProbability<float>() * 100);
YGNodeStyleSetHeight(child, fdp.ConsumeFloatingPoint<float>());
YGNodeInsertChild(root, child, i);
fillFuzzedTree(fdp, config, child, depth + 1);
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
YGConfigRef config = YGConfigNew();
YGNodeRef root = YGNodeNewWithConfig(config);
fillFuzzedTree(fdp, config, root);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
YGNodeFreeRecursive(root);
YGConfigFree(config);
return 0;
}

View File

@@ -1,4 +0,0 @@
source "https://rubygems.org"
gem 'watir', '~>7.2.0'
gem 'webdrivers', '~> 5.2.0'

View File

@@ -1,31 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
nokogiri (1.15.2-arm64-darwin)
racc (~> 1.4)
racc (1.7.0)
regexp_parser (2.8.1)
rexml (3.2.5)
rubyzip (2.3.2)
selenium-webdriver (4.10.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
watir (7.2.2)
regexp_parser (>= 1.2, < 3)
selenium-webdriver (~> 4.2)
webdrivers (5.2.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (~> 4.0)
websocket (1.2.9)
PLATFORMS
arm64-darwin-22
DEPENDENCIES
watir (~> 7.2.0)
webdrivers (~> 5.2.0)
BUNDLED WITH
2.4.10

View File

@@ -8,8 +8,5 @@
*/
module.exports = {
presets: [
['@babel/preset-env', {targets: 'defaults'}],
'@babel/preset-typescript',
],
presets: ['@babel/preset-typescript'],
};

View File

@@ -14,28 +14,33 @@
<div style="width:10px; height: 10px; position: absolute; start: 10px; top: 10px; end: 10px; bottom: 10px;"></div>
</div>
<div id="do_not_clamp_height_of_absolute_node_to_height_of_its_overflow_hidden_parent" style="height: 50px; width: 50px; overflow: hidden; flex-direction: row;">
<div id="do_not_clamp_height_of_absolute_node_to_height_of_its_overflow_hidden_parent"
style="height: 50px; width: 50px; overflow: hidden; flex-direction: row;">
<div style="position: absolute; start: 0px; top: 0px;">
<div style="width: 100px; height: 100px;"></div>
</div>
</div>
<div id="absolute_layout_within_border" style="height:100px; width:100px; border-width: 10px; margin: 10px; padding: 10px;">
<div id="absolute_layout_within_border"
style="height:100px; width:100px; border-width: 10px; margin: 10px; padding: 10px;">
<div style="position: absolute; width: 50px; height: 50px; left: 0px; top: 0px;"></div>
<div style="position: absolute; width: 50px; height: 50px; right: 0px; bottom: 0px;"></div>
<div style="position: absolute; width: 50px; height: 50px; left: 0px; top: 0px; margin: 10px;"></div>
<div style="position: absolute; width: 50px; height: 50px; right: 0px; bottom: 0px; margin: 10px;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_center" style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div id="absolute_layout_align_items_and_justify_content_center"
style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_flex_end" style="height: 100px; width: 110px; flex-grow: 1; align-items: flex-end; justify-content: flex-end;">
<div id="absolute_layout_align_items_and_justify_content_flex_end"
style="height: 100px; width: 110px; flex-grow: 1; align-items: flex-end; justify-content: flex-end;">
<div style="position: absolute; width: 60px; height: 40px;"></div>
</div>
<div id="absolute_layout_justify_content_center" style="height: 100px; width: 110px; flex-grow: 1; justify-content: center;">
<div id="absolute_layout_justify_content_center"
style="height: 100px; width: 110px; flex-grow: 1; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;"></div>
</div>
@@ -47,19 +52,23 @@
<div style="position: absolute; width: 60px; height: 40px;align-self: center;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_center_and_top_position" style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div id="absolute_layout_align_items_and_justify_content_center_and_top_position"
style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;top:10px;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_center_and_bottom_position" style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div id="absolute_layout_align_items_and_justify_content_center_and_bottom_position"
style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;bottom:10px;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_center_and_left_position" style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div id="absolute_layout_align_items_and_justify_content_center_and_left_position"
style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;left:5px;"></div>
</div>
<div id="absolute_layout_align_items_and_justify_content_center_and_right_position" style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div id="absolute_layout_align_items_and_justify_content_center_and_right_position"
style="height: 100px; width: 110px; flex-grow: 1; align-items: center; justify-content: center;">
<div style="position: absolute; width: 60px; height: 40px;right:5px;"></div>
</div>
@@ -67,24 +76,28 @@
</div>
<div id="absolute_layout_percentage_bottom_based_on_parent_height" style="width: 100px; height: 200px;">
<div style="position: absolute; top: 50%; width: 10px; height: 10px;"></div>
<div style="position: absolute; bottom: 50%; width: 10px; height: 10px;"></div>
<div style="position: absolute; top: 10%; width: 10px; bottom: 10%;"></div>
<div style="position: absolute; top: 50%; width: 10px; height: 10px;"></div>
<div style="position: absolute; bottom: 50%; width: 10px; height: 10px;"></div>
<div style="position: absolute; top: 10%; width: 10px; bottom: 10%;"></div>
</div>
<div id="absolute_layout_in_wrap_reverse_column_container" style="flex-direction:column; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div id="absolute_layout_in_wrap_reverse_column_container"
style="flex-direction:column; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div style="width:20px; height:20px; position: absolute;"></div>
</div>
<div id="absolute_layout_in_wrap_reverse_row_container" style="flex-direction:row; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div id="absolute_layout_in_wrap_reverse_row_container"
style="flex-direction:row; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div style="width:20px; height:20px; position: absolute;"></div>
</div>
<div id="absolute_layout_in_wrap_reverse_column_container_flex_end" style="flex-direction:column; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div id="absolute_layout_in_wrap_reverse_column_container_flex_end"
style="flex-direction:column; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div style="width:20px; height:20px; position: absolute; align-self: flex-end;"></div>
</div>
<div id="absolute_layout_in_wrap_reverse_row_container_flex_end" style="flex-direction:row; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div id="absolute_layout_in_wrap_reverse_row_container_flex_end"
style="flex-direction:row; width:100px; height:100px; flex-wrap: wrap-reverse;">
<div style="width:20px; height:20px; position: absolute; align-self: flex-end;"></div>
</div>
@@ -93,10 +106,35 @@
<div style="width:20%; height:20%; left:20%; top:20%; position: absolute;"></div>
</div>
<div id="absolute_layout_percentage_height_based_on_padded_parent" style="flex-direction:column; width:100px; height:100px; padding-top: 10px; border-top: 10px;">
<div id="absolute_layout_percentage_height_based_on_padded_parent"
style="flex-direction:column; width:100px; height:100px; padding-top: 10px; border-top: 10px solid black;">
<div style="width:100px; height:50%; position: absolute;"></div>
</div>
<div id="absolute_layout_percentage_height_based_on_padded_parent_and_align_items_center" style="position: relative; flex-direction:column; align-items:center; justify-content:center; width:100px; height:100px; padding-top:20px; padding-bottom:20px; ">
<div style="position:absolute; width:100px; height:50%;"></div>
<div id="absolute_layout_percentage_height_based_on_padded_parent_and_align_items_center"
style="position: relative; flex-direction:column; align-items:center; justify-content:center; width:100px; height:100px; padding-top:20px; padding-bottom:20px; ">
<div style="position:absolute; width:100px; height:50%;"></div>
</div>
<div id="absolute_layout_padding_left" style="width:200px; height:200px; padding-left:100px;">
<div style="position:absolute; width:50px; height:50px;"></div>
</div>
<div id="absolute_layout_padding_right" style="width:200px; height:200px; padding-right:100px;">
<div style="position:absolute; width:50px; height:50px;"></div>
</div>
<div id="absolute_layout_padding_top" style="width:200px; height:200px; padding-top:100px;">
<div style="position:absolute; width:50px; height:50px;"></div>
</div>
<div id="absolute_layout_padding_bottom" style="width:200px; height:200px; padding-bottom:100px;">
<div style="position:absolute; width:50px; height:50px;"></div>
</div>
<div id="absolute_layout_column_reverse_margin_border"
style="width:200px; height:200px; flex-direction: column-reverse;">
<div
style="position:absolute; width:50px; height:50px; left: 5px; right: 3px; margin-right: 4px; margin-left: 3px; border-right: 7px solid black; border-left: 1px solid black">
</div>
</div>

View File

@@ -1,4 +1,11 @@
<div id="align_content_flex_start" style="width: 130px; height: 100px; flex-wrap: wrap; flex-direction: row; align-content: flex-start;">
<!-- ALIGN CONTENT: FLEX-START -->
<div id="align_content_flex_start_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: flex-start;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_flex_start_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: flex-start;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
@@ -6,6 +13,28 @@
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_flex_start_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: flex-start;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_flex_start_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: flex-start; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_flex_start_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: flex-start; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_flex_start_without_height_on_children" style="width: 100px; height: 100px; flex-wrap: wrap; flex-direction: column; align-content: flex-start;">
<div style="width: 50px;"></div>
<div style="width: 50px; height: 10px;"></div>
@@ -22,7 +51,14 @@
<div style="width: 50px;"></div>
</div>
<div id="align_content_flex_end" style="width: 100px; height: 100px; flex-wrap: wrap; flex-direction: column; align-content: flex-end;">
<!-- ALIGN CONTENT: FLEX-END -->
<div id="align_content_flex_end_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: flex-end;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_flex_end_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: flex-end;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
@@ -30,6 +66,181 @@
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_flex_end_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: flex-end;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_flex_end_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: flex-end; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_flex_end_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: flex-end; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<!-- ALIGN CONTENT: CENTER -->
<div id="align_content_center_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: center;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_center_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: center;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_center_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: center;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_center_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: center; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_center_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: center; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<!-- ALIGN CONTENT: SPACE-BETWEEN -->
<div id="align_content_space_between_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: space-between;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_between_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-between;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_space_between_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-between;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_between_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-between; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_space_between_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-between; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<!-- ALIGN CONTENT: SPACE-AROUND -->
<div id="align_content_space_around_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: space-around;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_around_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-around;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_space_around_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-around;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_around_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-around; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_space_around_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-around; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<!-- ALIGN CONTENT: SPACE-EVENLY -->
<div id="align_content_space_evenly_nowrap" style="width: 140px; height: 120px; flex-direction: row; align-content: space-evenly;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_evenly_wrap" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-evenly;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<!-- This tests the case where `flex-wrap: wrap` but the content does not actually wrap -->
<div id="align_content_space_evenly_wrap_singleline" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-evenly;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_space_evenly_wrapped_negative_space" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-evenly; justify-content: center; height: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<div id="align_content_space_evenly_wrapped_negative_space_gap" style="display: flex; flex-direction: column; width: 320px; height: 320px; border-width: 60px;">
<div style="display: flex; flex-direction: row; flex-wrap: wrap; align-content: space-evenly; justify-content: center; height: 10px; gap: 10px;">
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
<div style="width: 80%; height: 20px; flex-shrink: 0;"></div>
</div>
</div>
<!-- ALIGN CONTENT: STRETCH -->
<div id="align_content_stretch" style="width: 150px; height: 100px; flex-wrap: wrap; flex-direction: column; align-content: stretch;">
<div style="width: 50px;"></div>
<div style="width: 50px;"></div>
@@ -38,22 +249,6 @@
<div style="width: 50px;"></div>
</div>
<div id="align_content_spacebetween" style="width: 130px; height: 100px; flex-wrap: wrap; flex-direction: row; align-content: space-between;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_spacearound" style="width: 140px; height: 120px; flex-wrap: wrap; flex-direction: row; align-content: space-around;">
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
<div style="width: 50px; height: 10px;"></div>
</div>
<div id="align_content_stretch_row" style="width: 150px; height: 100px; flex-wrap: wrap; flex-direction: row; align-content: stretch;">
<div style="width: 50px;"></div>
<div style="width: 50px;"></div>
@@ -148,3 +343,92 @@
<div style="height: 10px; width: 10px; align-content:stretch;"></div>
</div>
</div>
<div id="align_content_stretch_with_min_cross_axis" style="width: 500px; min-height: 500px; flex-direction: row; flex-wrap: wrap; align-content: stretch;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_stretch_with_max_cross_axis" style="width: 500px; max-height: 500px; flex-direction: row; flex-wrap: wrap; align-content: stretch;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_stretch_with_max_cross_axis_and_border_padding" style="width: 500px; max-height: 500px; flex-direction: row; flex-wrap: wrap; align-content: stretch; border: solid black 5px; padding: 2px">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_space_evenly_with_min_cross_axis" style="width: 500px; min-height: 500px; flex-direction: row; flex-wrap: wrap; align-content: space-evenly;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_space_evenly_with_max_cross_axis" style="width: 500px; max-height: 500px; flex-direction: row; flex-wrap: wrap; align-content: space-evenly;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_space_evenly_with_max_cross_axis_violated" style="width: 500px; max-height: 300px; flex-direction: row; flex-wrap: wrap; align-content: space-evenly;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_space_evenly_with_max_cross_axis_violated_padding_and_border" style="width: 500px; max-height: 300px; flex-direction: row; flex-wrap: wrap; align-content: space-evenly; border: solid black 5px; padding: 2px">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="align_content_space_around_and_align_items_flex_end_with_flex_wrap" style="width: 300px; height: 300px; flex-direction: row; flex-wrap: wrap; align-content: space-around;align-items: flex-end;">
<div style="height: 50px; width: 150px;"></div>
<div style="height: 100px; width: 120px;"></div>
<div style="height: 50px; width: 120px;"></div>
</div>
<div id="align_content_space_around_and_align_items_center_with_flex_wrap" style="width: 300px; height: 300px; flex-direction: row; flex-wrap: wrap; align-content: space-around;align-items: center;">
<div style="height: 50px; width: 150px;"></div>
<div style="height: 100px; width: 120px;"></div>
<div style="height: 50px; width: 120px;"></div>
</div>
<div id="align_content_space_around_and_align_items_flex_start_with_flex_wrap" style="width: 300px; height: 300px; flex-direction: row; flex-wrap: wrap; align-content: space-around;align-items: flex-start;">
<div style="height: 50px; width: 150px;"></div>
<div style="height: 100px; width: 120px;"></div>
<div style="height: 50px; width: 120px;"></div>
</div>
<div id="align_content_flex_start_stretch_doesnt_influence_line_box_dim" style="width: 400px; flex-direction: row; padding-top: 20px; padding-bottom: 20px; padding-left: 20px; padding-right: 20px;">
<div style="height: 100px; width: 100px; margin-right: 20px;"></div>
<div style="flex-direction: row; flex-wrap: wrap; flex-shrink: 1; flex-grow: 1; align-content: flex-start;">
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
</div>
<div style="height: 50px; width: 50px; margin-left: 20px;"></div>
</div>
<div id="align_content_stretch_stretch_does_influence_line_box_dim" style="width: 400px; flex-direction: row; padding-top: 20px; padding-bottom: 20px; padding-left: 20px; padding-right: 20px;">
<div style="height: 100px; width: 100px; margin-right: 20px;"></div>
<div style="flex-direction: row; flex-wrap: wrap; flex-shrink: 1; flex-grow: 1; align-content: stretch;">
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
</div>
<div style="height: 50px; width: 50px; margin-left: 20px;"></div>
</div>
<div id="align_content_space_evenly_stretch_does_influence_line_box_dim" style="width: 400px; flex-direction: row; padding-top: 20px; padding-bottom: 20px; padding-left: 20px; padding-right: 20px;">
<div style="height: 100px; width: 100px; margin-right: 20px;"></div>
<div style="flex-direction: row; flex-wrap: wrap; flex-shrink: 1; flex-grow: 1; align-content: stretch;">
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
<div style="height: 30px; width: 30px; margin-right: 20px;"></div>
</div>
<div style="height: 50px; width: 50px; margin-left: 20px;"></div>
</div>

View File

@@ -26,7 +26,8 @@
</div>
</div>
<div id="align_baseline_child_multiline" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div id="align_baseline_child_multiline"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div style="width: 50px; height: 60px;"></div>
<div style="width: 50px; height: 25px;flex-wrap:wrap;flex-direction:row;">
<div style="width: 25px; height: 20px;"></div>
@@ -36,7 +37,8 @@
</div>
</div>
<div id="align_baseline_child_multiline_override" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div id="align_baseline_child_multiline_override"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div style="width: 50px; height: 60px;"></div>
<div style="width: 50px; height: 25px;flex-wrap:wrap;flex-direction:row;">
<div style="width: 25px; height: 20px;"></div>
@@ -46,7 +48,8 @@
</div>
</div>
<div id="align_baseline_child_multiline_no_override_on_secondline" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div id="align_baseline_child_multiline_no_override_on_secondline"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div style="width: 50px; height: 60px;"></div>
<div style="width: 50px; height: 25px;flex-wrap:wrap;flex-direction:row;">
<div style="width: 25px; height: 20px;"></div>
@@ -71,7 +74,8 @@
</div>
</div>
<div id="align_baseline_double_nested_child" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div id="align_baseline_double_nested_child"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;">
<div style="width: 50px; height: 50px;">
<div style="width: 50px; height: 20px;"></div>
</div>
@@ -93,14 +97,16 @@
</div>
</div>
<div id="align_baseline_child_padding" style="width: 100px; height: 100px; padding:5px; flex-direction:row; align-items: baseline;">
<div id="align_baseline_child_padding"
style="width: 100px; height: 100px; padding:5px; flex-direction:row; align-items: baseline;">
<div style="width: 50px; height: 50px;"></div>
<div style="width: 50px; height: 20px;padding:5px;">
<div style="width: 50px; height: 10px;"></div>
</div>
</div>
<div id="align_baseline_multiline" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;flex-wrap:wrap;">
<div id="align_baseline_multiline"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;flex-wrap:wrap;">
<div style="width: 50px; height: 50px;"></div>
<div style="width: 50px; height: 20px;">
<div style="width: 50px; height: 10px;"></div>
@@ -112,7 +118,8 @@
</div>
<!-- TODO: Yoga's behavior is no longer in line with Chromium -->
<div id="align_baseline_multiline_column" data-disabled="true" style="width: 100px; height: 100px; flex-direction:column; align-items: baseline;flex-wrap:wrap;">
<div id="align_baseline_multiline_column" data-disabled="true"
style="width: 100px; height: 100px; flex-direction:column; align-items: baseline;flex-wrap:wrap;">
<div style="width: 50px; height: 50px;"></div>
<div style="width: 30px; height: 50px;">
<div style="width: 20px; height: 20px;"></div>
@@ -124,7 +131,8 @@
</div>
<!-- TODO: Yoga's behavior is no longer in line with Chromium -->
<div id="align_baseline_multiline_column2" data-disabled="true" style="width: 100px; height: 100px; flex-direction:column; align-items: baseline;flex-wrap:wrap;">
<div id="align_baseline_multiline_column2" data-disabled="true"
style="width: 100px; height: 100px; flex-direction:column; align-items: baseline;flex-wrap:wrap;">
<div style="width: 50px; height: 50px;flex-direction:column;"></div>
<div style="width: 30px; height: 50px;flex-direction:column;">
<div style="width: 20px; height: 20px;flex-direction:column;"></div>
@@ -135,7 +143,8 @@
<div style="width: 50px; height: 20px;flex-direction:column;"></div>
</div>
<div id="align_baseline_multiline_row_and_column" style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;flex-wrap:wrap;">
<div id="align_baseline_multiline_row_and_column"
style="width: 100px; height: 100px; flex-direction:row; align-items: baseline;flex-wrap:wrap;">
<div style="width: 50px; height: 50px;"></div>
<div style="width: 50px; height: 50px;flex-direction:column;">
<div style="width: 50px; height: 10px;"></div>
@@ -146,31 +155,36 @@
<div style="width: 50px; height: 20px;"></div>
</div>
<div id="align_items_center_child_with_margin_bigger_than_parent" style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div id="align_items_center_child_with_margin_bigger_than_parent"
style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div style="align-items: center;">
<div style="width: 52px; height: 52px; margin-left: 10px; margin-right: 10px;"></div>
</div>
</div>
<div id="align_items_flex_end_child_with_margin_bigger_than_parent" style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div id="align_items_flex_end_child_with_margin_bigger_than_parent"
style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div style="align-items: flex-end;">
<div style="width: 52px; height: 52px; margin-left: 10px; margin-right: 10px;"></div>
</div>
</div>
<div id="align_items_center_child_without_margin_bigger_than_parent" style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div id="align_items_center_child_without_margin_bigger_than_parent"
style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div style="align-items: center;">
<div style="width: 72px; height: 72px;"></div>
</div>
</div>
<div id="align_items_flex_end_child_without_margin_bigger_than_parent" style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div id="align_items_flex_end_child_without_margin_bigger_than_parent"
style="height: 52px; width: 52px; align-items: center; justify-content: center;">
<div style="align-items: flex-end;">
<div style="width: 72px; height: 72px;"></div>
</div>
</div>
<div id="align_center_should_size_based_on_content" style="width: 100px; height: 100px; align-items: center; margin-top: 20px;">
<div id="align_center_should_size_based_on_content"
style="width: 100px; height: 100px; align-items: center; margin-top: 20px;">
<div style="flex-grow: 0; flex-shrink: 1; justify-content: center;">
<div style="flex-grow: 1; flex-shrink: 1;">
<div style="width: 20px; height: 20px;"></div>
@@ -178,7 +192,8 @@
</div>
</div>
<div id="align_stretch_should_size_based_on_parent" style="width: 100px; height: 100px; align-items: stretch; margin-top: 20px;">
<div id="align_stretch_should_size_based_on_parent"
style="width: 100px; height: 100px; align-items: stretch; margin-top: 20px;">
<div style="flex-grow: 0; flex-shrink: 1; justify-content: center;">
<div style="flex-grow: 1; flex-shrink: 1;">
<div style="width: 20px; height: 20px;"></div>
@@ -209,3 +224,19 @@
</div>
</div>
</div>
<div id="align_flex_end_with_row_reverse"
style="width: 100px; height: 75px; align-items: flex-end; flex-direction: column; flex-wrap: wrap;">
<div style="position:relative; width:50px; height:50px; margin-right: 5px; margin-left: 3px;">
</div>
<div style="position:relative; width:50px; height:50px;">
</div>
</div>
<div id="align_stretch_with_row_reverse"
style="width: 100px; height: 75px; align-items: stretch; flex-direction: column; flex-wrap: wrap;">
<div style="position:relative; width:50px; height:50px; margin-right: 5px; margin-left: 3px;">
</div>
<div style="position:relative; width:50px; height:50px;">
</div>
</div>

View File

@@ -0,0 +1,15 @@
<!-- TODO: aspect-ratio behavior is inconsistent with Web -->
<div id="aspect_ratio_does_not_stretch_cross_axis_dim" data-disabled="true" style="width: 300px; height: 300px;">
<div style="flex: 1; overflow: scroll;">
<div style="flex-direction: row;">
<div style="flex: 2; aspect-ratio: 1;"></div>
<div style="width: 5px"></div>
<div style="flex: 1">
<div style="flex: 1; aspect-ratio: 1;">
<div style="width: 5px"></div>
<div style="flex: 1; aspect-ratio: 1;"></div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -33,3 +33,370 @@
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_margin_left"
style="height: 100px; width: 100px; flex-direction: row-reverse; margin-left: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<!-- gentest.rb will swap margin-start to margin-left. This is needed to use YGEdgeStart instead of YGEdgeLeft in the generated tests -->
<div id="flex_direction_row_reverse_margin_start"
style="height: 100px; width: 100px; flex-direction: row-reverse; margin-start: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_margin_right"
style="height: 100px; width: 100px; flex-direction: row-reverse; margin-right: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_margin_end"
style="height: 100px; width: 100px; flex-direction: row-reverse; margin-end: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_margin_top"
style="height: 100px; width: 100px; flex-direction: column-reverse; margin-top: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_margin_bottom"
style="height: 100px; width: 100px; flex-direction: column-reverse; margin-bottom: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_padding_left"
style="height: 100px; width: 100px; flex-direction: row-reverse; padding-left: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_padding_start"
style="height: 100px; width: 100px; flex-direction: row-reverse; padding-start: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_padding_right"
style="height: 100px; width: 100px; flex-direction: row-reverse; padding-right: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_padding_end"
style="height: 100px; width: 100px; flex-direction: row-reverse; padding-end: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_padding_top"
style="height: 100px; width: 100px; flex-direction: column-reverse; padding-top: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_padding_bottom"
style="height: 100px; width: 100px; flex-direction: column-reverse; padding-bottom: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_border_left"
style="height: 100px; width: 100px; flex-direction: row-reverse; border-left-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_border_start"
style="height: 100px; width: 100px; flex-direction: row-reverse; border-start-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_border_right"
style="height: 100px; width: 100px; flex-direction: row-reverse; border-right-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_border_end"
style="height: 100px; width: 100px; flex-direction: row-reverse; border-end-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_border_top"
style="height: 100px; width: 100px; flex-direction: column-reverse; border-top-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_column_reverse_border_bottom"
style="height: 100px; width: 100px; flex-direction: column-reverse; border-bottom-width: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
<div id="flex_direction_row_reverse_pos_left" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; left: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_pos_start" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; start: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_pos_right" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; right: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_pos_end" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; end: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_column_reverse_pos_top" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; top: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_column_reverse_pos_bottom" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; bottom: 100px;">
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_pos_left" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; left: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_pos_right" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; right: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_pos_top" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; top: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_pos_bottom" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; bottom: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_pos_start" data-disabled="true" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; start: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_pos_end" data-disabled="true" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; end: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_margin_left" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-left: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_margin_right" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-right: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_margin_top" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-top: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_margin_bottom" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-bottom: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_marign_start" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-start: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_margin_end" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; margin-end: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_border_left" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-left: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_border_right" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-right: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_border_top" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-top: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_border_bottom" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-bottom: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_border_start" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-start: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_border_end" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; border-end: 10px solid black"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_padding_left" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-left: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_padding_right" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-right: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_padding_top" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-top: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_col_reverse_inner_padding_bottom" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: column-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-bottom: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_padding_start" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-start: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>
<div id="flex_direction_row_reverse_inner_padding_end" style="height: 100px; width: 100px;">
<div style="height: 100px; width: 100px; flex-direction: row-reverse; ">
<div style="width: 10px; height: 10px; position: absolute; padding-end: 10px"></div>
<div style="width: 10px;"></div>
<div style="width: 10px;"></div>
</div>
</div>

View File

@@ -147,3 +147,21 @@
</div>
</div>
</div>
<div id="wrap_with_min_cross_axis" style="width: 500px; min-height: 500px; flex-direction: row; flex-wrap: wrap;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="wrap_with_max_cross_axis" style="width: 500px; max-height: 500px; flex-direction: row; flex-wrap: wrap;">
<div style="width: 400px; height: 200px;"></div>
<div style="width: 400px; height: 200px;"></div>
</div>
<div id="nowrap_expands_flexline_box_to_min_cross" style="min-height: 400px; flex-direction: row; align-items: stretch; align-content: flex-start;">
<div style="flex: 1"></div>
</div>
<div id="wrap_does_not_impose_min_cross_onto_single_flexline" style="min-height: 400px; flex-direction: row; align-items: stretch; align-content: flex-start; flex-wrap: wrap;">
<div style="flex: 1"></div>
</div>

Some files were not shown because too many files have changed in this diff Show More