Add support for RCTText under FlatUIImplementation
Summary: @public Initial version of FlatUIImplementation lacks any primitives support (such as RCTText, RCTImageView or RCTView). This diff add the first part, RCTText (alongside with RCTVirtualText and RCTRawText). Reviewed By: sriramramani Differential Revision: D2693348
This commit is contained in:
parent
44d2ee1c3f
commit
5c2f536e9a
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* Base class for all DrawCommands. Becomes immutable once it has its bounds set. Until then, a
|
||||
* subclass is able to mutate any of its properties (e.g. updating Layout in DrawTextLayout).
|
||||
*
|
||||
* The idea is to be able to reuse unmodified objects when we build up DrawCommands before we ship
|
||||
* them to UI thread, but we can only do that if DrawCommands are immutable.
|
||||
*/
|
||||
/* package */ abstract class AbstractDrawCommand implements DrawCommand, Cloneable {
|
||||
|
||||
private float mLeft;
|
||||
private float mTop;
|
||||
private float mRight;
|
||||
private float mBottom;
|
||||
private boolean mFrozen;
|
||||
|
||||
/**
|
||||
* Updates boundaries of the AbstractDrawCommand and freezes it.
|
||||
* Will return a frozen copy if the current AbstractDrawCommand cannot be mutated.
|
||||
*/
|
||||
public final AbstractDrawCommand updateBoundsAndFreeze(
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom) {
|
||||
if (mFrozen) {
|
||||
// see if we can reuse it
|
||||
if (boundsMatch(left, top, right, bottom)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
try {
|
||||
AbstractDrawCommand copy = (AbstractDrawCommand) clone();
|
||||
copy.setBounds(left, top, right, bottom);
|
||||
return copy;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
// This should not happen since AbstractDrawCommand implements Cloneable
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
setBounds(left, top, right, bottom);
|
||||
mFrozen = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this object was frozen and thus cannot be mutated.
|
||||
*/
|
||||
public final boolean isFrozen() {
|
||||
return mFrozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left position of this DrawCommand relative to the hosting View.
|
||||
*/
|
||||
public final float getLeft() {
|
||||
return mLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top position of this DrawCommand relative to the hosting View.
|
||||
*/
|
||||
public final float getTop() {
|
||||
return mTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Right position of this DrawCommand relative to the hosting View.
|
||||
*/
|
||||
public final float getRight() {
|
||||
return mRight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bottom position of this DrawCommand relative to the hosting View.
|
||||
*/
|
||||
public final float getBottom() {
|
||||
return mBottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates boundaries of this DrawCommand.
|
||||
*/
|
||||
private void setBounds(float left, float top, float right, float bottom) {
|
||||
mLeft = left;
|
||||
mTop = top;
|
||||
mRight = right;
|
||||
mBottom = bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if boundaries match and don't need to be updated. False otherwise.
|
||||
*/
|
||||
private boolean boundsMatch(float left, float top, float right, float bottom) {
|
||||
return mLeft == left && mTop == top && mRight == right && mBottom == bottom;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
/**
|
||||
* DrawCommand is an inteface that shadow nodes need to implement to do the drawing.
|
||||
* Instaces of DrawCommand are created in background thread and passed to UI thread.
|
||||
* Once a DrawCommand is shared with UI thread, it can no longer be mutated in background thread.
|
||||
*/
|
||||
public interface DrawCommand {
|
||||
// used by StateBuilder, FlatViewGroup and FlatShadowNode
|
||||
/* package */ static final DrawCommand[] EMPTY_ARRAY = new DrawCommand[0];
|
||||
|
||||
/**
|
||||
* Performs drawing into the given canvas.
|
||||
*
|
||||
* @param canvas The canvas to draw into
|
||||
*/
|
||||
public void draw(Canvas canvas);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.text.Layout;
|
||||
|
||||
/**
|
||||
* DrawTextLayout is a DrawCommand that draw {@link Layout}.
|
||||
*/
|
||||
/* package */ final class DrawTextLayout extends AbstractDrawCommand {
|
||||
|
||||
private Layout mLayout;
|
||||
|
||||
/* package */ DrawTextLayout(Layout layout) {
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a new {@link Layout} to draw.
|
||||
*/
|
||||
public void setLayout(Layout layout) {
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
float left = getLeft();
|
||||
float top = getTop();
|
||||
|
||||
canvas.translate(left, top);
|
||||
mLayout.draw(canvas);
|
||||
canvas.translate(-left, -top);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
|
||||
import com.facebook.react.uimanager.NativeViewHierarchyManager;
|
||||
import com.facebook.react.uimanager.SizeMonitoringFrameLayout;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
|
||||
/**
|
||||
* FlatNativeViewHierarchyManager is the only class that performs View manipulations. All of this
|
||||
* class methods can only be called from UI thread by {@link FlatUIViewOperationQueue}.
|
||||
*/
|
||||
/* package */ final class FlatNativeViewHierarchyManager extends NativeViewHierarchyManager {
|
||||
|
||||
/* package */ FlatNativeViewHierarchyManager(ViewManagerRegistry viewManagers) {
|
||||
super(viewManagers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootView(
|
||||
int tag,
|
||||
SizeMonitoringFrameLayout view,
|
||||
ThemedReactContext themedContext) {
|
||||
FlatViewGroup root = new FlatViewGroup(themedContext);
|
||||
view.addView(root);
|
||||
|
||||
addRootViewGroup(tag, root, themedContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns new DrawCommands to a FlatViewGroup specified by a reactTag.
|
||||
*
|
||||
* @param reactTag reactTag to lookup FlatViewGroup by
|
||||
* @param drawCommands new draw commands to execute during the drawing.
|
||||
*/
|
||||
/* package */ void updateMountState(int reactTag, DrawCommand[] drawCommands) {
|
||||
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
|
||||
view.mountDrawCommands(drawCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates View bounds, possibly re-measuring and re-layouting it if the size changed.
|
||||
*
|
||||
* @param reactTag reactTag to lookup a View by
|
||||
* @param left left coordinate relative to parent
|
||||
* @param top top coordinate relative to parent
|
||||
* @param right right coordinate relative to parent
|
||||
* @param bottom bottom coordinate relative to parent
|
||||
*/
|
||||
/* package */ void updateViewBounds(int reactTag, int left, int top, int right, int bottom) {
|
||||
View view = resolveView(reactTag);
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
if (view.getWidth() != width || view.getHeight() != height) {
|
||||
// size changed, we need to measure and layout the View
|
||||
view.measure(
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
||||
view.layout(left, top, right, bottom);
|
||||
} else {
|
||||
// same size, only location changed, there is a faster route.
|
||||
view.offsetLeftAndRight(left - view.getLeft());
|
||||
view.offsetTopAndBottom(top - view.getTop());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* Root node of the shadow node hierarchy. Currently, the only node that can actually map to a View.
|
||||
*/
|
||||
/* package */ final class FlatRootShadowNode extends FlatShadowNode {
|
||||
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
|
||||
private int mViewLeft;
|
||||
private int mViewTop;
|
||||
private int mViewRight;
|
||||
private int mViewBottom;
|
||||
|
||||
@Override
|
||||
public int getScreenX() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenY() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenWidth() {
|
||||
return mViewRight - mViewLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenHeight() {
|
||||
return mViewBottom - mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of DrawCommands to perform during the View's draw pass.
|
||||
*/
|
||||
/* package */ DrawCommand[] getDrawCommands() {
|
||||
return mDrawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an array of DrawCommands to perform during the View's draw pass. StateBuilder uses old
|
||||
* draw commands to compare to new draw commands and see if the View neds to be redrawn.
|
||||
*/
|
||||
/* package */ void setDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets boundaries of the View that this node maps to relative to the parent left/top coordinate.
|
||||
*/
|
||||
/* package */ void setViewBounds(int left, int top, int right, int bottom) {
|
||||
mViewLeft = left;
|
||||
mViewTop = top;
|
||||
mViewRight = right;
|
||||
mViewBottom = bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewLeft() {
|
||||
return mViewLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewTop() {
|
||||
return mViewTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Right position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewRight() {
|
||||
return mViewRight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bottom position of the View this node maps to relative to the parent View.
|
||||
*/
|
||||
/* package */ int getViewBottom() {
|
||||
return mViewBottom;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
/**
|
||||
* FlatShadowNode is a base class for all shadow node used in FlatUIImplementation. It extends
|
||||
* {@link LayoutShadowNode} by adding an ability to prepare DrawCommands off the UI thread.
|
||||
*/
|
||||
/* package */ class FlatShadowNode extends LayoutShadowNode {
|
||||
|
||||
/**
|
||||
* Collects DrawCommands produced by this FlatShadoNode.
|
||||
*/
|
||||
protected void collectState(
|
||||
StateBuilder stateBuilder,
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom) {
|
||||
// do nothing yet.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import com.facebook.react.uimanager.ReactShadowNode;
|
||||
|
||||
/**
|
||||
* Base class for RCTVirtualText and RCTRawText.
|
||||
*/
|
||||
/* package */ abstract class FlatTextShadowNode extends FlatShadowNode {
|
||||
|
||||
/**
|
||||
* Recursively visits FlatTextShadowNode and its children,
|
||||
* appending text to SpannableStringBuilder.
|
||||
*/
|
||||
protected abstract void collectText(SpannableStringBuilder builder);
|
||||
|
||||
/**
|
||||
* Recursively visits FlatTextShadowNode and its children,
|
||||
* applying spans to SpannableStringBuilder.
|
||||
*/
|
||||
protected abstract void applySpans(SpannableStringBuilder builder);
|
||||
|
||||
/**
|
||||
* Propagates changes up to RCTText without dirtying current node.
|
||||
*/
|
||||
protected void notifyChanged(boolean shouldRemeasure) {
|
||||
ReactShadowNode parent = getParent();
|
||||
if (parent instanceof FlatTextShadowNode) {
|
||||
((FlatTextShadowNode) parent).notifyChanged(shouldRemeasure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -33,20 +34,37 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
*/
|
||||
public class FlatUIImplementation extends UIImplementation {
|
||||
|
||||
public FlatUIImplementation(
|
||||
private final StateBuilder mStateBuilder;
|
||||
|
||||
public static FlatUIImplementation createInstance(
|
||||
ReactApplicationContext reactContext,
|
||||
List<ViewManager> viewManagers) {
|
||||
this(reactContext, new ViewManagerRegistry(viewManagers));
|
||||
|
||||
viewManagers = new ArrayList<ViewManager>(viewManagers);
|
||||
viewManagers.add(new RCTViewManager());
|
||||
viewManagers.add(new RCTTextManager());
|
||||
viewManagers.add(new RCTRawTextManager());
|
||||
viewManagers.add(new RCTVirtualTextManager());
|
||||
|
||||
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
|
||||
FlatNativeViewHierarchyManager nativeViewHierarchyManager = new FlatNativeViewHierarchyManager(
|
||||
viewManagerRegistry);
|
||||
FlatUIViewOperationQueue operationsQueue = new FlatUIViewOperationQueue(
|
||||
reactContext,
|
||||
nativeViewHierarchyManager);
|
||||
return new FlatUIImplementation(viewManagerRegistry, operationsQueue);
|
||||
}
|
||||
|
||||
private FlatUIImplementation(
|
||||
ReactApplicationContext reactContext,
|
||||
ViewManagerRegistry viewManagers) {
|
||||
super(
|
||||
viewManagers,
|
||||
new UIViewOperationQueue(
|
||||
reactContext,
|
||||
new NativeViewHierarchyManager(viewManagers)));
|
||||
ViewManagerRegistry viewManagers,
|
||||
FlatUIViewOperationQueue operationsQueue) {
|
||||
super(viewManagers, operationsQueue);
|
||||
mStateBuilder = new StateBuilder(operationsQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactShadowNode createRootShadowNode() {
|
||||
return new FlatRootShadowNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,16 +140,6 @@ public class FlatUIImplementation extends UIImplementation {
|
|||
float absoluteX,
|
||||
float absoluteY,
|
||||
EventDispatcher eventDispatcher) {
|
||||
markNodeLayoutSeen(cssNode);
|
||||
}
|
||||
|
||||
private void markNodeLayoutSeen(CSSNode node) {
|
||||
if (node.hasNewLayout()) {
|
||||
node.markLayoutSeen();
|
||||
}
|
||||
|
||||
for (int i = 0, childCount = node.getChildCount(); i != childCount; ++i) {
|
||||
markNodeLayoutSeen(node.getChildAt(i));
|
||||
}
|
||||
mStateBuilder.applyUpdates((FlatRootShadowNode) cssNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.UIViewOperationQueue;
|
||||
|
||||
/**
|
||||
* FlatUIViewOperationQueue extends {@link UIViewOperationQueue} to add
|
||||
* FlatUIImplementation-specific methods that need to run in UI thread.
|
||||
*/
|
||||
/* package */ final class FlatUIViewOperationQueue extends UIViewOperationQueue {
|
||||
|
||||
private final FlatNativeViewHierarchyManager mNativeViewHierarchyManager;
|
||||
|
||||
/**
|
||||
* UIOperation that updates DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
private final class UpdateMountState implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final DrawCommand[] mDrawCommands;
|
||||
|
||||
private UpdateMountState(int reactTag, DrawCommand[] drawCommands) {
|
||||
mReactTag = reactTag;
|
||||
mDrawCommands = drawCommands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.updateMountState(mReactTag, mDrawCommands);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIOperation that updates View bounds for a View defined by reactTag.
|
||||
*/
|
||||
private final class UpdateViewBounds implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final int mLeft;
|
||||
private final int mTop;
|
||||
private final int mRight;
|
||||
private final int mBottom;
|
||||
|
||||
private UpdateViewBounds(int reactTag, int left, int top, int right, int bottom) {
|
||||
mReactTag = reactTag;
|
||||
mLeft = left;
|
||||
mTop = top;
|
||||
mRight = right;
|
||||
mBottom = bottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.updateViewBounds(mReactTag, mLeft, mTop, mRight, mBottom);
|
||||
}
|
||||
}
|
||||
|
||||
public FlatUIViewOperationQueue(
|
||||
ReactApplicationContext reactContext,
|
||||
FlatNativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
super(reactContext, nativeViewHierarchyManager);
|
||||
|
||||
mNativeViewHierarchyManager = nativeViewHierarchyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a new UIOperation that will update DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
public void enqueueUpdateMountState(int reactTag, DrawCommand[] drawCommands) {
|
||||
enqueueUIOperation(new UpdateMountState(reactTag, drawCommands));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a new UIOperation that will update View bounds for a View defined by reactTag.
|
||||
*/
|
||||
public void enqueueUpdateViewBounds(int reactTag, int left, int top, int right, int bottom) {
|
||||
enqueueUIOperation(new UpdateViewBounds(reactTag, left, top, right, bottom));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over
|
||||
* array of DrawCommands, executing them one by one.
|
||||
*/
|
||||
/* package */ final class FlatViewGroup extends ViewGroup {
|
||||
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
|
||||
/* package */ FlatViewGroup(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
drawCommand.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
/* package */ void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
invalidate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.MetricAffectingSpan;
|
||||
|
||||
/* package */ final class FontStylingSpan extends MetricAffectingSpan {
|
||||
// text property
|
||||
private final double mTextColor;
|
||||
private final int mBackgroundColor;
|
||||
|
||||
// font properties
|
||||
private final int mFontSize;
|
||||
private final int mFontStyle;
|
||||
private final int mFontWeight;
|
||||
private final @Nullable String mFontFamily;
|
||||
|
||||
FontStylingSpan(
|
||||
double textColor,
|
||||
int backgroundColor,
|
||||
int fontSize,
|
||||
int fontStyle,
|
||||
int fontWeight,
|
||||
@Nullable String fontFamily) {
|
||||
mTextColor = textColor;
|
||||
mBackgroundColor = backgroundColor;
|
||||
mFontSize = fontSize;
|
||||
mFontStyle = fontStyle;
|
||||
mFontWeight = fontWeight;
|
||||
mFontFamily = fontFamily;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
if (!Double.isNaN(mTextColor)) {
|
||||
ds.setColor((int) mTextColor);
|
||||
}
|
||||
|
||||
ds.bgColor = mBackgroundColor;
|
||||
updateMeasureState(ds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMeasureState(TextPaint ds) {
|
||||
if (mFontSize != -1) {
|
||||
ds.setTextSize(mFontSize);
|
||||
}
|
||||
|
||||
updateTypeface(ds);
|
||||
}
|
||||
|
||||
private int getNewStyle(int oldStyle) {
|
||||
int newStyle = oldStyle;
|
||||
if (mFontStyle != -1) {
|
||||
newStyle = (newStyle & ~Typeface.ITALIC) | mFontStyle;
|
||||
}
|
||||
|
||||
if (mFontWeight != -1) {
|
||||
newStyle = (newStyle & ~Typeface.BOLD) | mFontWeight;
|
||||
}
|
||||
|
||||
return newStyle;
|
||||
}
|
||||
|
||||
private void updateTypeface(TextPaint ds) {
|
||||
Typeface typeface = ds.getTypeface();
|
||||
|
||||
int oldStyle = (typeface == null) ? 0 : typeface.getStyle();
|
||||
int newStyle = getNewStyle(oldStyle);
|
||||
|
||||
if (oldStyle == newStyle && mFontFamily == null) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: optimize this part (implemented in a followup patch)
|
||||
|
||||
if (mFontFamily != null) {
|
||||
// efficient in API 21+
|
||||
typeface = Typeface.create(mFontFamily, newStyle);
|
||||
} else {
|
||||
// efficient in API 16+
|
||||
typeface = Typeface.create(typeface, newStyle);
|
||||
}
|
||||
|
||||
ds.setTypeface(typeface);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import com.facebook.react.uimanager.ReactProp;
|
||||
|
||||
/**
|
||||
* RCTRawText is a FlatTextShadowNode that can only contain raw text (but not styling).
|
||||
*/
|
||||
/* package */ class RCTRawText extends FlatTextShadowNode {
|
||||
|
||||
private @Nullable String mText;
|
||||
|
||||
@Override
|
||||
protected void collectText(SpannableStringBuilder builder) {
|
||||
if (mText != null) {
|
||||
builder.append(mText);
|
||||
}
|
||||
|
||||
// RCTRawText cannot have any children, so no recursive calls needed.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applySpans(SpannableStringBuilder builder) {
|
||||
// no spans and no children so nothing to do here.
|
||||
}
|
||||
|
||||
@ReactProp(name = "text")
|
||||
public void setText(@Nullable String text) {
|
||||
mText = text;
|
||||
notifyChanged(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* ViewManager that creates instances of RCTRawText.
|
||||
*/
|
||||
/* package */ final class RCTRawTextManager extends VirtualViewManager<RCTRawText> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RCTRawText";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RCTRawText createShadowNodeInstance() {
|
||||
return new RCTRawText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RCTRawText> getShadowNodeClass() {
|
||||
return RCTRawText.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.text.BoringLayout;
|
||||
import android.text.Layout;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.facebook.csslayout.CSSNode;
|
||||
import com.facebook.csslayout.MeasureOutput;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ReactProp;
|
||||
import com.facebook.react.uimanager.ViewDefaults;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
|
||||
/**
|
||||
* RCTText is a top-level node for text. It extends {@link RCTVirtualText} because it can contain
|
||||
* styling information, but has the following differences:
|
||||
*
|
||||
* a) RCTText is not a virtual node, and can be measured and laid out.
|
||||
* b) when no font size is specified, a font size of ViewDefaults#FONT_SIZE_SP is assumed.
|
||||
*/
|
||||
/* package */ final class RCTText extends RCTVirtualText implements CSSNode.MeasureFunction {
|
||||
|
||||
private static final boolean INCLUDE_PADDING = true;
|
||||
private static final TextPaint PAINT = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
|
||||
|
||||
// this is optional, and helps saving a few BoringLayout.Metrics allocations during measure().
|
||||
private static @Nullable BoringLayout.Metrics sBoringLayoutMetrics;
|
||||
|
||||
private @Nullable CharSequence mText;
|
||||
private @Nullable DrawTextLayout mDrawCommand;
|
||||
private @Nullable BoringLayout.Metrics mBoringLayoutMetrics;
|
||||
private float mSpacingMult = 1.0f;
|
||||
private float mSpacingAdd = 0.0f;
|
||||
|
||||
public RCTText() {
|
||||
setMeasureFunction(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtualAnchor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void measure(CSSNode node, float width, MeasureOutput measureOutput) {
|
||||
CharSequence text = getText();
|
||||
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
// to indicate that we don't have anything to display
|
||||
mText = null;
|
||||
measureOutput.width = 0;
|
||||
measureOutput.height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
mText = text;
|
||||
|
||||
BoringLayout.Metrics metrics = BoringLayout.isBoring(text, PAINT, sBoringLayoutMetrics);
|
||||
if (metrics != null) {
|
||||
sBoringLayoutMetrics = mBoringLayoutMetrics;
|
||||
if (sBoringLayoutMetrics != null) {
|
||||
// make sure it's always empty, reported metrics can be incorrect otherwise
|
||||
sBoringLayoutMetrics.top = 0;
|
||||
sBoringLayoutMetrics.ascent = 0;
|
||||
sBoringLayoutMetrics.descent = 0;
|
||||
sBoringLayoutMetrics.bottom = 0;
|
||||
sBoringLayoutMetrics.leading = 0;
|
||||
}
|
||||
|
||||
mBoringLayoutMetrics = metrics;
|
||||
|
||||
float measuredWidth = (float) metrics.width;
|
||||
if (Float.isNaN(width) || measuredWidth <= width) {
|
||||
measureOutput.width = measuredWidth;
|
||||
measureOutput.height = getMetricsHeight(metrics, INCLUDE_PADDING);
|
||||
|
||||
// to indicate that text layout was not created during the measure pass
|
||||
mDrawCommand = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// width < measuredWidth -> more that a single line -> not boring
|
||||
}
|
||||
|
||||
int maximumWidth = Float.isNaN(width) ? Integer.MAX_VALUE : (int) width;
|
||||
|
||||
// at this point we need to create a StaticLayout to measure the text
|
||||
StaticLayout layout = new StaticLayout(
|
||||
text,
|
||||
PAINT,
|
||||
maximumWidth,
|
||||
Layout.Alignment.ALIGN_NORMAL,
|
||||
mSpacingMult,
|
||||
mSpacingAdd,
|
||||
INCLUDE_PADDING);
|
||||
|
||||
// determine how wide we actually are
|
||||
float maxLineWidth = 0;
|
||||
int lineCount = layout.getLineCount();
|
||||
for (int i = 0; i != lineCount; ++i) {
|
||||
maxLineWidth = Math.max(maxLineWidth, layout.getLineMax(i));
|
||||
}
|
||||
|
||||
measureOutput.width = maxLineWidth;
|
||||
measureOutput.height = layout.getHeight();
|
||||
|
||||
if (mDrawCommand != null && !mDrawCommand.isFrozen()) {
|
||||
mDrawCommand.setLayout(layout);
|
||||
} else {
|
||||
mDrawCommand = new DrawTextLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectState(
|
||||
StateBuilder stateBuilder,
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom) {
|
||||
super.collectState(stateBuilder, left, top, right, bottom);
|
||||
|
||||
if (mText == null) {
|
||||
// nothing to draw (empty text).
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDrawCommand == null) {
|
||||
// Layout was not created during the measure pass, must be Boring, create it now
|
||||
mDrawCommand = new DrawTextLayout(new BoringLayout(
|
||||
mText,
|
||||
PAINT,
|
||||
Integer.MAX_VALUE, // fits one line so don't care about the width
|
||||
Layout.Alignment.ALIGN_NORMAL,
|
||||
mSpacingMult,
|
||||
mSpacingAdd,
|
||||
mBoringLayoutMetrics,
|
||||
INCLUDE_PADDING));
|
||||
}
|
||||
|
||||
mDrawCommand = (DrawTextLayout) mDrawCommand.updateBoundsAndFreeze(left, top, right, bottom);
|
||||
stateBuilder.addDrawCommand(mDrawCommand);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.LINE_HEIGHT, defaultDouble = Double.NaN)
|
||||
public void setLineHeight(double lineHeight) {
|
||||
if (Double.isNaN(lineHeight)) {
|
||||
mSpacingMult = 1.0f;
|
||||
mSpacingAdd = 0.0f;
|
||||
} else {
|
||||
mSpacingMult = 0.0f;
|
||||
mSpacingAdd = PixelUtil.toPixelFromSP((float) lineHeight);
|
||||
}
|
||||
notifyChanged(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDefaultFontSize() {
|
||||
// top-level <Text /> should always specify font size.
|
||||
return fontSizeFromSp(ViewDefaults.FONT_SIZE_SP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyChanged(boolean shouldRemeasure) {
|
||||
// Future patch: should only recreate Layout if shouldRemeasure is false
|
||||
dirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new CharSequence that includes all the text and styling information to create Layout.
|
||||
*/
|
||||
private CharSequence getText() {
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
collectText(sb);
|
||||
applySpans(sb);
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns measured line height according to an includePadding flag.
|
||||
*/
|
||||
private static int getMetricsHeight(BoringLayout.Metrics metrics, boolean includePadding) {
|
||||
if (includePadding) {
|
||||
return metrics.bottom - metrics.top;
|
||||
} else {
|
||||
return metrics.descent - metrics.ascent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* ViewManager that creates instances of RCTText.
|
||||
*/
|
||||
/* package */ final class RCTTextManager extends VirtualViewManager<RCTText> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RCTText";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RCTText createShadowNodeInstance() {
|
||||
return new RCTText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RCTText> getShadowNodeClass() {
|
||||
return RCTText.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* Dummy implementation of RCTView.
|
||||
*/
|
||||
/* package */ final class RCTView extends FlatShadowNode {
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* ViewManager that creates instances of RCTView.
|
||||
*/
|
||||
/* package */ final class RCTViewManager extends VirtualViewManager<RCTView> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RCTView";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RCTView createShadowNodeInstance() {
|
||||
return new RCTView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RCTView> getShadowNodeClass() {
|
||||
return RCTView.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ReactProp;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
|
||||
/**
|
||||
* RCTVirtualText is a {@link FlatTextShadowNode} that can contain font styling information.
|
||||
*/
|
||||
/* package */ class RCTVirtualText extends FlatTextShadowNode {
|
||||
|
||||
private static final String BOLD = "bold";
|
||||
private static final String ITALIC = "italic";
|
||||
private static final String NORMAL = "normal";
|
||||
|
||||
// TODO: cache CustomStyleSpan and move remove these values from here
|
||||
// (implemented in a followup patch)
|
||||
private double mTextColor = Double.NaN;
|
||||
private int mBgColor;
|
||||
private int mFontSize = getDefaultFontSize();
|
||||
private int mFontStyle = -1; // -1, Typeface.NORMAL or Typeface.ITALIC
|
||||
private int mFontWeight = -1; // -1, Typeface.NORMAL or Typeface.BOLD
|
||||
private @Nullable String mFontFamily;
|
||||
|
||||
// these 2 are only used between collectText() and applySpans() calls.
|
||||
private int mTextBegin;
|
||||
private int mTextEnd;
|
||||
|
||||
@Override
|
||||
protected void collectText(SpannableStringBuilder builder) {
|
||||
int childCount = getChildCount();
|
||||
|
||||
mTextBegin = builder.length();
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
FlatTextShadowNode child = (FlatTextShadowNode) getChildAt(i);
|
||||
child.collectText(builder);
|
||||
}
|
||||
mTextEnd = builder.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applySpans(SpannableStringBuilder builder) {
|
||||
if (mTextBegin == mTextEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
builder.setSpan(
|
||||
// Future patch: cache last custom style span with a frozen flag
|
||||
new FontStylingSpan(mTextColor, mBgColor, mFontSize, mFontStyle, mFontWeight, mFontFamily),
|
||||
mTextBegin,
|
||||
mTextEnd,
|
||||
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
|
||||
int childCount = getChildCount();
|
||||
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
FlatTextShadowNode child = (FlatTextShadowNode) getChildAt(i);
|
||||
child.applySpans(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FONT_SIZE, defaultFloat = Float.NaN)
|
||||
public void setFontSize(float fontSizeSp) {
|
||||
final int fontSize;
|
||||
if (Float.isNaN(fontSizeSp)) {
|
||||
fontSize = getDefaultFontSize();
|
||||
} else {
|
||||
fontSize = fontSizeFromSp(fontSizeSp);
|
||||
}
|
||||
|
||||
if (mFontSize != fontSize) {
|
||||
mFontSize = fontSize;
|
||||
notifyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.COLOR, defaultDouble = Double.NaN)
|
||||
public void setColor(double textColor) {
|
||||
if (mTextColor != textColor) {
|
||||
mTextColor = textColor;
|
||||
notifyChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.BACKGROUND_COLOR)
|
||||
public void setBackgroundColor(int backgroundColor) {
|
||||
if (mBgColor != backgroundColor) {
|
||||
mBgColor = backgroundColor;
|
||||
notifyChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FONT_FAMILY)
|
||||
public void setFontFamily(@Nullable String fontFamily) {
|
||||
mFontFamily = fontFamily;
|
||||
notifyChanged(true);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FONT_WEIGHT)
|
||||
public void setFontWeight(@Nullable String fontWeightString) {
|
||||
final int fontWeight;
|
||||
if (fontWeightString == null) {
|
||||
fontWeight = -1;
|
||||
} else if (BOLD.equals(fontWeightString)) {
|
||||
fontWeight = Typeface.BOLD;
|
||||
} else if (NORMAL.equals(fontWeightString)) {
|
||||
fontWeight = Typeface.NORMAL;
|
||||
} else {
|
||||
int fontWeightNumeric = parseNumericFontWeight(fontWeightString);
|
||||
if (fontWeightNumeric == -1) {
|
||||
throw new RuntimeException("invalid font weight " + fontWeightString);
|
||||
}
|
||||
fontWeight = fontWeightNumeric >= 500 ? Typeface.BOLD : Typeface.NORMAL;
|
||||
}
|
||||
|
||||
if (mFontWeight != fontWeight) {
|
||||
mFontWeight = fontWeight;
|
||||
notifyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FONT_STYLE)
|
||||
public void setFontStyle(@Nullable String fontStyleString) {
|
||||
final int fontStyle;
|
||||
if (fontStyleString == null) {
|
||||
fontStyle = -1;
|
||||
} else if (ITALIC.equals(fontStyleString)) {
|
||||
fontStyle = Typeface.ITALIC;
|
||||
} else if (NORMAL.equals(fontStyleString)) {
|
||||
fontStyle = Typeface.NORMAL;
|
||||
} else {
|
||||
throw new RuntimeException("invalid font style " + fontStyleString);
|
||||
}
|
||||
|
||||
if (mFontStyle != fontStyle) {
|
||||
mFontStyle = fontStyle;
|
||||
notifyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getDefaultFontSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* package */ static int fontSizeFromSp(float sp) {
|
||||
return (int) Math.ceil(PixelUtil.toPixelFromSP(sp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return -1 if the input string is not a valid numeric fontWeight (100, 200, ..., 900), otherwise
|
||||
* return the weight.
|
||||
*/
|
||||
private static int parseNumericFontWeight(String fontWeightString) {
|
||||
// This should be much faster than using regex to verify input and Integer.parseInt
|
||||
return fontWeightString.length() == 3 && fontWeightString.endsWith("00")
|
||||
&& fontWeightString.charAt(0) <= '9' && fontWeightString.charAt(0) >= '1' ?
|
||||
100 * (fontWeightString.charAt(0) - '0') : -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
/**
|
||||
* ViewManager that creates instances of RCTVirtualText.
|
||||
*/
|
||||
/* package */ final class RCTVirtualTextManager extends VirtualViewManager<RCTVirtualText> {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RCTVirtualText";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RCTVirtualText createShadowNodeInstance() {
|
||||
return new RCTVirtualText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RCTVirtualText> getShadowNodeClass() {
|
||||
return RCTVirtualText.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
/**
|
||||
* Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should
|
||||
* be from JavaScript perspective. StateBuilder is a helper class that can walk the shadow node tree
|
||||
* and collect information that can then be passed to UI thread and applied to a hierarchy of Views
|
||||
* that Android finally can display.
|
||||
*/
|
||||
/* package */ final class StateBuilder {
|
||||
|
||||
private final FlatUIViewOperationQueue mOperationsQueue;
|
||||
|
||||
// DrawCommands
|
||||
private final ArrayDeque<DrawCommand> mDrawCommands = new ArrayDeque<>();
|
||||
private DrawCommand[] mPreviousDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private int mPreviousDrawCommandsIndex;
|
||||
|
||||
/* package */ StateBuilder(FlatUIViewOperationQueue operationsQueue) {
|
||||
mOperationsQueue = operationsQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a root of the laid-out shadow node hierarchy, walks the tree and generates an array of
|
||||
* DrawCommands that will then mount in UI thread to a root FlatViewGroup so that it can draw.
|
||||
*/
|
||||
/* package*/ void applyUpdates(FlatRootShadowNode node) {
|
||||
collectStateAndUpdateViewBounds(node, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a DrawCommand for current mountable node.
|
||||
*/
|
||||
/* package */ void addDrawCommand(AbstractDrawCommand drawCommand) {
|
||||
if (mPreviousDrawCommandsIndex < mPreviousDrawCommands.length &&
|
||||
mPreviousDrawCommands[mPreviousDrawCommandsIndex] == drawCommand) {
|
||||
++mPreviousDrawCommandsIndex;
|
||||
} else {
|
||||
mPreviousDrawCommandsIndex = mPreviousDrawCommands.length + 1;
|
||||
}
|
||||
|
||||
mDrawCommands.addLast(drawCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates boundaries of a View that a give nodes maps to.
|
||||
*/
|
||||
private void updateViewBounds(
|
||||
FlatRootShadowNode node,
|
||||
int tag,
|
||||
float leftInParent,
|
||||
float topInParent,
|
||||
float rightInParent,
|
||||
float bottomInParent) {
|
||||
int viewLeft = Math.round(leftInParent);
|
||||
int viewTop = Math.round(topInParent);
|
||||
int viewRight = Math.round(rightInParent);
|
||||
int viewBottom = Math.round(bottomInParent);
|
||||
|
||||
if (node.getViewLeft() == viewLeft && node.getViewTop() == viewTop &&
|
||||
node.getViewRight() == viewRight && node.getViewBottom() == viewBottom) {
|
||||
// nothing changed.
|
||||
return;
|
||||
}
|
||||
|
||||
// this will optionally measure and layout the View this node maps to.
|
||||
node.setViewBounds(viewLeft, viewTop, viewRight, viewBottom);
|
||||
mOperationsQueue.enqueueUpdateViewBounds(tag, viewLeft, viewTop, viewRight, viewBottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects state (DrawCommands) for a given node that will mount to a View.
|
||||
*/
|
||||
private void collectStateForMountableNode(
|
||||
FlatRootShadowNode node,
|
||||
int tag,
|
||||
float width,
|
||||
float height) {
|
||||
// save
|
||||
int d = mDrawCommands.size();
|
||||
DrawCommand[] previousDrawCommands = mPreviousDrawCommands;
|
||||
int previousDrawCommandsIndex = mPreviousDrawCommandsIndex;
|
||||
|
||||
// reset
|
||||
mPreviousDrawCommands = node.getDrawCommands();
|
||||
mPreviousDrawCommandsIndex = 0;
|
||||
|
||||
collectStateRecursively(node, 0, 0, width, height);
|
||||
|
||||
if (mPreviousDrawCommandsIndex != mPreviousDrawCommands.length) {
|
||||
// DrawCommands changes, need to re-mount them and re-draw the View.
|
||||
DrawCommand[] drawCommands = extractDrawCommands(d);
|
||||
node.setDrawCommands(drawCommands);
|
||||
|
||||
mOperationsQueue.enqueueUpdateMountState(tag, drawCommands);
|
||||
}
|
||||
|
||||
// restore
|
||||
mPreviousDrawCommandsIndex = previousDrawCommandsIndex;
|
||||
mPreviousDrawCommands = previousDrawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all DrawCommands collectes so far starting from a given index.
|
||||
*/
|
||||
private DrawCommand[] extractDrawCommands(int lowerBound) {
|
||||
int upperBound = mDrawCommands.size();
|
||||
int size = upperBound - lowerBound;
|
||||
if (size == 0) {
|
||||
// avoid allocating empty array
|
||||
return DrawCommand.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
DrawCommand[] drawCommands = new DrawCommand[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
drawCommands[i] = mDrawCommands.pollFirst();
|
||||
}
|
||||
|
||||
return drawCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walks node tree from a given node and collects DrawCommands.
|
||||
*/
|
||||
private void collectStateRecursively(
|
||||
FlatShadowNode node,
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom) {
|
||||
if (node.hasNewLayout()) {
|
||||
node.markLayoutSeen();
|
||||
}
|
||||
|
||||
node.collectState(this, left, top, right, bottom);
|
||||
|
||||
for (int i = 0, childCount = node.getChildCount(); i != childCount; ++i) {
|
||||
FlatShadowNode child = (FlatShadowNode) node.getChildAt(i);
|
||||
|
||||
float childLeft = left + child.getLayoutX();
|
||||
float childTop = top + child.getLayoutY();
|
||||
float childRight = childLeft + child.getLayoutWidth();
|
||||
float childBottom = childTop + child.getLayoutHeight();
|
||||
collectStateRecursively(child, childLeft, childTop, childRight, childBottom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects state and updates View boundaries for a given root node.
|
||||
*/
|
||||
private void collectStateAndUpdateViewBounds(
|
||||
FlatRootShadowNode node,
|
||||
float parentLeft,
|
||||
float parentTop) {
|
||||
int tag = node.getReactTag();
|
||||
|
||||
float width = node.getLayoutWidth();
|
||||
float height = node.getLayoutHeight();
|
||||
|
||||
float left = parentLeft + node.getLayoutX();
|
||||
float top = parentTop + node.getLayoutY();
|
||||
float right = left + width;
|
||||
float bottom = top + height;
|
||||
|
||||
collectStateForMountableNode(node, tag, width, height);
|
||||
|
||||
updateViewBounds(node, tag, left, top, right, bottom);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright (c) 2015-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.react.flat;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
/**
|
||||
* Base class to ViewManagers that don't map to a View.
|
||||
*/
|
||||
abstract class VirtualViewManager<C extends FlatShadowNode> extends ViewManager<View, C> {
|
||||
@Override
|
||||
protected View createViewInstance(ThemedReactContext reactContext) {
|
||||
throw new RuntimeException(getName() + " doesn't map to a View");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExtraData(View root, Object extraData) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue