Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
*/
experimental_mixBlendMode: true,

/**
* Isolation
*/
isolation: true,

/*
* BoxShadow
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export interface ViewStyle extends FlexStyle, ShadowStyleIOS, TransformsStyle {
* Controls whether the View can be the target of touch events.
*/
pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto' | undefined;
isolation?: 'auto' | 'isolate' | undefined;
cursor?: CursorValue | undefined;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
experimental_filter?: $ReadOnlyArray<FilterFunction> | string,
experimental_mixBlendMode?: ____BlendMode_Internal,
experimental_backgroundImage?: $ReadOnlyArray<GradientValue> | string,
isolation?: 'auto' | 'isolate',
}>;

export type ____ViewStyle_Internal = $ReadOnly<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8330,6 +8330,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
experimental_filter?: $ReadOnlyArray<FilterFunction> | string,
experimental_mixBlendMode?: ____BlendMode_Internal,
experimental_backgroundImage?: $ReadOnlyArray<GradientValue> | string,
isolation?: \\"auto\\" | \\"isolate\\",
}>;
export type ____ViewStyle_Internal = $ReadOnly<{
...____ViewStyle_InternalCore,
Expand Down
17 changes: 15 additions & 2 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -4685,6 +4685,12 @@ public final class com/facebook/react/uimanager/ReactInvalidPropertyException :
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
}

public abstract interface class com/facebook/react/uimanager/ReactMixBlendMode {
public abstract fun getMixBlendMode ()Landroid/graphics/Paint;
public abstract fun isBlendModeParent ()Z
public abstract fun setMixBlendMode (Landroid/graphics/Paint;)V
}

public abstract interface class com/facebook/react/uimanager/ReactOverflowView {
public abstract fun getOverflow ()Ljava/lang/String;
}
Expand Down Expand Up @@ -6640,11 +6646,13 @@ public class com/facebook/react/views/image/ReactImageManager$$PropsSetter : com
public final class com/facebook/react/views/image/ReactImageManager$Companion {
}

