diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 817f84dbe8516d..adb35b87914909 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -20,7 +20,6 @@ import type { AccessibilityValue, } from '../../Components/View/ViewAccessibility'; import type {EdgeInsetsProp} from '../../StyleSheet/EdgeInsetsPropType'; -import type {CursorValue} from '../../StyleSheet/StyleSheetTypes'; import type { BlurEvent, FocusEvent, @@ -49,7 +48,6 @@ type Props = $ReadOnly<{| accessibilityViewIsModal?: ?boolean, accessible?: ?boolean, children?: ?React.Node, - cursor?: ?CursorValue, delayLongPress?: ?number, delayPressIn?: ?number, delayPressOut?: ?number, diff --git a/Libraries/Components/View/ReactNativeStyleAttributes.js b/Libraries/Components/View/ReactNativeStyleAttributes.js index 9525991e831b53..7b4224b86d830c 100644 --- a/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -116,6 +116,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { borderTopLeftRadius: true, borderTopRightRadius: true, borderTopStartRadius: true, + cursor: true, opacity: true, /** diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index affb190960e820..ebbd5697597fd1 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -634,4 +634,9 @@ export type ViewProps = $ReadOnly<{| * @platform macos */ draggedTypes?: ?DraggedTypesType, // TODO(macOS GH#774) + + /* + * Sets the type of mouse cursor, to show when the mouse pointer is over the view. + */ + cursor?: ?CursorValue, // TODO(macOS GH#774) |}>; diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index 220f109d6d5489..57ba92b8154e75 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -16,6 +16,29 @@ import type {NativeColorValue} from './PlatformColorValueTypes'; export type ____ColorValue_Internal = null | string | NativeColorValue; +export type CursorValue = ?( + | 'alias' + | 'auto' + | 'col-resize' + | 'context-menu' + | 'copy' + | 'crosshair' + | 'default' + | 'disappearing-item' + | 'e-resize' + | 'grab' + | 'grabbing' + | 'n-resize' + | 'no-drop' + | 'not-allowed' + | 'pointer' + | 'row-resize' + | 's-resize' + | 'text' + | 'vertical-text' + | 'w-resize' +) + export type ColorArrayValue = null | $ReadOnlyArray<____ColorValue_Internal>; export type PointValue = {| x: number, @@ -639,6 +662,7 @@ export type ____TextStyle_Internal = $ReadOnly<{| textDecorationColor?: ____ColorValue_Internal, textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase', writingDirection?: 'auto' | 'ltr' | 'rtl', + cursor?: CursorValue, |}>; export type ____ImageStyle_Internal = $ReadOnly<{| diff --git a/Libraries/Text/RCTTextAttributes.m b/Libraries/Text/RCTTextAttributes.m index 0690ea3bdf27b9..38ea5a374842b4 100644 --- a/Libraries/Text/RCTTextAttributes.m +++ b/Libraries/Text/RCTTextAttributes.m @@ -109,27 +109,27 @@ - (NSParagraphStyle *)effectiveParagraphStyle alignment = NSTextAlignmentRight; } } - + paragraphStyle.alignment = alignment; isParagraphStyleUsed = YES; } - + if (_baseWritingDirection != NSWritingDirectionNatural) { paragraphStyle.baseWritingDirection = _baseWritingDirection; isParagraphStyleUsed = YES; } - + if (!isnan(_lineHeight)) { CGFloat lineHeight = _lineHeight * self.effectiveFontSizeMultiplier; paragraphStyle.minimumLineHeight = lineHeight; paragraphStyle.maximumLineHeight = lineHeight; isParagraphStyleUsed = YES; } - + if (isParagraphStyleUsed) { return [paragraphStyle copy]; } - + return nil; } diff --git a/React/Base/macOS/RCTUIKit.m b/React/Base/macOS/RCTUIKit.m index b7ee45c50841eb..3d0ea62bf04c9c 100644 --- a/React/Base/macOS/RCTUIKit.m +++ b/React/Base/macOS/RCTUIKit.m @@ -423,6 +423,13 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor } } +// We purposely don't use RCTCursor for the parameter type here because it would introduce an import cycle: +// RCTUIKit > RCTCursor > RCTConvert > RCTUIKit +- (void)setCursor:(NSInteger)cursor +{ + // This method is required to be defined due to [RCTVirtualTextViewManager view] returning a RCTUIView. +} + @end // RCTUIScrollView diff --git a/React/Views/RCTCursor.h b/React/Views/RCTCursor.h index 6d9de7cafb61ef..34a51ac9cb4aed 100644 --- a/React/Views/RCTCursor.h +++ b/React/Views/RCTCursor.h @@ -1,3 +1,10 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + #import typedef NS_ENUM(NSInteger, RCTCursor) { @@ -25,5 +32,6 @@ typedef NS_ENUM(NSInteger, RCTCursor) { @interface RCTConvert (RCTCursor) + (RCTCursor)RCTCursor:(id)json; ++ (NSCursor *)NSCursor:(RCTCursor)rctCursor; @end diff --git a/React/Views/RCTCursor.m b/React/Views/RCTCursor.m index 37e6a49e303d29..f70ad56f5bda17 100644 --- a/React/Views/RCTCursor.m +++ b/React/Views/RCTCursor.m @@ -1,3 +1,10 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + #import @implementation RCTConvert (RCTCursor) @@ -29,4 +36,68 @@ @implementation RCTConvert (RCTCursor) RCTCursorAuto, integerValue) ++ (NSCursor *)NSCursor:(RCTCursor)rctCursor +{ + NSCursor *cursor; + + switch (rctCursor) { + case RCTCursorArrow: + cursor = [NSCursor arrowCursor]; + break; + case RCTCursorClosedHand: + cursor = [NSCursor closedHandCursor]; + break; + case RCTCursorContextualMenu: + cursor = [NSCursor contextualMenuCursor]; + break; + case RCTCursorCrosshair: + cursor = [NSCursor crosshairCursor]; + break; + case RCTCursorDisappearingItem: + cursor = [NSCursor disappearingItemCursor]; + break; + case RCTCursorDragCopy: + cursor = [NSCursor dragCopyCursor]; + break; + case RCTCursorDragLink: + cursor = [NSCursor dragLinkCursor]; + break; + case RCTCursorIBeam: + cursor = [NSCursor IBeamCursor]; + break; + case RCTCursorIBeamCursorForVerticalLayout: + cursor = [NSCursor IBeamCursorForVerticalLayout]; + break; + case RCTCursorOpenHand: + cursor = [NSCursor openHandCursor]; + break; + case RCTCursorOperationNotAllowed: + cursor = [NSCursor operationNotAllowedCursor]; + break; + case RCTCursorPointingHand: + cursor = [NSCursor pointingHandCursor]; + break; + case RCTCursorResizeDown: + cursor = [NSCursor resizeDownCursor]; + break; + case RCTCursorResizeLeft: + cursor = [NSCursor resizeLeftCursor]; + break; + case RCTCursorResizeLeftRight: + cursor = [NSCursor resizeLeftRightCursor]; + break; + case RCTCursorResizeRight: + cursor = [NSCursor resizeRightCursor]; + break; + case RCTCursorResizeUp: + cursor = [NSCursor resizeUpCursor]; + break; + case RCTCursorResizeUpDown: + cursor = [NSCursor resizeUpDownCursor]; + break; + } + + return cursor; +} + @end diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index d1e40a0923c743..6ef286f8952c96 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -12,6 +12,10 @@ #import // TODO(OSS Candidate ISS#2710739) #import +#if TARGET_OS_OSX // TODO(macOS GH#774) +#import +#endif // TODO(macOS GH#774) + #if !TARGET_OS_OSX // TODO(macOS GH#774) extern const UIAccessibilityTraits SwitchAccessibilityTrait; #endif // TODO(macOS GH#774) @@ -119,6 +123,7 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait; /** * macOS Properties */ +@property (nonatomic, assign) RCTCursor cursor; @property (nonatomic, assign) CATransform3D transform3D; diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 072bf148edde6c..66d9c0b09261eb 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -742,16 +742,16 @@ - (void)viewBoundsChanged:(NSNotification*)__unused inNotif // the mouseExited: event does not get called on the view where mouseEntered: was previously called. // This creates an unnatural pairing of mouse enter and exit events and can cause problems. // We therefore explicitly check for this here and handle them by calling the appropriate callbacks. - + if (!_hasMouseOver && self.onMouseEnter) { NSPoint locationInWindow = [[self window] mouseLocationOutsideOfEventStream]; NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil]; - + if (NSPointInRect(locationInView, [self bounds])) { _hasMouseOver = YES; - + [self sendMouseEventWithBlock:self.onMouseEnter locationInfo:[self locationInfoFromDraggingLocation:locationInWindow] modifierFlags:0 @@ -762,11 +762,11 @@ - (void)viewBoundsChanged:(NSNotification*)__unused inNotif { NSPoint locationInWindow = [[self window] mouseLocationOutsideOfEventStream]; NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil]; - + if (!NSPointInRect(locationInView, [self bounds])) { _hasMouseOver = NO; - + [self sendMouseEventWithBlock:self.onMouseLeave locationInfo:[self locationInfoFromDraggingLocation:locationInWindow] modifierFlags:0 @@ -1367,67 +1367,8 @@ - (void)drawFocusRingMask #if TARGET_OS_OSX - (void)resetCursorRects { - NSCursor *cursor; - - switch (self.cursor) { - case RCTCursorArrow: - cursor = [NSCursor arrowCursor]; - break; - case RCTCursorClosedHand: - cursor = [NSCursor closedHandCursor]; - break; - case RCTCursorContextualMenu: - cursor = [NSCursor contextualMenuCursor]; - break; - case RCTCursorCrosshair: - cursor = [NSCursor crosshairCursor]; - break; - case RCTCursorDisappearingItem: - cursor = [NSCursor disappearingItemCursor]; - break; - case RCTCursorDragCopy: - cursor = [NSCursor dragCopyCursor]; - break; - case RCTCursorDragLink: - cursor = [NSCursor dragLinkCursor]; - break; - case RCTCursorIBeam: - cursor = [NSCursor IBeamCursor]; - break; - case RCTCursorIBeamCursorForVerticalLayout: - cursor = [NSCursor IBeamCursorForVerticalLayout]; - break; - case RCTCursorOpenHand: - cursor = [NSCursor openHandCursor]; - break; - case RCTCursorOperationNotAllowed: - cursor = [NSCursor operationNotAllowedCursor]; - break; - case RCTCursorPointingHand: - cursor = [NSCursor pointingHandCursor]; - break; - case RCTCursorResizeDown: - cursor = [NSCursor resizeDownCursor]; - break; - case RCTCursorResizeLeft: - cursor = [NSCursor resizeLeftCursor]; - break; - case RCTCursorResizeLeftRight: - cursor = [NSCursor resizeLeftRightCursor]; - break; - case RCTCursorResizeRight: - cursor = [NSCursor resizeRightCursor]; - break; - case RCTCursorResizeUp: - cursor = [NSCursor resizeUpCursor]; - break; - case RCTCursorResizeUpDown: - cursor = [NSCursor resizeUpDownCursor]; - break; - } - [self discardCursorRects]; - + NSCursor *cursor = [RCTConvert NSCursor:self.cursor]; if (cursor) { [self addCursorRect:self.bounds cursor:cursor]; } @@ -1460,7 +1401,7 @@ - (void)updateTrackingAreas if (_trackingArea) { [self removeTrackingArea:_trackingArea]; } - + if (self.onMouseEnter || self.onMouseLeave) { _trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:NSTrackingActiveAlways|NSTrackingMouseEnteredAndExited @@ -1468,7 +1409,7 @@ - (void)updateTrackingAreas userInfo:nil]; [self addTrackingArea:_trackingArea]; } - + [super updateTrackingAreas]; } @@ -1494,7 +1435,7 @@ - (NSDictionary*)locationInfoFromEvent:(NSEvent*)event { NSPoint locationInWindow = event.locationInWindow; NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil]; - + return @{@"screenX": @(locationInWindow.x), @"screenY": @(locationInWindow.y), @"clientX": @(locationInView.x), @@ -1525,15 +1466,15 @@ - (void)sendMouseEventWithBlock:(RCTDirectEventBlock)block if (modifierFlags & NSEventModifierFlagCommand) { body[@"metaKey"] = @YES; } - + if (locationInfo) { [body addEntriesFromDictionary:locationInfo]; } - + if (additionalData) { [body addEntriesFromDictionary:additionalData]; } - + block(body); } @@ -1563,7 +1504,7 @@ - (NSDictionary*)dataTransferInfoFromPastboard:(NSPasteboard*)pastboard MIMETypeString = (__bridge_transfer NSString *)MIMEType; } } - + NSNumber *fileSizeValue = nil; NSError *fileSizeError = nil; BOOL success = [fileURL getResourceValue:&fileSizeValue @@ -1575,11 +1516,11 @@ - (NSDictionary*)dataTransferInfoFromPastboard:(NSPasteboard*)pastboard @"uri": RCTNullIfNil(fileURL.absoluteString), @"size": success ? fileSizeValue : (id)kCFNull }]; - + [items addObject:@{@"kind": @"file", @"type": RCTNullIfNil(MIMETypeString), }]; - + [types addObject:RCTNullIfNil(MIMETypeString)]; } } @@ -1592,7 +1533,7 @@ - (NSDictionary*)dataTransferInfoFromPastboard:(NSPasteboard*)pastboard - (NSDictionary*)locationInfoFromDraggingLocation:(NSPoint)locationInWindow { NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil]; - + return @{@"screenX": @(locationInWindow.x), @"screenY": @(locationInWindow.y), @"clientX": @(locationInView.x), @@ -1604,7 +1545,7 @@ - (NSDragOperation)draggingEntered:(id )sender { NSPasteboard *pboard = sender.draggingPasteboard; NSDragOperation sourceDragMask = sender.draggingSourceOperationMask; - + [self sendMouseEventWithBlock:self.onDragEnter locationInfo:[self locationInfoFromDraggingLocation:sender.draggingLocation] modifierFlags:0 diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 16d37212237650..c1a1b424505a53 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -21,6 +21,10 @@ #import "RCTView.h" #import "UIView+React.h" +#if TARGET_OS_OSX // TODO(macOS GH#774) +#import "RCTCursor.h" +#endif // TODO(macOS GH774) + #if !TARGET_OS_OSX // TODO(macOS GH#774) @implementation RCTConvert (UIAccessibilityTraits)