mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-05 22:56:11 +00:00
feat: get currentColor from caller instead of parent (#2521)
# Summary Fixes #2520 When an element uses `currentColor`, it should look for color in its caller, not in its parent. Example: ```svg <Svg width="100" height="100" viewBox="0 0 100 100" color="red"> <Defs color="blue"> <G color="green"> <Rect id="a" x="0" y="0" width="50" height="50" fill="currentColor"/> </G> </Defs> <G color="pink"> <Use href="#a"/> <!-- #1 --> </G> <Use href="#a" transform="translate(25 25)"/> <!-- #2 --> <G color="green"> <Use href="#a" transform="translate(50 50)"/> <!-- #3 --> </G> </Svg> ``` * `#1` should be **pink** * `#2` should be **red** * `#3` should be **green**  ## Test Plan Example app -> test -> Test2520 ## Compatibility | OS | Implemented | | ------- | :---------: | | iOS | ✅ | | MacOS | ✅ | | Android | ✅ |
This commit is contained in:
@@ -96,6 +96,7 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
private @Nullable ArrayList<Object> mOriginProperties;
|
||||
private @Nullable ArrayList<String> mPropList;
|
||||
private @Nullable ArrayList<String> mAttributeList;
|
||||
private @Nullable RenderableView mCaller;
|
||||
|
||||
@Nullable String mFilter;
|
||||
|
||||
@@ -134,6 +135,9 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
if (this.mCurrentColor != 0) {
|
||||
return this.mCurrentColor;
|
||||
}
|
||||
if (this.mCaller != null) {
|
||||
return this.mCaller.getCurrentColor();
|
||||
}
|
||||
ViewParent parent = this.getParent();
|
||||
if (parent instanceof VirtualView) {
|
||||
return ((RenderableView) parent).getCurrentColor();
|
||||
@@ -756,6 +760,7 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
}
|
||||
|
||||
void mergeProperties(RenderableView target) {
|
||||
mCaller = target;
|
||||
ArrayList<String> targetAttributeList = target.getAttributeList();
|
||||
|
||||
if (targetAttributeList == null || targetAttributeList.size() == 0) {
|
||||
@@ -798,6 +803,7 @@ public abstract class RenderableView extends VirtualView implements ReactHitSlop
|
||||
mLastMergedList = null;
|
||||
mOriginProperties = null;
|
||||
mAttributeList = mPropList;
|
||||
mCaller = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.horcrux.svg;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
@@ -189,7 +190,7 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
|
||||
final Matrix mInvViewBoxMatrix = new Matrix();
|
||||
private boolean mInvertible = true;
|
||||
private boolean mRendered = false;
|
||||
int mCurrentColor = 0;
|
||||
int mCurrentColor = Color.BLACK;
|
||||
|
||||
boolean notRendered() {
|
||||
return !mRendered;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
BOOL fillColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
|
||||
CGContextSetFillColorWithColor(context, [element getCurrentColor]);
|
||||
fillColor = YES;
|
||||
} else {
|
||||
fillColor = [brush applyFillColor:context opacity:opacity];
|
||||
@@ -70,7 +70,7 @@
|
||||
BOOL strokeColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
|
||||
CGContextSetStrokeColorWithColor(context, [element getCurrentColor]);
|
||||
strokeColor = YES;
|
||||
} else {
|
||||
strokeColor = [brush applyStrokeColor:context opacity:opacity];
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
@interface RNSVGSvgView : RNSVGView <RNSVGContainer>
|
||||
|
||||
@property (nonatomic, strong) RNSVGColor *color;
|
||||
@property (nonatomic, strong) RNSVGLength *bbWidth;
|
||||
@property (nonatomic, strong) RNSVGLength *bbHeight;
|
||||
@property (nonatomic, assign) CGFloat minX;
|
||||
|
||||
@@ -47,8 +47,6 @@ using namespace facebook::react;
|
||||
// This is necessary to ensure that [self setNeedsDisplay] actually triggers
|
||||
// a redraw when our parent transitions between hidden and visible.
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
// We don't want the dimming effect on tint as it's used as currentColor
|
||||
self.tintAdjustmentMode = UIViewTintAdjustmentModeNormal;
|
||||
#endif // TARGET_OS_OSX
|
||||
rendered = false;
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
@@ -90,7 +88,7 @@ using namespace facebook::react;
|
||||
self.align = RCTNSStringFromStringNilIfEmpty(newProps.align);
|
||||
self.meetOrSlice = intToRNSVGVBMOS(newProps.meetOrSlice);
|
||||
if (RCTUIColorFromSharedColor(newProps.color)) {
|
||||
self.tintColor = RCTUIColorFromSharedColor(newProps.color);
|
||||
self.color = RCTUIColorFromSharedColor(newProps.color);
|
||||
}
|
||||
[super updateProps:props oldProps:oldProps];
|
||||
}
|
||||
@@ -184,10 +182,13 @@ using namespace facebook::react;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)tintColorDidChange
|
||||
- (void)setColor:(RNSVGColor *)color
|
||||
{
|
||||
if (color == _color) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
[self clearChildCache];
|
||||
_color = color;
|
||||
}
|
||||
|
||||
- (void)setMinX:(CGFloat)minX
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
@interface RNSVGRenderable : RNSVGNode
|
||||
|
||||
@property (class) RNSVGRenderable *contextElement;
|
||||
@property (nonatomic, strong) RNSVGColor *color;
|
||||
@property (nonatomic, strong) RNSVGBrush *fill;
|
||||
@property (nonatomic, assign) CGFloat fillOpacity;
|
||||
@property (nonatomic, assign) RNSVGCGFCRule fillRule;
|
||||
@@ -45,4 +46,6 @@
|
||||
|
||||
- (void)resetProperties;
|
||||
|
||||
- (CGColor *)getCurrentColor;
|
||||
|
||||
@end
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
NSArray<RNSVGLength *> *_sourceStrokeDashArray;
|
||||
CGFloat *_strokeDashArrayData;
|
||||
CGPathRef _srcHitPath;
|
||||
RNSVGRenderable *_caller;
|
||||
}
|
||||
|
||||
static RNSVGRenderable *_contextElement;
|
||||
@@ -61,11 +62,11 @@ static RNSVGRenderable *_contextElement;
|
||||
|
||||
- (void)setColor:(RNSVGColor *)color
|
||||
{
|
||||
if (color == self.tintColor) {
|
||||
if (color == _color) {
|
||||
return;
|
||||
}
|
||||
[self invalidate];
|
||||
self.tintColor = color;
|
||||
_color = color;
|
||||
}
|
||||
|
||||
- (void)setFill:(RNSVGBrush *)fill
|
||||
@@ -560,7 +561,7 @@ UInt32 saturate(CGFloat value)
|
||||
|
||||
if (self.fill) {
|
||||
if (self.fill.class == RNSVGBrush.class) {
|
||||
CGContextSetFillColorWithColor(context, [self.tintColor CGColor]);
|
||||
CGContextSetFillColorWithColor(context, [self getCurrentColor]);
|
||||
fillColor = YES;
|
||||
} else {
|
||||
fillColor = [self.fill applyFillColor:context opacity:self.fillOpacity];
|
||||
@@ -608,7 +609,7 @@ UInt32 saturate(CGFloat value)
|
||||
BOOL strokeColor;
|
||||
|
||||
if (self.stroke.class == RNSVGBrush.class) {
|
||||
CGContextSetStrokeColorWithColor(context, [self.tintColor CGColor]);
|
||||
CGContextSetStrokeColorWithColor(context, [self getCurrentColor]);
|
||||
strokeColor = YES;
|
||||
} else {
|
||||
strokeColor = [self.stroke applyStrokeColor:context opacity:self.strokeOpacity];
|
||||
@@ -724,6 +725,7 @@ UInt32 saturate(CGFloat value)
|
||||
|
||||
- (void)mergeProperties:(__kindof RNSVGRenderable *)target
|
||||
{
|
||||
_caller = target;
|
||||
NSArray<NSString *> *targetAttributeList = [target getAttributeList];
|
||||
|
||||
if (targetAttributeList.count == 0) {
|
||||
@@ -754,9 +756,28 @@ UInt32 saturate(CGFloat value)
|
||||
[self setValue:[_originProperties valueForKey:key] forKey:key];
|
||||
}
|
||||
|
||||
_caller = nil;
|
||||
_lastMergedList = nil;
|
||||
_attributeList = _propList;
|
||||
self.merging = false;
|
||||
}
|
||||
|
||||
- (CGColor *)getCurrentColor
|
||||
{
|
||||
if (self.color != nil) {
|
||||
return [self.color CGColor];
|
||||
}
|
||||
if (_caller != nil) {
|
||||
return [_caller getCurrentColor];
|
||||
}
|
||||
RNSVGPlatformView *parentView = [self superview];
|
||||
if ([parentView isKindOfClass:[RNSVGRenderable class]]) {
|
||||
return [(RNSVGRenderable *)parentView getCurrentColor];
|
||||
} else if ([parentView isKindOfClass:[RNSVGSvgView class]]) {
|
||||
return [[(RNSVGSvgView *)parentView color] CGColor];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#import "RNSVGUIKit.h"
|
||||
|
||||
@implementation RNSVGView {
|
||||
NSColor *_tintColor;
|
||||
}
|
||||
|
||||
- (CGPoint)center
|
||||
@@ -20,29 +19,6 @@
|
||||
self.frame = CGRectMake(xOrigin, yOrigin, frameRect.size.width, frameRect.size.height);
|
||||
}
|
||||
|
||||
- (NSColor *)tintColor
|
||||
{
|
||||
if (_tintColor != nil) {
|
||||
return _tintColor;
|
||||
}
|
||||
|
||||
// To mimic iOS's tintColor, we crawl up the view hierarchy until either:
|
||||
// (a) we find a valid color
|
||||
// (b) we reach a view that isn't an RNSVGView
|
||||
NSView *parentView = [self superview];
|
||||
if ([parentView isKindOfClass:[RNSVGView class]]) {
|
||||
return [(RNSVGView *)parentView tintColor];
|
||||
} else {
|
||||
return [NSColor controlAccentColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTintColor:(NSColor *)tintColor
|
||||
{
|
||||
_tintColor = tintColor;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSImage (RNSVGMacOSExtensions)
|
||||
|
||||
@@ -133,7 +133,7 @@ using namespace facebook::react;
|
||||
if (self.inlineSize != nil && self.inlineSize.value != 0) {
|
||||
if (self.fill) {
|
||||
if (self.fill.class == RNSVGBrush.class) {
|
||||
CGColorRef color = [self.tintColor CGColor];
|
||||
CGColorRef color = [self getCurrentColor];
|
||||
[self drawWrappedText:context gc:gc rect:rect color:color];
|
||||
} else {
|
||||
CGColorRef color = [self.fill getColorWithOpacity:self.fillOpacity];
|
||||
@@ -143,7 +143,7 @@ using namespace facebook::react;
|
||||
}
|
||||
if (self.stroke) {
|
||||
if (self.stroke.class == RNSVGBrush.class) {
|
||||
CGColorRef color = [self.tintColor CGColor];
|
||||
CGColorRef color = [self getCurrentColor];
|
||||
[self drawWrappedText:context gc:gc rect:rect color:color];
|
||||
} else {
|
||||
CGColorRef color = [self.stroke getColorWithOpacity:self.strokeOpacity];
|
||||
|
||||
@@ -24,7 +24,7 @@ RCT_EXPORT_MODULE()
|
||||
return [RNSVGRenderable new];
|
||||
}
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(color, tintColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(fill, RNSVGBrush)
|
||||
RCT_EXPORT_VIEW_PROPERTY(fillOpacity, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(fillRule, RNSVGCGFCRule)
|
||||
|
||||
@@ -26,7 +26,7 @@ RCT_EXPORT_VIEW_PROPERTY(vbWidth, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(vbHeight, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(align, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS)
|
||||
RCT_REMAP_VIEW_PROPERTY(color, tintColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(hitSlop, UIEdgeInsets, RNSVGSvgView)
|
||||
{
|
||||
if ([view respondsToSelector:@selector(setHitTestEdgeInsets:)]) {
|
||||
|
||||
32
apps/common/test/Test2520.tsx
Normal file
32
apps/common/test/Test2520.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
import {Defs, G, Rect, Svg, Use} from 'react-native-svg';
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Svg width="100" height="100" viewBox="0 0 100 100" color="red">
|
||||
{/* @ts-ignore */}
|
||||
<Defs color="blue">
|
||||
<G color="green">
|
||||
<Rect
|
||||
id="a"
|
||||
x="0"
|
||||
y="0"
|
||||
width="50"
|
||||
height="50"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</G>
|
||||
</Defs>
|
||||
<G color="pink">
|
||||
<Use href="#a" />
|
||||
</G>
|
||||
<Use href="#a" transform="translate(25 25)" />
|
||||
<G color="green">
|
||||
<Use href="#a" transform="translate(50 50)" />
|
||||
</G>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -34,6 +34,7 @@ import Test2407 from './Test2407';
|
||||
import Test2417 from './Test2417';
|
||||
import Test2455 from './Test2455';
|
||||
import Test2471 from './Test2471';
|
||||
import Test2520 from './Test2520';
|
||||
|
||||
export default function App() {
|
||||
return <ColorTest />;
|
||||
|
||||
@@ -107,7 +107,6 @@ export default class Svg extends Shape<SvgProps> {
|
||||
...extracted,
|
||||
};
|
||||
let {
|
||||
color,
|
||||
width,
|
||||
height,
|
||||
focusable,
|
||||
@@ -173,8 +172,6 @@ export default class Svg extends Shape<SvgProps> {
|
||||
|
||||
extractResponder(props, props, this as ResponderInstanceProps);
|
||||
|
||||
props.tintColor = color;
|
||||
|
||||
if (onLayout != null) {
|
||||
props.onLayout = onLayout;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user