Permalink
Browse files

ReactRawTextShadowNode does not inherit ReactTextShadowNode anymore

Summary:
ReactRawTextShadowNode represents "raw" text node (aka textContent), so it cannot have layout or styling, it is just a line of text, a pure string.
So, we must not interit it from HUGE ReactTextShadowNode (which represents <Text> node with ReactRawTextShadowNode instance inside).
We need this change to make future fancy (and valuable from product perspective) refactoring possible. Stay tuned!

Reviewed By: achen1

Differential Revision: D5712961

fbshipit-source-id: 009e48046bdf34ddfd40b93175934969af64b98b
  • Loading branch information...
shergin authored and facebook-github-bot committed Sep 11, 2017
1 parent 393bf88 commit 80027ce6dba1b632b080dc17a08b9f904d649217
@@ -9,16 +9,19 @@
package com.facebook.react.views.text;
import android.view.View;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManager;
/**
* Manages raw text nodes. Since they are used only as a virtual nodes any type of native view
* operation will throw an {@link IllegalStateException}
* Manages raw text nodes (aka {@code textContent} in terms of DOM).
* Since they are used only as a virtual nodes, any type of native view
* operation will throw an {@link IllegalStateException}.
*/
@ReactModule(name = ReactRawTextManager.REACT_CLASS)
public class ReactRawTextManager extends ReactTextViewManager {
public class ReactRawTextManager extends ViewManager<View, ReactRawTextShadowNode> {
@VisibleForTesting
public static final String REACT_CLASS = "RCTRawText";
@@ -30,15 +33,19 @@ public String getName() {
@Override
public ReactTextView createViewInstance(ThemedReactContext context) {
throw new IllegalStateException("RKRawText doesn't map into a native view");
throw new IllegalStateException("Attempt to create a native view for RCTRawText");
}
@Override
public void updateExtraData(ReactTextView view, Object extraData) {
public void updateExtraData(View view, Object extraData) {}
@Override
public Class<ReactRawTextShadowNode> getShadowNodeClass() {
return ReactRawTextShadowNode.class;
}
@Override
public ReactTextShadowNode createShadowNodeInstance() {
return new ReactVirtualTextShadowNode();
public ReactRawTextShadowNode createShadowNodeInstance() {
return new ReactRawTextShadowNode();
}
}
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
*
* <p>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.react.views.text;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
/**
* {@link ReactShadowNode} class for pure raw text node
* (aka {@code textContent} in terms of DOM).
* Raw text node can only have simple string value without any attributes,
* properties or state.
*/
public class ReactRawTextShadowNode extends ReactShadowNode {
@VisibleForTesting public static final String PROP_TEXT = "text";
private @Nullable String mText = null;
@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;
markUpdated();
}
public @Nullable String getText() {
return mText;
}
@Override
public boolean isVirtual() {
return true;
}
}
@@ -9,11 +9,6 @@
package com.facebook.react.views.text;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Typeface;
import android.os.Build;
import android.text.BoringLayout;
@@ -30,27 +25,28 @@
import android.text.style.UnderlineSpan;
import android.view.Gravity;
import android.widget.TextView;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaConstants;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.yoga.YogaConstants;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
/**
* {@link ReactShadowNode} class for spannable text view.
@@ -107,13 +103,15 @@ private static void buildSpannedFromShadowNode(
ReactTextShadowNode textShadowNode,
SpannableStringBuilder sb,
List<SetSpanOperation> ops) {
int start = sb.length();
if (textShadowNode.mText != null) {
sb.append(textShadowNode.mText);
}
for (int i = 0, length = textShadowNode.getChildCount(); i < length; i++) {
ReactShadowNode child = textShadowNode.getChildAt(i);
if (child instanceof ReactTextShadowNode) {
if (child instanceof ReactRawTextShadowNode) {
sb.append(((ReactRawTextShadowNode) child).getText());
} else if (child instanceof ReactTextShadowNode) {
buildSpannedFromShadowNode((ReactTextShadowNode) child, sb, ops);
} else if (child instanceof ReactTextInlineImageShadowNode) {
// We make the image take up 1 character in the span and put a corresponding character into
@@ -182,15 +180,22 @@ private static void buildSpannedFromShadowNode(
}
}
protected static Spannable spannedFromShadowNode(ReactTextShadowNode textShadowNode) {
protected static Spannable spannedFromShadowNode(
ReactTextShadowNode textShadowNode, String text) {
SpannableStringBuilder sb = new SpannableStringBuilder();
// TODO(5837930): Investigate whether it's worth optimizing this part and do it if so
// The {@link SpannableStringBuilder} implementation require setSpan operation to be called
// up-to-bottom, otherwise all the spannables that are withing the region for which one may set
// a new spannable will be wiped out
List<SetSpanOperation> ops = new ArrayList<>();
buildSpannedFromShadowNode(textShadowNode, sb, ops);
if (text != null) {
sb.append(text);
}
if (textShadowNode.mFontSize == UNSET) {
sb.setSpan(
new AbsoluteSizeSpan(textShadowNode.mAllowFontScaling
@@ -375,7 +380,6 @@ private static int parseNumericFontWeight(String fontWeightString) {
* <Text style={{fontFamily="serif}}>Bold Text</Text>
*/
private @Nullable String mFontFamily = null;
private @Nullable String mText = null;
private @Nullable Spannable mPreparedSpannableText;
@@ -415,7 +419,7 @@ public void onBeforeLayout() {
if (isVirtual()) {
return;
}
mPreparedSpannableText = spannedFromShadowNode(this);
mPreparedSpannableText = spannedFromShadowNode(this, null);
markUpdated();
}
@@ -428,12 +432,6 @@ public void markUpdated() {
}
}
@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;
markUpdated();
}
@ReactProp(name = ViewProps.NUMBER_OF_LINES, defaultInt = UNSET)
public void setNumberOfLines(int numberOfLines) {
mNumberOfLines = numberOfLines == 0 ? UNSET : numberOfLines;
@@ -11,13 +11,14 @@
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.ThemedReactContext;
/**
* Manages raw text nodes. Since they are used only as a virtual nodes any type of native view
* operation will throw an {@link IllegalStateException}
*/
@ReactModule(name = ReactVirtualTextViewManager.REACT_CLASS)
public class ReactVirtualTextViewManager extends ReactRawTextManager {
public class ReactVirtualTextViewManager extends ReactTextViewManager {
@VisibleForTesting
public static final String REACT_CLASS = "RCTVirtualText";
@@ -26,4 +27,17 @@
public String getName() {
return REACT_CLASS;
}
@Override
public ReactTextView createViewInstance(ThemedReactContext context) {
throw new IllegalStateException("Attempt to create a native view for RCTVirtualText");
}
@Override
public void updateExtraData(ReactTextView view, Object extraData) {}
@Override
public ReactTextShadowNode createShadowNodeInstance() {
return new ReactVirtualTextShadowNode();
}
}
@@ -9,21 +9,11 @@
package com.facebook.react.views.textinput;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import android.os.Build;
import android.text.Layout;
import android.text.Spannable;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.EditText;
import com.facebook.yoga.YogaDirection;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.common.annotations.VisibleForTesting;
@@ -32,11 +22,15 @@
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.ViewDefaults;
import com.facebook.react.uimanager.ViewProps;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.view.MeasureUtil;
import com.facebook.react.views.text.ReactTextShadowNode;
import com.facebook.react.views.text.ReactTextUpdate;
import com.facebook.react.views.view.MeasureUtil;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaMeasureMode;
import com.facebook.yoga.YogaMeasureOutput;
import com.facebook.yoga.YogaNode;
import javax.annotation.Nullable;
@VisibleForTesting
public class ReactTextInputShadowNode extends ReactTextShadowNode implements
@@ -45,6 +39,10 @@
private @Nullable EditText mEditText;
private int mMostRecentEventCount = UNSET;
@VisibleForTesting public static final String PROP_TEXT = "text";
private @Nullable String mText = null;
public ReactTextInputShadowNode() {
mTextBreakStrategy = (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ?
0 : Layout.BREAK_STRATEGY_SIMPLE;
@@ -114,6 +112,16 @@ public void setMostRecentEventCount(int mostRecentEventCount) {
mMostRecentEventCount = mostRecentEventCount;
}
@ReactProp(name = PROP_TEXT)
public void setText(@Nullable String text) {
mText = text;
markUpdated();
}
public @Nullable String getText() {
return mText;
}
@Override
public void setTextBreakStrategy(@Nullable String textBreakStrategy) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
@@ -136,19 +144,17 @@ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
super.onCollectExtraUpdates(uiViewOperationQueue);
if (mMostRecentEventCount != UNSET) {
Spannable preparedSpannableText = spannedFromShadowNode(this);
ReactTextUpdate reactTextUpdate =
new ReactTextUpdate(
preparedSpannableText,
mMostRecentEventCount,
mContainsImages,
getPadding(Spacing.LEFT),
getPadding(Spacing.TOP),
getPadding(Spacing.RIGHT),
getPadding(Spacing.BOTTOM),
mTextAlign,
mTextBreakStrategy
);
new ReactTextUpdate(
spannedFromShadowNode(this, getText()),
mMostRecentEventCount,
mContainsImages,
getPadding(Spacing.LEFT),
getPadding(Spacing.TOP),
getPadding(Spacing.RIGHT),
getPadding(Spacing.BOTTOM),
mTextAlign,
mTextBreakStrategy);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
}
}

0 comments on commit 80027ce

Please sign in to comment.