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