blob: c2406820af101ce84d257ff84788f2abe9e4b0f6 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h"
@interface FlutterKeyboardManager ()
/**
* The text input plugin set by initialization.
*/
@property(nonatomic) id<FlutterKeyboardViewDelegate> viewDelegate;
/**
* The primary responders added by addPrimaryResponder.
*/
@property(nonatomic) NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
/**
* Add a primary responder, which asynchronously decides whether to handle an
* event.
*/
- (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder;
- (void)dispatchToSecondaryResponders:(NSEvent*)event;
@end
@implementation FlutterKeyboardManager {
NextResponderProvider _getNextResponder;
}
- (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDelegate>)viewDelegate {
self = [super init];
if (self != nil) {
_viewDelegate = viewDelegate;
_primaryResponders = [[NSMutableArray alloc] init];
[self addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc]
initWithSendEvent:^(const FlutterKeyEvent& event,
FlutterKeyEventCallback callback,
void* userData) {
[_viewDelegate sendKeyEvent:event
callback:callback
userData:userData];
}]];
[self
addPrimaryResponder:[[FlutterChannelKeyResponder alloc]
initWithChannel:[FlutterBasicMessageChannel
messageChannelWithName:@"flutter/keyevent"
binaryMessenger:[_viewDelegate
getBinaryMessenger]
codec:[FlutterJSONMessageCodec
sharedInstance]]]];
}
return self;
}
- (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder {
[_primaryResponders addObject:responder];
}
- (void)handleEvent:(nonnull NSEvent*)event {
// Be sure to add a handling method in propagateKeyEvent when allowing more
// event types here.
if (event.type != NSEventTypeKeyDown && event.type != NSEventTypeKeyUp &&
event.type != NSEventTypeFlagsChanged) {
return;
}
if (_viewDelegate.isComposing) {
[self dispatchToSecondaryResponders:event];
return;
}
// Having no primary responders require extra logic, but Flutter hard-codes
// all primary responders, so this is a situation that Flutter will never
// encounter.
NSAssert([_primaryResponders count] >= 0, @"At least one primary responder must be added.");
__weak __typeof__(self) weakSelf = self;
__block int unreplied = [_primaryResponders count];
__block BOOL anyHandled = false;
FlutterAsyncKeyCallback replyCallback = ^(BOOL handled) {
unreplied -= 1;
NSAssert(unreplied >= 0, @"More primary responders replied than possible.");
anyHandled = anyHandled || handled;
if (unreplied == 0 && !anyHandled) {
[weakSelf dispatchToSecondaryResponders:event];
}
};
for (id<FlutterKeyPrimaryResponder> responder in _primaryResponders) {
[responder handleEvent:event callback:replyCallback];
}
}
#pragma mark - Private
- (void)dispatchToSecondaryResponders:(NSEvent*)event {
if ([_viewDelegate onTextInputKeyEvent:event]) {
return;
}
NSResponder* nextResponder = _viewDelegate.nextResponder;
if (nextResponder == nil) {
return;
}
switch (event.type) {
case NSEventTypeKeyDown:
if ([nextResponder respondsToSelector:@selector(keyDown:)]) {
[nextResponder keyDown:event];
}
break;
case NSEventTypeKeyUp:
if ([nextResponder respondsToSelector:@selector(keyUp:)]) {
[nextResponder keyUp:event];
}
break;
case NSEventTypeFlagsChanged:
if ([nextResponder respondsToSelector:@selector(flagsChanged:)]) {
[nextResponder flagsChanged:event];
}
break;
default:
NSAssert(false, @"Unexpected key event type (got %lu).", event.type);
}
}
@end