diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index a92c6de028f..fffd58a9ff4 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2338,6 +2338,7 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid public fun initialize ()V public fun invalidate ()V public fun markActiveTouchForTag (II)V + public fun measure (ILjava/lang/String;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;FFFF)J public fun onAllAnimationsComplete ()V public fun onAnimationStarted ()V public fun onHostDestroy ()V @@ -2402,7 +2403,6 @@ public class com/facebook/react/fabric/mounting/MountingManager { public fun getViewExists (I)Z public fun isWaitingForViewAttach (I)Z public fun measure (Lcom/facebook/react/bridge/ReactContext;Ljava/lang/String;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J - public fun measureMapBuffer (Lcom/facebook/react/bridge/ReactContext;Ljava/lang/String;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J public fun receiveCommand (IIILcom/facebook/react/bridge/ReadableArray;)V public fun receiveCommand (IILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun sendAccessibilityEvent (III)V @@ -4629,7 +4629,6 @@ public abstract class com/facebook/react/uimanager/ViewManager : com/facebook/re public fun getNativeProps ()Ljava/util/Map; public abstract fun getShadowNodeClass ()Ljava/lang/Class; public fun measure (Landroid/content/Context;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J - public fun measure (Landroid/content/Context;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J protected fun onAfterUpdateTransaction (Landroid/view/View;)V public fun onDropViewInstance (Landroid/view/View;)V public fun onSurfaceStopped (I)V @@ -6320,7 +6319,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi protected fun verifyDrawable (Landroid/graphics/drawable/Drawable;)Z } -public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/IViewManagerWithChildren { +public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback { protected field mReactTextViewManagerCallback Lcom/facebook/react/views/text/ReactTextViewManagerCallback; public fun ()V public fun (Lcom/facebook/react/views/text/ReactTextViewManagerCallback;)V @@ -6332,10 +6331,10 @@ public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/r public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map; public fun getName ()Ljava/lang/String; public fun getShadowNodeClass ()Ljava/lang/Class; - public fun measure (Landroid/content/Context;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;Lcom/facebook/react/common/mapbuffer/MapBuffer;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;[F)J public fun needsCustomLayoutForChildren ()Z protected synthetic fun onAfterUpdateTransaction (Landroid/view/View;)V protected fun onAfterUpdateTransaction (Lcom/facebook/react/views/text/ReactTextView;)V + public fun onPostProcessSpannable (Landroid/text/Spannable;)V protected synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View; protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/text/ReactTextView;)Lcom/facebook/react/views/text/ReactTextView; public fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 0b79dd68168..0222cb4082b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -79,6 +79,7 @@ import com.facebook.react.uimanager.RootViewUtil; import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManagerPropertyUpdater; import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.uimanager.events.BatchEventDispatchedListener; @@ -88,6 +89,8 @@ import com.facebook.react.uimanager.events.FabricEventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.SynchronousEventReceiver; import com.facebook.react.views.text.PreparedLayout; +import com.facebook.react.views.text.ReactTextViewManager; +import com.facebook.react.views.text.ReactTextViewManagerCallback; import com.facebook.react.views.text.TextLayoutManager; import java.util.ArrayList; import java.util.HashMap; @@ -533,31 +536,6 @@ public class FabricUIManager PixelUtil.toPixelFromDIP(height)); } - @SuppressWarnings("unused") - private long measure( - int rootTag, - String componentName, - ReadableMap localData, - ReadableMap props, - ReadableMap state, - float minWidth, - float maxWidth, - float minHeight, - float maxHeight) { - return measure( - rootTag, - componentName, - localData, - props, - state, - minWidth, - maxWidth, - minHeight, - maxHeight, - null); - } - - @SuppressWarnings("unused") public int getColor(int surfaceId, String[] resourcePaths) { ThemedReactContext context = mMountingManager.getSurfaceManagerEnforced(surfaceId, "getColor").getContext(); @@ -575,8 +553,13 @@ public class FabricUIManager return 0; } - @SuppressWarnings("unused") - private long measure( + /** + * Calls the measure() function on a specific view manager. This may be used for implementing + * custom Fabric ShadowNodes + */ + @AnyThread + @ThreadConfined(ANY) + public long measure( int surfaceId, String componentName, ReadableMap localData, @@ -585,9 +568,7 @@ public class FabricUIManager float minWidth, float maxWidth, float minHeight, - float maxHeight, - @Nullable float[] attachmentsPositions) { - + float maxHeight) { ReactContext context; if (surfaceId > 0) { SurfaceMountingManager surfaceMountingManager = @@ -612,16 +593,16 @@ public class FabricUIManager getYogaMeasureMode(minWidth, maxWidth), getYogaSize(minHeight, maxHeight), getYogaMeasureMode(minHeight, maxHeight), - attachmentsPositions); + null); } - @SuppressWarnings("unused") - private long measureMapBuffer( + @AnyThread + @ThreadConfined(ANY) + @UnstableReactNativeAPI + public long measureText( int surfaceId, - String componentName, - ReadableMapBuffer localData, - ReadableMapBuffer props, - @Nullable ReadableMapBuffer state, + ReadableMapBuffer attributedString, + ReadableMapBuffer paragraphAttributes, float minWidth, float maxWidth, float minHeight, @@ -631,7 +612,7 @@ public class FabricUIManager ReactContext context; if (surfaceId > 0) { SurfaceMountingManager surfaceMountingManager = - mMountingManager.getSurfaceManagerEnforced(surfaceId, "measure"); + mMountingManager.getSurfaceManagerEnforced(surfaceId, "measureText"); if (surfaceMountingManager.isStopped()) { return 0; } @@ -642,17 +623,19 @@ public class FabricUIManager context = mReactApplicationContext; } - // TODO: replace ReadableNativeMap -> ReadableMapBuffer - return mMountingManager.measureMapBuffer( + ViewManager textViewManager = mViewManagerRegistry.get(ReactTextViewManager.REACT_CLASS); + + return TextLayoutManager.measureText( context, - componentName, - localData, - props, - state, + attributedString, + paragraphAttributes, getYogaSize(minWidth, maxWidth), getYogaMeasureMode(minWidth, maxWidth), getYogaSize(minHeight, maxHeight), getYogaMeasureMode(minHeight, maxHeight), + textViewManager instanceof ReactTextViewManagerCallback + ? (ReactTextViewManagerCallback) textViewManager + : null, attachmentsPositions); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index c31a91e33ef..85fc6a26cc0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -381,49 +381,6 @@ public class MountingManager { attachmentsPositions); } - /** - * Measure a component, given localData, props, state, and measurement information. This needs to - * remain here for now - and not in SurfaceMountingManager - because sometimes measures are made - * outside of the context of a Surface; especially from C++ before StartSurface is called. - * - * @param context - * @param componentName - * @param localData - * @param props - * @param width - * @param widthMode - * @param height - * @param heightMode - * @param attachmentsPositions - * @return - */ - @AnyThread - public long measureMapBuffer( - ReactContext context, - String componentName, - MapBuffer localData, - MapBuffer props, - @Nullable MapBuffer state, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode, - @Nullable float[] attachmentsPositions) { - - return mViewManagerRegistry - .get(componentName) - .measure( - context, - localData, - props, - state, - width, - widthMode, - height, - heightMode, - attachmentsPositions); - } - /** * THIS PREFETCH METHOD IS EXPERIMENTAL, DO NOT USE IT FOR PRODUCTION CODE. IT WILL MOST LIKELY * CHANGE OR BE REMOVED IN THE FUTURE. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index 2c037b6b4ec..2a2009e5a8b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -427,26 +427,20 @@ public abstract class ViewManager } /** - * Subclasses can override this method to implement custom measure functions for the ViewManager + * Subclasses can override this method to implement custom measure functions for the ViewManager. + * This function is never called automatically, but may be called manually via + * FabricUIManager.measure(). * * @param context {@link com.facebook.react.bridge.ReactContext} used for the view. * @param localData {@link ReadableMap} containing "local data" defined in C++ * @param props {@link ReadableMap} containing JS props * @param state {@link ReadableMap} containing state defined in C++ - * @param width width of the view (usually zero) - * @param widthMode widthMode used during calculation of layout - * @param height height of the view (usually zero) - * @param heightMode widthMode used during calculation of layout - * @param attachmentsPositions {@link int[]} array containing 2x times the amount of attachments - * of the view. An attachment represents the position of an inline view that needs to be - * rendered inside a component and it requires the content of the parent view in order to be - * positioned. This array is meant to be used by the platform to RETURN the position of each - * attachment, as a result of the calculation of layout. (e.g. this array is used to measure - * inlineViews that are rendered inside Text components). On most of the components this array - * will be contain a null value. - *

Even values will represent the TOP of each attachment, Odd values represent the LEFT of - * each attachment. - * @return result of calculation of layout for the arguments received as a parameter. + * @param width width of the constraint, if YogaMeasureMode.EXACTLY or YogaMeasureMode.AT_MOST + * @param widthMode MeasureMode used during calculation of layout + * @param height height of the constraint, if YogaMeasureMode.EXACTLY or YogaMeasureMode.AT_MOST + * @param heightMode MeasureMode used during calculation of layout + * @param attachmentsPositions Always null. Only present for backwards compatibility. + * @return bit-packed width and height created via YogaMeasureOutput.make(). */ public long measure( Context context, @@ -461,47 +455,6 @@ public abstract class ViewManager return 0; } - /** - * THIS MEASURE METHOD IS EXPERIMENTAL, MOST LIKELY YOU ARE LOOKING TO USE THE OTHER OVERLOAD - * INSTEAD: {@link #measure(Context, ReadableMap, ReadableMap, ReadableMap, float, - * YogaMeasureMode, float, YogaMeasureMode, float[])} - * - *

Subclasses can override this method to implement custom measure functions for the - * ViewManager - * - * @param context {@link com.facebook.react.bridge.ReactContext} used for the view. - * @param localData {@link MapBuffer} containing "local data" defined in C++ - * @param props {@link MapBuffer} containing JS props - * @param state {@link MapBuffer} containing state defined in C++ - * @param width width of the view (usually zero) - * @param widthMode widthMode used during calculation of layout - * @param height height of the view (usually zero) - * @param heightMode widthMode used during calculation of layout - * @param attachmentsPositions {@link int[]} array containing 2x times the amount of attachments - * of the view. An attachment represents the position of an inline view that needs to be - * rendered inside a component and it requires the content of the parent view in order to be - * positioned. This array is meant to be used by the platform to RETURN the position of each - * attachment, as a result of the calculation of layout. (e.g. this array is used to measure - * inlineViews that are rendered inside Text components). On most of the components this array - * will be contain a null value. - *

Even values will represent the TOP of each attachment, Odd values represent the LEFT of - * each attachment. - * @return result of calculation of layout for the arguments received as a parameter. - */ - public long measure( - Context context, - MapBuffer localData, - MapBuffer props, - // TODO(T114731225): review whether state parameter is needed - @Nullable MapBuffer state, - float width, - YogaMeasureMode widthMode, - float height, - YogaMeasureMode heightMode, - @Nullable float[] attachmentsPositions) { - return 0; - } - /** * Subclasses can override this method to set padding for the given View in Fabric. Since not all * components support setting padding, the default implementation of this method does nothing. diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index 7ab73cd18ac..24fe0c1e091 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -7,7 +7,6 @@ package com.facebook.react.views.text; -import android.content.Context; import android.os.Build; import android.text.Spannable; import androidx.annotation.NonNull; @@ -27,7 +26,6 @@ import com.facebook.react.uimanager.StateWrapper; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.views.text.internal.span.TextInlineImageSpan; -import com.facebook.yoga.YogaMeasureMode; import java.util.HashMap; import java.util.Map; @@ -38,7 +36,7 @@ import java.util.Map; @Nullsafe(Nullsafe.Mode.LOCAL) @ReactModule(name = ReactTextViewManager.REACT_CLASS) public class ReactTextViewManager extends ReactTextAnchorViewManager - implements IViewManagerWithChildren { + implements IViewManagerWithChildren, ReactTextViewManagerCallback { private static final String TAG = "ReactTextViewManager"; @@ -201,26 +199,10 @@ public class ReactTextViewManager extends ReactTextAnchorViewManager& fabricUIManager = contextContainer->at>("FabricUIManager"); - auto componentNameRef = jni::make_jstring(componentName); static auto measure = jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") ->getMethod("measureMapBuffer"); + jfloatArray)>("measureText"); - auto localDataMap = - JReadableMapBuffer::createWithContents(std::move(localData)); - auto propsMap = JReadableMapBuffer::createWithContents(std::move(props)); + auto attributedStringBuffer = + JReadableMapBuffer::createWithContents(std::move(attributedString)); + auto paragraphAttributesBuffer = + JReadableMapBuffer::createWithContents(std::move(paragraphAttributes)); - auto size = yogaMeassureToSize(measure( + return yogaMeassureToSize(measure( fabricUIManager, rootTag, - componentNameRef.get(), - localDataMap.get(), - propsMap.get(), - nullptr, + attributedStringBuffer.get(), + paragraphAttributesBuffer.get(), minWidth, maxWidth, minHeight, maxHeight, attachmentPositions)); - - // Explicitly release smart pointers to free up space faster in JNI tables - componentNameRef.reset(); - localDataMap.reset(); - propsMap.reset(); - return size; } TextMeasurement doMeasure( @@ -114,10 +103,9 @@ TextMeasurement doMeasure( auto attributedStringMap = toMapBuffer(attributedString); auto paragraphAttributesMap = toMapBuffer(paragraphAttributes); - auto size = measureAndroidComponent( + auto size = measureText( contextContainer, layoutContext.surfaceId, - "RCTText", std::move(attributedStringMap), std::move(paragraphAttributesMap), minimumSize.width, @@ -227,10 +215,9 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById( // TODO: this is always sourced from an int, and Java expects an int localDataBuilder.putInt(AS_KEY_CACHE_ID, static_cast(cacheId)); - auto size = measureAndroidComponent( + auto size = measureText( contextContainer_, layoutContext.surfaceId, - "RCTText", localDataBuilder.build(), toMapBuffer(paragraphAttributes), minimumSize.width,