Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes to RCTView props #234

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions React/Views/NSView+React.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,9 @@
* UIKit replacement
*/
@property (nonatomic, assign) BOOL clipsToBounds;
@property (nonatomic, assign) CATransform3D transform;

/** Populate the `layer` ivar when nil */
- (void)ensureLayerExists;

@end
28 changes: 25 additions & 3 deletions React/Views/NSView+React.m
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ - (void)reactSetFrame:(CGRect)frame
return;
}

[self ensureLayerExists];
self.frame = frame;

// TODO: why position matters? It's only produce bugs
// OSX requires position and anchor point to rotate from center

// Ensure the anchorPoint is in the center.
self.layer.position = position;
self.layer.bounds = bounds;
self.layer.anchorPoint = anchor;
Expand Down Expand Up @@ -295,4 +295,26 @@ - (NSView *)reactAccessibilityElement
return self;
}

#pragma mark - Other

- (void)ensureLayerExists
{
if (!self.layer) {
self.wantsLayer = YES;
self.layer.delegate = (id<CALayerDelegate>)self;
}
}

- (CATransform3D)transform
{
return CATransform3DIdentity;
}

- (void)setTransform:(__unused CATransform3D)transform
{
// Do nothing by default.
// Native views must synthesize their own "transform" property,
// override "displayLayer:", and apply the transform there.
}

@end
14 changes: 5 additions & 9 deletions React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
*/
+ (NSEdgeInsets)contentInsetsForView:(NSView *)curView;

- (void)setBackgroundColor:(NSColor *)backgroundColor;

/**
* Layout direction of the view.
* This is inherited from UIView+React, but we override it here
Expand Down Expand Up @@ -114,19 +112,17 @@
@property (nonatomic, assign) CGFloat borderWidth;

/**
* Initial tranformation for a view which is not rendered yet
* Border styles.
*/
@property (nonatomic, assign) RCTBorderStyle borderStyle;


@property (nonatomic, assign) CATransform3D transform;
@property (nonatomic, assign) bool shouldBeTransformed;
@property (nonatomic, copy) NSColor *backgroundColor;

@property (nonatomic, copy) RCTDirectEventBlock onDragEnter;
@property (nonatomic, copy) RCTDirectEventBlock onDragLeave;
@property (nonatomic, copy) RCTDirectEventBlock onDrop;
@property (nonatomic, copy) RCTDirectEventBlock onContextMenuItemClick;
/**
* Border styles.
*/
@property (nonatomic, assign) RCTBorderStyle borderStyle;


@end
48 changes: 20 additions & 28 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ - (instancetype)initWithFrame:(CGRect)frame
_borderBottomStartRadius = -1;
_borderBottomEndRadius = -1;
_borderStyle = RCTBorderStyleSolid;
_transform = CATransform3DIdentity;
self.clipsToBounds = NO;
}

Expand Down Expand Up @@ -192,6 +193,7 @@ - (void)setPointerEvents:(RCTPointerEvents)pointerEvents
- (void)setTransform:(CATransform3D)transform
{
_transform = transform;
[self setNeedsDisplay:YES];
}

- (NSView *)hitTest:(CGPoint)point
Expand Down Expand Up @@ -424,22 +426,11 @@ - (NSColor *)backgroundColor

- (void)setBackgroundColor:(NSColor *)backgroundColor
{
if ([_backgroundColor isEqual:backgroundColor]) {
return;
}
if (backgroundColor == nil) {
[self setWantsLayer:NO];
self.layer = NULL;
return;
}
if (![self wantsLayer] || self.layer == nil) {
[self setWantsLayer:YES];
self.layer.delegate = self;
}
[self.layer setBackgroundColor:[backgroundColor CGColor]];
[self.layer setNeedsDisplay];
[self setNeedsDisplay:YES];
_backgroundColor = backgroundColor;

[self ensureLayerExists];
self.layer.backgroundColor = backgroundColor.CGColor;
[self.layer setNeedsDisplay];
}

static CGFloat RCTDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x) {
Expand Down Expand Up @@ -573,28 +564,29 @@ - (void)reactSetFrame:(CGRect)frame
[super reactSetFrame:frame];
if (!CGSizeEqualToSize(self.bounds.size, oldSize)) {
[self.layer setNeedsDisplay];
} else if (!CATransform3DIsIdentity(_transform)) {
[self applyTransform:self.layer];
}
}

- (void)ensureLayerExists
- (void)applyTransform:(CALayer *)layer
{
if (!self.layer) {
// Set `wantsLayer` first to create a "layer-backed view" instead of a "layer-hosting view".
self.wantsLayer = YES;

CALayer *layer = [CALayer layer];
layer.delegate = self;
self.layer = layer;
if (!CATransform3DEqualToTransform(_transform, layer.transform)) {
layer.transform = _transform;
// Enable edge antialiasing in perspective transforms
layer.edgeAntialiasingMask = !(_transform.m34 == 0.0f);
}
}

- (void)displayLayer:(CALayer *)layer
{
if (self.shouldBeTransformed) {
self.layer.transform = self.transform;
self.shouldBeTransformed = NO;
}

// Applying the transform here ensures it's not overridden by AppKit internals.
[self applyTransform:layer];

// Ensure the anchorPoint is in the center.
layer.position = (CGPoint){CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)};
layer.anchorPoint = (CGPoint){0.5, 0.5};

if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) {
return;
}
Expand Down
20 changes: 20 additions & 0 deletions React/Views/RCTViewManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,24 @@ RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \
RCT_REMAP_SHADOW_PROPERTY(name, __custom__, type) \
- (void)set_##name:(id)json forShadowView:(viewClass *)view