public final class com/facebook/react/views/image/ReactImageView : com/facebook/drawee/view/GenericDraweeView {
public final class com/facebook/react/views/image/ReactImageView : com/facebook/drawee/view/GenericDraweeView, com/facebook/react/uimanager/ReactMixBlendMode {
public static final field Companion Lcom/facebook/react/views/image/ReactImageView$Companion;
public static final field REMOTE_IMAGE_FADE_DURATION_MS I
public fun <init> (Landroid/content/Context;Lcom/facebook/drawee/controller/AbstractDraweeControllerBuilder;Lcom/facebook/react/views/image/GlobalImageLoadListener;Ljava/lang/Object;)V
public fun getMixBlendMode ()Landroid/graphics/Paint;
public fun hasOverlappingRendering ()Z
public fun isBlendModeParent ()Z
public final fun maybeUpdateView ()V
public fun onDraw (Landroid/graphics/Canvas;)V
public fun setBackgroundColor (I)V
Expand All @@ -6657,6 +6665,7 @@ public final class com/facebook/react/views/image/ReactImageView : com/facebook/
public final fun setFadeDuration (I)V
public final fun setHeaders (Lcom/facebook/react/bridge/ReadableMap;)V
public final fun setLoadingIndicatorSource (Ljava/lang/String;)V
public fun setMixBlendMode (Landroid/graphics/Paint;)V
public final fun setOverlayColor (I)V
public final fun setProgressiveRenderingEnabled (Z)V
public final fun setResizeMethod (Lcom/facebook/react/views/image/ImageResizeMethod;)V
Expand Down Expand Up @@ -8177,25 +8186,28 @@ public class com/facebook/react/views/view/ReactViewBackgroundDrawable : com/fac
public fun <init> (Landroid/content/Context;)V
}

public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGroup, com/facebook/react/touch/ReactHitSlopView, com/facebook/react/touch/ReactInterceptingViewGroup, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/uimanager/ReactPointerEventsView, com/facebook/react/uimanager/ReactZIndexedViewGroup {
public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGroup, com/facebook/react/touch/ReactHitSlopView, com/facebook/react/touch/ReactInterceptingViewGroup, com/facebook/react/uimanager/ReactClippingViewGroup, com/facebook/react/uimanager/ReactMixBlendMode, com/facebook/react/uimanager/ReactOverflowViewWithInset, com/facebook/react/uimanager/ReactPointerEventsView, com/facebook/react/uimanager/ReactZIndexedViewGroup {
public fun <init> (Landroid/content/Context;)V
public fun addView (Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;)V
protected fun addViewInLayout (Landroid/view/View;ILandroid/view/ViewGroup$LayoutParams;Z)Z
protected fun dispatchDraw (Landroid/graphics/Canvas;)V
public fun dispatchGenericMotionEvent (Landroid/view/MotionEvent;)Z
public fun dispatchProvideStructure (Landroid/view/ViewStructure;)V
protected fun dispatchSetPressed (Z)V
public fun draw (Landroid/graphics/Canvas;)V
protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
protected fun getChildDrawingOrder (II)I
public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z
public fun getClippingRect (Landroid/graphics/Rect;)V
public fun getHitSlopRect ()Landroid/graphics/Rect;
public fun getMixBlendMode ()Landroid/graphics/Paint;
public fun getOverflow ()Ljava/lang/String;
public fun getOverflowInset ()Landroid/graphics/Rect;
public fun getPointerEvents ()Lcom/facebook/react/uimanager/PointerEvents;
public fun getRemoveClippedSubviews ()Z
public fun getZIndexMappedChildIndex (I)I
public fun hasOverlappingRendering ()Z
public fun isBlendModeParent ()Z
protected fun onAttachedToWindow ()V
public fun onInterceptTouchEvent (Landroid/view/MotionEvent;)Z
protected fun onLayout (ZIIII)V
Expand All @@ -8218,6 +8230,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
public fun setBorderStyle (Ljava/lang/String;)V
public fun setBorderWidth (IF)V
public fun setHitSlopRect (Landroid/graphics/Rect;)V
public fun setMixBlendMode (Landroid/graphics/Paint;)V
public fun setNeedsOffscreenAlphaCompositing (Z)V
public fun setOnInterceptTouchEventListener (Lcom/facebook/react/touch/OnInterceptTouchEventListener;)V
public fun setOpacityIfPossible (F)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,10 @@ public static void apply(
}
}

if (mixBlendMode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (view instanceof ReactMixBlendMode
&& !((ReactMixBlendMode) view).isBlendModeParent()
&& mixBlendMode != null
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
p = p == null ? new Paint() : p;
p.setBlendMode(mixBlendMode);
}
Expand Down Expand Up @@ -667,6 +670,15 @@ protected void onAfterUpdateTransaction(@NonNull T view) {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
? (BlendMode) view.getTag(R.id.mix_blend_mode)
: null;

if (mixBlendMode != null
&& view instanceof ReactMixBlendMode
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Paint blendModePaint = new Paint();
blendModePaint.setBlendMode(mixBlendMode);
((ReactMixBlendMode) view).setMixBlendMode(blendModePaint);
}

Boolean useHWLayer = (Boolean) view.getTag(R.id.use_hardware_layer);

LayerEffectsHelper.apply(view, filter, mixBlendMode, useHWLayer);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.uimanager

import android.graphics.Paint

/**
* This interface should be implemented by all [View] subclasses that want to use the mixBlendMode
* prop to blend with its parent.
*/
public interface ReactMixBlendMode {
public var mixBlendMode: Paint?

public fun isBlendModeParent(): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import com.facebook.react.uimanager.FloatUtil.floatsEqual
import com.facebook.react.uimanager.LengthPercentage
import com.facebook.react.uimanager.LengthPercentageType
import com.facebook.react.uimanager.PixelUtil.toPixelFromDIP
import com.facebook.react.uimanager.ReactMixBlendMode
import com.facebook.react.uimanager.Spacing
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.style.BorderRadiusProp
Expand Down Expand Up @@ -89,7 +90,7 @@ public class ReactImageView(
private val draweeControllerBuilder: AbstractDraweeControllerBuilder<*, *, *, *>,
private val globalImageLoadListener: GlobalImageLoadListener?,
private var callerContext: Any?
) : GenericDraweeView(context, buildHierarchy(context)) {
) : GenericDraweeView(context, buildHierarchy(context)), ReactMixBlendMode {

private val sources: MutableList<ImageSource> = mutableListOf()
internal var imageSource: ImageSource? = null
Expand Down Expand Up @@ -117,6 +118,12 @@ public class ReactImageView(
private val reactBackgroundManager = ReactViewBackgroundManager(this)
private var resizeMethod = ImageResizeMethod.AUTO

override var mixBlendMode: Paint? = null

override fun isBlendModeParent(): Boolean {
return false
}

init {
reactBackgroundManager.setOverflow("hidden")
// Workaround Android bug where ImageView visibility is not propagated to the Drawable, so you
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStructure;
import android.view.animation.Animation;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
Expand All @@ -48,6 +51,7 @@
import com.facebook.react.uimanager.ReactClippingProhibitedView;
import com.facebook.react.uimanager.ReactClippingViewGroup;
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
import com.facebook.react.uimanager.ReactMixBlendMode;
import com.facebook.react.uimanager.ReactOverflowViewWithInset;
import com.facebook.react.uimanager.ReactPointerEventsView;
import com.facebook.react.uimanager.ReactZIndexedViewGroup;
Expand All @@ -74,7 +78,8 @@ public class ReactViewGroup extends ViewGroup
ReactPointerEventsView,
ReactHitSlopView,
ReactZIndexedViewGroup,
ReactOverflowViewWithInset {
ReactOverflowViewWithInset,
ReactMixBlendMode {

private static final int ARRAY_CAPACITY_INCREMENT = 12;
private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT;
Expand Down Expand Up @@ -130,6 +135,7 @@ public void onLayoutChange(
private @Nullable Rect mClippingRect;
private @Nullable Rect mHitSlopRect;
private Overflow mOverflow;
private @Nullable Paint mMixBlendMode;
private PointerEvents mPointerEvents;
private @Nullable ChildrenLayoutChangeListener mChildrenLayoutChangeListener;
private @Nullable CSSBackgroundDrawable mCSSBackgroundDrawable;
Expand Down Expand Up @@ -864,6 +870,21 @@ public void setHitSlopRect(@Nullable Rect rect) {
mHitSlopRect = rect;
}

@Override
public @Nullable Paint getMixBlendMode() {
return mMixBlendMode;
}

@Override
public void setMixBlendMode(@Nullable Paint p) {
mMixBlendMode = p;
}

@Override
public boolean isBlendModeParent() {
return true;
}

public void setOverflow(@Nullable String overflow) {
mOverflow = overflow == null ? Overflow.VISIBLE : Overflow.fromString(overflow);
invalidate();
Expand All @@ -885,6 +906,12 @@ public void setOverflow(@Nullable String overflow) {

@Override
public void setOverflowInset(int left, int top, int right, int bottom) {
if (mOverflowInset.left != left
|| mOverflowInset.top != top
|| mOverflowInset.right != right
|| mOverflowInset.bottom != bottom) {
invalidate();
}
mOverflowInset.set(left, top, right, bottom);
}

Expand All @@ -904,6 +931,38 @@ public Rect getOverflowInset() {
super.setBackground(drawable);
}

@Override
public void draw(@NonNull Canvas canvas) {
if (ViewUtil.getUIManagerType(this) == UIManagerType.FABRIC) {
boolean needsOffscreenRender = false;
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof ReactMixBlendMode
&& ((ReactMixBlendMode) getChildAt(i)).getMixBlendMode() != null) {
needsOffscreenRender = true;
}
}

// Check if the view is a stacking context (has children) if it does, do the rendering
// offscreen and then composite back.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
&& (needsOffscreenRender || mMixBlendMode != null)) {
Rect overflowInset = getOverflowInset();
canvas.saveLayer(
overflowInset.left,
overflowInset.top,
getWidth() + -overflowInset.right,
getHeight() + -overflowInset.bottom,
mMixBlendMode);
super.draw(canvas);
canvas.restore();
} else {
super.draw(canvas);
}
} else {
super.draw(canvas);
}
}

@Override
protected void dispatchDraw(Canvas canvas) {
if (ReactNativeFeatureFlags.enableBackgroundStyleApplicator()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ BaseViewProps::BaseViewProps(
"experimental_mixBlendMode",
sourceProps.mixBlendMode,
{})),
isolation(
CoreFeatures::enablePropIteratorSetter ? sourceProps.isolation
: convertRawProp(
context,
rawProps,
"isolation",
sourceProps.isolation,
{})),
transform(
CoreFeatures::enablePropIteratorSetter ? sourceProps.transform
: convertRawProp(
Expand Down Expand Up @@ -330,6 +338,7 @@ void BaseViewProps::setProp(
RAW_SET_PROP_SWITCH_CASE_BASIC(shouldRasterize);
RAW_SET_PROP_SWITCH_CASE_BASIC(zIndex);
RAW_SET_PROP_SWITCH_CASE_BASIC(pointerEvents);
RAW_SET_PROP_SWITCH_CASE_BASIC(isolation);
RAW_SET_PROP_SWITCH_CASE_BASIC(hitSlop);
RAW_SET_PROP_SWITCH_CASE_BASIC(onLayout);
RAW_SET_PROP_SWITCH_CASE_BASIC(collapsable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/Color.h>
#include <react/renderer/graphics/Filter.h>
#include <react/renderer/graphics/Isolation.h>
#include <react/renderer/graphics/Transform.h>

#include <optional>
Expand Down Expand Up @@ -68,7 +69,10 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
std::vector<GradientValue> backgroundImage{};

// MixBlendMode
BlendMode mixBlendMode;
BlendMode mixBlendMode{BlendMode::Normal};

// Isolate
Isolation isolation{Isolation::Auto};

// Transform
Transform transform{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void ViewShadowNode::initialize() noexcept {
viewProps.removeClippedSubviews || viewProps.cursor != Cursor::Auto ||
!viewProps.filter.empty() ||
viewProps.mixBlendMode != BlendMode::Normal ||
viewProps.isolation == Isolation::Isolate ||
HostPlatformViewTraitsInitializer::formsStackingContext(viewProps);

bool formsView = formsStackingContext ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <react/renderer/graphics/BlendMode.h>
#include <react/renderer/graphics/BoxShadow.h>
#include <react/renderer/graphics/Filter.h>
#include <react/renderer/graphics/Isolation.h>
#include <react/renderer/graphics/PlatformColorParser.h>
#include <react/renderer/graphics/Transform.h>
#include <react/renderer/graphics/ValueUnit.h>
Expand Down Expand Up @@ -1184,6 +1185,27 @@ inline void fromRawValue(
result = backgroundImage;
}

inline void fromRawValue(
const PropsParserContext& /*context*/,
const RawValue& value,
Isolation& result) {
react_native_expect(value.hasType<std::string>());
result = Isolation::Auto;
if (!value.hasType<std::string>()) {
return;
}

auto rawIsolation = static_cast<std::string>(value);
std::optional<Isolation> isolation = isolationFromString(rawIsolation);

if (!isolation) {
LOG(ERROR) << "Could not parse isolation: " << rawIsolation;
return;
}

result = isolation.value();
}

template <size_t N>
inline std::string toString(const std::array<float, N> vec) {
std::string s;
Expand Down
Loading