From b5117bf6ea74db0eba47ee1416da008a8ec9f849 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Wed, 4 Mar 2020 12:30:01 -0800 Subject: [PATCH] Fabric: New two ShadowNode Traits: FormsStackingContext & FormsView Summary: This diff introduces two new `ShadowNodeTrait`s that we will use in the future in the new (slightly tweaked) view-flattening algorithm. (Note: this diff does not enable the new flattening, it's just preparation.) The idea is that we split the notion of `isLayoutOnlyView` into two traits: * `FormsView`: `ShadowNode`s with this trait must be represented as `ShadowView`s. Normal "visible" ShadowNodes will have this trait, but "layout only views" in old nomenclature will not. * `FormsStackingContext`: `ShadowNode`s with this thread not only must be represented as `ShadowView`s but also have to form a "stacking context" which means that their children must be mounted as `ShadowView`'s children. Our implementation does not exactly follow W3C spec in terms of sets of props that create the stacking context (because of historical reasons and mobile specifics) but ideologically it's the same. We start from the very conservative implementation where only views with background-color and borders do not form stacking context and then we probably extend that to more props. Most importantly for us now, we will enforce the absence of ``FormsStackingContext` for `ParagraphShadowNode` and the presence of `FormsView` for `TextShadowNode` on Android where it's essential for mounting layer. Read more about stacking context here: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D20212253 fbshipit-source-id: 0fbaee214ce2c5886cb0232843a2a3c7bb20655d --- .../text/paragraph/ParagraphShadowNode.h | 1 + .../components/text/text/TextShadowNode.h | 11 ++++ .../components/view/ConcreteViewShadowNode.h | 2 + .../fabric/components/view/ViewShadowNode.cpp | 53 +++++++++++++++++++ .../fabric/components/view/ViewShadowNode.h | 12 ++++- .../fabric/core/shadownode/ShadowNode.h | 1 + .../fabric/core/shadownode/ShadowNodeTraits.h | 8 +++ 7 files changed, 87 insertions(+), 1 deletion(-) diff --git a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.h b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.h index c6513462cf9..e82fd18295e 100644 --- a/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.h +++ b/ReactCommon/fabric/components/text/paragraph/ParagraphShadowNode.h @@ -41,6 +41,7 @@ class ParagraphShadowNode : public ConcreteViewShadowNode< static ShadowNodeTraits BaseTraits() { auto traits = ConcreteViewShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::LeafYogaNode); + return traits; } diff --git a/ReactCommon/fabric/components/text/text/TextShadowNode.h b/ReactCommon/fabric/components/text/text/TextShadowNode.h index c6824e55ce8..8841e73d89e 100644 --- a/ReactCommon/fabric/components/text/text/TextShadowNode.h +++ b/ReactCommon/fabric/components/text/text/TextShadowNode.h @@ -26,6 +26,17 @@ class TextShadowNode : public ConcreteShadowNode< TextEventEmitter>, public BaseTextShadowNode { public: + static ShadowNodeTraits BaseTraits() { + auto traits = ConcreteShadowNode::BaseTraits(); + +#ifdef ANDROID + traits.set(ShadowNodeTraits::Trait::FormsView); + traits.set(ShadowNodeTraits::Trait::FormsStackingContext); +#endif + + return traits; + } + using ConcreteShadowNode::ConcreteShadowNode; }; diff --git a/ReactCommon/fabric/components/view/ConcreteViewShadowNode.h b/ReactCommon/fabric/components/view/ConcreteViewShadowNode.h index fbe6d32dcad..8ae629b3dcd 100644 --- a/ReactCommon/fabric/components/view/ConcreteViewShadowNode.h +++ b/ReactCommon/fabric/components/view/ConcreteViewShadowNode.h @@ -59,6 +59,8 @@ class ConcreteViewShadowNode : public ConcreteShadowNode< static ShadowNodeTraits BaseTraits() { auto traits = BaseShadowNode::BaseTraits(); traits.set(ShadowNodeTraits::Trait::ViewKind); + traits.set(ShadowNodeTraits::Trait::FormsStackingContext); + traits.set(ShadowNodeTraits::Trait::FormsView); return traits; } diff --git a/ReactCommon/fabric/components/view/ViewShadowNode.cpp b/ReactCommon/fabric/components/view/ViewShadowNode.cpp index 3dd1cb622da..d00b4e3be8e 100644 --- a/ReactCommon/fabric/components/view/ViewShadowNode.cpp +++ b/ReactCommon/fabric/components/view/ViewShadowNode.cpp @@ -33,5 +33,58 @@ bool ViewShadowNode::isLayoutOnly() const { getLayoutMetrics().borderWidth == EdgeInsets{}; } +ViewShadowNode::ViewShadowNode( + ShadowNodeFragment const &fragment, + ShadowNodeFamily::Shared const &family, + ShadowNodeTraits traits) + : ConcreteViewShadowNode(fragment, family, traits) { + updateTraits(); +} + +ViewShadowNode::ViewShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment) + : ConcreteViewShadowNode(sourceShadowNode, fragment) { + updateTraits(); +} + +static bool isColorMeaningful(SharedColor const &color) { + if (!color) { + return false; + } + + return colorComponentsFromColor(color).alpha > 0; +} + +void ViewShadowNode::updateTraits() { + auto &viewProps = static_cast(*props_); + + bool formsStackingContext = !viewProps.collapsable || + viewProps.pointerEvents == PointerEventsMode::None || + !viewProps.nativeId.empty() || viewProps.accessible || + viewProps.opacity != 1.0 || viewProps.transform != Transform{} || + viewProps.zIndex != 0 || viewProps.getClipsContentToBounds() || + viewProps.yogaStyle.positionType() == YGPositionTypeAbsolute; + + bool formsView = isColorMeaningful(viewProps.backgroundColor) || + isColorMeaningful(viewProps.foregroundColor) || + isColorMeaningful(viewProps.shadowColor) || + !(viewProps.yogaStyle.border() == YGStyle::Edges{}); + + formsView = formsView || formsStackingContext; + + if (formsView) { + traits_.set(ShadowNodeTraits::Trait::FormsView); + } else { + traits_.unset(ShadowNodeTraits::Trait::FormsView); + } + + if (formsStackingContext) { + traits_.set(ShadowNodeTraits::Trait::FormsStackingContext); + } else { + traits_.unset(ShadowNodeTraits::Trait::FormsStackingContext); + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/fabric/components/view/ViewShadowNode.h b/ReactCommon/fabric/components/view/ViewShadowNode.h index daffb630fd9..f29b6760bd3 100644 --- a/ReactCommon/fabric/components/view/ViewShadowNode.h +++ b/ReactCommon/fabric/components/view/ViewShadowNode.h @@ -23,9 +23,19 @@ class ViewShadowNode final : public ConcreteViewShadowNode< ViewProps, ViewEventEmitter> { public: - using ConcreteViewShadowNode::ConcreteViewShadowNode; + ViewShadowNode( + ShadowNodeFragment const &fragment, + ShadowNodeFamily::Shared const &family, + ShadowNodeTraits traits); + + ViewShadowNode( + ShadowNode const &sourceShadowNode, + ShadowNodeFragment const &fragment); bool isLayoutOnly() const; + + private: + void updateTraits(); }; } // namespace react diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index 66d2c6ce218..cb3b265a1e1 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -207,6 +207,7 @@ class ShadowNode : public Sealable, public DebugStringConvertible { */ ShadowNodeFamily::Shared family_; + protected: /* * Traits associated with the particular `ShadowNode` class and an instance of * that class. diff --git a/ReactCommon/fabric/core/shadownode/ShadowNodeTraits.h b/ReactCommon/fabric/core/shadownode/ShadowNodeTraits.h index 46ad563d712..607d308e96d 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNodeTraits.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNodeTraits.h @@ -57,6 +57,14 @@ class ShadowNodeTraits { // Inherits `LayoutableShadowNode` and calls `measure()`. HasMeasure = 1 << 11, + // Indicates that the `ShadowNode` must form a stacking context (a level + // of the hierarchy; `ShadowView`s formed by descendants the node will be + // descendants of a `ShadowView` formed by the node). + FormsStackingContext = 1 << 13, + + // Indicates that the node must form a `ShadowView`. + FormsView = 1 << 14, + // Internal to `ShadowNode`; do not use it outside. // Indicates that `children` list is shared between nodes and need // to be cloned before the first mutation.