From 965e60b05aee4782d0da85baaaabd3e387bb06ee Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Sun, 18 Mar 2018 19:04:25 -0700 Subject: [PATCH] `fabric/view` module Summary: Defines ``: Yoga-powered, Accessible and styleable component. Reviewed By: fkgozali Differential Revision: D7230673 fbshipit-source-id: 08a1d8626c0b41260fafdca938d4fe9489b1b793 --- ReactCommon/fabric/BUCK | 3 +- ReactCommon/fabric/core/BUCK | 2 +- ReactCommon/fabric/graphics/Geometry.h | 12 +- ReactCommon/fabric/view/BUCK | 52 +++ .../fabric/view/ViewComponentDescriptor.h | 24 ++ ReactCommon/fabric/view/ViewProps.cpp | 75 +++++ ReactCommon/fabric/view/ViewProps.h | 48 +++ ReactCommon/fabric/view/ViewShadowNode.cpp | 104 ++++++ ReactCommon/fabric/view/ViewShadowNode.h | 63 ++++ .../accessibility/AccessibilityPrimitives.h | 38 +++ .../view/accessibility/AccessibilityProps.cpp | 31 ++ .../view/accessibility/AccessibilityProps.h | 42 +++ .../accessibility/AccessibleShadowNode.cpp | 29 ++ .../view/accessibility/AccessibleShadowNode.h | 41 +++ .../accessibilityValuesConversions.cpp | 23 ++ .../accessibilityValuesConversions.h | 20 ++ .../view/yoga/YogaLayoutableShadowNode.cpp | 228 +++++++++++++ .../view/yoga/YogaLayoutableShadowNode.h | 93 ++++++ .../fabric/view/yoga/YogaStylableProps.cpp | 148 +++++++++ .../fabric/view/yoga/YogaStylableProps.h | 39 +++ .../view/yoga/yogaValuesConversions.cpp | 303 ++++++++++++++++++ .../fabric/view/yoga/yogaValuesConversions.h | 57 ++++ 22 files changed, 1470 insertions(+), 5 deletions(-) create mode 100644 ReactCommon/fabric/view/BUCK create mode 100644 ReactCommon/fabric/view/ViewComponentDescriptor.h create mode 100644 ReactCommon/fabric/view/ViewProps.cpp create mode 100644 ReactCommon/fabric/view/ViewProps.h create mode 100644 ReactCommon/fabric/view/ViewShadowNode.cpp create mode 100644 ReactCommon/fabric/view/ViewShadowNode.h create mode 100644 ReactCommon/fabric/view/accessibility/AccessibilityPrimitives.h create mode 100644 ReactCommon/fabric/view/accessibility/AccessibilityProps.cpp create mode 100644 ReactCommon/fabric/view/accessibility/AccessibilityProps.h create mode 100644 ReactCommon/fabric/view/accessibility/AccessibleShadowNode.cpp create mode 100644 ReactCommon/fabric/view/accessibility/AccessibleShadowNode.h create mode 100644 ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.cpp create mode 100644 ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.h create mode 100644 ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.cpp create mode 100644 ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.h create mode 100644 ReactCommon/fabric/view/yoga/YogaStylableProps.cpp create mode 100644 ReactCommon/fabric/view/yoga/YogaStylableProps.h create mode 100644 ReactCommon/fabric/view/yoga/yogaValuesConversions.cpp create mode 100644 ReactCommon/fabric/view/yoga/yogaValuesConversions.h diff --git a/ReactCommon/fabric/BUCK b/ReactCommon/fabric/BUCK index 6788c76801c..162ae5ec423 100644 --- a/ReactCommon/fabric/BUCK +++ b/ReactCommon/fabric/BUCK @@ -1,4 +1,4 @@ -load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags") +load("//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags") load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS") APPLE_COMPILER_FLAGS = [] @@ -40,5 +40,6 @@ rn_xplat_cxx_library( "xplat//folly:molly", "xplat//third-party/glog:glog", react_native_xplat_target("fabric/core:core"), + react_native_xplat_target("fabric/view:view"), ], ) diff --git a/ReactCommon/fabric/core/BUCK b/ReactCommon/fabric/core/BUCK index 932e86410f1..f566a70f6ee 100644 --- a/ReactCommon/fabric/core/BUCK +++ b/ReactCommon/fabric/core/BUCK @@ -1,4 +1,4 @@ -load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags") +load("//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags") load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS") APPLE_COMPILER_FLAGS = [] diff --git a/ReactCommon/fabric/graphics/Geometry.h b/ReactCommon/fabric/graphics/Geometry.h index 915cc3b7683..dcaf88f43f5 100644 --- a/ReactCommon/fabric/graphics/Geometry.h +++ b/ReactCommon/fabric/graphics/Geometry.h @@ -2,15 +2,21 @@ #pragma once +#include + namespace facebook { namespace react { /* * Exact type of float numbers which ideally should match a type behing - * platform- and chip-architecture-specific float type (something like - * CGFloat on iOS). + * platform- and chip-architecture-specific float type. */ -using Float = double; +using Float = CGFloat; + +/* + * Large positive number signifies that the `Float` values is `undefined`. + */ +const Float kFloatUndefined = CGFLOAT_MAX; /* * Point diff --git a/ReactCommon/fabric/view/BUCK b/ReactCommon/fabric/view/BUCK new file mode 100644 index 00000000000..3db0dd82bf7 --- /dev/null +++ b/ReactCommon/fabric/view/BUCK @@ -0,0 +1,52 @@ +load("//configurations/buck/apple:flag_defs.bzl", "get_application_ios_flags", "get_debug_preprocessor_flags", "OBJC_ARC_PREPROCESSOR_FLAGS") +load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS") +load("//ReactNative:DEFS.bzl", "react_native_xplat_target") + +CXX_LIBRARY_COMPILER_FLAGS = [ + "-std=c++14", + "-Wall", +] + +rn_xplat_cxx_library( + name = "view", + srcs = glob( + [ + "**/*.cpp", + ], + ), + headers = glob( + [ + "**/*.h", + ], + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ("accessibility", "*.h"), + ("yoga", "*.h"), + ], + prefix = "fabric/view", + ), + compiler_flags = CXX_LIBRARY_COMPILER_FLAGS + [ + "-fexceptions", + "-frtti", + ], + force_static = True, + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = ["PUBLIC"], + deps = [ + "xplat//fbsystrace:fbsystrace", + "xplat//folly:headers_only", + "xplat//folly:memory", + "xplat//folly:molly", + "xplat//third-party/glog:glog", + "xplat//yoga:yoga", + react_native_xplat_target("fabric/debug:debug"), + react_native_xplat_target("fabric/core:core"), + react_native_xplat_target("fabric/graphics:graphics"), + ], +) diff --git a/ReactCommon/fabric/view/ViewComponentDescriptor.h b/ReactCommon/fabric/view/ViewComponentDescriptor.h new file mode 100644 index 00000000000..1db26dc3b5b --- /dev/null +++ b/ReactCommon/fabric/view/ViewComponentDescriptor.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include + +namespace facebook { +namespace react { + +class ViewComponentDescriptor: public ConcreteComponentDescriptor { +public: + ComponentName getComponentName() const override { + return "View"; + } +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/ViewProps.cpp b/ReactCommon/fabric/view/ViewProps.cpp new file mode 100644 index 00000000000..e3f9f6fddc0 --- /dev/null +++ b/ReactCommon/fabric/view/ViewProps.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ViewProps.h" + +#include +#include + +namespace facebook { +namespace react { + +void ViewProps::apply(const RawProps &rawProps) { + Props::apply(rawProps); + YogaStylableProps::apply(rawProps); + + for (auto const &pair : rawProps) { + auto const &name = pair.first; + auto const &value = pair.second; + + #pragma mark View Specific Properties + + if (name == "zIndex") { + zIndex_ = value.asInt(); + continue; + } + + if (name == "opacity") { + opacity_ = value.asDouble(); + continue; + } + + if (name == "color") { + foregroundColor_ = colorFromDynamic(value); + continue; + } + + if (name == "backgroundColor") { + backgroundColor_ = colorFromDynamic(value); + continue; + } + } +} + +SharedDebugStringConvertibleList ViewProps::getDebugProps() const { + ViewProps defaultProps = {}; + + SharedDebugStringConvertibleList list = {}; + +#define VIEW_PROPS_ADD_TO_SET(stringName, propertyName, accessor, convertor) \ + if (propertyName != defaultProps.propertyName) { \ + list.push_back(std::make_shared(#stringName, convertor(propertyName accessor))); \ + } + + VIEW_PROPS_ADD_TO_SET(zIndex, zIndex_, , std::to_string) + VIEW_PROPS_ADD_TO_SET(opacity, opacity_, , std::to_string) + + VIEW_PROPS_ADD_TO_SET(backgroundColor, backgroundColor_, , colorNameFromColor) + VIEW_PROPS_ADD_TO_SET(foregroundColor, foregroundColor_, , colorNameFromColor) + + // Accessibility Props + auto accessibilityPropsList = AccessibilityProps::getDebugProps(); + std::move(accessibilityPropsList.begin(), accessibilityPropsList.end(), std::back_inserter(list)); + + // Yoga styles + list.push_back(std::make_shared("style", "", YogaStylableProps::getDebugProps())); + + return list; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/ViewProps.h b/ReactCommon/fabric/view/ViewProps.h new file mode 100644 index 00000000000..89b20dec799 --- /dev/null +++ b/ReactCommon/fabric/view/ViewProps.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ViewProps; + +using SharedViewProps = std::shared_ptr; + +class ViewProps: + public Props, + public YogaStylableProps, + public AccessibilityProps { + +public: + void apply(const RawProps &rawProps) override; + +private: + int zIndex_ {0}; + float opacity_ {1.0}; + + SharedColor foregroundColor_ {nullptr}; + SharedColor backgroundColor_ {nullptr}; + + SharedColor shadowColor_ {nullptr}; + Point shadowOffset_ {0, 0}; + +#pragma mark - DebugStringConvertible + + SharedDebugStringConvertibleList getDebugProps() const override; +}; + +} // namespace react +} // namespace facebook + diff --git a/ReactCommon/fabric/view/ViewShadowNode.cpp b/ReactCommon/fabric/view/ViewShadowNode.cpp new file mode 100644 index 00000000000..7b07fec2277 --- /dev/null +++ b/ReactCommon/fabric/view/ViewShadowNode.cpp @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ViewShadowNode.h" + +#include + +namespace facebook { +namespace react { + +#pragma mark - Constructors + +ViewShadowNode::ViewShadowNode( + const Tag &tag, + const Tag &rootTag, + const InstanceHandle &instanceHandle, + const SharedViewProps &props, + const SharedShadowNodeSharedList &children +): + ConcreteShadowNode( + tag, + rootTag, + instanceHandle, + props, + children + ), + AccessibleShadowNode( + props + ), + YogaLayoutableShadowNode( + props, + children + ) {}; + +ViewShadowNode::ViewShadowNode( + const SharedViewShadowNode &shadowNode, + const SharedViewProps &props, + const SharedShadowNodeSharedList &children +): + ConcreteShadowNode( + shadowNode, + props, + children + ), + AccessibleShadowNode( + shadowNode, + props + ), + YogaLayoutableShadowNode( + shadowNode, + props, + children + ) {}; + +ComponentName ViewShadowNode::getComponentName() const { + return ComponentName("View"); +} + +void ViewShadowNode::appendChild(const SharedShadowNode &child) { + ensureUnsealed(); + + ShadowNode::appendChild(child); + + auto yogaLayoutableChild = std::dynamic_pointer_cast(child); + if (yogaLayoutableChild) { + YogaLayoutableShadowNode::appendChild(yogaLayoutableChild); + } +} + +#pragma mark - YogaLayoutableShadowNode + +SharedLayoutableShadowNodeList ViewShadowNode::getChildren() const { + SharedLayoutableShadowNodeList sharedLayoutableShadowNodeList = {}; + for (auto child : *children_) { + const SharedLayoutableShadowNode layoutableShadowNode = std::dynamic_pointer_cast(child); + if (!layoutableShadowNode) { + continue; + } + + sharedLayoutableShadowNodeList.push_back(layoutableShadowNode); + } + + return sharedLayoutableShadowNodeList; +} + +#pragma mark - DebugStringConvertible + +SharedDebugStringConvertibleList ViewShadowNode::getDebugProps() const { + SharedDebugStringConvertibleList list = {}; + + auto basePropsList = ShadowNode::getDebugProps(); + std::move(basePropsList.begin(), basePropsList.end(), std::back_inserter(list)); + + list.push_back(std::make_shared("layout", "", YogaLayoutableShadowNode::getDebugProps())); + + return list; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/ViewShadowNode.h b/ReactCommon/fabric/view/ViewShadowNode.h new file mode 100644 index 00000000000..fffaaa20c98 --- /dev/null +++ b/ReactCommon/fabric/view/ViewShadowNode.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class ViewShadowNode; + +using SharedViewShadowNode = std::shared_ptr; + +class ViewShadowNode: + public ConcreteShadowNode, + public AccessibleShadowNode, + public YogaLayoutableShadowNode { + + static_assert(std::is_base_of::value, "ViewProps must be a descendant of YogaStylableProps"); + static_assert(std::is_base_of::value, "ViewProps must be a descendant of AccessibilityProps"); + +public: + ViewShadowNode( + const Tag &tag, + const Tag &rootTag, + const InstanceHandle &instanceHandle, + const SharedViewProps &props = ViewShadowNode::defaultSharedProps(), + const SharedShadowNodeSharedList &children = ShadowNode::emptySharedShadowNodeSharedList() + ); + + ViewShadowNode( + const SharedViewShadowNode &shadowNode, + const SharedViewProps &props = nullptr, + const SharedShadowNodeSharedList &children = nullptr + ); + + ComponentName getComponentName() const override; + + void appendChild(const SharedShadowNode &child); + +#pragma mark - DebugStringConvertible + + SharedDebugStringConvertibleList getDebugProps() const override; + +private: + +#pragma mark - LayoutableShadowNode + + SharedLayoutableShadowNodeList getChildren() const override; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/AccessibilityPrimitives.h b/ReactCommon/fabric/view/accessibility/AccessibilityPrimitives.h new file mode 100644 index 00000000000..60e73f1c485 --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/AccessibilityPrimitives.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace react { + +enum class AccessibilityTraits: uint32_t { + None = 0, + Button = (1 << 0), + Link = (1 << 1), + Image = (1 << 2), + Selected = (1 << 3), + PlaysSound = (1 << 4), + KeyboardKey = (1 << 5), + StaticText = (1 << 6), + SummaryElement = (1 << 7), + NotEnabled = (1 << 8), + UpdatesFrequently = (1 << 9), + SearchField = (1 << 10), + StartsMediaSession = (1 << 11), + Adjustable = (1 << 12), + DirectInteraction = (1 << 13), + CausesPageTurn = (1 << 14), + Header = (1 << 15), +}; + +constexpr enum AccessibilityTraits operator |(const enum AccessibilityTraits lhs, const enum AccessibilityTraits rhs) { + return (enum AccessibilityTraits)((uint32_t)lhs | (uint32_t)rhs); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/AccessibilityProps.cpp b/ReactCommon/fabric/view/accessibility/AccessibilityProps.cpp new file mode 100644 index 00000000000..373b4d9412c --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/AccessibilityProps.cpp @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "AccessibilityProps.h" + +#include "accessibilityValuesConversions.h" + +namespace facebook { +namespace react { + +void AccessibilityProps::apply(const RawProps &rawProps) { + for (auto const &pair : rawProps) { + auto const &name = pair.first; + auto const &value = pair.second; + +#define ACCESSIBILITY_PROPERTY(stringName, variableName, accessor, convertor) \ + if (name == #stringName) { \ + variableName = convertor(value accessor); \ + continue; \ + } + + ACCESSIBILITY_PROPERTY(accessibilityLabel, accessibilityLabel_, .asString(),) + } +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/AccessibilityProps.h b/ReactCommon/fabric/view/accessibility/AccessibilityProps.h new file mode 100644 index 00000000000..12d1dbaf0a7 --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/AccessibilityProps.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include +#include +#include + +namespace facebook { +namespace react { + +class AccessibilityProps; + +typedef std::shared_ptr SharedAccessibilityProps; + +class AccessibilityProps: + public virtual DebugStringConvertible +{ + +public: + void apply(const RawProps &rawProps); + +protected: + bool accessible_ {true}; + std::string accessibilityActions_ {""}; + std::string accessibilityLabel_ {""}; + AccessibilityTraits accessibilityTraits_ {AccessibilityTraits::None}; + bool accessibilityViewIsModal_ {false}; + bool accessibilityElementsHidden_ {false}; + SharedDirectEventHandler onAccessibilityAction_ {nullptr}; + SharedDirectEventHandler onAccessibilityTap_ {nullptr}; + SharedDirectEventHandler onMagicTap_ {nullptr}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.cpp b/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.cpp new file mode 100644 index 00000000000..4941cd49e28 --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "AccessibleShadowNode.h" + +#include + +namespace facebook { +namespace react { + +AccessibleShadowNode::AccessibleShadowNode( + const SharedAccessibilityProps &props +) { + assert(props); +} + +AccessibleShadowNode::AccessibleShadowNode( + const SharedAccessibleShadowNode &shadowNode, + const SharedAccessibilityProps &props +) { + assert(shadowNode); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.h b/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.h new file mode 100644 index 00000000000..1fa8316ba6d --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/AccessibleShadowNode.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 + +#include +#include + +namespace facebook { +namespace react { + +class AccessibleShadowNode; + +typedef std::shared_ptr SharedAccessibleShadowNode; + +class AccessibleShadowNode { + +public: + +#pragma mark - Constructors + + AccessibleShadowNode() = default; + + AccessibleShadowNode( + const SharedAccessibilityProps &props + ); + + AccessibleShadowNode( + const SharedAccessibleShadowNode &shadowNode, + const SharedAccessibilityProps &props = nullptr + ); +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.cpp b/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.cpp new file mode 100644 index 00000000000..1e97d64e265 --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.cpp @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "accessibilityValuesConversions.h" + +#include + +namespace facebook { +namespace react { + +AccessibilityTraits accessibilityTraitsFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + + // FIXME: Not clear yet. + abort(); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.h b/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.h new file mode 100644 index 00000000000..7240a7cd9aa --- /dev/null +++ b/ReactCommon/fabric/view/accessibility/accessibilityValuesConversions.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 + +#include + +namespace facebook { +namespace react { + +AccessibilityTraits accessibilityTraitsFromDynamic(const folly::dynamic &value); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.cpp b/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.cpp new file mode 100644 index 00000000000..3684f75abc4 --- /dev/null +++ b/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.cpp @@ -0,0 +1,228 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YogaLayoutableShadowNode.h" + +#include +#include + +#include +#include +#include + +#include "yogaValuesConversions.h" + +namespace facebook { +namespace react { + +SharedYogaConfig YogaLayoutableShadowNode::suitableYogaConfig() { + static SharedYogaConfig sharedYogaConfig; + + if (!sharedYogaConfig) { + sharedYogaConfig = std::make_shared(YGConfig({ + .cloneNodeCallback = YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector + })); + } + + return sharedYogaConfig; +} + +YogaLayoutableShadowNode::YogaLayoutableShadowNode() { + auto yogaNode = std::make_shared(); + yogaNode->markDirtyAndPropogate(); + yogaNode->setConfig(suitableYogaConfig().get()); + yogaNode->setContext(this); + yogaNode_ = yogaNode; +} + +YogaLayoutableShadowNode::YogaLayoutableShadowNode( + const SharedYogaStylableProps &props, + const SharedShadowNodeSharedList &children +) { + assert(props); + assert(children); + + auto yogaNode = std::make_shared(); + yogaNode->setConfig(suitableYogaConfig().get()); + yogaNode->setStyle(props->getYogaStyle()); + yogaNode->setContext(this); + yogaNode->markDirtyAndPropogate(); + YogaLayoutableShadowNode::setYogaNodeChildrenBasedOnShadowNodeChildren(*yogaNode, children); + yogaNode_ = yogaNode; +} + +YogaLayoutableShadowNode::YogaLayoutableShadowNode( + const SharedYogaLayoutableShadowNode &shadowNode, + const SharedYogaStylableProps &props, + const SharedShadowNodeSharedList &children +) { + auto yogaNode = std::make_shared(*shadowNode->yogaNode_); + yogaNode->setContext(this); + yogaNode->setParent(nullptr); + + if (props) { + yogaNode->setStyle(props->getYogaStyle()); + } + + if (children) { + YogaLayoutableShadowNode::setYogaNodeChildrenBasedOnShadowNodeChildren(*yogaNode, children); + } + + yogaNode->markDirtyAndPropogate(); + + yogaNode_ = yogaNode; +} + +void YogaLayoutableShadowNode::cleanLayout() { + yogaNode_->setDirty(false); +} + +void YogaLayoutableShadowNode::dirtyLayout() { + yogaNode_->markDirtyAndPropogate(); +} + +bool YogaLayoutableShadowNode::getIsLayoutClean() const { + return !yogaNode_->isDirty(); +} + +bool YogaLayoutableShadowNode::getHasNewLayout() const { + return yogaNode_->getHasNewLayout(); +} + +void YogaLayoutableShadowNode::setHasNewLayout(bool hasNewLayout) { + yogaNode_->setHasNewLayout(hasNewLayout); +} + +#pragma mark - Mutating Methods + +void YogaLayoutableShadowNode::appendChild(SharedYogaLayoutableShadowNode child) { + ensureUnsealed(); + + auto nonConstYogaNode = std::const_pointer_cast(yogaNode_); + auto nonConstChildYogaNode = std::const_pointer_cast(child->yogaNode_); + nonConstYogaNode->insertChild(nonConstChildYogaNode.get(), nonConstYogaNode->getChildrenCount()); +} + +void YogaLayoutableShadowNode::layout(LayoutContext layoutContext) { + ensureUnsealed(); + + if (!getIsLayoutClean()) { + YGNode *yogaNode = const_cast(yogaNode_.get()); + YGNodeCalculateLayout(yogaNode, YGUndefined, YGUndefined, YGDirectionInherit); + } + + LayoutableShadowNode::layout(layoutContext); +} + +void YogaLayoutableShadowNode::layoutChildren(LayoutContext layoutContext) { + ensureUnsealed(); + + for (auto child : getChildren()) { + auto yogaLayoutableChild = std::dynamic_pointer_cast(child); + if (!yogaLayoutableChild) { + continue; + } + + auto nonConstYogaLayoutableChild = std::const_pointer_cast(yogaLayoutableChild); + + LayoutMetrics childLayoutMetrics = layoutMetricsFromYogaNode(*nonConstYogaLayoutableChild->yogaNode_); + bool isAffected = nonConstYogaLayoutableChild->setLayoutMetrics(childLayoutMetrics); + if (isAffected) { + layoutContext.affectedShadowNodes->insert(child); + } + } +} + +#pragma mark - DebugStringConvertible + +SharedDebugStringConvertibleList YogaLayoutableShadowNode::getDebugProps() const { + // TODO: Move to the base class and return `layoutMetrics` instead. + + SharedDebugStringConvertibleList list = {}; + + if (getHasNewLayout()) { + list.push_back(std::make_shared("hasNewLayout")); + } + + YGLayout defaultYogaLayout = YGLayout(); + YGLayout currentYogaLayout = std::const_pointer_cast(yogaNode_)->getLayout(); + +#define YOGA_LAYOUT_PROPS_ADD_TO_SET(stringName, propertyName, accessor, convertor) \ + { \ + auto currentValueString = convertor(currentYogaLayout.propertyName accessor); \ + auto defaultValueString = convertor(defaultYogaLayout.propertyName accessor); \ + if (currentValueString != defaultValueString) { \ + list.push_back(std::make_shared(#stringName, currentValueString)); \ + } \ + } + + YOGA_LAYOUT_PROPS_ADD_TO_SET(position, position, , stringFromYogaPosition) + YOGA_LAYOUT_PROPS_ADD_TO_SET(dimensions, dimensions, , stringFromYogaDimensions) + YOGA_LAYOUT_PROPS_ADD_TO_SET(margin, margin, , stringFromYogaEdges) + YOGA_LAYOUT_PROPS_ADD_TO_SET(border, border, , stringFromYogaEdges) + YOGA_LAYOUT_PROPS_ADD_TO_SET(padding, padding, , stringFromYogaEdges) + YOGA_LAYOUT_PROPS_ADD_TO_SET(direction, direction, , stringFromYogaStyleDirection) + + return list; +} + +#pragma mark - Helpers + +#pragma mark - Yoga Connectors + +void YogaLayoutableShadowNode::yogaNodeCloneCallbackConnector(YGNode *oldYogaNode, YGNode *newYogaNode, YGNode *parentYogaNode, int childIndex) { + // We have only raw pointer to the parent shadow node, but that's enough for now. + YogaLayoutableShadowNode *parentShadowNodeRawPtr = (YogaLayoutableShadowNode *)parentYogaNode->getContext(); + assert(parentShadowNodeRawPtr); + + // Old child shadow node already exists but we have only raw pointer to it... + YogaLayoutableShadowNode *oldShadowNodeRawPtr = (YogaLayoutableShadowNode *)oldYogaNode->getContext(); + assert(oldShadowNodeRawPtr); + + // ... but we have to address this by `shared_ptr`. We cannot create a new `shared_ptr` for it because we will end up with two shared pointers to + // single object which will cause preluminary destroyng the object. + // Another approaches to consider: + // * Create a new `shared_ptr` with empty deleter. + // * Using `childIndex` to find exact node. + SharedLayoutableShadowNode oldShadowNode = nullptr; + for (auto child : parentShadowNodeRawPtr->getChildren()) { + if (child.get() == oldShadowNodeRawPtr) { + oldShadowNode = child; + break; + } + } + + assert(oldShadowNode); + + // The new one does not exist yet. So, we have to clone and replace this using `cloneAndReplaceChild`. + SharedYogaLayoutableShadowNode newShadowNode = + std::dynamic_pointer_cast(parentShadowNodeRawPtr->cloneAndReplaceChild(oldShadowNode)); + assert(newShadowNode); + + // And finally, we have to replace underline yoga node with the new one provided by Yoga. + newShadowNode->yogaNode_ = std::shared_ptr(newYogaNode); +} + +void YogaLayoutableShadowNode::setYogaNodeChildrenBasedOnShadowNodeChildren(YGNode &yogaNode, const SharedShadowNodeSharedList &children) { + auto yogaNodeChildren = YGVector(); + + for (const SharedShadowNode &shadowNode : *children) { + const SharedYogaLayoutableShadowNode yogaLayoutableShadowNode = std::dynamic_pointer_cast(shadowNode); + + if (!yogaLayoutableShadowNode) { + continue; + } + + yogaNodeChildren.push_back((YGNode *)yogaLayoutableShadowNode->yogaNode_.get()); + } + + yogaNode.setChildren(yogaNodeChildren); + yogaNode.setDirty(true); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.h b/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.h new file mode 100644 index 00000000000..cba25fe87d2 --- /dev/null +++ b/ReactCommon/fabric/view/yoga/YogaLayoutableShadowNode.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include + +#include + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +class YogaLayoutableShadowNode; + +// We accept that Yoga node is highly mutable thing and we don't try to enforce immutability, +// so it does not have `const` qualifier (and we mark it as `mutable` in the class). +using SharedYogaNode = std::shared_ptr; +using SharedYogaConfig = std::shared_ptr; + +using SharedYogaLayoutableShadowNode = std::shared_ptr; +using SharedYogaLayoutableShadowNodeList = std::vector; +using SharedYogaLayoutableShadowNodeSharedList = std::shared_ptr; + +class YogaLayoutableShadowNode: + public LayoutableShadowNode, + public virtual DebugStringConvertible, + public virtual Sealable { + +public: + +#pragma mark - Constructors + YogaLayoutableShadowNode(); + + YogaLayoutableShadowNode( + const SharedYogaStylableProps &props, + const SharedShadowNodeSharedList &children + ); + + YogaLayoutableShadowNode( + const SharedYogaLayoutableShadowNode &shadowNode, + const SharedYogaStylableProps &props = nullptr, + const SharedShadowNodeSharedList &children = nullptr + ); + +#pragma mark - Mutating Methods + + /* + * Appends `child`'s Yoga node to the own Yoga node. + * So, it complements `ShadowNode::appendChild(...)` functionality from Yoga + * perspective. + */ + void appendChild(SharedYogaLayoutableShadowNode child); + + void cleanLayout() override; + void dirtyLayout() override; + bool getIsLayoutClean() const override; + + void setHasNewLayout(bool hasNewLayout) override; + bool getHasNewLayout() const override; + + /* + * Computes layout using Yoga layout engine. + * See `LayoutableShadowNode` for more details. + */ + void layout(LayoutContext layoutContext) override; + + void layoutChildren(LayoutContext layoutContext) override; + +#pragma mark - DebugStringConvertible + + SharedDebugStringConvertibleList getDebugProps() const override; + +private: + mutable SharedYogaNode yogaNode_; + + static SharedYogaConfig suitableYogaConfig(); + static void setYogaNodeChildrenBasedOnShadowNodeChildren(YGNode &yogaNode, const SharedShadowNodeSharedList &children); + static void yogaNodeCloneCallbackConnector(YGNode *oldYogaNode, YGNode *newYogaNode, YGNode *parentYogaNode, int childIndex); +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/YogaStylableProps.cpp b/ReactCommon/fabric/view/yoga/YogaStylableProps.cpp new file mode 100644 index 00000000000..24a2ca5682d --- /dev/null +++ b/ReactCommon/fabric/view/yoga/YogaStylableProps.cpp @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YogaStylableProps.h" + +#include +#include + +#include + +#include "yogaValuesConversions.h" + +namespace facebook { +namespace react { + +const YGStyle &YogaStylableProps::getYogaStyle() const { + return yogaStyle_; +} + +void YogaStylableProps::apply(const RawProps &rawProps) { + for (auto const &pair : rawProps) { + auto const &name = pair.first; + auto const &value = pair.second; + +#define YOGA_STYLE_PROPERTY(stringName, yogaName, accessor, convertor) \ + if (name == #stringName) { \ + yogaStyle_.yogaName = convertor(value accessor); \ + continue; \ + } + +#define YOGA_STYLE_SIMPLE_FLOAT_PROPERTY(name) \ + YOGA_STYLE_PROPERTY(name, name, .asDouble(), ) + +#define YOGA_STYLE_OPTIONAL_FLOAT_PROPERTY(name) \ + YOGA_STYLE_PROPERTY(name, name, .asDouble(), yogaOptionalFloatFromFabricFloat) + +#define YOGA_STYLE_SIMPLE_INTEGER_PROPERTY(name) \ + YOGA_STYLE_PROPERTY(name, name, .asInt(), ) + +// Dimension Properties +#define YOGA_STYLE_DIMENSION_PROPERTY() \ + YOGA_STYLE_PROPERTY(width, dimensions[YGDimensionWidth], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(height, dimensions[YGDimensionHeight], , yogaStyleValueFromDynamic) + +#define YOGA_STYLE_PREFIXED_DIMENSION_PROPERTY(prefix) \ + YOGA_STYLE_PROPERTY(prefix##Width, prefix##Dimensions[YGDimensionWidth], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Height, prefix##Dimensions[YGDimensionHeight], , yogaStyleValueFromDynamic) + +// Edge Properties +#define YOGA_STYLE_POSITION_EDGE_PROPERTY() \ + YOGA_STYLE_PROPERTY(left, position[YGEdgeLeft], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(top, position[YGEdgeTop], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(right, position[YGEdgeRight], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(bottom, position[YGEdgeBottom], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(start, position[YGEdgeStart], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(end, position[YGEdgeEnd], , yogaStyleValueFromDynamic) + +#define YOGA_STYLE_PREFIXED_EDGE_PROPERTY(prefix) \ + YOGA_STYLE_PROPERTY(prefix##Left, prefix[YGEdgeLeft], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Top, prefix[YGEdgeTop], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Right, prefix[YGEdgeRight], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Bottom, prefix[YGEdgeBottom], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Start, prefix[YGEdgeStart], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##End, prefix[YGEdgeEnd], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Horizontal, prefix[YGEdgeHorizontal], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix##Vertical, prefix[YGEdgeVertical], , yogaStyleValueFromDynamic) \ + YOGA_STYLE_PROPERTY(prefix, prefix[YGEdgeAll], , yogaStyleValueFromDynamic) + + YOGA_STYLE_PROPERTY(direction, direction, , yogaStyleDirectionFromDynamic) + YOGA_STYLE_PROPERTY(flexDirection, flexDirection, , yogaStyleFlexDirectionFromDynamic) + YOGA_STYLE_PROPERTY(justifyContent, justifyContent, , yogaStyleJustifyFromDynamic) + YOGA_STYLE_PROPERTY(alignContent, alignContent, , yogaStyleAlignFromDynamic) + YOGA_STYLE_PROPERTY(alignItems, alignItems, , yogaStyleAlignFromDynamic) + YOGA_STYLE_PROPERTY(alignSelf, alignSelf, , yogaStyleAlignFromDynamic) + YOGA_STYLE_PROPERTY(positionType, positionType, , yogaStylePositionTypeFromDynamic) + YOGA_STYLE_PROPERTY(flexWrap, flexWrap, , yogaStyleWrapFromDynamic) + YOGA_STYLE_PROPERTY(overflow, overflow, , yogaStyleOverflowFromDynamic) + YOGA_STYLE_PROPERTY(display, display, , yogaStyleDisplayFromDynamic) + + YOGA_STYLE_OPTIONAL_FLOAT_PROPERTY(flex) + YOGA_STYLE_OPTIONAL_FLOAT_PROPERTY(flexGrow) + YOGA_STYLE_OPTIONAL_FLOAT_PROPERTY(flexShrink) + YOGA_STYLE_PROPERTY(flexBasis, flexBasis, , yogaStyleValueFromDynamic) + + YOGA_STYLE_DIMENSION_PROPERTY() + YOGA_STYLE_PREFIXED_DIMENSION_PROPERTY(min) + YOGA_STYLE_PREFIXED_DIMENSION_PROPERTY(max) + + YOGA_STYLE_POSITION_EDGE_PROPERTY() + YOGA_STYLE_PREFIXED_EDGE_PROPERTY(margin) + YOGA_STYLE_PREFIXED_EDGE_PROPERTY(padding) + YOGA_STYLE_PREFIXED_EDGE_PROPERTY(border) + + YOGA_STYLE_SIMPLE_FLOAT_PROPERTY(aspectRatio) + } +} + +#pragma mark - DebugStringConvertible + +SharedDebugStringConvertibleList YogaStylableProps::getDebugProps() const { + SharedDebugStringConvertibleList list = {}; + + YGStyle defaultYogaStyle = YGStyle(); + YGStyle currentYogaStyle = yogaStyle_; + +#define YOGA_STYLE_PROPS_ADD_TO_SET(stringName, propertyName, accessor, convertor) \ + { \ + auto currentValueString = convertor(currentYogaStyle.propertyName accessor); \ + auto defaultValueString = convertor(defaultYogaStyle.propertyName accessor); \ + if (currentValueString != defaultValueString) { \ + list.push_back(std::make_shared(#stringName, currentValueString)); \ + } \ + } + + YOGA_STYLE_PROPS_ADD_TO_SET(direction, direction, , stringFromYogaStyleDirection) + + YOGA_STYLE_PROPS_ADD_TO_SET(flexDirection, flexDirection, , stringFromYogaStyleFlexDirection) + YOGA_STYLE_PROPS_ADD_TO_SET(justifyContent, justifyContent, , stringFromYogaStyleJustify) + YOGA_STYLE_PROPS_ADD_TO_SET(alignItems, alignItems, , stringFromYogaStyleAlign) + YOGA_STYLE_PROPS_ADD_TO_SET(alignSelf, alignSelf, , stringFromYogaStyleAlign) + YOGA_STYLE_PROPS_ADD_TO_SET(positionType, positionType, , stringFromYogaStylePositionType) + YOGA_STYLE_PROPS_ADD_TO_SET(flexWrap, flexWrap, , stringFromYogaStyleWrap) + YOGA_STYLE_PROPS_ADD_TO_SET(overflow, overflow, , stringFromYogaStyleOverflow) + + YOGA_STYLE_PROPS_ADD_TO_SET(flex, flex, , stringFromYogaStyleOptionalFloat) + YOGA_STYLE_PROPS_ADD_TO_SET(flexGrow, flexGrow, , stringFromYogaStyleOptionalFloat) + YOGA_STYLE_PROPS_ADD_TO_SET(flexShrink, flexShrink, , stringFromYogaStyleOptionalFloat) + YOGA_STYLE_PROPS_ADD_TO_SET(flexBasis, flexBasis, , stringFromYogaStyleValue) + YOGA_STYLE_PROPS_ADD_TO_SET(margin, margin, , stringFromYogaStyleEdge) + YOGA_STYLE_PROPS_ADD_TO_SET(position, position, , stringFromYogaStyleEdge) + YOGA_STYLE_PROPS_ADD_TO_SET(padding, padding, , stringFromYogaStyleEdge) + YOGA_STYLE_PROPS_ADD_TO_SET(border, border, , stringFromYogaStyleEdge) + + YOGA_STYLE_PROPS_ADD_TO_SET(size, dimensions, , stringFromYogaStyleDimensions) + YOGA_STYLE_PROPS_ADD_TO_SET(minSize, minDimensions, , stringFromYogaStyleDimensions) + YOGA_STYLE_PROPS_ADD_TO_SET(maxSize, maxDimensions, , stringFromYogaStyleDimensions) + + YOGA_STYLE_PROPS_ADD_TO_SET(aspectRatio, aspectRatio, , folly::to) + + return list; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/YogaStylableProps.h b/ReactCommon/fabric/view/yoga/YogaStylableProps.h new file mode 100644 index 00000000000..61d935a22f2 --- /dev/null +++ b/ReactCommon/fabric/view/yoga/YogaStylableProps.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 + +#include +#include + +namespace facebook { +namespace react { + +class YogaStylableProps; + +typedef std::shared_ptr SharedYogaStylableProps; + +class YogaStylableProps: + public virtual DebugStringConvertible +{ +public: + const YGStyle &getYogaStyle() const; + + void apply(const RawProps &rawProps); + +#pragma mark - DebugStringConvertible + + SharedDebugStringConvertibleList getDebugProps() const override; + +protected: + YGStyle yogaStyle_ {}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/yogaValuesConversions.cpp b/ReactCommon/fabric/view/yoga/yogaValuesConversions.cpp new file mode 100644 index 00000000000..e329b843764 --- /dev/null +++ b/ReactCommon/fabric/view/yoga/yogaValuesConversions.cpp @@ -0,0 +1,303 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "yogaValuesConversions.h" + +#include + +#include + +namespace facebook { +namespace react { + +Float fabricFloatFromYogaFloat(float value) { + return (Float)value; +} + +float yogaFloatFromFabricFloat(Float value) { + return (float)value; +} + +Float fabricFloatFromYogaOptionalFloat(YGFloatOptional value) { + if (value.isUndefined()) { + return kFloatUndefined; + } + + return fabricFloatFromYogaFloat(value.getValue()); +} + +YGFloatOptional yogaOptionalFloatFromFabricFloat(Float value) { + if (value == kFloatUndefined) { + return YGFloatOptional(); + } + + return YGFloatOptional(yogaFloatFromFabricFloat(value)); +} + +LayoutMetrics layoutMetricsFromYogaNode(YGNode &yogaNode) { + LayoutMetrics layoutMetrics; + + YGLayout layout = yogaNode.getLayout(); + + layoutMetrics.frame = Rect { + Point {fabricFloatFromYogaFloat(layout.position[0]), fabricFloatFromYogaFloat(layout.position[1])}, + Size {fabricFloatFromYogaFloat(layout.dimensions[0]), fabricFloatFromYogaFloat(layout.dimensions[1])} + }; + + // FIXME: Add more. + + return layoutMetrics; +} + +YGDirection yogaStyleDirectionFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "inherit") { return YGDirectionInherit; } + if (stringValue == "ltr") { return YGDirectionLTR; } + if (stringValue == "rtl") { return YGDirectionRTL; } + + abort(); +} + +YGFlexDirection yogaStyleFlexDirectionFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "column") { return YGFlexDirectionColumn; } + if (stringValue == "column-reverse") { return YGFlexDirectionColumnReverse; } + if (stringValue == "row") { return YGFlexDirectionRow; } + if (stringValue == "row-reverse") { return YGFlexDirectionRowReverse; } + + abort(); +} + +YGJustify yogaStyleJustifyFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "flex-start") { return YGJustifyFlexStart; } + if (stringValue == "center") { return YGJustifyCenter; } + if (stringValue == "flex-end") { return YGJustifyFlexEnd; } + if (stringValue == "space-between") { return YGJustifySpaceBetween; } + if (stringValue == "space-around") { return YGJustifySpaceAround; } + if (stringValue == "space-evenly") { return YGJustifySpaceEvenly; } + + abort(); +} + +YGAlign yogaStyleAlignFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "auto") { return YGAlignAuto; } + if (stringValue == "flex-start") { return YGAlignFlexStart; } + if (stringValue == "center") { return YGAlignCenter; } + if (stringValue == "flex-end") { return YGAlignFlexEnd; } + if (stringValue == "stretch") { return YGAlignStretch; } + if (stringValue == "baseline") { return YGAlignBaseline; } + if (stringValue == "between") { return YGAlignSpaceBetween; } + if (stringValue == "space-around") { return YGAlignSpaceAround; } + + abort(); +} + +YGPositionType yogaStylePositionTypeFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "relative") { return YGPositionTypeRelative; } + if (stringValue == "absolute") { return YGPositionTypeAbsolute; } + + abort(); +} + +YGWrap yogaStyleWrapFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "no-wrap") { return YGWrapNoWrap; } + if (stringValue == "wrap") { return YGWrapWrap; } + if (stringValue == "wrap-reverse") { return YGWrapWrapReverse; } + + abort(); +} + +YGOverflow yogaStyleOverflowFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "visible") { return YGOverflowVisible; } + if (stringValue == "hidden") { return YGOverflowHidden; } + if (stringValue == "scroll") { return YGOverflowScroll; } + + abort(); +} + +YGDisplay yogaStyleDisplayFromDynamic(const folly::dynamic &value) { + assert(value.isString()); + auto stringValue = value.asString(); + + if (stringValue == "flex") { return YGDisplayFlex; } + if (stringValue == "none") { return YGDisplayNone; } + + abort(); +} + +YGValue yogaStyleValueFromDynamic(const folly::dynamic &value) { + if (value.isNumber()) { + float x = value.asDouble(); + return { x, YGUnitPoint }; + } else if (value.isString()) { + const auto stringValue = value.asString(); + if (stringValue == "auto") { + return { YGUndefined, YGUnitAuto }; + } else { + if (stringValue.back() == '%') { + return { folly::to(stringValue.substr(stringValue.length() - 1)), YGUnitPercent }; + } else { + return { folly::to(stringValue), YGUnitPoint }; + } + } + } + + return YGValueUndefined; +} + +YGFloatOptional yogaStyleOptionalFloatFromDynamic(const folly::dynamic &value) { + if (value.isNumber()) { + return YGFloatOptional(value.asDouble()); + } else if (value.isString()) { + const auto stringValue = value.asString(); + if (stringValue == "auto") { + return YGFloatOptional(); + } + } + + abort(); +} + +std::string stringFromYogaDimensions(std::array dimensions) { + return "{" + folly::to(dimensions[0]) + ", " + folly::to(dimensions[1]) + "}"; +} + +std::string stringFromYogaPosition(std::array position) { + return "{" + folly::to(position[0]) + ", " + folly::to(position[1]) + "}"; +} + +std::string stringFromYogaEdges(std::array edges) { + return "{" + + folly::to(edges[0]) + ", " + + folly::to(edges[1]) + ", " + + folly::to(edges[2]) + ", " + + folly::to(edges[3]) + "}"; +} + +std::string stringFromYogaStyleDirection(YGDirection value) { + switch (value) { + case YGDirectionInherit: return "inherit"; + case YGDirectionLTR: return "ltr"; + case YGDirectionRTL: return "rtl"; + } +} + +std::string stringFromYogaStyleFlexDirection(YGFlexDirection value) { + switch (value) { + case YGFlexDirectionColumn: return "column"; + case YGFlexDirectionColumnReverse: return "column-reverse"; + case YGFlexDirectionRow: return "row"; + case YGFlexDirectionRowReverse: return "row-reverse"; + } +} + +std::string stringFromYogaStyleJustify(YGJustify value) { + switch (value) { + case YGJustifyFlexStart: return "flex-start"; + case YGJustifyCenter: return "center"; + case YGJustifyFlexEnd: return "flex-end"; + case YGJustifySpaceBetween: return "space-between"; + case YGJustifySpaceAround: return "space-around"; + case YGJustifySpaceEvenly: return "space-evenly"; + } +} + +std::string stringFromYogaStyleAlign(YGAlign value) { + switch (value) { + case YGAlignAuto: return "auto"; + case YGAlignFlexStart: return "flex-start"; + case YGAlignCenter: return "center"; + case YGAlignFlexEnd: return "flex-end"; + case YGAlignStretch: return "stretch"; + case YGAlignBaseline: return "baseline"; + case YGAlignSpaceBetween: return "space-between"; + case YGAlignSpaceAround: return "space-around"; + } +} + +std::string stringFromYogaStylePositionType(YGPositionType value) { + switch (value) { + case YGPositionTypeRelative: return "relative"; + case YGPositionTypeAbsolute: return "absolute"; + } +} + +std::string stringFromYogaStyleWrap(YGWrap value) { + switch (value) { + case YGWrapNoWrap: return "no-wrap"; + case YGWrapWrap: return "wrap"; + case YGWrapWrapReverse: return "wrap-reverse"; + } +} + +std::string stringFromYogaStyleOverflow(YGOverflow value) { + switch (value) { + case YGOverflowVisible: return "visible"; + case YGOverflowScroll: return "scroll"; + case YGOverflowHidden: return "hidden"; + } +} + +std::string stringFromYogaStyleDisplay(YGDisplay value) { + switch (value) { + case YGDisplayFlex: return "flex"; + case YGDisplayNone: return "none"; + } +} + +std::string stringFromYogaStyleValue(YGValue value) { + switch (value.unit) { + case YGUnitUndefined: return "undefined"; + case YGUnitPoint: return folly::to(value.value); + case YGUnitPercent: return folly::to(value.value) + "%"; + case YGUnitAuto: return "auto"; + } +} + +std::string stringFromYogaStyleOptionalFloat(YGFloatOptional value) { + if (value.isUndefined()) { + return "undefined"; + } + + return folly::to(fabricFloatFromYogaFloat(value.getValue())); +} + +std::string stringFromYogaStyleDimensions(std::array value) { + return "{" + + stringFromYogaStyleValue(value[0]) + ", " + + stringFromYogaStyleValue(value[1]) + "}"; +} + +std::string stringFromYogaStyleEdge(std::array value) { + return "{" + + stringFromYogaStyleValue(value[0]) + ", " + + stringFromYogaStyleValue(value[1]) + ", " + + stringFromYogaStyleValue(value[2]) + ", " + + stringFromYogaStyleValue(value[3]) + "}"; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/view/yoga/yogaValuesConversions.h b/ReactCommon/fabric/view/yoga/yogaValuesConversions.h new file mode 100644 index 00000000000..6c515659830 --- /dev/null +++ b/ReactCommon/fabric/view/yoga/yogaValuesConversions.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * 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 +#include +#include + +#include + +namespace facebook { +namespace react { + +struct LayoutMetrics; + +Float fabricFloatFromYogaFloat(float value); +float yogaFloatFromFabricFloat(Float value); + +Float fabricFloatFromYogaOptionalFloat(YGFloatOptional value); +YGFloatOptional yogaOptionalFloatFromFabricFloat(Float value); + +LayoutMetrics layoutMetricsFromYogaNode(YGNode &yogaNode); + +YGDirection yogaStyleDirectionFromDynamic(const folly::dynamic &value); +YGFlexDirection yogaStyleFlexDirectionFromDynamic(const folly::dynamic &value); +YGJustify yogaStyleJustifyFromDynamic(const folly::dynamic &value); +YGAlign yogaStyleAlignFromDynamic(const folly::dynamic &value); +YGPositionType yogaStylePositionTypeFromDynamic(const folly::dynamic &value); +YGWrap yogaStyleWrapFromDynamic(const folly::dynamic &value); +YGOverflow yogaStyleOverflowFromDynamic(const folly::dynamic &value); +YGDisplay yogaStyleDisplayFromDynamic(const folly::dynamic &value); +YGValue yogaStyleValueFromDynamic(const folly::dynamic &value); +YGFloatOptional yogaStyleOptionalFloatFromDynamic(const folly::dynamic &value); + +std::string stringFromYogaDimensions(std::array dimensions); +std::string stringFromYogaPosition(std::array position); +std::string stringFromYogaEdges(std::array edges); +std::string stringFromYogaStyleDirection(YGDirection direction); +std::string stringFromYogaStyleFlexDirection(YGFlexDirection value); +std::string stringFromYogaStyleJustify(YGJustify value); +std::string stringFromYogaStyleAlign(YGAlign value); +std::string stringFromYogaStylePositionType(YGPositionType value); +std::string stringFromYogaStyleWrap(YGWrap value); +std::string stringFromYogaStyleOverflow(YGOverflow value); +std::string stringFromYogaStyleDisplay(YGDisplay value); +std::string stringFromYogaStyleValue(YGValue value); +std::string stringFromYogaStyleOptionalFloat(YGFloatOptional value); +std::string stringFromYogaStyleDimensions(std::array value); +std::string stringFromYogaStyleEdge(std::array value); + +} // namespace react +} // namespace facebook