Skip to content

Commit 2ba86ca

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Clean up measurements path and ReactTextViewManagerCallback injection (#51758)
Summary: Pull Request resolved: #51758 ViewManager’s may implement a measure function (using MapBuffer, or ReadableMap). This isn’t used automatically, but may instead be used, by calling into FabricUIManager via JNI, and passing the component name of the view manager to use. This is only ever called manually, through specific C++ ShadowNodes. Confusingly, for some cases, like `TextInput`, we use the measure function on `RCTText` instead, because this call to FabricUIManager is hidden behind `TextLayoutManager`. This ends up really breaking Facsimile, since we want to measure these in a different way, while still measuring `TextInput` without preparing a layout. This mechanism is also used to inject `ReactTextViewManager` from the Text View Manager, into the measurement process, for both Text, and TextInput. I think we would ideally remove and replace the current View Manager measurement mechanism entirely. The interface doesn’t do what it claims to, and requires calling private Java methods via JNI, which we shouldn’t be encouraging external libraries to do. Only a single 3p librar (react-native-picker) uses this, but we have a lot of internal usages, and the current facility is valuable, for translating surface ID into a context. Ie we could not deprecate it without a well thought out replacement. Instead, this change: 1. Removes the "generalized" version of this for MapBuffer, only ever used by Text 2. Given ourselves a `measureText` function, that will use a spannable processor provided, but go through TextLayoutManager, instead of trying to use this generalized path 3. Documents some of the weirdness of the current setup, without yet deprecating it This will let the Facsimile View Manager: 1. Provide a ReactTextViewManagerCallback, like the previous version allowed, that influences measurement of both Text, and TextInput (which is... strange, but... not trying to boil the ocean here) 2. TextInput can now measure text, even if Facsimile View Manager doesn't implement this traditional measure interface Changelog: [Android][Breaking] - Remove FabricUIManager.measureMapBuffer() and MapBuffer measure functions on ViewManager. Please use ReadableMap variant. [Android][Breaking] - Remove FabricUIManager.measure overload which accepts attachment positions Reviewed By: javache Differential Revision: D75826792 fbshipit-source-id: 6739b1e2e214351b9b95ba782d73cf4278211ab8
1 parent 997b7c9 commit 2ba86ca

File tree

6 files changed

+57
-196
lines changed

6 files changed

+57
-196
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,7 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid
23382338
public fun initialize ()V
23392339
public fun invalidate ()V
23402340
public fun markActiveTouchForTag (II)V
2341+
public fun measure (ILjava/lang/String;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/ReadableMap;FFFF)J
23412342
public fun onAllAnimationsComplete ()V
23422343
public fun onAnimationStarted ()V
23432344
public fun onHostDestroy ()V
@@ -2402,7 +2403,6 @@ public class com/facebook/react/fabric/mounting/MountingManager {
24022403
public fun getViewExists (I)Z
24032404
public fun isWaitingForViewAttach (I)Z
24042405
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
2405-
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
24062406
public fun receiveCommand (IIILcom/facebook/react/bridge/ReadableArray;)V
24072407
public fun receiveCommand (IILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V
24082408
public fun sendAccessibilityEvent (III)V
@@ -4629,7 +4629,6 @@ public abstract class com/facebook/react/uimanager/ViewManager : com/facebook/re
46294629
public fun getNativeProps ()Ljava/util/Map;
46304630
public abstract fun getShadowNodeClass ()Ljava/lang/Class;
46314631
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
4632-
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
46334632
protected fun onAfterUpdateTransaction (Landroid/view/View;)V
46344633
public fun onDropViewInstance (Landroid/view/View;)V
46354634
public fun onSurfaceStopped (I)V
@@ -6320,7 +6319,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi
63206319
protected fun verifyDrawable (Landroid/graphics/drawable/Drawable;)Z
63216320
}
63226321

6323-
public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/IViewManagerWithChildren {
6322+
public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/react/uimanager/IViewManagerWithChildren, com/facebook/react/views/text/ReactTextViewManagerCallback {
63246323
protected field mReactTextViewManagerCallback Lcom/facebook/react/views/text/ReactTextViewManagerCallback;
63256324
public fun <init> ()V
63266325
public fun <init> (Lcom/facebook/react/views/text/ReactTextViewManagerCallback;)V
@@ -6332,10 +6331,10 @@ public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/r
63326331
public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map;
63336332
public fun getName ()Ljava/lang/String;
63346333
public fun getShadowNodeClass ()Ljava/lang/Class;
6335-
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
63366334
public fun needsCustomLayoutForChildren ()Z
63376335
protected synthetic fun onAfterUpdateTransaction (Landroid/view/View;)V
63386336
protected fun onAfterUpdateTransaction (Lcom/facebook/react/views/text/ReactTextView;)V
6337+
public fun onPostProcessSpannable (Landroid/text/Spannable;)V
63396338
protected synthetic fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
63406339
protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Lcom/facebook/react/views/text/ReactTextView;)Lcom/facebook/react/views/text/ReactTextView;
63416340
public fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import com.facebook.react.uimanager.StateWrapper;
8080
import com.facebook.react.uimanager.ThemedReactContext;
8181
import com.facebook.react.uimanager.UIManagerHelper;
82+
import com.facebook.react.uimanager.ViewManager;
8283
import com.facebook.react.uimanager.ViewManagerPropertyUpdater;
8384
import com.facebook.react.uimanager.ViewManagerRegistry;
8485
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
@@ -88,6 +89,8 @@
8889
import com.facebook.react.uimanager.events.RCTEventEmitter;
8990
import com.facebook.react.uimanager.events.SynchronousEventReceiver;
9091
import com.facebook.react.views.text.PreparedLayout;
92+
import com.facebook.react.views.text.ReactTextViewManager;
93+
import com.facebook.react.views.text.ReactTextViewManagerCallback;
9194
import com.facebook.react.views.text.TextLayoutManager;
9295
import java.util.ArrayList;
9396
import java.util.HashMap;
@@ -533,31 +536,6 @@ private NativeArray measureLines(
533536
PixelUtil.toPixelFromDIP(height));
534537
}
535538

536-
@SuppressWarnings("unused")
537-
private long measure(
538-
int rootTag,
539-
String componentName,
540-
ReadableMap localData,
541-
ReadableMap props,
542-
ReadableMap state,
543-
float minWidth,
544-
float maxWidth,
545-
float minHeight,
546-
float maxHeight) {
547-
return measure(
548-
rootTag,
549-
componentName,
550-
localData,
551-
props,
552-
state,
553-
minWidth,
554-
maxWidth,
555-
minHeight,
556-
maxHeight,
557-
null);
558-
}
559-
560-
@SuppressWarnings("unused")
561539
public int getColor(int surfaceId, String[] resourcePaths) {
562540
ThemedReactContext context =
563541
mMountingManager.getSurfaceManagerEnforced(surfaceId, "getColor").getContext();
@@ -575,8 +553,13 @@ public int getColor(int surfaceId, String[] resourcePaths) {
575553
return 0;
576554
}
577555

578-
@SuppressWarnings("unused")
579-
private long measure(
556+
/**
557+
* Calls the measure() function on a specific view manager. This may be used for implementing
558+
* custom Fabric ShadowNodes
559+
*/
560+
@AnyThread
561+
@ThreadConfined(ANY)
562+
public long measure(
580563
int surfaceId,
581564
String componentName,
582565
ReadableMap localData,
@@ -585,9 +568,7 @@ private long measure(
585568
float minWidth,
586569
float maxWidth,
587570
float minHeight,
588-
float maxHeight,
589-
@Nullable float[] attachmentsPositions) {
590-
571+
float maxHeight) {
591572
ReactContext context;
592573
if (surfaceId > 0) {
593574
SurfaceMountingManager surfaceMountingManager =
@@ -612,16 +593,16 @@ private long measure(
612593
getYogaMeasureMode(minWidth, maxWidth),
613594
getYogaSize(minHeight, maxHeight),
614595
getYogaMeasureMode(minHeight, maxHeight),
615-
attachmentsPositions);
596+
null);
616597
}
617598

618-
@SuppressWarnings("unused")
619-
private long measureMapBuffer(
599+
@AnyThread
600+
@ThreadConfined(ANY)
601+
@UnstableReactNativeAPI
602+
public long measureText(
620603
int surfaceId,
621-
String componentName,
622-
ReadableMapBuffer localData,
623-
ReadableMapBuffer props,
624-
@Nullable ReadableMapBuffer state,
604+
ReadableMapBuffer attributedString,
605+
ReadableMapBuffer paragraphAttributes,
625606
float minWidth,
626607
float maxWidth,
627608
float minHeight,
@@ -631,7 +612,7 @@ private long measureMapBuffer(
631612
ReactContext context;
632613
if (surfaceId > 0) {
633614
SurfaceMountingManager surfaceMountingManager =
634-
mMountingManager.getSurfaceManagerEnforced(surfaceId, "measure");
615+
mMountingManager.getSurfaceManagerEnforced(surfaceId, "measureText");
635616
if (surfaceMountingManager.isStopped()) {
636617
return 0;
637618
}
@@ -642,17 +623,19 @@ private long measureMapBuffer(
642623
context = mReactApplicationContext;
643624
}
644625

645-
// TODO: replace ReadableNativeMap -> ReadableMapBuffer
646-
return mMountingManager.measureMapBuffer(
626+
ViewManager textViewManager = mViewManagerRegistry.get(ReactTextViewManager.REACT_CLASS);
627+
628+
return TextLayoutManager.measureText(
647629
context,
648-
componentName,
649-
localData,
650-
props,
651-
state,
630+
attributedString,
631+
paragraphAttributes,
652632
getYogaSize(minWidth, maxWidth),
653633
getYogaMeasureMode(minWidth, maxWidth),
654634
getYogaSize(minHeight, maxHeight),
655635
getYogaMeasureMode(minHeight, maxHeight),
636+
textViewManager instanceof ReactTextViewManagerCallback
637+
? (ReactTextViewManagerCallback) textViewManager
638+
: null,
656639
attachmentsPositions);
657640
}
658641

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -381,49 +381,6 @@ public long measure(
381381
attachmentsPositions);
382382
}
383383

384-
/**
385-
* Measure a component, given localData, props, state, and measurement information. This needs to
386-
* remain here for now - and not in SurfaceMountingManager - because sometimes measures are made
387-
* outside of the context of a Surface; especially from C++ before StartSurface is called.
388-
*
389-
* @param context
390-
* @param componentName
391-
* @param localData
392-
* @param props
393-
* @param width
394-
* @param widthMode
395-
* @param height
396-
* @param heightMode
397-
* @param attachmentsPositions
398-
* @return
399-
*/
400-
@AnyThread
401-
public long measureMapBuffer(
402-
ReactContext context,
403-
String componentName,
404-
MapBuffer localData,
405-
MapBuffer props,
406-
@Nullable MapBuffer state,
407-
float width,
408-
YogaMeasureMode widthMode,
409-
float height,
410-
YogaMeasureMode heightMode,
411-
@Nullable float[] attachmentsPositions) {
412-
413-
return mViewManagerRegistry
414-
.get(componentName)
415-
.measure(
416-
context,
417-
localData,
418-
props,
419-
state,
420-
width,
421-
widthMode,
422-
height,
423-
heightMode,
424-
attachmentsPositions);
425-
}
426-
427384
/**
428385
* THIS PREFETCH METHOD IS EXPERIMENTAL, DO NOT USE IT FOR PRODUCTION CODE. IT WILL MOST LIKELY
429386
* CHANGE OR BE REMOVED IN THE FUTURE.

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -427,26 +427,20 @@ public Map<String, String> getNativeProps() {
427427
}
428428

429429
/**
430-
* Subclasses can override this method to implement custom measure functions for the ViewManager
430+
* Subclasses can override this method to implement custom measure functions for the ViewManager.
431+
* This function is never called automatically, but may be called manually via
432+
* FabricUIManager.measure().
431433
*
432434
* @param context {@link com.facebook.react.bridge.ReactContext} used for the view.
433435
* @param localData {@link ReadableMap} containing "local data" defined in C++
434436
* @param props {@link ReadableMap} containing JS props
435437
* @param state {@link ReadableMap} containing state defined in C++
436-
* @param width width of the view (usually zero)
437-
* @param widthMode widthMode used during calculation of layout
438-
* @param height height of the view (usually zero)
439-
* @param heightMode widthMode used during calculation of layout
440-
* @param attachmentsPositions {@link int[]} array containing 2x times the amount of attachments
441-
* of the view. An attachment represents the position of an inline view that needs to be
442-
* rendered inside a component and it requires the content of the parent view in order to be
443-
* positioned. This array is meant to be used by the platform to RETURN the position of each
444-
* attachment, as a result of the calculation of layout. (e.g. this array is used to measure
445-
* inlineViews that are rendered inside Text components). On most of the components this array
446-
* will be contain a null value.
447-
* <p>Even values will represent the TOP of each attachment, Odd values represent the LEFT of
448-
* each attachment.
449-
* @return result of calculation of layout for the arguments received as a parameter.
438+
* @param width width of the constraint, if YogaMeasureMode.EXACTLY or YogaMeasureMode.AT_MOST
439+
* @param widthMode MeasureMode used during calculation of layout
440+
* @param height height of the constraint, if YogaMeasureMode.EXACTLY or YogaMeasureMode.AT_MOST
441+
* @param heightMode MeasureMode used during calculation of layout
442+
* @param attachmentsPositions Always null. Only present for backwards compatibility.
443+
* @return bit-packed width and height created via YogaMeasureOutput.make().
450444
*/
451445
public long measure(
452446
Context context,
@@ -461,47 +455,6 @@ public long measure(
461455
return 0;
462456
}
463457

464-
/**
465-
* THIS MEASURE METHOD IS EXPERIMENTAL, MOST LIKELY YOU ARE LOOKING TO USE THE OTHER OVERLOAD
466-
* INSTEAD: {@link #measure(Context, ReadableMap, ReadableMap, ReadableMap, float,
467-
* YogaMeasureMode, float, YogaMeasureMode, float[])}
468-
*
469-
* <p>Subclasses can override this method to implement custom measure functions for the
470-
* ViewManager
471-
*
472-
* @param context {@link com.facebook.react.bridge.ReactContext} used for the view.
473-
* @param localData {@link MapBuffer} containing "local data" defined in C++
474-
* @param props {@link MapBuffer} containing JS props
475-
* @param state {@link MapBuffer} containing state defined in C++
476-
* @param width width of the view (usually zero)
477-
* @param widthMode widthMode used during calculation of layout
478-
* @param height height of the view (usually zero)
479-
* @param heightMode widthMode used during calculation of layout
480-
* @param attachmentsPositions {@link int[]} array containing 2x times the amount of attachments
481-
* of the view. An attachment represents the position of an inline view that needs to be
482-
* rendered inside a component and it requires the content of the parent view in order to be
483-
* positioned. This array is meant to be used by the platform to RETURN the position of each
484-
* attachment, as a result of the calculation of layout. (e.g. this array is used to measure
485-
* inlineViews that are rendered inside Text components). On most of the components this array
486-
* will be contain a null value.
487-
* <p>Even values will represent the TOP of each attachment, Odd values represent the LEFT of
488-
* each attachment.
489-
* @return result of calculation of layout for the arguments received as a parameter.
490-
*/
491-
public long measure(
492-
Context context,
493-
MapBuffer localData,
494-
MapBuffer props,
495-
// TODO(T114731225): review whether state parameter is needed
496-
@Nullable MapBuffer state,
497-
float width,
498-
YogaMeasureMode widthMode,
499-
float height,
500-
YogaMeasureMode heightMode,
501-
@Nullable float[] attachmentsPositions) {
502-
return 0;
503-
}
504-
505458
/**
506459
* Subclasses can override this method to set padding for the given View in Fabric. Since not all
507460
* components support setting padding, the default implementation of this method does nothing.

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
package com.facebook.react.views.text;
99

10-
import android.content.Context;
1110
import android.os.Build;
1211
import android.text.Spannable;
1312
import androidx.annotation.NonNull;
@@ -27,7 +26,6 @@
2726
import com.facebook.react.uimanager.ThemedReactContext;
2827
import com.facebook.react.uimanager.annotations.ReactProp;
2928
import com.facebook.react.views.text.internal.span.TextInlineImageSpan;
30-
import com.facebook.yoga.YogaMeasureMode;
3129
import java.util.HashMap;
3230
import java.util.Map;
3331

@@ -38,7 +36,7 @@
3836
@Nullsafe(Nullsafe.Mode.LOCAL)
3937
@ReactModule(name = ReactTextViewManager.REACT_CLASS)
4038
public class ReactTextViewManager extends ReactTextAnchorViewManager<ReactTextShadowNode>
41-
implements IViewManagerWithChildren {
39+
implements IViewManagerWithChildren, ReactTextViewManagerCallback {
4240

4341
private static final String TAG = "ReactTextViewManager";
4442

@@ -201,26 +199,10 @@ private Object getReactTextUpdate(ReactTextView view, ReactStylesDiffMap props,
201199
}
202200

203201
@Override
204-
public long measure(
205-
Context context,
206-
MapBuffer localData,
207-
MapBuffer props,
208-
@Nullable MapBuffer state,
209-
float width,
210-
YogaMeasureMode widthMode,
211-
float height,
212-
YogaMeasureMode heightMode,
213-
@Nullable float[] attachmentsPositions) {
214-
return TextLayoutManager.measureText(
215-
context,
216-
localData,
217-
props,
218-
width,
219-
widthMode,
220-
height,
221-
heightMode,
222-
mReactTextViewManagerCallback,
223-
attachmentsPositions);
202+
public void onPostProcessSpannable(Spannable text) {
203+
if (mReactTextViewManagerCallback != null) {
204+
mReactTextViewManagerCallback.onPostProcessSpannable(text);
205+
}
224206
}
225207

226208
@Override

0 commit comments

Comments
 (0)