diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaConfig.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaConfig.java new file mode 100644 index 000000000..716a212e7 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaConfig.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.yoga; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; + +@DoNotStrip +public class YogaConfig { + + static { + SoLoader.loadLibrary("yoga"); + } + + long mNativePointer; + + private native long jni_YGConfigNew(); + public YogaConfig() { + mNativePointer = jni_YGConfigNew(); + if (mNativePointer == 0) { + throw new IllegalStateException("Failed to allocate native memory"); + } + } + + private native void jni_YGConfigFree(long nativePointer); + @Override + protected void finalize() throws Throwable { + try { + jni_YGConfigFree(mNativePointer); + } finally { + super.finalize(); + } + } + + private native void jni_YGConfigSetExperimentalFeatureEnabled( + long nativePointer, + int feature, + boolean enabled); + public void setExperimentalFeatureEnabled(YogaExperimentalFeature feature, boolean enabled) { + jni_YGConfigSetExperimentalFeatureEnabled(mNativePointer, feature.intValue(), enabled); + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java index 94f16a991..3772c4f11 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java @@ -35,20 +35,6 @@ public class YogaNode implements YogaNodeAPI { jni_YGSetLogger(logger); } - private static native void jni_YGSetExperimentalFeatureEnabled( - int feature, - boolean enabled); - public static void setExperimentalFeatureEnabled( - YogaExperimentalFeature feature, - boolean enabled) { - jni_YGSetExperimentalFeatureEnabled(feature.intValue(), enabled); - } - - private static native boolean jni_YGIsExperimentalFeatureEnabled(int feature); - public static boolean isExperimentalFeatureEnabled(YogaExperimentalFeature feature) { - return jni_YGIsExperimentalFeatureEnabled(feature.intValue()); - } - private YogaNode mParent; private List mChildren; private YogaMeasureFunction mMeasureFunction; @@ -104,6 +90,14 @@ public class YogaNode implements YogaNodeAPI { } } + private native long jni_YGNodeNewWithConfig(long configPointer); + public YogaNode(YogaConfig config) { + mNativePointer = jni_YGNodeNewWithConfig(config.mNativePointer); + if (mNativePointer == 0) { + throw new IllegalStateException("Failed to allocate native memory"); + } + } + private native void jni_YGNodeFree(long nativePointer); @Override protected void finalize() throws Throwable { diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp index 15f6d46f5..72e704c8c 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp @@ -150,6 +150,10 @@ static inline YGNodeRef _jlong2YGNodeRef(jlong addr) { return reinterpret_cast(static_cast(addr)); } +static inline YGConfigRef _jlong2YGConfigRef(jlong addr) { + return reinterpret_cast(static_cast(addr)); +} + void jni_YGSetLogger(alias_ref clazz, alias_ref logger) { if (jLogger) { jLogger->releaseAlias(); @@ -171,18 +175,6 @@ void jni_YGLog(alias_ref clazz, jint level, jstring message) { Environment::current()->ReleaseStringUTFChars(message, nMessage); } -void jni_YGSetExperimentalFeatureEnabled(alias_ref clazz, jint feature, jboolean enabled) { - YGSetExperimentalFeatureEnabled(static_cast(feature), enabled); -} - -jboolean jni_YGIsExperimentalFeatureEnabled(alias_ref clazz, jint feature) { - return YGIsExperimentalFeatureEnabled(static_cast(feature)); -} - -jint jni_YGNodeGetInstanceCount(alias_ref clazz) { - return YGNodeGetInstanceCount(); -} - jlong jni_YGNodeNew(alias_ref thiz) { const YGNodeRef node = YGNodeNew(); YGNodeSetContext(node, new weak_ref(make_weak(thiz))); @@ -190,6 +182,13 @@ jlong jni_YGNodeNew(alias_ref thiz) { return reinterpret_cast(node); } +jlong jni_YGNodeNewWithConfig(alias_ref thiz, jlong configPointer) { + const YGNodeRef node = YGNodeNewWithConfig(_jlong2YGConfigRef(configPointer)); + YGNodeSetContext(node, new weak_ref(make_weak(thiz))); + YGNodeSetPrintFunc(node, YGPrint); + return reinterpret_cast(node); +} + void jni_YGNodeFree(alias_ref thiz, jlong nativePointer) { const YGNodeRef node = _jlong2YGNodeRef(nativePointer); delete YGNodeJobject(node); @@ -368,6 +367,24 @@ YG_NODE_JNI_STYLE_UNIT_PROP(MaxHeight); // Yoga specific properties, not compatible with flexbox specification YG_NODE_JNI_STYLE_PROP(jfloat, float, AspectRatio); +jlong jni_YGConfigNew(alias_ref) { + return reinterpret_cast(YGConfigNew()); +} + +void jni_YGConfigFree(alias_ref, jlong nativePointer) { + const YGConfigRef config = _jlong2YGConfigRef(nativePointer); + YGConfigFree(config); +} + +void jni_YGConfigSetExperimentalFeatureEnabled(alias_ref, jlong nativePointer, jint feature, jboolean enabled) { + const YGConfigRef config = _jlong2YGConfigRef(nativePointer); + YGConfigSetExperimentalFeatureEnabled(config, static_cast(feature), enabled); +} + +jint jni_YGNodeGetInstanceCount(alias_ref clazz) { + return YGNodeGetInstanceCount(); +} + #define YGMakeNativeMethod(name) makeNativeMethod(#name, name) jint JNI_OnLoad(JavaVM *vm, void *) { @@ -375,6 +392,7 @@ jint JNI_OnLoad(JavaVM *vm, void *) { registerNatives("com/facebook/yoga/YogaNode", { YGMakeNativeMethod(jni_YGNodeNew), + YGMakeNativeMethod(jni_YGNodeNewWithConfig), YGMakeNativeMethod(jni_YGNodeFree), YGMakeNativeMethod(jni_YGNodeReset), YGMakeNativeMethod(jni_YGNodeInsertChild), @@ -452,8 +470,12 @@ jint JNI_OnLoad(JavaVM *vm, void *) { YGMakeNativeMethod(jni_YGNodeGetInstanceCount), YGMakeNativeMethod(jni_YGSetLogger), YGMakeNativeMethod(jni_YGLog), - YGMakeNativeMethod(jni_YGSetExperimentalFeatureEnabled), - YGMakeNativeMethod(jni_YGIsExperimentalFeatureEnabled), + }); + registerNatives("com/facebook/yoga/YogaConfig", + { + YGMakeNativeMethod(jni_YGConfigNew), + YGMakeNativeMethod(jni_YGConfigFree), + YGMakeNativeMethod(jni_YGConfigSetExperimentalFeatureEnabled), }); }); } diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index 9407835a4..3f85dfeb9 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -94,6 +94,8 @@ typedef struct YGStyle { float aspectRatio; } YGStyle; +typedef struct YGConfig { bool experimentalFeatures[YGExperimentalFeatureCount + 1]; } YGConfig; + typedef struct YGNode { YGStyle style; YGLayout layout; @@ -107,6 +109,7 @@ typedef struct YGNode { YGMeasureFunc measure; YGBaselineFunc baseline; YGPrintFunc print; + YGConfigRef config; void *context; bool isDirty; @@ -191,6 +194,14 @@ static YGNode gYGNodeDefaults = { }, }; +static YGConfig gYGConfigDefaults = { + .experimentalFeatures = + { + [YGExperimentalFeatureRounding] = false, + [YGExperimentalFeatureWebFlexBasis] = false, + }, +}; + static void YGNodeMarkDirtyInternal(const YGNodeRef node); YGMalloc gYGMalloc = &malloc; @@ -290,15 +301,21 @@ static inline float YGValueResolveMargin(const YGValue *const value, const float int32_t gNodeInstanceCount = 0; -YGNodeRef YGNodeNew(void) { + +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { const YGNodeRef node = gYGMalloc(sizeof(YGNode)); YG_ASSERT(node, "Could not allocate memory for node"); gNodeInstanceCount++; memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); + node->config = config; return node; } +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(&gYGConfigDefaults); +} + void YGNodeFree(const YGNodeRef node) { if (node->parent) { YGNodeListDelete(node->parent->children, node); @@ -331,13 +348,27 @@ void YGNodeReset(const YGNodeRef node) { YG_ASSERT(node->parent == NULL, "Cannot reset a node still attached to a parent"); YGNodeListFree(node->children); + + const YGConfigRef config = node->config; memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); + node->config = config; } int32_t YGNodeGetInstanceCount(void) { return gNodeInstanceCount; } +YGConfigRef YGConfigNew(void) { + const YGConfigRef config = gYGMalloc(sizeof(YGConfig)); + YG_ASSERT(config, "Could not allocate memory for config"); + memcpy(config, &gYGConfigDefaults, sizeof(YGConfig)); + return config; +} + +void YGConfigFree(const YGConfigRef config) { + gYGFree(config); +} + static void YGNodeMarkDirtyInternal(const YGNodeRef node) { if (!node->isDirty) { node->isDirty = true; @@ -678,7 +709,8 @@ bool YGLayoutNodeInternal(const YGNodeRef node, const float parentWidth, const float parentHeight, const bool performLayout, - const char *reason); + const char *reason, + const YGConfigRef config); inline bool YGFloatIsUndefined(const float value) { return isnan(value); @@ -1335,7 +1367,8 @@ static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, const float parentWidth, const float parentHeight, const YGMeasureMode heightMode, - const YGDirection direction) { + const YGDirection direction, + const YGConfigRef config) { const YGFlexDirection mainAxis = YGFlexDirectionResolve(node->style.flexDirection, direction); const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); const float mainAxisSize = isMainAxisRow ? width : height; @@ -1354,7 +1387,7 @@ static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, if (!YGFloatIsUndefined(resolvedFlexBasis) && !YGFloatIsUndefined(mainAxisSize)) { if (YGFloatIsUndefined(child->layout.computedFlexBasis) || - (YGIsExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis) && + (YGConfigIsExperimentalFeatureEnabled(config, YGExperimentalFeatureWebFlexBasis) && child->layout.computedFlexBasisGeneration != gCurrentGenerationCount)) { child->layout.computedFlexBasis = fmaxf(resolvedFlexBasis, YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth)); @@ -1456,7 +1489,8 @@ static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, parentWidth, parentHeight, false, - "measure"); + "measure", + config); child->layout.computedFlexBasis = fmaxf(child->layout.measuredDimensions[dim[mainAxis]], @@ -1471,7 +1505,8 @@ static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, const float width, const YGMeasureMode widthMode, const float height, - const YGDirection direction) { + const YGDirection direction, + const YGConfigRef config) { const YGFlexDirection mainAxis = YGFlexDirectionResolve(node->style.flexDirection, direction); const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); @@ -1560,7 +1595,8 @@ static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, childWidth, childHeight, false, - "abs-measure"); + "abs-measure", + config); childWidth = child->layout.measuredDimensions[YGDimensionWidth] + YGNodeMarginForAxis(child, YGFlexDirectionRow, width); childHeight = child->layout.measuredDimensions[YGDimensionHeight] + @@ -1576,7 +1612,8 @@ static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, childWidth, childHeight, true, - "abs-layout"); + "abs-layout", + config); if (YGNodeIsTrailingPosDefined(child, mainAxis) && !YGNodeIsLeadingPosDefined(child, mainAxis)) { child->layout.position[leading[mainAxis]] = node->layout.measuredDimensions[dim[mainAxis]] - @@ -1854,7 +1891,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, const YGMeasureMode heightMeasureMode, const float parentWidth, const float parentHeight, - const bool performLayout) { + const bool performLayout, + const YGConfigRef config) { YG_ASSERT(YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined : true, "availableWidth is indefinite so widthMeasureMode must be " "YGMeasureModeUndefined"); @@ -2056,7 +2094,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerWidth, availableInnerHeight, heightMeasureMode, - direction); + direction, + config); } } @@ -2173,7 +2212,7 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerMainDim = minInnerMainDim; } else if (!YGFloatIsUndefined(maxInnerMainDim) && sizeConsumedOnCurrentLine > maxInnerMainDim) { availableInnerMainDim = maxInnerMainDim; - } else if (YGIsExperimentalFeatureEnabled(YGExperimentalFeatureMinFlexFix)) { + } else if (YGConfigIsExperimentalFeatureEnabled(config, YGExperimentalFeatureMinFlexFix)) { // TODO: this needs to be moved out of experimental feature, as this is legitimate fix // If the measurement isn't exact, we want to use as little space as possible availableInnerMainDim = sizeConsumedOnCurrentLine; @@ -2422,7 +2461,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerWidth, availableInnerHeight, performLayout && !requiresStretchLayout, - "flex"); + "flex", + config); currentRelativeChild = currentRelativeChild->nextChild; } @@ -2660,7 +2700,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerWidth, availableInnerHeight, true, - "stretch"); + "stretch", + config); } } else { const float remainingCrossDim = @@ -2827,7 +2868,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerWidth, availableInnerHeight, true, - "stretch"); + "stretch", + config); } } break; @@ -2915,7 +2957,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, availableInnerWidth, isMainAxisRow ? measureModeMainDim : measureModeCrossDim, availableInnerHeight, - direction); + direction, + config); } // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN @@ -3057,7 +3100,8 @@ bool YGLayoutNodeInternal(const YGNodeRef node, const float parentWidth, const float parentHeight, const bool performLayout, - const char *reason) { + const char *reason, + const YGConfigRef config) { YGLayout *layout = &node->layout; gDepth++; @@ -3186,7 +3230,8 @@ bool YGLayoutNodeInternal(const YGNodeRef node, heightMeasureMode, parentWidth, parentHeight, - performLayout); + performLayout, + config); if (gPrintChanges) { printf("%s%d.}%s", YGSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); @@ -3352,11 +3397,11 @@ void YGNodeCalculateLayout(const YGNodeRef node, parentWidth, parentHeight, true, - "initia" - "l")) { - YGNodeSetPosition(node, node->layout.direction, node->layout.dimensions[YGDimensionWidth], node->layout.dimensions[YGDimensionHeight], parentWidth); + "initial", + node->config)) { + YGNodeSetPosition(node, node->layout.direction, parentWidth, parentHeight, parentWidth); - if (YGIsExperimentalFeatureEnabled(YGExperimentalFeatureRounding)) { + if (YGConfigIsExperimentalFeatureEnabled(node->config, YGExperimentalFeatureRounding)) { YGRoundToPixelGrid(node); } @@ -3377,14 +3422,15 @@ void YGLog(YGLogLevel level, const char *format, ...) { va_end(args); } -static bool experimentalFeatures[YGExperimentalFeatureCount + 1]; - -void YGSetExperimentalFeatureEnabled(YGExperimentalFeature feature, bool enabled) { - experimentalFeatures[feature] = enabled; +void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + config->experimentalFeatures[feature] = enabled; } -inline bool YGIsExperimentalFeatureEnabled(YGExperimentalFeature feature) { - return experimentalFeatures[feature]; +inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature) { + return config->experimentalFeatures[feature]; } void YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree) { diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index 64b0cda07..cbcf92eec 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -46,6 +46,7 @@ typedef struct YGValue { static const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; static const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; +typedef struct YGConfig *YGConfigRef; typedef struct YGNode *YGNodeRef; typedef YGSize (*YGMeasureFunc)(YGNodeRef node, float width, @@ -63,6 +64,7 @@ typedef void (*YGFree)(void *ptr); // YGNode WIN_EXPORT YGNodeRef YGNodeNew(void); +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config); WIN_EXPORT void YGNodeFree(const YGNodeRef node); WIN_EXPORT void YGNodeFreeRecursive(const YGNodeRef node); WIN_EXPORT void YGNodeReset(const YGNodeRef node); @@ -223,8 +225,15 @@ WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...); // If you want to avoid rounding - set PointScaleFactor to 0 WIN_EXPORT void YGSetPointScaleFactor(float pixelsInPoint); -WIN_EXPORT void YGSetExperimentalFeatureEnabled(YGExperimentalFeature feature, bool enabled); -WIN_EXPORT bool YGIsExperimentalFeatureEnabled(YGExperimentalFeature feature); +// YGConfig +WIN_EXPORT YGConfigRef YGConfigNew(void); +WIN_EXPORT void YGConfigFree(const YGConfigRef config); + +WIN_EXPORT void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled); +WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature); WIN_EXPORT void YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree);