diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 928bab1184605..0ba83ef38090a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -126,6 +126,7 @@ @implementation FlutterEngine { fml::scoped_nsobject _platformViewsChannel; fml::scoped_nsobject _textInputChannel; fml::scoped_nsobject _undoManagerChannel; + fml::scoped_nsobject _scribbleChannel; fml::scoped_nsobject _spellCheckChannel; fml::scoped_nsobject _lifecycleChannel; fml::scoped_nsobject _systemChannel; @@ -471,6 +472,9 @@ - (FlutterMethodChannel*)textInputChannel { - (FlutterMethodChannel*)undoManagerChannel { return _undoManagerChannel.get(); } +- (FlutterMethodChannel*)scribbleChannel { + return _scribbleChannel.get(); +} - (FlutterMethodChannel*)spellCheckChannel { return _spellCheckChannel.get(); } @@ -499,6 +503,7 @@ - (void)resetChannels { _platformViewsChannel.reset(); _textInputChannel.reset(); _undoManagerChannel.reset(); + _scribbleChannel.reset(); _lifecycleChannel.reset(); _systemChannel.reset(); _settingsChannel.reset(); @@ -572,6 +577,11 @@ - (void)setupChannels { binaryMessenger:self.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]); + _scribbleChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/scribble" + binaryMessenger:self.binaryMessenger + codec:[FlutterJSONMethodCodec sharedInstance]]); + _spellCheckChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/spellcheck" binaryMessenger:self.binaryMessenger @@ -965,22 +975,40 @@ - (void)flutterTextInputView:(FlutterTextInputView*)textInputView #pragma mark - FlutterViewEngineDelegate - (void)flutterTextInputView:(FlutterTextInputView*)textInputView showToolbar:(int)client { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.showToolbar" arguments:@[ @(client) ]]; + [_scribbleChannel.get() invokeMethod:@"Scribble.showToolbar" arguments:@[ @(client) ]]; } - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin focusElement:(UIScribbleElementIdentifier)elementIdentifier atPoint:(CGPoint)referencePoint result:(FlutterResult)callback { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.focusElement" arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ] result:callback]; + [_scribbleChannel.get() + invokeMethod:@"Scribble.focusElement" + arguments:@[ elementIdentifier, @(referencePoint.x), @(referencePoint.y) ] + result:callback]; } - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin requestElementsInRect:(CGRect)rect result:(FlutterResult)callback { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 + [_scribbleChannel.get() + invokeMethod:@"Scribble.requestElementsInRect" + arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ] + result:callback]; [_textInputChannel.get() invokeMethod:@"TextInputClient.requestElementsInRect" arguments:@[ @(rect.origin.x), @(rect.origin.y), @(rect.size.width), @(rect.size.height) ] @@ -988,25 +1016,42 @@ - (void)flutterTextInputPlugin:(FlutterTextInputPlugin*)textInputPlugin } - (void)flutterTextInputViewScribbleInteractionBegan:(FlutterTextInputView*)textInputView { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionBegan" arguments:nil]; + [_scribbleChannel.get() invokeMethod:@"Scribble.scribbleInteractionBegan" arguments:nil]; } - (void)flutterTextInputViewScribbleInteractionFinished:(FlutterTextInputView*)textInputView { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.scribbleInteractionFinished" arguments:nil]; + [_scribbleChannel.get() invokeMethod:@"Scribble.scribbleInteractionFinished" arguments:nil]; } - (void)flutterTextInputView:(FlutterTextInputView*)textInputView insertTextPlaceholderWithSize:(CGSize)size withClient:(int)client { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.insertTextPlaceholder" arguments:@[ @(client), @(size.width), @(size.height) ]]; + [_scribbleChannel.get() invokeMethod:@"Scribble.insertTextPlaceholder" + arguments:@[ @(client), @(size.width), @(size.height) ]]; } - (void)flutterTextInputView:(FlutterTextInputView*)textInputView removeTextPlaceholder:(int)client { + // TODO(justinmc): Remove the TextInputClient usage when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 [_textInputChannel.get() invokeMethod:@"TextInputClient.removeTextPlaceholder" arguments:@[ @(client) ]]; + [_scribbleChannel.get() invokeMethod:@"Scribble.removeTextPlaceholder" arguments:@[ @(client) ]]; } - (void)flutterTextInputViewDidResignFirstResponder:(FlutterTextInputView*)textInputView { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 6b0e9af3c0cb4..4ecdc6845e73a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -48,7 +48,11 @@ @"TextInput.setEditableSizeAndTransform"; static NSString* const kSetMarkedTextRectMethod = @"TextInput.setMarkedTextRect"; static NSString* const kFinishAutofillContextMethod = @"TextInput.finishAutofillContext"; -static NSString* const kSetSelectionRectsMethod = @"TextInput.setSelectionRects"; +// TODO(justinmc): Remove the TextInput method constant when the framework has +// finished transitioning to using the Scribble channel. +// https://github.com/flutter/flutter/pull/104128 +static NSString* const kDeprecatedSetSelectionRectsMethod = @"TextInput.setSelectionRects"; +static NSString* const kSetSelectionRectsMethod = @"Scribble.setSelectionRects"; static NSString* const kStartLiveTextInputMethod = @"TextInput.startLiveTextInput"; #pragma mark - TextInputConfiguration Field Names @@ -2170,6 +2174,12 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([method isEqualToString:kFinishAutofillContextMethod]) { [self triggerAutofillSave:[args boolValue]]; result(nil); + // TODO(justinmc): Remove the TextInput method constant when the framework has + // finished transitioning to using the Scribble channel. + // https://github.com/flutter/flutter/pull/104128 + } else if ([method isEqualToString:kDeprecatedSetSelectionRectsMethod]) { + [self setSelectionRects:args]; + result(nil); } else if ([method isEqualToString:kSetSelectionRectsMethod]) { [self setSelectionRects:args]; result(nil); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm index c83bd15640d99..462ea9e8359af 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm @@ -1854,6 +1854,38 @@ - (void)testGarbageInputViewsAreNotRemovedImmediately { [self commitAutofillContextAndVerify]; } +- (void)testScribbleSetSelectionRects { + NSMutableDictionary* regularField = self.mutableTemplateCopy; + NSDictionary* editingValue = @{ + @"text" : @"REGULAR_TEXT_FIELD", + @"composingBase" : @0, + @"composingExtent" : @3, + @"selectionBase" : @1, + @"selectionExtent" : @4 + }; + [regularField setValue:@{ + @"uniqueIdentifier" : @"field1", + @"hints" : @[ @"hint2" ], + @"editingValue" : editingValue, + } + forKey:@"autofill"]; + [regularField addEntriesFromDictionary:editingValue]; + [self setClientId:123 configuration:regularField]; + XCTAssertEqual(self.installedInputViews.count, 1ul); + XCTAssertEqual([textInputPlugin.activeView.selectionRects count], 0u); + + NSArray* selectionRect = [NSArray arrayWithObjects:@0, @0, @100, @100, @0, nil]; + NSArray* selectionRects = [NSArray arrayWithObjects:selectionRect, nil]; + FlutterMethodCall* methodCall = + [FlutterMethodCall methodCallWithMethodName:@"Scribble.setSelectionRects" + arguments:selectionRects]; + [textInputPlugin handleMethodCall:methodCall + result:^(id _Nullable result){ + }]; + + XCTAssertEqual([textInputPlugin.activeView.selectionRects count], 1u); +} + - (void)testDecommissionedViewAreNotReusedByAutofill { // Regression test for https://github.com/flutter/flutter/issues/84407. NSMutableDictionary* configuration = self.mutableTemplateCopy;