Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Commit

Permalink
feat: add "cursor" prop to RCTView
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Aug 14, 2019
1 parent e63b776 commit d679a7c
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 54 deletions.
5 changes: 5 additions & 0 deletions @types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2093,6 +2093,11 @@ export interface ViewProps
*/
pointerEvents?: "box-none" | "none" | "box-only" | "auto";

/**
* The cursor image to show while the mouse is in inside this view.
*/
cursor?: "inherit" | "none" | "default" | "pointer" | "text" | "move" | "grab" | "grabbing"

/**
*
* This is a special performance property exposed by RCTView and is useful for scrolling content when there are many subviews,
Expand Down
10 changes: 10 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,16 @@ module.exports = {
onDrop: PropTypes.func,
onContextMenu: PropTypes.func,
onContextMenuItemClick: PropTypes.func,
cursor: PropTypes.oneOf([
'inherit',
'none',
'default',
'pointer',
'text',
'move',
'grab',
'grabbing',
]),
/**
* Mapped to toolTip property of NSView. Used to show extra information when
* mouse hovering.
Expand Down
1 change: 1 addition & 0 deletions Libraries/Text/TextInput/RCTBaseTextInputView.m
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
_eventDispatcher = bridge.eventDispatcher;

self.clipsToBounds = YES;
self.cursor = RCTCursorText;
}

return self;
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Text/TextInput/Singleline/RCTUITextField.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ - (void)textDidEndEditing:(NSNotification *)notification
}
}

// Do nothing here, as it messes with RCTWindow cursor support.
- (void)resetCursorRects {}

- (BOOL)becomeFirstResponder
{
if ([super becomeFirstResponder]) {
Expand Down
2 changes: 2 additions & 0 deletions React/Base/RCTConvert.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import <React/RCTAnimationType.h>
#import <React/RCTBlendMode.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTCursor.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTPointerEvents.h>
Expand Down Expand Up @@ -114,6 +115,7 @@ typedef BOOL css_backface_visibility_t;
+ (RCTAnimationType)RCTAnimationType:(id)json;
+ (RCTBlendMode)RCTBlendMode:(id)json;
+ (RCTBorderStyle)RCTBorderStyle:(id)json;
+ (RCTCursor)RCTCursor:(id)json;
+ (RCTTextDecorationLineType)RCTTextDecorationLineType:(id)json;

@end
Expand Down
11 changes: 11 additions & 0 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,17 @@ + (NSPropertyList)NSPropertyList:(id)json
@"overlay": @(RCTBlendModeOverlay),
}), RCTBlendModeNone, integerValue)

RCT_ENUM_CONVERTER(RCTCursor, (@{
@"inherit": @(RCTCursorInherit),
@"none": @(RCTCursorNone),
@"default": @(RCTCursorDefault),
@"pointer": @(RCTCursorPointer),
@"text": @(RCTCursorText),
@"move": @(RCTCursorMove),
@"grab": @(RCTCursorGrab),
@"grabbing": @(RCTCursorGrabbing),
}), RCTCursorInherit, integerValue)

@end

@interface RCTImageSource (Packager)
Expand Down
42 changes: 0 additions & 42 deletions React/Modules/RCTCursorManager.m

This file was deleted.

14 changes: 6 additions & 8 deletions React/React.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@
70347E2122FF448600A4F09B /* RCTGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2022FF448600A4F09B /* RCTGradient.m */; };
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 70347E2222FF44B700A4F09B /* RCTGradientManager.m */; };
70347E2522FF44C100A4F09B /* RCTGradientManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 70347E2422FF44BF00A4F09B /* RCTGradientManager.h */; };
7045C32223038208000AB285 /* RCTCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7045C32123038208000AB285 /* RCTCursor.h */; };
7045C323230384FE000AB285 /* RCTCursor.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 7045C32123038208000AB285 /* RCTCursor.h */; };
705EDE2922107DD0000CAA67 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 705EDE2722107DD0000CAA67 /* Utils.h */; };
705EDE2A22107DD0000CAA67 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2822107DD0000CAA67 /* Utils.cpp */; };
705EDE2C221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */; };
Expand Down Expand Up @@ -599,8 +601,6 @@
CF2731C11E7B8DE40044CA4F /* RCTDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */; };
D426ACD020D57642003B4C4D /* RCTAppearance.h in Headers */ = {isa = PBXBuildFile; fileRef = D426ACCF20D57642003B4C4D /* RCTAppearance.h */; };
D426ACD220D57659003B4C4D /* RCTAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = D426ACD120D57659003B4C4D /* RCTAppearance.m */; };
D49593DE202C937C00A7694B /* RCTCursorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D49593D8202C937B00A7694B /* RCTCursorManager.h */; };
D49593DF202C937C00A7694B /* RCTCursorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D49593D9202C937B00A7694B /* RCTCursorManager.m */; };
D49593E0202C937C00A7694B /* RCTMenuManager.h in Headers */ = {isa = PBXBuildFile; fileRef = D49593DA202C937B00A7694B /* RCTMenuManager.h */; };
D49593E1202C937C00A7694B /* RCTMenuManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D49593DB202C937C00A7694B /* RCTMenuManager.m */; };
D49593E7202C970600A7694B /* YGNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D49593E3202C96FF00A7694B /* YGNode.h */; };
Expand Down Expand Up @@ -704,6 +704,7 @@
dstPath = include/React;
dstSubfolderSpec = 16;
files = (
7045C323230384FE000AB285 /* RCTCursor.h in Copy Headers */,
70CDF31522F9E3BE00ECA452 /* RCTBlendMode.h in Copy Headers */,
7095B6D02247C86300BE2245 /* RCTFieldEditor.h in Copy Headers */,
702B7FFF221C88D70027174A /* RCTWindow.h in Copy Headers */,
Expand Down Expand Up @@ -1286,6 +1287,7 @@
70347E2022FF448600A4F09B /* RCTGradient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradient.m; sourceTree = "<group>"; };
70347E2222FF44B700A4F09B /* RCTGradientManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTGradientManager.m; sourceTree = "<group>"; };
70347E2422FF44BF00A4F09B /* RCTGradientManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTGradientManager.h; sourceTree = "<group>"; };
7045C32123038208000AB285 /* RCTCursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTCursor.h; sourceTree = "<group>"; };
705EDE2722107DD0000CAA67 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = "<group>"; };
705EDE2822107DD0000CAA67 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = "<group>"; };
705EDE2B221082CB000CAA67 /* RCTSurfaceSizeMeasureMode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTSurfaceSizeMeasureMode.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1362,8 +1364,6 @@
CF2731BF1E7B8DE40044CA4F /* RCTDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDeviceInfo.m; sourceTree = "<group>"; };
D426ACCF20D57642003B4C4D /* RCTAppearance.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTAppearance.h; sourceTree = "<group>"; };
D426ACD120D57659003B4C4D /* RCTAppearance.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTAppearance.m; sourceTree = "<group>"; };
D49593D8202C937B00A7694B /* RCTCursorManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCursorManager.h; sourceTree = "<group>"; };
D49593D9202C937B00A7694B /* RCTCursorManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCursorManager.m; sourceTree = "<group>"; };
D49593DA202C937B00A7694B /* RCTMenuManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMenuManager.h; sourceTree = "<group>"; };
D49593DB202C937C00A7694B /* RCTMenuManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMenuManager.m; sourceTree = "<group>"; };
D49593E3202C96FF00A7694B /* YGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNode.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1562,8 +1562,6 @@
13B07FE01A69315300A75B9A /* Modules */ = {
isa = PBXGroup;
children = (
D49593D8202C937B00A7694B /* RCTCursorManager.h */,
D49593D9202C937B00A7694B /* RCTCursorManager.m */,
D49593DA202C937B00A7694B /* RCTMenuManager.h */,
D49593DB202C937C00A7694B /* RCTMenuManager.m */,
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
Expand Down Expand Up @@ -1638,6 +1636,7 @@
13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */,
130443C31E401A8C00D93A67 /* RCTConvert+Transform.h */,
130443C41E401A8C00D93A67 /* RCTConvert+Transform.m */,
7045C32123038208000AB285 /* RCTCursor.h */,
133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */,
133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */,
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */,
Expand Down Expand Up @@ -2251,6 +2250,7 @@
3D80DA1E1DF820620028D040 /* RCTNetworkTask.h in Headers */,
657734841EE834C900A0E9EA /* RCTInspectorDevServerHelper.h in Headers */,
3D80DA1F1DF820620028D040 /* RCTPushNotificationManager.h in Headers */,
7045C32223038208000AB285 /* RCTCursor.h in Headers */,
3D80DA211DF820620028D040 /* RCTBridge.h in Headers */,
13F880381E296D2800C3C7A1 /* JSCWrapper.h in Headers */,
3D80DA221DF820620028D040 /* RCTBridge+Private.h in Headers */,
Expand Down Expand Up @@ -2382,7 +2382,6 @@
657734901EE8354A00A0E9EA /* RCTInspectorPackagerConnection.h in Headers */,
3D7BFD1D1EA8E351008DFB7A /* RCTPackagerConnection.h in Headers */,
3D80DA811DF820620028D040 /* RCTSegmentedControl.h in Headers */,
D49593DE202C937C00A7694B /* RCTCursorManager.h in Headers */,
599FAA3C1FB274980058CCF6 /* RCTSurfaceRootShadowView.h in Headers */,
3D80DA821DF820620028D040 /* RCTSegmentedControlManager.h in Headers */,
3D80DA831DF820620028D040 /* RCTShadowView.h in Headers */,
Expand Down Expand Up @@ -2849,7 +2848,6 @@
3D7BFD1F1EA8E351008DFB7A /* RCTPackagerConnection.mm in Sources */,
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */,
70347E2322FF44B700A4F09B /* RCTGradientManager.m in Sources */,
D49593DF202C937C00A7694B /* RCTCursorManager.m in Sources */,
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
130443C61E401A8C00D93A67 /* RCTConvert+Transform.m in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions React/Views/NSView+React.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <AppKit/AppKit.h>

#import <React/RCTComponent.h>
#import <React/RCTCursor.h>
#import <yoga/YGEnums.h>

@class RCTShadowView;
Expand Down Expand Up @@ -119,6 +120,9 @@
/** Populate the `layer` ivar when nil */
- (void)ensureLayerExists;

/** Default implementation to avoid crashes */
- (RCTCursor)cursor;

@end

@interface CALayer (React)
Expand Down
6 changes: 6 additions & 0 deletions React/Views/NSView+React.m
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ - (void)setTransform:(__unused CATransform3D)transform
// override "displayLayer:", and apply the transform there.
}

// Default implementation for React-managed views.
- (RCTCursor)cursor
{
return RCTCursorInherit;
}

@end

#pragma mark -
Expand Down
19 changes: 16 additions & 3 deletions React/Modules/RCTCursorManager.h → React/Views/RCTCursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <AppKit/AppKit.h>
#import "RCTBridgeModule.h"
#import <Foundation/Foundation.h>

@interface NSCursor (Private)

+ (NSCursor *)_moveCursor;

@interface RCTCursorManager : NSObject <RCTBridgeModule>
@end

typedef NS_ENUM(NSInteger, RCTCursor) {
RCTCursorInherit = 0,
RCTCursorNone,
RCTCursorDefault,
RCTCursorPointer,
RCTCursorText,
RCTCursorMove,
RCTCursorGrab,
RCTCursorGrabbing,
};
6 changes: 6 additions & 0 deletions React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#import <React/RCTBorderStyle.h>
#import <React/RCTComponent.h>
#import <React/RCTCursor.h>
#import <React/RCTPointerEvents.h>
#import <React/RCTView.h>

Expand Down Expand Up @@ -129,4 +130,9 @@
@property (nonatomic, copy) RCTDirectEventBlock onDrop;
@property (nonatomic, copy) RCTDirectEventBlock onContextMenuItemClick;

/**
* The cursor image to show while the mouse is inside this view.
*/
@property (nonatomic, assign) RCTCursor cursor;

@end
3 changes: 2 additions & 1 deletion React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ - (RCTShadowView *)shadowView
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_EXPORT_VIEW_PROPERTY(cursor, RCTCursor)
RCT_EXPORT_VIEW_PROPERTY(toolTip, NSString)
RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView)
{
if (json) {
Expand Down
53 changes: 53 additions & 0 deletions React/Views/RCTWindow.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "RCTWindow.h"

#import "RCTUtils.h"
#import "RCTCursor.h"
#import "RCTMouseEvent.h"
#import "RCTTouchEvent.h"
#import "RCTFieldEditor.h"
Expand All @@ -19,6 +20,7 @@ @implementation RCTWindow
{
RCTBridge *_bridge;

RCTCursor _lastCursor;
NSMutableDictionary *_mouseInfo;
NSView *_clickTarget;
NSEventType _clickType;
Expand Down Expand Up @@ -256,6 +258,53 @@ - (NSView *)hitTest:(NSPoint)point withEvent:(NSEvent *)event
return targetView;
}

static NSCursor *NSCursorForRCTCursor(RCTCursor cursor)
{
switch (cursor) {
case RCTCursorDefault: return NSCursor.arrowCursor;
case RCTCursorPointer: return NSCursor.pointingHandCursor;
case RCTCursorText: return NSCursor.IBeamCursor;
case RCTCursorMove: return NSCursor._moveCursor;
case RCTCursorGrab: return NSCursor.openHandCursor;
case RCTCursorGrabbing: return NSCursor.closedHandCursor;
default: return nil;
}
}

// HACK: Do nothing here to prevent AppKit default behavior of updating the cursor whenever a view moves.
- (void)_setCursorForMouseLocation:(__unused CGPoint)point {}

- (void)_updateCursor:(NSView *)view
{
// Find the nearest React-managed view with a defined cursor.
while (view) {
RCTCursor cursor = view.cursor;
if (cursor != RCTCursorInherit) {
if (cursor == _lastCursor) {
return;
}

if (cursor == RCTCursorNone) {
[NSCursor hide];
} else {
[NSCursor unhide];
[NSCursorForRCTCursor(cursor) set];
}

_lastCursor = cursor;
return;
}
view = view.superview;
}
// Reset the cursor to its default.
if (_lastCursor != RCTCursorDefault) {
_lastCursor = RCTCursorDefault;

[NSCursor unhide];
[NSCursor.arrowCursor set];
}
}

- (void)_setHoverTarget:(NSView *)view
{
NSNumber *target = view.reactTag;
Expand All @@ -269,6 +318,9 @@ - (void)_setHoverTarget:(NSView *)view

_hoverTarget = nil;
[self _sendMouseEvent:@"mouseOut"];
if (!view) {
[self _updateCursor:nil];
}
}
}

Expand All @@ -279,6 +331,7 @@ - (void)_setHoverTarget:(NSView *)view
if (_hoverTarget == nil) {
_hoverTarget = view;
[self _sendMouseEvent:@"mouseOver"];
[self _updateCursor:view];

// Ensure "mouseMove" events have no "relatedTarget" property.
_mouseInfo[@"relatedTarget"] = nil;
Expand Down

0 comments on commit d679a7c

Please sign in to comment.