/**
* This macro maps a named property to an arbitrary key path in the view's layer.
*/
#define RCT_REMAP_LAYER_PROPERTY(name, keyPath, type) \
RCT_CUSTOM_VIEW_PROPERTY(name, type, RCTView) \
{ \
if (json) { \
[view ensureLayerExists]; \
view.layer.keyPath = [RCTConvert type:json]; \
} else { \
view.layer.keyPath = defaultView.layer.keyPath; \
} \
}

/**
* This handles the simple case, where JS and native property names match.
*/
#define RCT_EXPORT_LAYER_PROPERTY(name, type) \
RCT_REMAP_LAYER_PROPERTY(name, name, type)

@end
74 changes: 26 additions & 48 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,6 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio
return nil;
}

- (void)checkLayerExists:(NSView *)view
{
if (!view.layer) {
[view setWantsLayer:YES];
CALayer *viewLayer = [CALayer layer];
viewLayer.delegate = (id<CALayerDelegate>)view;
[view setLayer:viewLayer];
}
}

#pragma mark - View properties

#if TARGET_OS_TV
Expand All @@ -121,11 +111,12 @@ - (void)checkLayerExists:(NSView *)view
RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString)

RCT_EXPORT_VIEW_PROPERTY(backgroundColor, NSColor)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize)
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float)
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
RCT_REMAP_LAYER_PROPERTY(backfaceVisibility, doubleSided, css_backface_visibility_t)
RCT_EXPORT_LAYER_PROPERTY(opacity, float)
RCT_EXPORT_LAYER_PROPERTY(shadowColor, CGColor)
RCT_EXPORT_LAYER_PROPERTY(shadowOffset, CGSize)
RCT_EXPORT_LAYER_PROPERTY(shadowOpacity, float)
RCT_EXPORT_LAYER_PROPERTY(shadowRadius, CGFloat)
RCT_REMAP_VIEW_PROPERTY(toolTip, toolTip, NSString)
RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView)
{
Expand All @@ -137,23 +128,14 @@ - (void)checkLayerExists:(NSView *)view
}
RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView)
{
if (json) {
[view ensureLayerExists];
}
view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize;
view.layer.rasterizationScale = view.layer.shouldRasterize ? [NSScreen mainScreen].backingScaleFactor : defaultView.layer.rasterizationScale;
}

RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RCTView)
{
CATransform3D transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform;
if ([view respondsToSelector:@selector(shouldBeTransformed)] && !view.superview) {
view.shouldBeTransformed = YES;
view.transform = transform;
} else {
view.layer.transform = transform;
}

// TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing
view.layer.edgeAntialiasingMask = !CATransform3DIsIdentity(transform);
}
RCT_EXPORT_VIEW_PROPERTY(transform, CATransform3D)

RCT_CUSTOM_VIEW_PROPERTY(draggedTypes, NSArray*<NSString *>, RCTView)
{
Expand All @@ -164,61 +146,57 @@ - (void)checkLayerExists:(NSView *)view
[view registerForDraggedTypes:defaultView.registeredDraggedTypes];
}
}

RCT_CUSTOM_VIEW_PROPERTY(opacity, float, RCTView)
{
if (json) {
[self checkLayerExists:view];
[view.layer setOpacity:[RCTConvert float:json]];
} else {
[view.layer setOpacity:1];
}
}


RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView)
{
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents;
return;
}
}


RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
{
if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) {
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView)
{
if (json) {
[view ensureLayerExists];
}
if ([view respondsToSelector:@selector(setBorderRadius:)]) {
[self checkLayerExists:view];
view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius;
} else {
view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
{
if (json) {
[view ensureLayerExists];
}
if ([view respondsToSelector:@selector(setBorderColor:)]) {
[self checkLayerExists:view];
view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor;
} else {
view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderWidth, float, RCTView)
{
if (json) {
[view ensureLayerExists];
}
if ([view respondsToSelector:@selector(setBorderWidth:)]) {
[self checkLayerExists:view];
view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth;
} else {
view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderStyle, RCTBorderStyle, RCTView)
{
if (json) {
[view ensureLayerExists];
}
if ([view respondsToSelector:@selector(setBorderStyle:)]) {
view.borderStyle = json ? [RCTConvert RCTBorderStyle:json] : defaultView.borderStyle;
}
Expand Down Expand Up @@ -262,14 +240,14 @@ - (void)checkLayerExists:(NSView *)view
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, float, RCTView) \
{ \
if ([view respondsToSelector:@selector(setBorder##SIDE##Width:)]) { \
[self checkLayerExists:view]; \
[view ensureLayerExists]; \
view.border##SIDE##Width = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Width; \
} \
} \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Color, NSColor, RCTView) \
{ \
if ([view respondsToSelector:@selector(setBorder##SIDE##Color:)]) { \
[self checkLayerExists:view]; \
[view ensureLayerExists]; \
view.border##SIDE##Color = json ? [RCTConvert CGColor:json] : defaultView.border##SIDE##Color; \
} \
}
Expand Down