Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
11
12static 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.
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
int count
static float next(float f)
static CFStringRef kMessageLoopCFRunLoopMode
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
void(^ FlutterAsyncKeyCallback)(BOOL handled)
void(^ KeyEventCompleteCallback)(bool, FlutterUIPressProxy *_Nonnull) API_AVAILABLE(ios(13.4))
static FLUTTER_ASSERT_ARC constexpr CFTimeInterval kDistantFuture
SK_API sk_sp< SkSurface > ios(9.0)
Definition copy.py:1
int BOOL