2023-05-09 22:21:01 -07:00
|
|
|
#!/usr/bin/env python3
|
2022-09-29 22:25:24 -07:00
|
|
|
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
2016-11-15 08:42:33 -08:00
|
|
|
#
|
2018-02-16 18:24:55 -08:00
|
|
|
# This source code is licensed under the MIT license found in the
|
|
|
|
# LICENSE file in the root directory of this source tree.
|
2016-11-15 08:42:33 -08:00
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
ENUMS = {
|
2019-04-11 05:19:00 -07:00
|
|
|
"Direction": ["Inherit", "LTR", "RTL"],
|
2024-11-25 00:40:03 -08:00
|
|
|
"Unit": ["Undefined", "Point", "Percent", "Auto"],
|
2019-04-11 05:19:00 -07:00
|
|
|
"FlexDirection": ["Column", "ColumnReverse", "Row", "RowReverse"],
|
|
|
|
"Justify": [
|
|
|
|
"FlexStart",
|
|
|
|
"Center",
|
|
|
|
"FlexEnd",
|
|
|
|
"SpaceBetween",
|
|
|
|
"SpaceAround",
|
|
|
|
"SpaceEvenly",
|
|
|
|
],
|
|
|
|
"Overflow": ["Visible", "Hidden", "Scroll"],
|
|
|
|
"Align": [
|
|
|
|
"Auto",
|
|
|
|
"FlexStart",
|
|
|
|
"Center",
|
|
|
|
"FlexEnd",
|
|
|
|
"Stretch",
|
|
|
|
"Baseline",
|
|
|
|
"SpaceBetween",
|
|
|
|
"SpaceAround",
|
2023-10-17 20:59:51 -07:00
|
|
|
"SpaceEvenly",
|
2019-04-11 05:19:00 -07:00
|
|
|
],
|
2022-09-29 22:25:24 -07:00
|
|
|
"PositionType": ["Static", "Relative", "Absolute"],
|
Add support for `display: contents` style (#1726)
Summary:
X-link: https://github.com/facebook/react-native/pull/47035
This PR adds support for `display: contents` style by effectively skipping nodes with `display: contents` set during layout.
This required changes in the logic related to children traversal - before this PR a node would be always laid out in the context of its direct parent. After this PR that assumption is no longer true - `display: contents` allows nodes to be skipped, i.e.:
```html
<div id="node1">
<div id="node2" style="display: contents;">
<div id="node3" />
</div>
</div>
```
`node3` will be laid out as if it were a child of `node1`.
Because of this, iterating over direct children of a node is no longer correct to achieve the correct layout. This PR introduces `LayoutableChildren::Iterator` which can traverse the subtree of a given node in a way that nodes with `display: contents` are replaced with their concrete children.
A tree like this:
```mermaid
flowchart TD
A((A))
B((B))
C((C))
D((D))
E((E))
F((F))
G((G))
H((H))
I((I))
J((J))
A --> B
A --> C
B --> D
B --> E
C --> F
D --> G
F --> H
G --> I
H --> J
style B fill:https://github.com/facebook/yoga/issues/050
style C fill:https://github.com/facebook/yoga/issues/050
style D fill:https://github.com/facebook/yoga/issues/050
style H fill:https://github.com/facebook/yoga/issues/050
style I fill:https://github.com/facebook/yoga/issues/050
```
would be laid out as if the green nodes (ones with `display: contents`) did not exist. It also changes the logic where children were accessed by index to use the iterator instead as random access would be non-trivial to implement and it's not really necessary - the iteration was always sequential and indices were only used as boundaries.
There's one place where knowledge of layoutable children is required to calculate the gap. An optimization for this is for a node to keep a counter of how many `display: contents` nodes are its children. If there are none, a short path of just returning the size of the children vector can be taken, otherwise it needs to iterate over layoutable children and count them, since the structure may be complex.
One more major change this PR introduces is `cleanupContentsNodesRecursively`. Since nodes with `display: contents` would be entirely skipped during the layout pass, they would keep previous metrics, would be kept as dirty, and, in the case of nested `contents` nodes, would not be cloned, breaking `doesOwn` relation. All of this is handled in the new method which clones `contents` nodes recursively, sets empty layout, and marks them as clean and having a new layout so that it can be used on the React Native side.
Relies on https://github.com/facebook/yoga/pull/1725
Changelog: [Internal]
Pull Request resolved: https://github.com/facebook/yoga/pull/1726
Test Plan: Added tests for `display: contents` based on existing tests for `display: none` and ensured that all the tests were passing.
Reviewed By: joevilches
Differential Revision: D64404340
Pulled By: NickGerleman
fbshipit-source-id: f6f6e9a6fad82873f18c8a0ead58aad897df5d09
2024-10-18 22:05:41 -07:00
|
|
|
"Display": ["Flex", "None", "Contents"],
|
2019-04-11 05:19:00 -07:00
|
|
|
"Wrap": ["NoWrap", "Wrap", "WrapReverse"],
|
2024-10-03 12:21:16 -07:00
|
|
|
"BoxSizing": ["BorderBox", "ContentBox"],
|
2019-04-11 05:19:00 -07:00
|
|
|
"MeasureMode": ["Undefined", "Exactly", "AtMost"],
|
|
|
|
"Dimension": ["Width", "Height"],
|
|
|
|
"Edge": [
|
|
|
|
"Left",
|
|
|
|
"Top",
|
|
|
|
"Right",
|
|
|
|
"Bottom",
|
|
|
|
"Start",
|
|
|
|
"End",
|
|
|
|
"Horizontal",
|
|
|
|
"Vertical",
|
|
|
|
"All",
|
|
|
|
],
|
|
|
|
"NodeType": ["Default", "Text"],
|
|
|
|
"LogLevel": ["Error", "Warn", "Info", "Debug", "Verbose", "Fatal"],
|
|
|
|
"ExperimentalFeature": [
|
2023-01-08 13:41:27 -08:00
|
|
|
# Mimic web flex-basis behavior (experiment may be broken)
|
|
|
|
"WebFlexBasis",
|
2016-11-15 08:42:33 -08:00
|
|
|
],
|
Add YGGutter Enum
Summary:
This adds the YGGutter enum, used to choose between row/column gap variants (row-gap, column-gap, gap).
This used later in changes from https://github.com/facebook/yoga/pull/1116, in the APIs which deal with setting gap on style on yoga node.
Note the original PR called this `YGGap`, but this ending up leading to a couple public method signatures that could appear ambiguous:
1. `SetGap(YGGap gap, float gapLength)`: Enums like `YGAlign` are the vaues for an `align` prop. `YGGap` controls the variant of the gap (like `YGEdge` does for left/right/top/bottom variants). So the enum reads as if it is the `gapValue`, and it looks like we have two of the same parameter.
2. `SetGap(YGGap gapDirection, float gap)`: This is misleading, because the direction gaps flow is the cross-axis of flex-direction.
3. `GetGap(YGGap gap)`: `gap` is the variant, but looks like an out param.
The [CSS Box Alignment](https://www.w3.org/TR/css-align-3/#column-row-gap) spec refers to these gaps as "Gutters", which removes the ambiguity.
Changelog:
[General][Added] - Add YGGutter Enum
Reviewed By: yungsters
Differential Revision: D39922412
fbshipit-source-id: 4b0baf800fecb3d03560a4267c7fb4c4330fd39e
2022-09-29 22:25:24 -07:00
|
|
|
"Gutter": ["Column", "Row", "All"],
|
2023-04-27 03:15:14 -07:00
|
|
|
# Known incorrect behavior which can be enabled for compatibility
|
|
|
|
"Errata": [
|
|
|
|
# Default: Standards conformant mode
|
|
|
|
("None", 0),
|
|
|
|
# Allows main-axis flex basis to be stretched without flexGrow being
|
|
|
|
# set (previously referred to as "UseLegacyStretchBehaviour")
|
|
|
|
("StretchFlexBasis", 1 << 0),
|
2024-10-17 22:40:16 -07:00
|
|
|
# Absolute position in a given axis will be relative to the padding
|
|
|
|
# edge of the parent container instead of the content edge when a
|
|
|
|
# specific inset (top/bottom/left/right) is not set.
|
|
|
|
("AbsolutePositionWithoutInsetsExcludesPadding", 1 << 1),
|
2024-01-18 21:22:05 -08:00
|
|
|
# Absolute nodes will resolve percentages against the inner size of
|
|
|
|
# their containing node, not the padding box
|
|
|
|
("AbsolutePercentAgainstInnerSize", 1 << 2),
|
2023-04-27 03:15:14 -07:00
|
|
|
# Enable all incorrect behavior (preserve compatibility)
|
|
|
|
("All", 0x7FFFFFFF),
|
|
|
|
# Enable all errata except for "StretchFlexBasis" (Defaults behavior
|
|
|
|
# before Yoga 2.0)
|
|
|
|
("Classic", 0x7FFFFFFF & (~(1 << 0))),
|
|
|
|
],
|
2016-11-15 08:42:33 -08:00
|
|
|
}
|
|
|
|
|
2022-09-29 22:25:24 -07:00
|
|
|
DO_NOT_STRIP = ["LogLevel"]
|
|
|
|
|
2024-02-09 16:44:32 -08:00
|
|
|
BITSET_ENUMS = ["Errata"]
|
2023-04-27 03:15:14 -07:00
|
|
|
|
2022-09-29 22:25:24 -07:00
|
|
|
|
|
|
|
def get_license(ext):
|
2023-05-09 22:21:01 -07:00
|
|
|
return f"""{"/**" if ext == "js" else "/*"}
|
2022-09-29 22:25:24 -07:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2016-11-15 08:42:33 -08:00
|
|
|
*
|
2018-02-16 18:24:55 -08:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-11-15 08:42:33 -08:00
|
|
|
*/
|
|
|
|
|
2023-05-09 22:21:01 -07:00
|
|
|
// @{"generated"} by enums.py
|
2023-09-14 23:06:34 -07:00
|
|
|
{"// clang-format off" if ext == "cpp" else ""}
|
2023-05-09 22:21:01 -07:00
|
|
|
"""
|
2016-11-15 08:42:33 -08:00
|
|
|
|
2019-04-11 05:19:00 -07:00
|
|
|
|
2023-05-09 22:21:01 -07:00
|
|
|
def _format_name(symbol, delimiter=None, transform=None):
|
2016-11-15 08:42:33 -08:00
|
|
|
symbol = str(symbol)
|
2019-04-11 05:19:00 -07:00
|
|
|
out = ""
|
2016-11-15 08:42:33 -08:00
|
|
|
for i in range(0, len(symbol)):
|
|
|
|
c = symbol[i]
|
2023-05-09 22:21:01 -07:00
|
|
|
if str.istitle(c) and i != 0 and not str.istitle(symbol[i - 1]):
|
|
|
|
out += delimiter or ""
|
|
|
|
if transform is None:
|
|
|
|
out += c
|
|
|
|
else:
|
|
|
|
out += getattr(c, transform)()
|
2016-11-15 08:42:33 -08:00
|
|
|
return out
|
|
|
|
|
2019-04-11 05:19:00 -07:00
|
|
|
|
2023-05-09 22:21:01 -07:00
|
|
|
def to_java_upper(symbol):
|
|
|
|
return _format_name(symbol, "_", "upper")
|
|
|
|
|
|
|
|
|
|
|
|
def to_hyphenated_lower(symbol):
|
|
|
|
return _format_name(symbol, "-", "lower")
|
2017-04-03 09:34:42 -07:00
|
|
|
|
2016-11-15 08:42:33 -08:00
|
|
|
|
2016-12-22 02:57:19 -08:00
|
|
|
root = os.path.dirname(os.path.abspath(__file__))
|
2016-11-15 08:42:33 -08:00
|
|
|
|
2017-01-08 11:42:43 -08:00
|
|
|
# write out C & Objective-C headers
|
2019-04-11 05:19:00 -07:00
|
|
|
with open(root + "/yoga/YGEnums.h", "w") as f:
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(get_license("cpp"))
|
|
|
|
f.write("#pragma once\n")
|
2023-05-04 00:41:12 -07:00
|
|
|
f.write("#include <yoga/YGMacros.h>\n\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
|
2022-10-17 23:35:01 -07:00
|
|
|
f.write("YG_EXTERN_C_BEGIN\n\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
items = sorted(ENUMS.items())
|
|
|
|
for name, values in items:
|
2023-11-27 21:20:20 -08:00
|
|
|
f.write("YG_ENUM_DECL(\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
|
|
|
|
f.write(" YG%s,\n" % name)
|
2016-11-15 08:42:33 -08:00
|
|
|
for value in values:
|
|
|
|
if isinstance(value, tuple):
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(" YG%s%s = %d" % (name, value[0], value[1]))
|
2016-11-15 08:42:33 -08:00
|
|
|
else:
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(" YG%s%s" % (name, value))
|
|
|
|
if value == values[-1]:
|
|
|
|
f.write(")\n")
|
|
|
|
else:
|
|
|
|
f.write(",\n")
|
2023-04-27 03:15:14 -07:00
|
|
|
if name in BITSET_ENUMS:
|
|
|
|
f.write("YG_DEFINE_ENUM_FLAG_OPERATORS(YG%s)\n" % name)
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write("\n")
|
|
|
|
f.write("YG_EXTERN_C_END\n")
|
2016-11-15 08:42:33 -08:00
|
|
|
|
2023-09-14 23:06:34 -07:00
|
|
|
# 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")
|
2023-11-22 11:06:31 -08:00
|
|
|
|
|
|
|
if name in BITSET_ENUMS:
|
|
|
|
f.write(f"YG_DEFINE_ENUM_FLAG_OPERATORS({name})\n\n")
|
|
|
|
else:
|
|
|
|
f.write("template <>\n")
|
2023-12-12 08:52:11 -08:00
|
|
|
f.write(f"constexpr int32_t ordinalCount<{name}>() {{\n")
|
2023-11-22 11:06:31 -08:00
|
|
|
f.write(f" return {len(values)};\n")
|
2023-12-12 08:52:11 -08:00
|
|
|
f.write("}\n\n")
|
2023-11-22 11:06:31 -08:00
|
|
|
|
2023-12-12 08:52:11 -08:00
|
|
|
f.write(f"constexpr {name} scopedEnum(YG{name} unscoped) {{\n")
|
2023-09-14 23:06:34 -07:00
|
|
|
f.write(f" return static_cast<{name}>(unscoped);\n")
|
|
|
|
f.write("}\n\n")
|
|
|
|
|
2023-12-12 08:52:11 -08:00
|
|
|
f.write(f"constexpr YG{name} unscopedEnum({name} scoped) {{\n")
|
2023-09-14 23:06:34 -07:00
|
|
|
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")
|
|
|
|
|
2017-04-03 09:34:42 -07:00
|
|
|
# write out C body for printing
|
2019-04-11 05:19:00 -07:00
|
|
|
with open(root + "/yoga/YGEnums.cpp", "w") as f:
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(get_license("cpp"))
|
2023-05-04 00:41:12 -07:00
|
|
|
f.write("#include <yoga/YGEnums.h>\n\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
items = sorted(ENUMS.items())
|
|
|
|
for name, values in items:
|
|
|
|
f.write("const char* YG%sToString(const YG%s value) {\n" % (name, name))
|
|
|
|
f.write(" switch (value) {\n")
|
2017-04-03 09:34:42 -07:00
|
|
|
for value in values:
|
|
|
|
if isinstance(value, tuple):
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" case YG%s%s:\n" % (name, value[0]))
|
2023-05-09 22:21:01 -07:00
|
|
|
f.write(' return "%s";\n' % to_hyphenated_lower(value[0]))
|
2017-04-03 09:34:42 -07:00
|
|
|
else:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" case YG%s%s:\n" % (name, value))
|
2023-05-09 22:21:01 -07:00
|
|
|
f.write(' return "%s";\n' % to_hyphenated_lower(value))
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" }\n")
|
2017-04-03 09:34:42 -07:00
|
|
|
f.write(' return "unknown";\n')
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write("}\n")
|
|
|
|
if name != items[-1][0]:
|
|
|
|
f.write("\n")
|
2017-04-03 09:34:42 -07:00
|
|
|
|
2016-11-15 08:42:33 -08:00
|
|
|
# write out java files
|
2017-02-03 04:21:50 -08:00
|
|
|
for name, values in sorted(ENUMS.items()):
|
2019-04-11 05:19:00 -07:00
|
|
|
with open(root + "/java/com/facebook/yoga/Yoga%s.java" % name, "w") as f:
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(get_license("java"))
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write("package com.facebook.yoga;\n\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
if name in DO_NOT_STRIP:
|
2023-01-16 11:25:59 -08:00
|
|
|
f.write("import com.facebook.yoga.annotations.DoNotStrip;\n\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write("@DoNotStrip\n")
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write("public enum Yoga%s {\n" % name)
|
2016-11-15 08:42:33 -08:00
|
|
|
if len(values) > 0:
|
|
|
|
for value in values:
|
|
|
|
if isinstance(value, tuple):
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" %s(%d)" % (to_java_upper(value[0]), value[1]))
|
2016-11-15 08:42:33 -08:00
|
|
|
else:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" %s(%d)" % (to_java_upper(value), values.index(value)))
|
2016-11-15 08:42:33 -08:00
|
|
|
if values.index(value) is len(values) - 1:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(";\n")
|
2016-11-15 08:42:33 -08:00
|
|
|
else:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(",\n")
|
2016-11-15 08:42:33 -08:00
|
|
|
else:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write("__EMPTY(-1);")
|
|
|
|
f.write("\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(" private final int mIntValue;\n")
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write("\n")
|
|
|
|
f.write(" Yoga%s(int intValue) {\n" % name)
|
|
|
|
f.write(" mIntValue = intValue;\n")
|
|
|
|
f.write(" }\n")
|
|
|
|
f.write("\n")
|
|
|
|
f.write(" public int intValue() {\n")
|
|
|
|
f.write(" return mIntValue;\n")
|
|
|
|
f.write(" }\n")
|
|
|
|
f.write("\n")
|
2022-09-29 22:25:24 -07:00
|
|
|
if name in DO_NOT_STRIP:
|
|
|
|
f.write(" @DoNotStrip\n")
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(" public static Yoga%s fromInt(int value) {\n" % name)
|
|
|
|
f.write(" switch (value) {\n")
|
2016-11-15 08:42:33 -08:00
|
|
|
for value in values:
|
|
|
|
if isinstance(value, tuple):
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(
|
|
|
|
" case %d: return %s;\n" % (value[1], to_java_upper(value[0]))
|
|
|
|
)
|
2016-11-15 08:42:33 -08:00
|
|
|
else:
|
2019-04-11 05:19:00 -07:00
|
|
|
f.write(
|
|
|
|
" case %d: return %s;\n"
|
|
|
|
% (values.index(value), to_java_upper(value))
|
|
|
|
)
|
|
|
|
f.write(
|
|
|
|
' default: throw new IllegalArgumentException("Unknown enum value: " + value);\n'
|
|
|
|
)
|
|
|
|
f.write(" }\n")
|
|
|
|
f.write(" }\n")
|
|
|
|
f.write("}\n")
|
2016-11-15 08:42:33 -08:00
|
|
|
|
2023-05-09 22:21:01 -07:00
|
|
|
# write out TypeScript file
|
|
|
|
with open(root + "/javascript/src/generated/YGEnums.ts", "w") as f:
|
2022-09-29 22:25:24 -07:00
|
|
|
f.write(get_license("js"))
|
2023-05-09 22:21:01 -07:00
|
|
|
enums = sorted(ENUMS.items())
|
|
|
|
for enum_name, ordinals in enums:
|
|
|
|
f.write(f"export enum {enum_name} {{\n")
|
|
|
|
for ordinal_index, ordinal in enumerate(ordinals):
|
|
|
|
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
|
|
|
|
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
|
|
|
|
f.write(f" {ordinal_name} = {ordinal_value},\n")
|
|
|
|
|
|
|
|
f.write("}\n\n")
|
|
|
|
|
|
|
|
f.write("const constants = {\n")
|
|
|
|
for enum_name, ordinals in enums:
|
|
|
|
for ordinal in ordinals:
|
|
|
|
ordinal_name = ordinal[0] if isinstance(ordinal, tuple) else ordinal
|
|
|
|
ordinal_value = ordinal[1] if isinstance(ordinal, tuple) else ordinal_index
|
2022-12-28 01:27:12 -08:00
|
|
|
f.write(
|
2023-05-09 22:21:01 -07:00
|
|
|
f" {to_java_upper(enum_name)}_{to_java_upper(ordinal_name)}: {enum_name}.{ordinal_name},\n"
|
2022-12-28 01:27:12 -08:00
|
|
|
)
|
2022-09-29 22:25:24 -07:00
|
|
|
|
2023-05-09 22:21:01 -07:00
|
|
|
f.write("}\n")
|
|
|
|
f.write("export default constants")
|