@@ -82,9 +82,14 @@ void Reset() {
8282@interface FlutterViewController () <FlutterViewReshapeListener>
8383
8484/* *
85- * A list of additional responders to keyboard events. Keybord events are forwarded to all of them.
85+ * A list of additional responders to keyboard events.
86+ *
87+ * Keyboard events received by FlutterViewController are first dispatched to
88+ * each additional responder in order. If any of them handle the event (by
89+ * returning true), the event is not dispatched to later additional responders
90+ * or to the nextResponder.
8691 */
87- @property (nonatomic ) NSMutableOrderedSet <NSResponder *>* additionalKeyResponders;
92+ @property (nonatomic ) NSMutableOrderedSet <FlutterIntermediateKeyResponder *>* additionalKeyResponders;
8893
8994/* *
9095 * The tracking area used to generate hover events, if enabled.
@@ -135,7 +140,15 @@ - (void)dispatchMouseEvent:(nonnull NSEvent*)event;
135140- (void )dispatchMouseEvent : (nonnull NSEvent *)event phase : (FlutterPointerPhase)phase ;
136141
137142/* *
138- * Converts |event| to a key event channel message, and sends it to the engine.
143+ * Sends |event| to all responders in additionalKeyResponders and then to the
144+ * nextResponder if none of the additional responders handled the event.
145+ */
146+ - (void )propagateKeyEvent : (NSEvent *)event ofType : (NSString *)type ;
147+
148+ /* *
149+ * Converts |event| to a key event channel message, and sends it to the engine to
150+ * hand to the framework. Once the framework responds, if the event was not handled,
151+ * propagates the event to any additional key responders.
139152 */
140153- (void )dispatchKeyEvent : (NSEvent *)event ofType : (NSString *)type ;
141154
@@ -206,9 +219,11 @@ @implementation FlutterViewController {
206219 * Performs initialization that's common between the different init paths.
207220 */
208221static void CommonInit (FlutterViewController* controller) {
209- controller->_engine = [[FlutterEngine alloc ] initWithName: @" io.flutter"
210- project: controller->_project
211- allowHeadlessExecution: NO ];
222+ if (!controller->_engine ) {
223+ controller->_engine = [[FlutterEngine alloc ] initWithName: @" io.flutter"
224+ project: controller->_project
225+ allowHeadlessExecution: NO ];
226+ }
212227 controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc ] init ];
213228 controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow;
214229}
@@ -238,6 +253,27 @@ - (instancetype)initWithProject:(nullable FlutterDartProject*)project {
238253 return self;
239254}
240255
256+ - (instancetype )initWithEngine : (nonnull FlutterEngine*)engine
257+ nibName : (nullable NSString *)nibName
258+ bundle : (nullable NSBundle *)nibBundle {
259+ NSAssert (engine != nil , @" Engine is required" );
260+ self = [super initWithNibName: nibName bundle: nibBundle];
261+ if (self) {
262+ if (engine.viewController ) {
263+ NSLog (@" The supplied FlutterEngine %@ is already used with FlutterViewController "
264+ " instance %@ . One instance of the FlutterEngine can only be attached to one "
265+ " FlutterViewController at a time. Set FlutterEngine.viewController "
266+ " to nil before attaching it to another FlutterViewController." ,
267+ [engine description ], [engine.viewController description ]);
268+ }
269+ _engine = engine;
270+ CommonInit (self);
271+ [engine setViewController: self ];
272+ }
273+
274+ return self;
275+ }
276+
241277- (void )loadView {
242278 NSOpenGLContext * resourceContext = _engine.resourceContext ;
243279 if (!resourceContext) {
@@ -288,11 +324,12 @@ - (FlutterView*)flutterView {
288324 return static_cast <FlutterView*>(self.view );
289325}
290326
291- - (void )addKeyResponder : (NSResponder *)responder {
327+ - (void )addKeyResponder : (FlutterIntermediateKeyResponder *)responder {
292328 [self .additionalKeyResponders addObject: responder];
293329}
294330
295- - (void )removeKeyResponder : (NSResponder *)responder {
331+ - (void )removeKeyResponder : (FlutterIntermediateKeyResponder*)responder {
332+ [self .additionalKeyResponders removeObject: responder];
296333}
297334
298335#pragma mark - Private methods
@@ -460,19 +497,56 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase {
460497 }
461498}
462499
500+ - (void )propagateKeyEvent : (NSEvent *)event ofType : (NSString *)type {
501+ if ([type isEqual: @" keydown" ]) {
502+ for (FlutterIntermediateKeyResponder* responder in self.additionalKeyResponders ) {
503+ if ([responder handleKeyDown: event]) {
504+ return ;
505+ }
506+ }
507+ if ([self .nextResponder respondsToSelector: @selector (keyDown: )]) {
508+ [self .nextResponder keyDown: event];
509+ }
510+ } else if ([type isEqual: @" keyup" ]) {
511+ for (FlutterIntermediateKeyResponder* responder in self.additionalKeyResponders ) {
512+ if ([responder handleKeyUp: event]) {
513+ return ;
514+ }
515+ }
516+ if ([self .nextResponder respondsToSelector: @selector (keyUp: )]) {
517+ [self .nextResponder keyUp: event];
518+ }
519+ }
520+ }
521+
463522- (void )dispatchKeyEvent : (NSEvent *)event ofType : (NSString *)type {
523+ if (event.type != NSEventTypeKeyDown && event.type != NSEventTypeKeyUp &&
524+ event.type != NSEventTypeFlagsChanged) {
525+ return ;
526+ }
464527 NSMutableDictionary * keyMessage = [@{
465528 @" keymap" : @" macos" ,
466529 @" type" : type,
467530 @" keyCode" : @(event.keyCode ),
468531 @" modifiers" : @(event.modifierFlags ),
469532 } mutableCopy];
470- // Calling these methods on any other type of event will raise an exception.
533+ // Calling these methods on any other type of event
534+ // (e.g NSEventTypeFlagsChanged) will raise an exception.
471535 if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) {
472536 keyMessage[@" characters" ] = event.characters ;
473537 keyMessage[@" charactersIgnoringModifiers" ] = event.charactersIgnoringModifiers ;
474538 }
475- [_keyEventChannel sendMessage: keyMessage];
539+ __weak __typeof__ (self) weakSelf = self;
540+ FlutterReply replyHandler = ^(id _Nullable reply) {
541+ if (!reply) {
542+ return ;
543+ }
544+ // Only re-dispatch the event to other responders if the framework didn't handle it.
545+ if (![[reply valueForKey: @" handled" ] boolValue ]) {
546+ [weakSelf propagateKeyEvent: event ofType: type];
547+ }
548+ };
549+ [_keyEventChannel sendMessage: keyMessage reply: replyHandler];
476550}
477551
478552- (void )onSettingsChanged : (NSNotification *)notification {
@@ -571,20 +645,10 @@ - (BOOL)acceptsFirstResponder {
571645
572646- (void )keyDown : (NSEvent *)event {
573647 [self dispatchKeyEvent: event ofType: @" keydown" ];
574- for (NSResponder * responder in self.additionalKeyResponders ) {
575- if ([responder respondsToSelector: @selector (keyDown: )]) {
576- [responder keyDown: event];
577- }
578- }
579648}
580649
581650- (void )keyUp : (NSEvent *)event {
582651 [self dispatchKeyEvent: event ofType: @" keyup" ];
583- for (NSResponder * responder in self.additionalKeyResponders ) {
584- if ([responder respondsToSelector: @selector (keyUp: )]) {
585- [responder keyUp: event];
586- }
587- }
588652}
589653
590654- (void )flagsChanged : (NSEvent *)event {
0 commit comments