mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
6f06f76e38
Summary: Prior to this patch DrawCommands weren't get clipped by parent DrawCommands at all. For example, a <View> element with size 200x200 with overflow:hidden should clip its child which is 400x400, but this didn't happen. However, if parent <View> would mount to an Android View, it would clip the child regardless of the overflow attribute value (because Android Views always clip whatever is drawing inside that View against its boundaries). This diff is fixing these issue, implementing overflow attribute support and making clipping behavior consistent between nodes that mount to View and nodes that don't. Reviewed By: ahmedre Differential Revision: D2768643
180 lines
4.9 KiB
Java
180 lines
4.9 KiB
Java
/**
|
|
* 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;
|
|
|
|
/**
|
|
* 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 float mClipLeft;
|
|
private float mClipTop;
|
|
private float mClipRight;
|
|
private float mClipBottom;
|
|
private boolean mFrozen;
|
|
|
|
@Override
|
|
public final void draw(FlatViewGroup parent, Canvas canvas) {
|
|
if (shouldClip()) {
|
|
canvas.save();
|
|
canvas.clipRect(mClipLeft, mClipTop, mClipRight, mClipBottom);
|
|
onDraw(canvas);
|
|
canvas.restore();
|
|
} else {
|
|
onDraw(canvas);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
if (mFrozen) {
|
|
// see if we can reuse it
|
|
boolean boundsMatch = boundsMatch(left, top, right, bottom);
|
|
boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom);
|
|
if (boundsMatch && clipBoundsMatch) {
|
|
return this;
|
|
}
|
|
|
|
try {
|
|
AbstractDrawCommand copy = (AbstractDrawCommand) clone();
|
|
if (!boundsMatch) {
|
|
copy.setBounds(left, top, right, bottom);
|
|
}
|
|
if (!clipBoundsMatch) {
|
|
copy.setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
}
|
|
return copy;
|
|
} catch (CloneNotSupportedException e) {
|
|
// This should not happen since AbstractDrawCommand implements Cloneable
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
setBounds(left, top, right, bottom);
|
|
setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
mFrozen = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns a non-frozen shallow copy of AbstractDrawCommand as defined by {@link Object#clone()}.
|
|
*/
|
|
public final AbstractDrawCommand mutableCopy() {
|
|
try {
|
|
AbstractDrawCommand copy = (AbstractDrawCommand) super.clone();
|
|
copy.mFrozen = false;
|
|
return copy;
|
|
} catch (CloneNotSupportedException e) {
|
|
// should not happen since we implement Cloneable
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
protected abstract void onDraw(Canvas canvas);
|
|
|
|
protected boolean shouldClip() {
|
|
return mLeft != mClipLeft || mTop != mClipTop || mRight != mClipRight || mBottom != mClipBottom;
|
|
}
|
|
|
|
protected void onBoundsChanged() {
|
|
}
|
|
|
|
/**
|
|
* Updates boundaries of this DrawCommand.
|
|
*/
|
|
private void setBounds(float left, float top, float right, float bottom) {
|
|
mLeft = left;
|
|
mTop = top;
|
|
mRight = right;
|
|
mBottom = bottom;
|
|
|
|
onBoundsChanged();
|
|
}
|
|
|
|
private void setClipBounds(float clipLeft, float clipTop, float clipRight, float clipBottom) {
|
|
mClipLeft = clipLeft;
|
|
mClipTop = clipTop;
|
|
mClipRight = clipRight;
|
|
mClipBottom = clipBottom;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
private boolean clipBoundsMatch(
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
return mClipLeft == clipLeft && mClipTop == clipTop &&
|
|
mClipRight == clipRight && mClipBottom == clipBottom;
|
|
}
|
|
}
|