16
16
17
17
static const int kDefaultWindowFramebuffer = 0 ;
18
18
19
+ namespace {
20
+
21
+ /* *
22
+ * State tracking for mouse events, to adapt between the events coming from the system and the
23
+ * events that the embedding API expects.
24
+ */
25
+ struct MouseState {
26
+ /* *
27
+ * Whether or not a kAdd event has been sent (or sent again since the last kRemove if tracking is
28
+ * enabled). Used to determine whether to send a kAdd event before sending an incoming mouse
29
+ * event, since Flutter expects pointers to be added before events are sent for them.
30
+ */
31
+ bool flutter_state_is_added = false ;
32
+
33
+ /* *
34
+ * Whether or not a kDown has been sent since the last kAdd/kUp.
35
+ */
36
+ bool flutter_state_is_down = false ;
37
+
38
+ /* *
39
+ * Whether or not mouseExited: was received while a button was down. Cocoa's behavior when
40
+ * dragging out of a tracked area is to send an exit, then keep sending drag events until the last
41
+ * button is released. If it was released inside the view, mouseEntered: is sent the next time the
42
+ * mouse moves. Flutter doesn't expect to receive events after a kRemove, so the kRemove for the
43
+ * exit needs to be delayed until after the last mouse button is released.
44
+ */
45
+ bool has_pending_exit = false ;
46
+
47
+ /* *
48
+ * The currently pressed buttons, as represented in FlutterPointerEvent.
49
+ */
50
+ int64_t buttons = 0 ;
51
+
52
+ /* *
53
+ * Resets all state to default values.
54
+ */
55
+ void Reset () {
56
+ flutter_state_is_added = false ;
57
+ flutter_state_is_down = false ;
58
+ has_pending_exit = false ;
59
+ buttons = 0 ;
60
+ }
61
+ };
62
+
63
+ } // namespace
64
+
19
65
#pragma mark - Private interface declaration.
20
66
21
67
/* *
@@ -34,12 +80,9 @@ @interface FLEViewController ()
34
80
@property (nonatomic ) NSTrackingArea * trackingArea;
35
81
36
82
/* *
37
- * Whether or not a kAdd event has been sent for the mouse (or sent again since
38
- * the last kRemove was sent if tracking is enabled). Used to determine whether
39
- * to send an Add event before sending an incoming mouse event, since Flutter
40
- * expects a pointers to be added before events are sent for them.
83
+ * The current state of the mouse and the sent mouse events.
41
84
*/
42
- @property (nonatomic ) BOOL mouseCurrentlyAdded ;
85
+ @property (nonatomic ) MouseState mouseState ;
43
86
44
87
/* *
45
88
* Updates |trackingArea| for the current tracking settings, creating it with
@@ -81,6 +124,13 @@ - (void)makeResourceContextCurrent;
81
124
*/
82
125
- (void )handlePlatformMessage : (const FlutterPlatformMessage*)message ;
83
126
127
+ /* *
128
+ * Calls dispatchMouseEvent:phase: with a phase determined by self.mouseState.
129
+ *
130
+ * mouseState.buttons should be updated before calling this method.
131
+ */
132
+ - (void )dispatchMouseEvent : (nonnull NSEvent *)event ;
133
+
84
134
/* *
85
135
* Converts |event| to a FlutterPointerEvent with the given phase, and sends it to the engine.
86
136
*/
@@ -447,10 +497,24 @@ - (void)handlePlatformMessage:(const FlutterPlatformMessage*)message {
447
497
}
448
498
}
449
499
500
+ - (void )dispatchMouseEvent : (nonnull NSEvent *)event {
501
+ FlutterPointerPhase phase = _mouseState.buttons == 0
502
+ ? (_mouseState.flutter_state_is_down ? kUp : kHover )
503
+ : (_mouseState.flutter_state_is_down ? kMove : kDown );
504
+ [self dispatchMouseEvent: event phase: phase];
505
+ }
506
+
450
507
- (void )dispatchMouseEvent : (NSEvent *)event phase : (FlutterPointerPhase)phase {
508
+ // There are edge cases where the system will deliver enter out of order relative to other
509
+ // events (e.g., drag out and back in, release, then click; mouseDown: will be called before
510
+ // mouseEntered:). Discard those events, since the add will already have been synthesized.
511
+ if (_mouseState.flutter_state_is_added && phase == kAdd ) {
512
+ return ;
513
+ }
514
+
451
515
// If a pointer added event hasn't been sent, synthesize one using this event for the basic
452
516
// information.
453
- if (!_mouseCurrentlyAdded && phase != kAdd ) {
517
+ if (!_mouseState. flutter_state_is_added && phase != kAdd ) {
454
518
// Only the values extracted for use in flutterEvent below matter, the rest are dummy values.
455
519
NSEvent * addEvent = [NSEvent enterExitEventWithType: NSEventTypeMouseEntered
456
520
location: event.locationInWindow
@@ -468,10 +532,13 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
468
532
NSPoint locationInBackingCoordinates = [self .view convertPointToBacking: locationInView];
469
533
FlutterPointerEvent flutterEvent = {
470
534
.struct_size = sizeof (flutterEvent),
535
+ .device_kind = kFlutterPointerDeviceKindMouse ,
471
536
.phase = phase,
472
537
.x = locationInBackingCoordinates.x ,
473
538
.y = -locationInBackingCoordinates.y , // convertPointToBacking makes this negative.
474
539
.timestamp = static_cast <size_t >(event.timestamp * NSEC_PER_MSEC),
540
+ // If a click triggered a synthesized kAdd, don't pass the buttons in that event.
541
+ .buttons = phase == kAdd ? 0 : _mouseState.buttons ,
475
542
};
476
543
477
544
if (event.type == NSEventTypeScrollWheel) {
@@ -491,10 +558,19 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
491
558
}
492
559
FlutterEngineSendPointerEvent (_engine, &flutterEvent, 1 );
493
560
494
- if (phase == kAdd ) {
495
- _mouseCurrentlyAdded = YES ;
561
+ // Update tracking of state as reported to Flutter.
562
+ if (phase == kDown ) {
563
+ _mouseState.flutter_state_is_down = true ;
564
+ } else if (phase == kUp ) {
565
+ _mouseState.flutter_state_is_down = false ;
566
+ if (_mouseState.has_pending_exit ) {
567
+ [self dispatchMouseEvent: event phase: kRemove ];
568
+ _mouseState.has_pending_exit = false ;
569
+ }
570
+ } else if (phase == kAdd ) {
571
+ _mouseState.flutter_state_is_added = true ;
496
572
} else if (phase == kRemove ) {
497
- _mouseCurrentlyAdded = NO ;
573
+ _mouseState. Reset () ;
498
574
}
499
575
}
500
576
@@ -622,28 +698,62 @@ - (void)keyUp:(NSEvent*)event {
622
698
}
623
699
}
624
700
701
+ - (void )mouseEntered : (NSEvent *)event {
702
+ [self dispatchMouseEvent: event phase: kAdd ];
703
+ }
704
+
705
+ - (void )mouseExited : (NSEvent *)event {
706
+ if (_mouseState.buttons != 0 ) {
707
+ _mouseState.has_pending_exit = true ;
708
+ return ;
709
+ }
710
+ [self dispatchMouseEvent: event phase: kRemove ];
711
+ }
712
+
625
713
- (void )mouseDown : (NSEvent *)event {
626
- [self dispatchMouseEvent: event phase: kDown ];
714
+ _mouseState.buttons |= kFlutterPointerButtonMousePrimary ;
715
+ [self dispatchMouseEvent: event];
627
716
}
628
717
629
718
- (void )mouseUp : (NSEvent *)event {
630
- [self dispatchMouseEvent: event phase: kUp ];
719
+ _mouseState.buttons &= ~static_cast <uint64_t >(kFlutterPointerButtonMousePrimary );
720
+ [self dispatchMouseEvent: event];
631
721
}
632
722
633
723
- (void )mouseDragged : (NSEvent *)event {
634
- [self dispatchMouseEvent: event phase: kMove ];
724
+ [self dispatchMouseEvent: event];
635
725
}
636
726
637
- - (void )mouseEntered : (NSEvent *)event {
638
- [self dispatchMouseEvent: event phase: kAdd ];
727
+ - (void )rightMouseDown : (NSEvent *)event {
728
+ _mouseState.buttons |= kFlutterPointerButtonMouseSecondary ;
729
+ [self dispatchMouseEvent: event];
639
730
}
640
731
641
- - (void )mouseExited : (NSEvent *)event {
642
- [self dispatchMouseEvent: event phase: kRemove ];
732
+ - (void )rightMouseUp : (NSEvent *)event {
733
+ _mouseState.buttons &= ~static_cast <uint64_t >(kFlutterPointerButtonMouseSecondary );
734
+ [self dispatchMouseEvent: event];
735
+ }
736
+
737
+ - (void )rightMouseDragged : (NSEvent *)event {
738
+ [self dispatchMouseEvent: event];
739
+ }
740
+
741
+ - (void )otherMouseDown : (NSEvent *)event {
742
+ _mouseState.buttons |= (1 << event.buttonNumber );
743
+ [self dispatchMouseEvent: event];
744
+ }
745
+
746
+ - (void )otherMouseUp : (NSEvent *)event {
747
+ _mouseState.buttons &= ~static_cast <uint64_t >(1 << event.buttonNumber );
748
+ [self dispatchMouseEvent: event];
749
+ }
750
+
751
+ - (void )otherMouseDragged : (NSEvent *)event {
752
+ [self dispatchMouseEvent: event];
643
753
}
644
754
645
755
- (void )mouseMoved : (NSEvent *)event {
646
- [self dispatchMouseEvent: event phase: kHover ];
756
+ [self dispatchMouseEvent: event];
647
757
}
648
758
649
759
- (void )scrollWheel : (NSEvent *)event {
0 commit comments