Skip to content

Commit

Permalink
Move mouse event implementations to RCTUIView class
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Gleitman committed Jun 14, 2024
1 parent f8498f1 commit 0d77cef
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 137 deletions.
9 changes: 9 additions & 0 deletions packages/react-native/React/Base/RCTUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ CGPathRef UIBezierPathCreateCGPathRef(UIBezierPath *path);

- (void)setNeedsDisplay;

// Methods related to mouse events
- (NSDictionary*)locationInfoFromDraggingLocation:(NSPoint)locationInWindow;
- (NSDictionary*)locationInfoFromEvent:(NSEvent*)event;

- (void)sendMouseEventWithBlock:(RCTDirectEventBlock)block
locationInfo:(NSDictionary*)locationInfo
modifierFlags:(NSEventModifierFlags)modifierFlags
additionalData:(NSDictionary*)additionalData;

// FUTURE: When Xcode 14 is no longer supported (CI is building with Xcode 15), we can remove this override since it's now declared on NSView
@property BOOL clipsToBounds;
@property (nonatomic, copy) NSColor *backgroundColor;
Expand Down
135 changes: 135 additions & 0 deletions packages/react-native/React/Base/macOS/RCTUIKit.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import <React/RCTUIKit.h>

#import <React/RCTAssert.h>
#import <UIView+React.h>

#import <objc/runtime.h>

Expand Down Expand Up @@ -214,6 +215,7 @@ @implementation RCTUIView
@private
NSColor *_backgroundColor;
BOOL _clipsToBounds;
BOOL _hasMouseOver;
BOOL _userInteractionEnabled;
BOOL _mouseDownCanMoveWindow;
}
Expand Down Expand Up @@ -287,9 +289,142 @@ - (BOOL)isFirstResponder {

- (void)viewDidMoveToWindow
{
// Subscribe to view bounds changed notification so that the view can be notified when a
// scroll event occurs either due to trackpad/gesture based scrolling or a scrollwheel event
// both of which would not cause the mouseExited to be invoked.

if ([self window] == nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSViewBoundsDidChangeNotification
object:nil];
}
else if ([[self enclosingScrollView] contentView] != nil) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(viewBoundsChanged:)
name:NSViewBoundsDidChangeNotification
object:[[self enclosingScrollView] contentView]];
}

[self reactViewDidMoveToWindow]; // [macOS] Github#1412

[self didMoveToWindow];
}

- (void)viewBoundsChanged:(NSNotification*)__unused inNotif
{
// When an enclosing scrollview is scrolled using the scrollWheel or trackpad,
// 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
additionalData:nil];
}
}
else if (_hasMouseOver && self.onMouseLeave)
{
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
additionalData:nil];
}
}
}

- (NSDictionary*)locationInfoFromDraggingLocation:(NSPoint)locationInWindow
{
NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil];

return @{@"screenX": @(locationInWindow.x),
@"screenY": @(locationInWindow.y),
@"clientX": @(locationInView.x),
@"clientY": @(locationInView.y)
};
}

- (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),
@"clientY": @(locationInView.y)
};
}

- (void)mouseEntered:(NSEvent *)event
{
_hasMouseOver = YES;
[self sendMouseEventWithBlock:self.onMouseEnter
locationInfo:[self locationInfoFromEvent:event]
modifierFlags:event.modifierFlags
additionalData:nil];
}

- (void)mouseExited:(NSEvent *)event
{
_hasMouseOver = NO;
[self sendMouseEventWithBlock:self.onMouseLeave
locationInfo:[self locationInfoFromEvent:event]
modifierFlags:event.modifierFlags
additionalData:nil];
}

