Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e21a58d

Browse files
authored
[macOS, Keyboard] Refactor: Clean up keyboard initialization, connection, and unit test framework (#31940)
1 parent 2c94cc5 commit e21a58d

11 files changed

+378
-254
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1582,10 +1582,10 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCom
15821582
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h
15831583
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm
15841584
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h
1585-
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h
15861585
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h
15871586
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm
15881587
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm
1588+
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h
15891589
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSExternalTexture.h
15901590
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h
15911591
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm

shell/platform/darwin/macos/BUILD.gn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ source_set("flutter_framework_source") {
8282
"framework/Source/FlutterIOSurfaceHolder.h",
8383
"framework/Source/FlutterIOSurfaceHolder.mm",
8484
"framework/Source/FlutterKeyPrimaryResponder.h",
85-
"framework/Source/FlutterKeySecondaryResponder.h",
8685
"framework/Source/FlutterKeyboardManager.h",
8786
"framework/Source/FlutterKeyboardManager.mm",
87+
"framework/Source/FlutterKeyboardViewDelegate.h",
8888
"framework/Source/FlutterMacOSExternalTexture.h",
8989
"framework/Source/FlutterMacOSExternalTexture.h",
9090
"framework/Source/FlutterMetalCompositor.h",

shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h

Lines changed: 0 additions & 24 deletions
This file was deleted.

shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,68 +2,50 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h"
6-
75
#import <Cocoa/Cocoa.h>
86

9-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h"
10-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h"
7+
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
8+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h"
9+
10+
namespace {
11+
// Someohow this pointer type must be defined as a single type for the compiler
12+
// to compile the function pointer type (due to _Nullable).
13+
typedef NSResponder* _NSResponderPtr;
14+
}
15+
16+
typedef _Nullable _NSResponderPtr (^NextResponderProvider)();
1117

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

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

5244
/**
53-
* Add a primary responder, which asynchronously decides whether to handle an
54-
* event.
55-
*/
56-
- (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder;
57-
58-
/**
59-
* Add a secondary responder, which synchronously decides whether to handle an
60-
* event in order if no earlier responders handle.
61-
*/
62-
- (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder;
63-
64-
/**
65-
* Dispatch a key event to all responders, and possibly the next |NSResponder|
66-
* afterwards.
45+
* Processes a key event.
46+
*
47+
* Unhandled events will be dispatched to the text input system, and possibly
48+
* the next responder afterwards.
6749
*/
6850
- (void)handleEvent:(nonnull NSEvent*)event;
6951

shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,59 @@
44

55
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h"
66

7+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h"
8+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h"
9+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
10+
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h"
11+
712
@interface FlutterKeyboardManager ()
813

914
/**
10-
* The owner set by initWithOwner.
15+
* The text input plugin set by initialization.
1116
*/
12-
@property(nonatomic, weak) NSResponder* owner;
17+
@property(nonatomic) id<FlutterKeyboardViewDelegate> viewDelegate;
1318

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

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

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

2632
@end
2733

28-
@implementation FlutterKeyboardManager
34+
@implementation FlutterKeyboardManager {
35+
NextResponderProvider _getNextResponder;
36+
}
2937

30-
- (nonnull instancetype)initWithOwner:(NSResponder*)weakOwner {
38+
- (nonnull instancetype)initWithViewDelegate:(nonnull id<FlutterKeyboardViewDelegate>)viewDelegate {
3139
self = [super init];
3240
if (self != nil) {
33-
_owner = weakOwner;
41+
_viewDelegate = viewDelegate;
42+
3443
_primaryResponders = [[NSMutableArray alloc] init];
35-
_secondaryResponders = [[NSMutableArray alloc] init];
44+
[self addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc]
45+
initWithSendEvent:^(const FlutterKeyEvent& event,
46+
FlutterKeyEventCallback callback,
47+
void* userData) {
48+
[_viewDelegate sendKeyEvent:event
49+
callback:callback
50+
userData:userData];
51+
}]];
52+
[self
53+
addPrimaryResponder:[[FlutterChannelKeyResponder alloc]
54+
initWithChannel:[FlutterBasicMessageChannel
55+
messageChannelWithName:@"flutter/keyevent"
56+
binaryMessenger:[_viewDelegate
57+
getBinaryMessenger]
58+
codec:[FlutterJSONMessageCodec
59+
sharedInstance]]]];
3660
}
3761
return self;
3862
}
@@ -41,10 +65,6 @@ - (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder {
4165
[_primaryResponders addObject:responder];
4266
}
4367

44-
- (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder {
45-
[_secondaryResponders addObject:responder];
46-
}
47-
4868
- (void)handleEvent:(nonnull NSEvent*)event {
4969
// Be sure to add a handling method in propagateKeyEvent when allowing more
5070
// event types here.
@@ -77,25 +97,27 @@ - (void)handleEvent:(nonnull NSEvent*)event {
7797
#pragma mark - Private
7898

7999
- (void)dispatchToSecondaryResponders:(NSEvent*)event {
80-
for (id<FlutterKeySecondaryResponder> responder in _secondaryResponders) {
81-
if ([responder handleKeyEvent:event]) {
82-
return;
83-
}
100+
if ([_viewDelegate onTextInputKeyEvent:event]) {
101+
return;
102+
}
103+
NSResponder* nextResponder = _viewDelegate.nextResponder;
104+
if (nextResponder == nil) {
105+
return;
84106
}
85107
switch (event.type) {
86108
case NSEventTypeKeyDown:
87-
if ([_owner.nextResponder respondsToSelector:@selector(keyDown:)]) {
88-
[_owner.nextResponder keyDown:event];
109+
if ([nextResponder respondsToSelector:@selector(keyDown:)]) {
110+
[nextResponder keyDown:event];
89111
}
90112
break;
91113
case NSEventTypeKeyUp:
92-
if ([_owner.nextResponder respondsToSelector:@selector(keyUp:)]) {
93-
[_owner.nextResponder keyUp:event];
114+
if ([nextResponder respondsToSelector:@selector(keyUp:)]) {
115+
[nextResponder keyUp:event];
94116
}
95117
break;
96118
case NSEventTypeFlagsChanged:
97-
if ([_owner.nextResponder respondsToSelector:@selector(flagsChanged:)]) {
98-
[_owner.nextResponder flagsChanged:event];
119+
if ([nextResponder respondsToSelector:@selector(flagsChanged:)]) {
120+
[nextResponder flagsChanged:event];
99121
}
100122
break;
101123
default:

0 commit comments

Comments
 (0)