• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <UIKit/UIKit.h>
6
7#include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h"
8#include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h"
9
10@implementation FlutterInactiveTextInput {
11}
12
13@synthesize tokenizer = _tokenizer;
14@synthesize beginningOfDocument = _beginningOfDocument;
15@synthesize endOfDocument = _endOfDocument;
16
17- (instancetype)init {
18  return [super init];
19}
20
21- (BOOL)hasText {
22  return self.text.length > 0;
23}
24
25- (NSString*)textInRange:(UITextRange*)range {
26  NSRange textRange = ((FlutterTextRange*)range).range;
27  return [self.text substringWithRange:textRange];
28}
29
30- (void)replaceRange:(UITextRange*)range withText:(NSString*)text {
31  // This method is required but not called by accessibility API for
32  // features we are using it for. It may need to be implemented if
33  // requirements change.
34}
35
36- (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelectedRange {
37  // This method is required but not called by accessibility API for
38  // features we are using it for. It may need to be implemented if
39  // requirements change.
40}
41
42- (void)unmarkText {
43  // This method is required but not called by accessibility API for
44  // features we are using it for. It may need to be implemented if
45  // requirements change.
46}
47
48- (UITextRange*)textRangeFromPosition:(UITextPosition*)fromPosition
49                           toPosition:(UITextPosition*)toPosition {
50  NSUInteger fromIndex = ((FlutterTextPosition*)fromPosition).index;
51  NSUInteger toIndex = ((FlutterTextPosition*)toPosition).index;
52  return [FlutterTextRange rangeWithNSRange:NSMakeRange(fromIndex, toIndex - fromIndex)];
53}
54
55- (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInteger)offset {
56  // This method is required but not called by accessibility API for
57  // features we are using it for. It may need to be implemented if
58  // requirements change.
59  return nil;
60}
61
62- (UITextPosition*)positionFromPosition:(UITextPosition*)position
63                            inDirection:(UITextLayoutDirection)direction
64                                 offset:(NSInteger)offset {
65  // This method is required but not called by accessibility API for
66  // features we are using it for. It may need to be implemented if
67  // requirements change.
68  return nil;
69}
70
71- (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other {
72  // This method is required but not called by accessibility API for
73  // features we are using it for. It may need to be implemented if
74  // requirements change.
75  return NSOrderedSame;
76}
77
78- (NSInteger)offsetFromPosition:(UITextPosition*)from toPosition:(UITextPosition*)toPosition {
79  // This method is required but not called by accessibility API for
80  // features we are using it for. It may need to be implemented if
81  // requirements change.
82  return 0;
83}
84
85- (UITextPosition*)positionWithinRange:(UITextRange*)range
86                   farthestInDirection:(UITextLayoutDirection)direction {
87  // This method is required but not called by accessibility API for
88  // features we are using it for. It may need to be implemented if
89  // requirements change.
90  return nil;
91}
92
93- (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position
94                                      inDirection:(UITextLayoutDirection)direction {
95  // This method is required but not called by accessibility API for
96  // features we are using it for. It may need to be implemented if
97  // requirements change.
98  return nil;
99}
100
101- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position
102                                              inDirection:(UITextStorageDirection)direction {
103  // Not editable. Does not apply.
104  return UITextWritingDirectionNatural;
105}
106
107- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection
108                       forRange:(UITextRange*)range {
109  // Not editable. Does not apply.
110}
111
112- (CGRect)firstRectForRange:(UITextRange*)range {
113  // This method is required but not called by accessibility API for
114  // features we are using it for. It may need to be implemented if
115  // requirements change.
116  return CGRectZero;
117}
118
119- (CGRect)caretRectForPosition:(UITextPosition*)position {
120  // This method is required but not called by accessibility API for
121  // features we are using it for. It may need to be implemented if
122  // requirements change.
123  return CGRectZero;
124}
125
126- (UITextPosition*)closestPositionToPoint:(CGPoint)point {
127  // This method is required but not called by accessibility API for
128  // features we are using it for. It may need to be implemented if
129  // requirements change.
130  return nil;
131}
132
133- (UITextPosition*)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange*)range {
134  // This method is required but not called by accessibility API for
135  // features we are using it for. It may need to be implemented if
136  // requirements change.
137  return nil;
138}
139
140- (NSArray*)selectionRectsForRange:(UITextRange*)range {
141  // This method is required but not called by accessibility API for
142  // features we are using it for. It may need to be implemented if
143  // requirements change.
144  return @[];
145}
146
147- (UITextRange*)characterRangeAtPoint:(CGPoint)point {
148  // This method is required but not called by accessibility API for
149  // features we are using it for. It may need to be implemented if
150  // requirements change.
151  return nil;
152}
153
154- (void)insertText:(NSString*)text {
155  // This method is required but not called by accessibility API for
156  // features we are using it for. It may need to be implemented if
157  // requirements change.
158}
159
160- (void)deleteBackward {
161  // This method is required but not called by accessibility API for
162  // features we are using it for. It may need to be implemented if
163  // requirements change.
164}
165
166@end
167
168@implementation TextInputSemanticsObject {
169  FlutterInactiveTextInput* _inactive_text_input;
170}
171
172- (instancetype)initWithBridge:(fml::WeakPtr<flutter::AccessibilityBridge>)bridge uid:(int32_t)uid {
173  self = [super initWithBridge:bridge uid:uid];
174
175  if (self) {
176    _inactive_text_input = [[FlutterInactiveTextInput alloc] init];
177  }
178
179  return self;
180}
181
182- (void)dealloc {
183  [_inactive_text_input release];
184  [super dealloc];
185}
186
187#pragma mark - SemanticsObject overrides
188
189- (void)setSemanticsNode:(const flutter::SemanticsNode*)node {
190  [super setSemanticsNode:node];
191  _inactive_text_input.text = @(node->value.data());
192  if ([self node].HasFlag(flutter::SemanticsFlags::kIsFocused)) {
193    // The text input view must have a non-trivial size for the accessibility
194    // system to send text editing events.
195    [self bridge] -> textInputView().frame = CGRectMake(0.0, 0.0, 1.0, 1.0);
196  }
197}
198
199#pragma mark - UIAccessibility overrides
200
201/**
202 * The UITextInput whose accessibility properties we present to UIKit as
203 * substitutes for Flutter's text field properties.
204 *
205 * When the field is currently focused (i.e. it is being edited), we use
206 * the FlutterTextInputView used by FlutterTextInputPlugin. Otherwise,
207 * we use an FlutterInactiveTextInput.
208 */
209- (UIView<UITextInput>*)textInputSurrogate {
210  if ([self node].HasFlag(flutter::SemanticsFlags::kIsFocused)) {
211    return [self bridge] -> textInputView();
212  } else {
213    return _inactive_text_input;
214  }
215}
216
217- (UIView*)textInputView {
218  return [self textInputSurrogate];
219}
220
221- (void)accessibilityElementDidBecomeFocused {
222  [[self textInputSurrogate] accessibilityElementDidBecomeFocused];
223  [super accessibilityElementDidBecomeFocused];
224}
225
226- (void)accessibilityElementDidLoseFocus {
227  [[self textInputSurrogate] accessibilityElementDidLoseFocus];
228  [super accessibilityElementDidLoseFocus];
229}
230
231- (BOOL)accessibilityElementIsFocused {
232  return [self node].HasFlag(flutter::SemanticsFlags::kIsFocused);
233}
234
235- (BOOL)accessibilityActivate {
236  return [[self textInputSurrogate] accessibilityActivate];
237}
238
239- (NSString*)accessibilityLabel {
240  NSString* label = [super accessibilityLabel];
241  if (label != nil)
242    return label;
243  return [self textInputSurrogate].accessibilityLabel;
244}
245
246- (NSString*)accessibilityHint {
247  NSString* hint = [super accessibilityHint];
248  if (hint != nil)
249    return hint;
250  return [self textInputSurrogate].accessibilityHint;
251}
252
253- (NSString*)accessibilityValue {
254  NSString* value = [super accessibilityValue];
255  if (value != nil)
256    return value;
257  return [self textInputSurrogate].accessibilityValue;
258}
259
260- (UIAccessibilityTraits)accessibilityTraits {
261  // Adding UIAccessibilityTraitKeyboardKey to the trait list so that iOS treats it like
262  // a keyboard entry control, thus adding support for text editing features, such as
263  // pinch to select text, and up/down fling to move cursor.
264  return [super accessibilityTraits] | [self textInputSurrogate].accessibilityTraits |
265         UIAccessibilityTraitKeyboardKey;
266}
267
268#pragma mark - UITextInput overrides
269
270- (NSString*)textInRange:(UITextRange*)range {
271  return [[self textInputSurrogate] textInRange:range];
272}
273
274- (void)replaceRange:(UITextRange*)range withText:(NSString*)text {
275  return [[self textInputSurrogate] replaceRange:range withText:text];
276}
277
278- (BOOL)shouldChangeTextInRange:(UITextRange*)range replacementText:(NSString*)text {
279  return [[self textInputSurrogate] shouldChangeTextInRange:range replacementText:text];
280}
281
282- (UITextRange*)selectedTextRange {
283  return [[self textInputSurrogate] selectedTextRange];
284}
285
286- (void)setSelectedTextRange:(UITextRange*)range {
287  [[self textInputSurrogate] setSelectedTextRange:range];
288}
289
290- (UITextRange*)markedTextRange {
291  return [[self textInputSurrogate] markedTextRange];
292}
293
294- (NSDictionary*)markedTextStyle {
295  return [[self textInputSurrogate] markedTextStyle];
296}
297
298- (void)setMarkedTextStyle:(NSDictionary*)style {
299  [[self textInputSurrogate] setMarkedTextStyle:style];
300}
301
302- (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)selectedRange {
303  [[self textInputSurrogate] setMarkedText:markedText selectedRange:selectedRange];
304}
305
306- (void)unmarkText {
307  [[self textInputSurrogate] unmarkText];
308}
309
310- (UITextStorageDirection)selectionAffinity {
311  return [[self textInputSurrogate] selectionAffinity];
312}
313
314- (UITextPosition*)beginningOfDocument {
315  return [[self textInputSurrogate] beginningOfDocument];
316}
317
318- (UITextPosition*)endOfDocument {
319  return [[self textInputSurrogate] endOfDocument];
320}
321
322- (id<UITextInputDelegate>)inputDelegate {
323  return [[self textInputSurrogate] inputDelegate];
324}
325
326- (void)setInputDelegate:(id<UITextInputDelegate>)delegate {
327  [[self textInputSurrogate] setInputDelegate:delegate];
328}
329
330- (id<UITextInputTokenizer>)tokenizer {
331  return [[self textInputSurrogate] tokenizer];
332}
333
334- (UITextRange*)textRangeFromPosition:(UITextPosition*)fromPosition
335                           toPosition:(UITextPosition*)toPosition {
336  return [[self textInputSurrogate] textRangeFromPosition:fromPosition toPosition:toPosition];
337}
338
339- (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInteger)offset {
340  return [[self textInputSurrogate] positionFromPosition:position offset:offset];
341}
342
343- (UITextPosition*)positionFromPosition:(UITextPosition*)position
344                            inDirection:(UITextLayoutDirection)direction
345                                 offset:(NSInteger)offset {
346  return [[self textInputSurrogate] positionFromPosition:position
347                                             inDirection:direction
348                                                  offset:offset];
349}
350
351- (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other {
352  return [[self textInputSurrogate] comparePosition:position toPosition:other];
353}
354
355- (NSInteger)offsetFromPosition:(UITextPosition*)from toPosition:(UITextPosition*)toPosition {
356  return [[self textInputSurrogate] offsetFromPosition:from toPosition:toPosition];
357}
358
359- (UITextPosition*)positionWithinRange:(UITextRange*)range
360                   farthestInDirection:(UITextLayoutDirection)direction {
361  return [[self textInputSurrogate] positionWithinRange:range farthestInDirection:direction];
362}
363
364- (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position
365                                      inDirection:(UITextLayoutDirection)direction {
366  return [[self textInputSurrogate] characterRangeByExtendingPosition:position
367                                                          inDirection:direction];
368}
369
370- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position
371                                              inDirection:(UITextStorageDirection)direction {
372  return [[self textInputSurrogate] baseWritingDirectionForPosition:position inDirection:direction];
373}
374
375- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection
376                       forRange:(UITextRange*)range {
377  [[self textInputSurrogate] setBaseWritingDirection:writingDirection forRange:range];
378}
379
380- (CGRect)firstRectForRange:(UITextRange*)range {
381  return [[self textInputSurrogate] firstRectForRange:range];
382}
383
384- (CGRect)caretRectForPosition:(UITextPosition*)position {
385  return [[self textInputSurrogate] caretRectForPosition:position];
386}
387
388- (UITextPosition*)closestPositionToPoint:(CGPoint)point {
389  return [[self textInputSurrogate] closestPositionToPoint:point];
390}
391
392- (UITextPosition*)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange*)range {
393  return [[self textInputSurrogate] closestPositionToPoint:point withinRange:range];
394}
395
396- (NSArray*)selectionRectsForRange:(UITextRange*)range {
397  return [[self textInputSurrogate] selectionRectsForRange:range];
398}
399
400- (UITextRange*)characterRangeAtPoint:(CGPoint)point {
401  return [[self textInputSurrogate] characterRangeAtPoint:point];
402}
403
404- (void)insertText:(NSString*)text {
405  [[self textInputSurrogate] insertText:text];
406}
407
408- (void)deleteBackward {
409  [[self textInputSurrogate] deleteBackward];
410}
411
412#pragma mark - UIKeyInput overrides
413
414- (BOOL)hasText {
415  return [[self textInputSurrogate] hasText];
416}
417
418@end
419