| Flutter iOS Embedder |

FlutterKeyboardManager.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h"
6
7 #include "flutter/fml/platform/darwin/message_loop_darwin.h"
8 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
9
10 FLUTTER_ASSERT_ARC
11
12 static constexpr CFTimeInterval kDistantFuture = 1.0e10;
13
14 @interface FlutterKeyboardManager ()
15
16 /**
17 * The primary responders added by addPrimaryResponder.
18 */
19 @property(nonatomic, copy, readonly)
20 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
21
22 /**
23 * The secondary responders added by addSecondaryResponder.
24 */
25 @property(nonatomic, copy, readonly)
26 NSMutableArray<id<FlutterKeySecondaryResponder>>* secondaryResponders;
27
28 - (void)dispatchToSecondaryResponders:(nonnull FlutterUIPressProxy*)press
29 complete:(nonnull KeyEventCompleteCallback)callback
30 API_AVAILABLE(ios(13.4));
31
32 @end
33
34 @implementation FlutterKeyboardManager
35
36 - (nonnull instancetype)init {
37 self = [super init];
38 if (self != nil) {
39 _primaryResponders = [[NSMutableArray alloc] init];
40 _secondaryResponders = [[NSMutableArray alloc] init];
41 }
42 return self;
43 }
44
45 - (void)addPrimaryResponder:(nonnull id<FlutterKeyPrimaryResponder>)responder {
46 [_primaryResponders addObject:responder];
47 }
48
49 - (void)addSecondaryResponder:(nonnull id<FlutterKeySecondaryResponder>)responder {
50 [_secondaryResponders addObject:responder];
51 }
52
53 - (void)handlePress:(nonnull FlutterUIPressProxy*)press
54 nextAction:(nonnull void (^)())next API_AVAILABLE(ios(13.4)) {
55 if (@available(iOS 13.4, *)) {
56 // no-op
57 } else {
58 return;
59 }
60
61 bool __block wasHandled = false;
62 KeyEventCompleteCallback completeCallback = ^void(bool handled, FlutterUIPressProxy* press) {
63 wasHandled = handled;
64 CFRunLoopStop(CFRunLoopGetCurrent());
65 };
66 switch (press.phase) {
67 case UIPressPhaseBegan:
68 case UIPressPhaseEnded: {
69 // Having no primary responders requires extra logic, but Flutter hard-codes
70 // all primary responders, so this is a situation that Flutter will never
71 // encounter.
72 NSAssert([_primaryResponders count] >= 0, @"At least one primary responder must be added.");
73
74 __block __weak __typeof(self) weakSelf = self;
75 __block NSUInteger unreplied = [self.primaryResponders count];
76 __block BOOL anyHandled = false;
77 FlutterAsyncKeyCallback replyCallback = ^(BOOL handled) {
78 unreplied--;
79 NSAssert(unreplied >= 0, @"More primary responders replied than expected.");
80 anyHandled = anyHandled || handled;
81 if (unreplied == 0) {
82 if (!anyHandled && weakSelf) {
83 [weakSelf dispatchToSecondaryResponders:press complete:completeCallback];
84 } else {
85 completeCallback(true, press);
86 }
87 }
88 };
89 for (id<FlutterKeyPrimaryResponder> responder in _primaryResponders) {
90 [responder handlePress:press callback:replyCallback];
91 }
92 // Create a nested run loop while we wait for a response from the
93 // framework. Once the completeCallback is called, this run loop will exit
94 // and the main one will resume. The completeCallback MUST be called, or
95 // the app will get stuck in this run loop indefinitely.
96 //
97 // We need to run in this mode so that UIKit doesn't give us new
98 // events until we are done processing this one.
99 CFRunLoopRunInMode(fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, kDistantFuture, NO);
100 break;
101 }
102 case UIPressPhaseChanged:
103 case UIPressPhaseCancelled:
104 case UIPressPhaseStationary:
105 break;
106 }
107 if (!wasHandled) {
108 next();
109 }
110 }
111
112 #pragma mark - Private
113
114 - (void)dispatchToSecondaryResponders:(nonnull FlutterUIPressProxy*)press
115 complete:(nonnull KeyEventCompleteCallback)callback
116 API_AVAILABLE(ios(13.4)) {
117 if (@available(iOS 13.4, *)) {
118 // no-op
119 } else {
120 callback(false, press);
121 return;
122 }
123
124 for (id<FlutterKeySecondaryResponder> responder in _secondaryResponders) {
125 if ([responder handlePress:press]) {
126 callback(true, press);
127 return;
128 }
129 }
130 callback(false, press);
131 }
132
133 @end
FlutterAsyncKeyCallback
void(^ FlutterAsyncKeyCallback)(BOOL handled)
Definition: FlutterKeyPrimaryResponder.h:10
FlutterKeyboardManager.h
KeyEventCompleteCallback
void(^ KeyEventCompleteCallback)(bool, FlutterUIPressProxy *_Nonnull) API_AVAILABLE(ios(13.4))
Definition: FlutterKeyboardManager.h:17
kDistantFuture
static constexpr FLUTTER_ASSERT_ARC CFTimeInterval kDistantFuture
Definition: FlutterKeyboardManager.mm:12
FlutterMacros.h
self
return self
Definition: FlutterTextureRegistryRelay.mm:19
FlutterKeyboardManager
Definition: FlutterKeyboardManager.h:54
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterKeyPrimaryResponder-p
Definition: FlutterKeyPrimaryResponder.h:19
-[FlutterKeyPrimaryResponder-p handlePress:callback:]
void handlePress:callback:(nonnull FlutterUIPressProxy *press,[callback] ios(13.4) API_AVAILABLE)
FlutterKeySecondaryResponder-p
Definition: FlutterKeySecondaryResponder.h:17