- (void)sendMouseEventWithBlock:(RCTDirectEventBlock)block
locationInfo:(NSDictionary*)locationInfo
modifierFlags:(NSEventModifierFlags)modifierFlags
additionalData:(NSDictionary*)additionalData
{
if (block == nil) {
return;
}

NSMutableDictionary *body = [NSMutableDictionary new];

if (modifierFlags & NSEventModifierFlagShift) {
body[@"shiftKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagControl) {
body[@"ctrlKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagOption) {
body[@"altKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagCommand) {
body[@"metaKey"] = @YES;
}

if (locationInfo) {
[body addEntriesFromDictionary:locationInfo];
}

if (additionalData) {
[body addEntriesFromDictionary:additionalData];
}

block(body);
}

- (BOOL)mouseDownCanMoveWindow{
return _mouseDownCanMoveWindow;
}
Expand Down
137 changes: 0 additions & 137 deletions packages/react-native/React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ @implementation RCTView {
id<RCTEventDispatcherProtocol> _eventDispatcher; // [macOS]
#if TARGET_OS_OSX // [macOS
NSTrackingArea *_trackingArea;
BOOL _hasMouseOver;
BOOL _mouseDownCanMoveWindow;
#endif // macOS]
NSMutableDictionary<NSString *, NSDictionary *> *accessibilityActionsNameMap;
Expand Down Expand Up @@ -778,67 +777,7 @@ -(void)didUpdateShadow
[self setShadow:shadow];
}

- (void)viewDidMoveToWindow
{
// Subscribe to view bounds changed notification so that the view can be notified when a
// scroll event occurs either due to trackpad/gesture based scrolling or a scrollwheel event
// both of which would not cause the mouseExited to be invoked.

if ([self window] == nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSViewBoundsDidChangeNotification
object:nil];
}
else if ([[self enclosingScrollView] contentView] != nil) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(viewBoundsChanged:)
name:NSViewBoundsDidChangeNotification
object:[[self enclosingScrollView] contentView]];
}

[self reactViewDidMoveToWindow]; // [macOS] Github#1412

[super viewDidMoveToWindow];
}

- (void)viewBoundsChanged:(NSNotification*)__unused inNotif
{
// When an enclosing scrollview is scrolled using the scrollWheel or trackpad,
// 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
additionalData:nil];
}
}
else if (_hasMouseOver && self.onMouseLeave)
{
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
additionalData:nil];
}
}
}
#endif // macOS]

#pragma mark - Statics for dealing with layoutGuides
Expand Down Expand Up @@ -1549,24 +1488,6 @@ - (void)updateTrackingAreas
[super updateTrackingAreas];
}

- (void)mouseEntered:(NSEvent *)event
{
_hasMouseOver = YES;
[self sendMouseEventWithBlock:self.onMouseEnter
locationInfo:[self locationInfoFromEvent:event]
modifierFlags:event.modifierFlags
additionalData:nil];
}

- (void)mouseExited:(NSEvent *)event
{
_hasMouseOver = NO;
[self sendMouseEventWithBlock:self.onMouseLeave
locationInfo:[self locationInfoFromEvent:event]
modifierFlags:event.modifierFlags
additionalData:nil];
}

- (BOOL)mouseDownCanMoveWindow{
return _mouseDownCanMoveWindow;
}
Expand All @@ -1579,53 +1500,6 @@ - (BOOL)allowsVibrancy {
return _allowsVibrancyInternal;
}

- (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),
@"clientY": @(locationInView.y)
};
}

- (void)sendMouseEventWithBlock:(RCTDirectEventBlock)block
locationInfo:(NSDictionary*)locationInfo
modifierFlags:(NSEventModifierFlags)modifierFlags
additionalData:(NSDictionary*)additionalData
{
if (block == nil) {
return;
}

NSMutableDictionary *body = [NSMutableDictionary new];

if (modifierFlags & NSEventModifierFlagShift) {
body[@"shiftKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagControl) {
body[@"ctrlKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagOption) {
body[@"altKey"] = @YES;
}
if (modifierFlags & NSEventModifierFlagCommand) {
body[@"metaKey"] = @YES;
}

if (locationInfo) {
[body addEntriesFromDictionary:locationInfo];
}

if (additionalData) {
[body addEntriesFromDictionary:additionalData];
}

block(body);
}

- (NSDictionary*)dataTransferInfoFromPasteboard:(NSPasteboard*)pasteboard
{
NSArray *fileNames = [pasteboard propertyListForType:NSFilenamesPboardType] ?: @[];
Expand Down Expand Up @@ -1704,17 +1578,6 @@ - (NSDictionary*)dataTransferInfoFromPasteboard:(NSPasteboard*)pasteboard
@"types": types}};
}

- (NSDictionary*)locationInfoFromDraggingLocation:(NSPoint)locationInWindow
{
NSPoint locationInView = [self convertPoint:locationInWindow fromView:nil];

return @{@"screenX": @(locationInWindow.x),
@"screenY": @(locationInWindow.y),
@"clientX": @(locationInView.x),
@"clientY": @(locationInView.y)
};
}

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
NSPasteboard *pboard = sender.draggingPasteboard;
Expand Down

0 comments on commit 0d77cef

Please sign in to comment.