Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1582,10 +1582,10 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCom
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSExternalTexture.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterIOSurfaceHolder.h",
"framework/Source/FlutterIOSurfaceHolder.mm",
"framework/Source/FlutterKeyPrimaryResponder.h",
"framework/Source/FlutterKeySecondaryResponder.h",
"framework/Source/FlutterKeyboardManager.h",
"framework/Source/FlutterKeyboardManager.mm",
"framework/Source/FlutterKeyboardViewDelegate.h",
"framework/Source/FlutterMacOSExternalTexture.h",
"framework/Source/FlutterMacOSExternalTexture.h",
"framework/Source/FlutterMetalCompositor.h",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,50 @@
// 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/FlutterKeyPrimaryResponder.h"

#import <Cocoa/Cocoa.h>

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h"

namespace {
// Someohow this pointer type must be defined as a single type for the compiler
// to compile the function pointer type (due to _Nullable).
typedef NSResponder* _NSResponderPtr;
}

typedef _Nullable _NSResponderPtr (^NextResponderProvider)();

/**
* A hub that manages how key events are dispatched to various Flutter key
* responders, and whether the event is propagated to the next NSResponder.
*
* This class manages one or more primary responders, as well as zero or more
* secondary responders.
* Processes keyboard events and cooperate with |TextInputPlugin|.
*
* An event that is received by |handleEvent| is first dispatched to *all*
* primary responders. Each primary responder responds *asynchronously* with a
* boolean, indicating whether it handles the event.
* A keyboard event goes through a few sections, each can choose to handled the
* event, and only unhandled events can move to the next section:
*
* An event that is not handled by any primary responders is then passed to to
* the first secondary responder (in the chronological order of addition),
* which responds *synchronously* with a boolean, indicating whether it handles
* the event. If not, the event is passed to the next secondary responder, and
* so on.
*
* If no responders handle the event, the event is then handed over to the
* owner's |nextResponder| if not nil, dispatching to method |keyDown|,
* |keyUp|, or |flagsChanged| depending on the event's type. If the
* |nextResponder| is nil, then the event will be propagated no further.
*
* Preventing primary responders from receiving events is not supported,
* because in reality this class will only support 2 hardcoded ones (channel
* and embedder), where the only purpose of supporting two is to support the
* legacy API (channel) during the deprecation window, after which the channel
* responder should be removed.
* - Pre-filtering: Events during IME are sent to the system immediately
* (to be implemented).
* - Keyboard: Dispatch to the embedder responder and the channel responder
* simultaneously. After both responders have responded (asynchronously), the
* event is considered handled if either responder handles.
* - Text input: Events are sent to |TextInputPlugin| and are handled
* synchronously.
* - Next responder: Events are sent to the next responder as specified by
* |viewDelegate|.
*/
@interface FlutterKeyboardManager : NSObject

/**
* Create a manager by specifying the owner.
* Create a keyboard manager.
*
* The owner should be an object that handles the lifecycle of this instance.
* The |owner.nextResponder| can be nil, but if it isn't, it will be where the
* key events are propagated to if no responders handle the event. The owner
* is typically a |FlutterViewController|.
* The |viewDelegate| is a weak reference, typically implemented by
* |FlutterViewController|.
*/
- (nonnull instancetype)initWithOwner:(nonnull NSResponder*)weakOwner;
- (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDelegate>)viewDelegate;

/**
* Add a primary responder, which asynchronously decides whether to handle an
* event.
*/
- (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder;

/**
* Add a secondary responder, which synchronously decides whether to handle an
* event in order if no earlier responders handle.
*/
- (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder;

/**
* Dispatch a key event to all responders, and possibly the next |NSResponder|
* afterwards.
* Processes a key event.
*
* Unhandled events will be dispatched to the text input system, and possibly
* the next responder afterwards.
*/
- (void)handleEvent:(nonnull NSEvent*)event;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,59 @@

#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 owner set by initWithOwner.
* The text input plugin set by initialization.
*/
@property(nonatomic, weak) NSResponder* owner;
@property(nonatomic) id<FlutterKeyboardViewDelegate> viewDelegate;

/**
* The primary responders added by addPrimaryResponder.
*/
@property(nonatomic) NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;

/**
* The secondary responders added by addSecondaryResponder.
* Add a primary responder, which asynchronously decides whether to handle an
* event.
*/
@property(nonatomic) NSMutableArray<id<FlutterKeySecondaryResponder>>* secondaryResponders;
- (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder;

- (void)dispatchToSecondaryResponders:(NSEvent*)event;

@end

@implementation FlutterKeyboardManager
@implementation FlutterKeyboardManager {
NextResponderProvider _getNextResponder;
}

- (nonnull instancetype)initWithOwner:(NSResponder*)weakOwner {
- (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDelegate>)viewDelegate {
self = [super init];
if (self != nil) {
_owner = weakOwner;
_viewDelegate = viewDelegate;

_primaryResponders = [[NSMutableArray alloc] init];
_secondaryResponders = [[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;
}
Expand All @@ -41,10 +65,6 @@ - (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder {
[_primaryResponders addObject:responder];
}

- (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder {
[_secondaryResponders addObject:responder];
}

- (void)handleEvent:(nonnull NSEvent*)event {
// Be sure to add a handling method in propagateKeyEvent when allowing more
// event types here.
Expand Down Expand Up @@ -77,25 +97,27 @@ - (void)handleEvent:(nonnull NSEvent*)event {
#pragma mark - Private

- (void)dispatchToSecondaryResponders:(NSEvent*)event {
for (id<FlutterKeySecondaryResponder> responder in _secondaryResponders) {
if ([responder handleKeyEvent:event]) {
return;
}
if ([_viewDelegate onTextInputKeyEvent:event]) {
return;
}
NSResponder* nextResponder = _viewDelegate.nextResponder;
if (nextResponder == nil) {
return;
}
switch (event.type) {
case NSEventTypeKeyDown:
if ([_owner.nextResponder respondsToSelector:@selector(keyDown:)]) {
[_owner.nextResponder keyDown:event];
if ([nextResponder respondsToSelector:@selector(keyDown:)]) {
[nextResponder keyDown:event];
}
break;
case NSEventTypeKeyUp:
if ([_owner.nextResponder respondsToSelector:@selector(keyUp:)]) {
[_owner.nextResponder keyUp:event];
if ([nextResponder respondsToSelector:@selector(keyUp:)]) {
[nextResponder keyUp:event];
}
break;
case NSEventTypeFlagsChanged:
if ([_owner.nextResponder respondsToSelector:@selector(flagsChanged:)]) {
[_owner.nextResponder flagsChanged:event];
if ([nextResponder respondsToSelector:@selector(flagsChanged:)]) {
[nextResponder flagsChanged:event];
}
break;
default:
Expand Down
Loading