diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 724af2afdc5e0..ead0e0c5816b0 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -868,6 +868,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginA FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index bfb4a2d2ff8cd..c3302194b039a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -598,6 +598,13 @@ - (void)performAction:(FlutterTextInputAction)action withClient:(int)client { arguments:@[ @(client), actionString ]]; } +- (void)showAutocorrectionPromptRectForStart:(NSUInteger)start + end:(NSUInteger)end + withClient:(int)client { + [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect" + arguments:@[ @(client), @(start), @(end) ]]; +} + #pragma mark - Screenshot Delegate - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index d3451b5bd4cd4..8446a79946d63 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -34,7 +34,9 @@ typedef NS_ENUM(NSInteger, FlutterFloatingCursorDragState) { - (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state withClient:(int)client withPosition:(NSDictionary*)point; - +- (void)showAutocorrectionPromptRectForStart:(NSUInteger)start + end:(NSUInteger)end + withClient:(int)client; @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h index 70bd949579ce2..3677c0f78284a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h @@ -36,6 +36,9 @@ @end /** A range of text in the buffer of a Flutter text editing widget. */ +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +FLUTTER_EXPORT +#endif @interface FlutterTextRange : UITextRange @property(nonatomic, readonly) NSRange range; @@ -44,4 +47,32 @@ @end +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +FLUTTER_EXPORT +#endif +@interface FlutterTextInputView : UIView + +// UITextInput +@property(nonatomic, readonly) NSMutableString* text; +@property(nonatomic, readonly) NSMutableString* markedText; +@property(readwrite, copy) UITextRange* selectedTextRange; +@property(nonatomic, strong) UITextRange* markedTextRange; +@property(nonatomic, copy) NSDictionary* markedTextStyle; +@property(nonatomic, assign) id inputDelegate; + +// UITextInputTraits +@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; +@property(nonatomic) UITextAutocorrectionType autocorrectionType; +@property(nonatomic) UITextSpellCheckingType spellCheckingType; +@property(nonatomic) BOOL enablesReturnKeyAutomatically; +@property(nonatomic) UIKeyboardAppearance keyboardAppearance; +@property(nonatomic) UIKeyboardType keyboardType; +@property(nonatomic) UIReturnKeyType returnKeyType; +@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; +@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); +@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); + +@property(nonatomic, assign) id textInputDelegate; + +@end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 029932489f4fd..f9ddd6621d6d8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -140,32 +140,6 @@ - (id)copyWithZone:(NSZone*)zone { @end -@interface FlutterTextInputView : UIView - -// UITextInput -@property(nonatomic, readonly) NSMutableString* text; -@property(nonatomic, readonly) NSMutableString* markedText; -@property(readwrite, copy) UITextRange* selectedTextRange; -@property(nonatomic, strong) UITextRange* markedTextRange; -@property(nonatomic, copy) NSDictionary* markedTextStyle; -@property(nonatomic, assign) id inputDelegate; - -// UITextInputTraits -@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; -@property(nonatomic) UITextAutocorrectionType autocorrectionType; -@property(nonatomic) UITextSpellCheckingType spellCheckingType; -@property(nonatomic) BOOL enablesReturnKeyAutomatically; -@property(nonatomic) UIKeyboardAppearance keyboardAppearance; -@property(nonatomic) UIKeyboardType keyboardType; -@property(nonatomic) UIReturnKeyType returnKeyType; -@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; -@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); -@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); - -@property(nonatomic, assign) id textInputDelegate; - -@end - @implementation FlutterTextInputView { int _textInputClient; const char* _selectionAffinity; @@ -554,6 +528,16 @@ - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection // physical keyboard. - (CGRect)firstRectForRange:(UITextRange*)range { + // multi-stage text is handled somewhere else. + if (_markedTextRange != nil) { + return CGRectZero; + } + + NSUInteger start = ((FlutterTextPosition*)range.start).index; + NSUInteger end = ((FlutterTextPosition*)range.end).index; + [_textInputDelegate showAutocorrectionPromptRectForStart:start + end:end + withClient:_textInputClient]; // TODO(cbracken) Implement. return CGRectZero; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m new file mode 100644 index 0000000000000..6610d9fe4d8cd --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" + +FLUTTER_ASSERT_ARC + +@interface FlutterTextInputPluginTest : XCTestCase +@end + +@implementation FlutterTextInputPluginTest + +- (void)testAutocorrectionPromptRectAppears { + // Setup test. + id engine = OCMClassMock([FlutterEngine class]); + + FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithFrame:CGRectZero]; + inputView.textInputDelegate = engine; + [inputView firstRectForRange:[FlutterTextRange rangeWithNSRange:NSMakeRange(0, 1)]]; + + // Verify behavior. + OCMVerify([engine showAutocorrectionPromptRectForStart:0 end:1 withClient:0]); + + // Clean up mocks + [engine stopMocking]; +} +@end diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 82f36199ac95a..533ff80fd389c 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D6AB72C22BC339F00EEE540 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D6AB72522BC336100EEE540 /* libOCMock.a */; }; 0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */; }; + 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -94,6 +95,7 @@ 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterEngineTest.mm; sourceTree = ""; }; 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OCMock.xcodeproj; path = ../../../../../third_party/ocmock/Source/OCMock.xcodeproj; sourceTree = ""; }; 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; + 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterTextInputPluginTest.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -176,6 +178,7 @@ children = ( 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */, 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */, + 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */, 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */, 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */, ); @@ -387,6 +390,7 @@ buildActionMask = 2147483647; files = ( 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */, + 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */, 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */, 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */, 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */,