Skip to content
6 changes: 4 additions & 2 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,10 @@ var TouchableOpacity = createReactClass({
onResponderMove={this.touchableHandleResponderMove}
onResponderRelease={this.touchableHandleResponderRelease}
onResponderTerminate={this.touchableHandleResponderTerminate}
onMouseEnter={this.props.onMouseEnter}
onMouseLeave={this.props.onMouseLeave}
onMouseMove={this.props.onMouseMove}
onMouseOver={this.props.onMouseOver}
onMouseOut={this.props.onMouseOut}
onContextMenu={this.props.onContextMenu}
onContextMenuItemClick={this.props.onContextMenuItemClick}
contextMenu={this.props.contextMenu}>
{this.props.children}
Expand Down
12 changes: 8 additions & 4 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ const TouchableWithoutFeedback = createReactClass({
* views.
*/
hitSlop: EdgeInsetsPropType,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onMouseMove: PropTypes.func,
onMouseOver: PropTypes.func,
onMouseOut: PropTypes.func,
onContextMenu: PropTypes.func,
onContextMenuItemClick: PropTypes.func,
contextMenu: PropTypes.array,
},
Expand Down Expand Up @@ -213,8 +215,10 @@ const TouchableWithoutFeedback = createReactClass({
onResponderMove: this.touchableHandleResponderMove,
onResponderRelease: this.touchableHandleResponderRelease,
onResponderTerminate: this.touchableHandleResponderTerminate,
onMouseEnter: this.props.onMouseEnter,
onMouseLeave: this.props.onMouseLeave,
onMouseMove: this.props.onMouseMove,
onMouseOver: this.props.onMouseOver,
onMouseOut: this.props.onMouseOut,
onContextMenu: this.props.onContextMenu,
onContextMenuItemClick: this.props.onContextMenuItemClick,
contextMenu: this.props.contextMenu,
style,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ exports[`TouchableHighlight renders correctly 1`] = `
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onMouseEnter={undefined}
onMouseLeave={undefined}
onMouseOver={undefined}
onMouseOut={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
Expand Down
6 changes: 4 additions & 2 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ ReactNativeViewAttributes.UIView = {
renderToHardwareTextureAndroid: true,
shouldRasterizeIOS: true,
onLayout: true,
onMouseEnter: true,
onMouseLeave: true,
onMouseMove: true,
onMouseOver: true,
onMouseOut: true,
onContextMenu: true,
onAccessibilityTap: true,
onMagicTap: true,
collapsable: true,
Expand Down
6 changes: 4 additions & 2 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,13 @@ module.exports = {
* Desktop specific events
* @platform macos
*/
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onMouseMove: PropTypes.func,
onMouseOver: PropTypes.func,
onMouseOut: PropTypes.func,
onDragEnter: PropTypes.func,
onDragLeave: PropTypes.func,
onDrop: PropTypes.func,
onContextMenu: PropTypes.func,
onContextMenuItemClick: PropTypes.func,
/**
* Mapped to toolTip property of NSView. Used to show extra information when
Expand Down
60 changes: 24 additions & 36 deletions RNTester/RNTester/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,61 +21,41 @@
#import <React/RCTLinkingManager.h>
#import <React/RCTRootView.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTWindow.h>

@interface AppDelegate() <RCTBridgeDelegate, NSSearchFieldDelegate>

@end


@implementation AppDelegate

-(id)init
{
if(self = [super init]) {

// -- Init Window
NSRect contentSize = NSMakeRect(200, 500, 1000, 500);

self.window = [[NSWindow alloc] initWithContentRect:contentSize
styleMask:NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
NSWindowController *windowController = [[NSWindowController alloc] initWithWindow:self.window];

[[self window] setTitle:@"RNTester"];
[[self window] setTitleVisibility:NSWindowTitleHidden];
[windowController showWindow:self.window];

[windowController setShouldCascadeWindows:NO];
[windowController setWindowFrameAutosaveName:@"RNTester"];
[self setDefaultURL];

// -- Init Toolbar
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"mainToolbar"];
[toolbar setDelegate:self];
[toolbar setSizeMode:NSToolbarSizeModeRegular];

[self.window setToolbar:toolbar];

// -- Init Menu
[self setUpMainMenu];
}
return self;
NSToolbar *_toolbar;
}

- (void)applicationDidFinishLaunching:(NSNotification * __unused)aNotification
{
[self setDefaultURL];

_bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:@{@"argv": [self argv]}];

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:_bridge
moduleName:@"RNTesterApp"
initialProperties:nil];
_window = [[RCTWindow alloc] initWithBridge:_bridge
contentRect:NSMakeRect(200, 500, 1000, 500)
styleMask:(NSTitledWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask)
defer:NO];

_window.title = @"RNTester";
_window.titleVisibility = NSWindowTitleHidden;

[self setUpToolbar];
[self setUpMainMenu];

_window.contentView = [[RCTRootView alloc] initWithBridge:_bridge
moduleName:@"RNTesterApp"
initialProperties:nil];

[self.window setContentView:rootView];
[_window makeKeyAndOrderFront:nil];
}

- (void)setDefaultURL
Expand Down Expand Up @@ -105,6 +85,14 @@ - (void)loadSourceForBridge:(RCTBridge *)bridge
onComplete:loadCallback];
}

- (void)setUpToolbar
{
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"mainToolbar"];
toolbar.delegate = self;
toolbar.sizeMode = NSToolbarSizeModeRegular;
_window.toolbar = toolbar;
}

- (NSArray *)toolbarAllowedItemIdentifiers:(__unused NSToolbar *)toolbar
{
return @[NSToolbarFlexibleSpaceItemIdentifier, @"searchBar", NSToolbarFlexibleSpaceItemIdentifier, @"resetButton"];
Expand Down
4 changes: 2 additions & 2 deletions RNTester/js/DragnDropExample.macos.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class DragExample extends React.Component {
this.state.mouseOver ? 'orange' : 'white',
padding: 40, alignItems: 'center'}}
draggedTypes={['NSFilenamesPboardType']}
onMouseEnter={() => this.setState({mouseOver: true})}
onMouseLeave={() => this.setState({mouseOver: false})}
onMouseOver={() => this.setState({mouseOver: true})}
onMouseOut={() => this.setState({mouseOver: false})}
onDragEnter={() => this.setState({dragOver: true})}
onDragLeave={() => this.setState({dragOver: false})}
onDrop={(e) => this.setState({files: e.nativeEvent.files, dragOver: false})}>
Expand Down
23 changes: 23 additions & 0 deletions React/Base/RCTMouseEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <Foundation/Foundation.h>

#import <React/RCTEventDispatcher.h>

@interface RCTMouseEvent : NSObject <RCTEvent>

- (instancetype)initWithEventName:(NSString *)eventName
target:(NSNumber *)target
userInfo:(NSDictionary *)userInfo
coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER;

@property (readonly) NSTimeInterval timestamp;

@end
78 changes: 78 additions & 0 deletions React/Base/RCTMouseEvent.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "RCTMouseEvent.h"

#import "RCTAssert.h"

@implementation RCTMouseEvent
{
NSDictionary *_userInfo;
uint16_t _coalescingKey;
}

@synthesize eventName = _eventName;
@synthesize viewTag = _viewTag;

- (instancetype)initWithEventName:(NSString *)eventName
target:(NSNumber *)target
userInfo:(NSDictionary *)userInfo
coalescingKey:(uint16_t)coalescingKey
{
if (self = [super init]) {
_viewTag = target;
_userInfo = [NSDictionary dictionaryWithDictionary:userInfo];
_eventName = eventName;
_coalescingKey = coalescingKey;
}
return self;
}

RCT_NOT_IMPLEMENTED(- (instancetype)init)

#pragma mark - RCTEvent

- (BOOL)canCoalesce
{
return [_eventName isEqual:@"mouseMove"];
}

// We coalesce only move events, while holding some assumptions that seem reasonable but there are no explicit guarantees about them.
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
{
RCTAssert([newEvent isKindOfClass:[RCTMouseEvent class]], @"Mouse event cannot be coalesced with any other type of event, such as provided %@", newEvent);
return ((RCTMouseEvent *)newEvent).timestamp > self.timestamp ? newEvent : self;
}

+ (NSString *)moduleDotMethod
{
return @"RCTEventEmitter.receiveEvent";
}

- (NSArray *)arguments
{
return @[_viewTag, RCTNormalizeInputEventName(_eventName), _userInfo];
}

- (uint16_t)coalescingKey
{
return _coalescingKey;
}

- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p; name = %@; coalescing key = %hu>", [self class], self, _eventName, _coalescingKey];
}

- (NSTimeInterval)timestamp
{
return [_userInfo[@"timestamp"] doubleValue];
}

@end
34 changes: 24 additions & 10 deletions React/Base/RCTRootContentView.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "RCTRootViewInternal.h"
#import "RCTTouchHandler.h"
#import "RCTUIManager.h"
#import "RCTWindow.h"
#import "NSView+React.h"

@implementation RCTRootContentView
Expand All @@ -28,8 +29,6 @@ - (instancetype)initWithFrame:(CGRect)frame
_bridge = bridge;
self.reactTag = reactTag;
_sizeFlexibility = sizeFlexibility;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_touchHandler attachToView:self];
[_bridge.uiManager registerRootView:self];
}
return self;
Expand Down Expand Up @@ -85,15 +84,13 @@ - (void)updateAvailableSize
[_bridge.uiManager setAvailableSize:self.availableSize forRootView:self];
}

- (NSView *)hitTest:(CGPoint)point withEvent:(NSEvent *)event
- (NSView *)hitTest:(CGPoint)point
{
// The root content view itself should never receive touches
// NSView *hitView = [super hitTest:point withEvent:event];
// if (_passThroughTouches && hitView == self) {
// return nil;
// }
// return hitView;
return nil;
// Flip the coordinate system to top-left origin.
NSPoint convertedPoint = [self convertPoint:point fromView:nil];

NSView *hitView = [super hitTest:convertedPoint];
return _passThroughTouches && hitView == self ? nil : hitView;
}

- (void)invalidate
Expand All @@ -108,4 +105,21 @@ - (void)invalidate
//}
}

- (void)viewDidMoveToWindow
{
if (self.window == nil) {
return;
}
// RCTWindow handles all touches within
if ([self.window isKindOfClass:RCTWindow.class] == NO) {
if (_touchHandler == nil) {
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_touchHandler attachToView:self];
}
} else if (_touchHandler) {
[_touchHandler detachFromView:self];
_touchHandler = nil;
}
}

@end
13 changes: 6 additions & 7 deletions React/Base/RCTRootView.m
Original file line number Diff line number Diff line change
Expand Up @@ -333,15 +333,14 @@ - (void)setSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
_contentView.sizeFlexibility = _sizeFlexibility;
}

- (NSView *)hitTest:(CGPoint)point withEvent:(NSEvent *)event
- (NSView *)hitTest:(CGPoint)point
{
// The root view itself should never receive touches
// NSView *hitView = [super hitTest:point withEvent:event];
// if (self.passThroughTouches && hitView == self) {
// return nil;
// }
// return hitView;
return nil;
NSView *hitView = [super hitTest:point];
if (self.passThroughTouches && hitView == self) {
return nil;
}
return hitView;
}

- (void)setAppProperties:(NSDictionary *)appProperties
Expand Down
8 changes: 4 additions & 4 deletions React/Base/RCTTouchHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ @implementation RCTTouchHandler
CFTimeInterval _mostRecentEnqueueJS;

/*
* Storing tag to dispatch mouseEnter and mouseLeave events
* Storing tag to dispatch mouseOver and mouseOut events
*/
NSNumber *_currentMouseOverTag;
}
Expand Down Expand Up @@ -336,13 +336,13 @@ - (void)mouseMoved:(NSEvent *)event
}
if (_currentMouseOverTag != reactTag && _currentMouseOverTag.intValue > 0) {
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:@[_currentMouseOverTag, @"topMouseLeave"]];
args:@[_currentMouseOverTag, @"topMouseOut"]];
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:@[reactTag, @"topMouseEnter"]];
args:@[reactTag, @"topMouseOver"]];
_currentMouseOverTag = reactTag;
} else if (_currentMouseOverTag == 0) {
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:@[reactTag, @"topMouseEnter"]];
args:@[reactTag, @"topMouseOver"]];
_currentMouseOverTag = reactTag;
}
}
Expand Down
Loading