Support RTL for Nodes

Summary: Nodes supports RTL now, just like non-Nodes does.

Differential Revision: D3727028
This commit is contained in:
Ahmed El-Helw 2016-08-18 12:50:23 -07:00
parent d785390a35
commit 1f6642b34a
4 changed files with 53 additions and 18 deletions

View File

@ -15,10 +15,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.facebook.csslayout.CSSDirection;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.UIImplementation;
@ -82,6 +84,7 @@ public class FlatUIImplementation extends UIImplementation {
private final MoveProxy mMoveProxy = new MoveProxy();
private final StateBuilder mStateBuilder;
private @Nullable ReactImageManager mReactImageManager;
private final ReactApplicationContext mReactContext;
private FlatUIImplementation(
ReactApplicationContext reactContext,
@ -89,6 +92,7 @@ public class FlatUIImplementation extends UIImplementation {
ViewManagerRegistry viewManagers,
FlatUIViewOperationQueue operationsQueue) {
super(reactContext, viewManagers, operationsQueue);
mReactContext = reactContext;
mStateBuilder = new StateBuilder(operationsQueue);
mReactImageManager = reactImageManager;
}
@ -105,7 +109,12 @@ public class FlatUIImplementation extends UIImplementation {
mReactImageManager = null;
}
return new FlatRootShadowNode();
ReactShadowNode node = new FlatRootShadowNode();
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
if (sharedI18nUtilInstance.isRTL(mReactContext)) {
node.setDirection(CSSDirection.RTL);
}
return node;
}
@Override

View File

@ -201,14 +201,14 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
@Override
public int reactTagForTouch(float touchX, float touchY) {
/**
/*
* Make sure we don't find any children if the pointer events are set to BOX_ONLY.
* There is no need to special-case any other modes, because if PointerEvents are set to:
* a) PointerEvents.AUTO - all children are included, nothing to exclude
* b) PointerEvents.NONE - this method will NOT be executed, because the View will be filtered
* out by TouchTargetHelper.
* c) PointerEvents.BOX_NONE - TouchTargetHelper will make sure that {@link #reactTagForTouch()}
* doesn't return getId().
* doesn't return getId().
*/
SoftAssertions.assertCondition(
mPointerEvents != PointerEvents.NONE,
@ -1011,11 +1011,12 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
right = Math.max(right, child.getRight());
bottom = Math.max(bottom, child.getBottom());
}
for (int i = 0; i < mDrawCommands.length; i++) {
if (!(mDrawCommands[i] instanceof AbstractDrawCommand)) {
for (DrawCommand mDrawCommand : mDrawCommands) {
if (!(mDrawCommand instanceof AbstractDrawCommand)) {
continue;
}
AbstractDrawCommand drawCommand = (AbstractDrawCommand) mDrawCommands[i];
AbstractDrawCommand drawCommand = (AbstractDrawCommand) mDrawCommand;
left = Math.min(left, Math.round(drawCommand.getLeft()));
top = Math.min(top, Math.round(drawCommand.getTop()));
right = Math.max(right, Math.round(drawCommand.getRight()));
@ -1180,7 +1181,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
return mHitSlopRect;
}
public void setHitSlopRect(@Nullable Rect rect) {
/* package */ void setHitSlopRect(@Nullable Rect rect) {
mHitSlopRect = rect;
}
}

View File

@ -14,7 +14,9 @@ import javax.annotation.Nullable;
import android.support.v4.text.TextDirectionHeuristicsCompat;
import android.text.Layout;
import android.text.TextUtils;
import android.view.Gravity;
import com.facebook.csslayout.CSSDirection;
import com.facebook.csslayout.CSSMeasureMode;
import com.facebook.csslayout.CSSNodeAPI;
import com.facebook.csslayout.MeasureOutput;
@ -49,7 +51,7 @@ import com.facebook.textcachewarmer.DefaultTextLayoutCacheWarmer;
private float mSpacingMult = 1.0f;
private float mSpacingAdd = 0.0f;
private int mNumberOfLines = Integer.MAX_VALUE;
private Layout.Alignment mAlignment = Layout.Alignment.ALIGN_NORMAL;
private int mAlignment = Gravity.NO_GRAVITY;
public RCTText() {
setMeasureFunction(this);
@ -98,7 +100,7 @@ import com.facebook.textcachewarmer.DefaultTextLayoutCacheWarmer;
mSpacingAdd,
mSpacingMult,
getFontStyle(),
mAlignment);
getAlignment());
if (mDrawCommand != null && !mDrawCommand.isFrozen()) {
mDrawCommand.setLayout(layout);
@ -163,7 +165,7 @@ import com.facebook.textcachewarmer.DefaultTextLayoutCacheWarmer;
mSpacingAdd,
mSpacingMult,
getFontStyle(),
mAlignment));
getAlignment()));
updateNodeRegion = true;
}
@ -259,21 +261,40 @@ import com.facebook.textcachewarmer.DefaultTextLayoutCacheWarmer;
@ReactProp(name = ViewProps.TEXT_ALIGN)
public void setTextAlign(@Nullable String textAlign) {
if (textAlign == null || "auto".equals(textAlign)) {
mAlignment = Layout.Alignment.ALIGN_NORMAL;
mAlignment = Gravity.NO_GRAVITY;
} else if ("left".equals(textAlign)) {
// left and right may yield potentially different results (relative to non-nodes) in cases
// when supportsRTL="true" in the manifest.
mAlignment = Layout.Alignment.ALIGN_NORMAL;
mAlignment = Gravity.LEFT;
} else if ("right".equals(textAlign)) {
mAlignment = Layout.Alignment.ALIGN_OPPOSITE;
mAlignment = Gravity.RIGHT;
} else if ("center".equals(textAlign)) {
mAlignment = Layout.Alignment.ALIGN_CENTER;
mAlignment = Gravity.CENTER;
} else {
throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlign);
}
notifyChanged(false);
}
public Layout.Alignment getAlignment() {
boolean isRtl = getLayoutDirection() == CSSDirection.RTL;
switch (mAlignment) {
// Layout.Alignment.RIGHT and Layout.Alignment.LEFT are @hide :(
case Gravity.LEFT:
int index = isRtl ? /* RIGHT */ 4 : /* LEFT */ 3;
return Layout.Alignment.values()[index];
case Gravity.RIGHT:
index = isRtl ? /* LEFT */ 3 : /* RIGHT */ 4;
return Layout.Alignment.values()[index];
case Gravity.CENTER:
return Layout.Alignment.ALIGN_CENTER;
case Gravity.NO_GRAVITY:
default:
return Layout.Alignment.ALIGN_NORMAL;
}
}
private static Layout createTextLayout(
int width,
CSSMeasureMode widthMode,

View File

@ -11,11 +11,14 @@ package com.facebook.react.flat;
import javax.annotation.Nullable;
import android.annotation.TargetApi;
import android.os.Build;
import android.text.SpannableStringBuilder;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.EditText;
import com.facebook.csslayout.CSSDirection;
import com.facebook.csslayout.CSSMeasureMode;
import com.facebook.csslayout.CSSNodeAPI;
import com.facebook.csslayout.MeasureOutput;
@ -53,6 +56,7 @@ public class RCTTextInput extends RCTVirtualText implements AndroidView, CSSNode
markUpdated();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void setThemedContext(ThemedReactContext themedContext) {
super.setThemedContext(themedContext);
@ -65,9 +69,9 @@ public class RCTTextInput extends RCTVirtualText implements AndroidView, CSSNode
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
setDefaultPadding(Spacing.LEFT, mEditText.getPaddingLeft());
setDefaultPadding(Spacing.START, mEditText.getPaddingStart());
setDefaultPadding(Spacing.TOP, mEditText.getPaddingTop());
setDefaultPadding(Spacing.RIGHT, mEditText.getPaddingRight());
setDefaultPadding(Spacing.END, mEditText.getPaddingEnd());
setDefaultPadding(Spacing.BOTTOM, mEditText.getPaddingBottom());
}
@ -89,9 +93,9 @@ public class RCTTextInput extends RCTVirtualText implements AndroidView, CSSNode
(int) Math.ceil(PixelUtil.toPixelFromSP(ViewDefaults.FONT_SIZE_SP)) : fontSize);
Spacing padding = getPadding();
editText.setPadding(
(int) Math.ceil(padding.get(Spacing.LEFT)),
(int) Math.ceil(padding.get(Spacing.START)),
(int) Math.ceil(padding.get(Spacing.TOP)),
(int) Math.ceil(padding.get(Spacing.RIGHT)),
(int) Math.ceil(padding.get(Spacing.END)),
(int) Math.ceil(padding.get(Spacing.BOTTOM)));
if (mNumberOfLines != UNSET) {