@@ -61,6 +61,7 @@ @interface FlutterSecureTextInputView : FlutterTextInputView
61
61
62
62
@interface FlutterTextInputPlugin ()
63
63
@property (nonatomic , assign ) FlutterTextInputView* activeView;
64
+ @property (nonatomic , readonly ) UIView* inputHider;
64
65
@property (nonatomic , readonly ) UIView* keyboardViewContainer;
65
66
@property (nonatomic , readonly ) UIView* keyboardView;
66
67
@property (nonatomic , assign ) UIView* cachedFirstResponder;
@@ -1426,44 +1427,53 @@ - (void)testUpdateFirstRectForRange {
1426
1427
@(-6.0 ), @(3.0 ), @(9.0 ), @(1.0 )
1427
1428
];
1428
1429
1430
+ CGRect kInvalidFirstRectRelative =
1431
+ [textInputPlugin.viewController.view convertRect: kInvalidFirstRect toView: inputView];
1432
+
1429
1433
// Invalid since we don't have the transform or the rect.
1430
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1434
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1431
1435
1432
1436
[inputView setEditableTransform: yOffsetMatrix];
1433
1437
// Invalid since we don't have the rect.
1434
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1438
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1435
1439
1436
1440
// Valid rect and transform.
1437
1441
CGRect testRect = CGRectMake (0 , 0 , 100 , 100 );
1438
1442
[inputView setMarkedRect: testRect];
1439
1443
1440
1444
CGRect finalRect = CGRectOffset (testRect, 0 , 200 );
1441
- XCTAssertTrue (CGRectEqualToRect (finalRect, [inputView firstRectForRange: range]));
1445
+ CGRect finalRectRelative = [textInputPlugin.viewController.view convertRect: finalRect
1446
+ toView: inputView];
1447
+ XCTAssertTrue (CGRectEqualToRect (finalRectRelative, [inputView firstRectForRange: range]));
1442
1448
// Idempotent.
1443
- XCTAssertTrue (CGRectEqualToRect (finalRect , [inputView firstRectForRange: range]));
1449
+ XCTAssertTrue (CGRectEqualToRect (finalRectRelative , [inputView firstRectForRange: range]));
1444
1450
1445
1451
// Use an invalid matrix:
1446
1452
[inputView setEditableTransform: zeroMatrix];
1447
1453
// Invalid matrix is invalid.
1448
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1449
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1454
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1455
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1450
1456
1451
1457
// Revert the invalid matrix change.
1452
1458
[inputView setEditableTransform: yOffsetMatrix];
1453
1459
[inputView setMarkedRect: testRect];
1454
- XCTAssertTrue (CGRectEqualToRect (finalRect , [inputView firstRectForRange: range]));
1460
+ XCTAssertTrue (CGRectEqualToRect (finalRectRelative , [inputView firstRectForRange: range]));
1455
1461
1456
1462
// Use an invalid rect:
1457
1463
[inputView setMarkedRect: kInvalidFirstRect ];
1458
1464
// Invalid marked rect is invalid.
1459
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1460
- XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRect , [inputView firstRectForRange: range]));
1465
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1466
+ XCTAssertTrue (CGRectEqualToRect (kInvalidFirstRectRelative , [inputView firstRectForRange: range]));
1461
1467
1462
1468
// Use a 3d affine transform that does 3d-scaling, z-index rotating and 3d translation.
1463
1469
[inputView setEditableTransform: affineMatrix];
1464
1470
[inputView setMarkedRect: testRect];
1465
- XCTAssertTrue (
1466
- CGRectEqualToRect (CGRectMake (-306 , 3 , 300 , 300 ), [inputView firstRectForRange: range]));
1471
+
1472
+ CGRect relativeRect =
1473
+ [textInputPlugin.viewController.view convertRect: CGRectMake (-306 , 3 , 300 , 300 )
1474
+ toView: inputView];
1475
+
1476
+ XCTAssertTrue (CGRectEqualToRect (relativeRect, [inputView firstRectForRange: range]));
1467
1477
1468
1478
NSAssert (inputView.superview, @" inputView is not in the view hierarchy!" );
1469
1479
const CGPoint offset = CGPointMake (113 , 119 );
@@ -1472,8 +1482,8 @@ - (void)testUpdateFirstRectForRange {
1472
1482
inputView.frame = currentFrame;
1473
1483
// Moving the input view within the FlutterView shouldn't affect the coordinates,
1474
1484
// since the framework sends us global coordinates.
1475
- XCTAssertTrue ( CGRectEqualToRect ( CGRectMake (- 306 - 113 , 3 - 119 , 300 , 300 ),
1476
- [inputView firstRectForRange: range]));
1485
+ CGRect target = CGRectOffset (relativeRect, - 113 , - 119 );
1486
+ XCTAssertTrue ( CGRectEqualToRect (target, [inputView firstRectForRange: range]));
1477
1487
}
1478
1488
1479
1489
- (void )testFirstRectForRangeReturnsCorrectSelectionRect {
@@ -2295,6 +2305,76 @@ - (void)testInitialActiveViewCantAccessTextInputDelegate {
2295
2305
XCTAssertNil (textInputPlugin.activeView .textInputDelegate );
2296
2306
}
2297
2307
2308
+ - (void )testInputHiderIsOffScreenWhenScribbleIsDisabled {
2309
+ FlutterTextInputPlugin* myInputPlugin =
2310
+ [[FlutterTextInputPlugin alloc ] initWithDelegate: OCMClassMock ([FlutterEngine class ])];
2311
+ myInputPlugin.viewController = viewController;
2312
+
2313
+ NSSet <UIScene*>* scenes = UIApplication.sharedApplication .connectedScenes ;
2314
+ XCTAssertEqual (scenes.count , 1UL , @" There must only be 1 scene for test" );
2315
+ UIScene* scene = scenes.anyObject ;
2316
+ XCTAssert ([scene isKindOfClass: [UIWindowScene class ]], @" Must be a window scene for test" );
2317
+ UIWindowScene* windowScene = (UIWindowScene*)scene;
2318
+ XCTAssert (windowScene.windows .count > 0 , @" There must be at least 1 window for test" );
2319
+ UIWindow* window = windowScene.windows [0 ];
2320
+ [window addSubview: viewController.view];
2321
+ [viewController loadView ];
2322
+ UIScreen* screen = viewController.flutterScreenIfViewLoaded ;
2323
+ XCTAssertNotNil (screen, @" Screen must be present at this point" );
2324
+
2325
+ FlutterMethodCall* setClientCall =
2326
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setClient"
2327
+ arguments: @[ @(123 ), self .mutableTemplateCopy ]];
2328
+ [myInputPlugin handleMethodCall: setClientCall
2329
+ result: ^(id _Nullable result){
2330
+ }];
2331
+
2332
+ FlutterTextInputView* mockInputView = OCMPartialMock (myInputPlugin.activeView );
2333
+ OCMStub ([mockInputView isScribbleAvailable ]).andReturn (NO );
2334
+
2335
+ // yOffset = 200.
2336
+ NSArray * yOffsetMatrix = @[ @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @200 , @0 , @1 ];
2337
+
2338
+ FlutterMethodCall* setPlatformViewClientCall =
2339
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setEditableSizeAndTransform"
2340
+ arguments: @{@" transform" : yOffsetMatrix}];
2341
+ [myInputPlugin handleMethodCall: setPlatformViewClientCall
2342
+ result: ^(id _Nullable result){
2343
+ }];
2344
+
2345
+ CGRect offScreenRect = CGRectMake (0 , -screen.bounds .size .height , 0 , 0 );
2346
+ XCTAssert (CGRectEqualToRect (myInputPlugin.inputHider .frame , offScreenRect),
2347
+ @" The input hider should stay offScreen if scribble is disabled." );
2348
+ }
2349
+
2350
+ - (void )testInputHiderIsOnScreenWhenScribbleIsEnabled {
2351
+ FlutterTextInputPlugin* myInputPlugin =
2352
+ [[FlutterTextInputPlugin alloc ] initWithDelegate: OCMClassMock ([FlutterEngine class ])];
2353
+
2354
+ FlutterMethodCall* setClientCall =
2355
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setClient"
2356
+ arguments: @[ @(123 ), self .mutableTemplateCopy ]];
2357
+ [myInputPlugin handleMethodCall: setClientCall
2358
+ result: ^(id _Nullable result){
2359
+ }];
2360
+
2361
+ FlutterTextInputView* mockInputView = OCMPartialMock (myInputPlugin.activeView );
2362
+ OCMStub ([mockInputView isScribbleAvailable ]).andReturn (YES );
2363
+
2364
+ // yOffset = 200.
2365
+ NSArray * yOffsetMatrix = @[ @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @200 , @0 , @1 ];
2366
+
2367
+ FlutterMethodCall* setPlatformViewClientCall =
2368
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setEditableSizeAndTransform"
2369
+ arguments: @{@" transform" : yOffsetMatrix}];
2370
+ [myInputPlugin handleMethodCall: setPlatformViewClientCall
2371
+ result: ^(id _Nullable result){
2372
+ }];
2373
+
2374
+ XCTAssertEqual (myInputPlugin.inputHider .frame .origin .y , 200 ,
2375
+ @" The input hider should be brought on screen if scribble is enabled" );
2376
+ }
2377
+
2298
2378
#pragma mark - Accessibility - Tests
2299
2379
2300
2380
- (void )testUITextInputAccessibilityNotHiddenWhenShowed {
0 commit comments