Skip to content

Commit

Permalink
[iOS] Remove selectionDidChange call in UndoManager (#45657)
Browse files Browse the repository at this point in the history
Fixes flutter/flutter#133424

The `-[TextInputDelegate selectionDidChange:]` call actually triggers some unwanted keyboard NLP actions that generate a bunch of candidates and automatically accept the first candidate. This causes `-[UITextInput setMarkedText:selection]` to be called with the first candidate and that inserts extraneous characters after the user types certain characters on the iPad software keyboard.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
LongCatIsLooong committed Sep 18, 2023
1 parent e1c784e commit 2178248
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) {
// This is needed to notify the iPadOS keyboard that it needs to update the
// state of the UIBarButtons. Otherwise, the state changes to NSUndoManager
// will not show up until the next keystroke (or other trigger).
id<UITextInputDelegate> inputDelegate =
_viewController.engine.textInputPlugin.textInputView.inputDelegate;
[inputDelegate selectionDidChange:_viewController.engine.textInputPlugin.textInputView];
UITextInputAssistantItem* assistantItem =
_viewController.engine.textInputPlugin.textInputView.inputAssistantItem;
assistantItem.leadingBarButtonGroups = assistantItem.leadingBarButtonGroups;
}
[self undoManager].groupsByEvent = groupsByEvent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"

FLUTTER_ASSERT_ARC

@interface FlutterEngine ()
- (nonnull FlutterUndoManagerPlugin*)undoManagerPlugin;
- (nonnull FlutterTextInputPlugin*)textInputPlugin;
@end

@interface FlutterUndoManagerPluginForTest : FlutterUndoManagerPlugin
Expand Down Expand Up @@ -50,7 +52,6 @@ - (void)setUp {

- (void)tearDown {
[self.undoManager removeAllActionsWithTarget:self.undoManagerPlugin];
self.undoManagerPlugin = nil;
self.engine = nil;
self.viewController = nil;
self.undoManager = nil;
Expand Down Expand Up @@ -140,4 +141,26 @@ - (void)testSetUndoState {
XCTAssertEqual(2, delegateRedoCount);
}

- (void)testSetUndoStateDoesInteractWithInputDelegate {
// Regression test for https://github.com/flutter/flutter/issues/133424
FlutterViewController* viewController = OCMPartialMock(self.viewController);
self.undoManagerPlugin.viewController = self.viewController;

FlutterTextInputPlugin* textInputPlugin = OCMClassMock([FlutterTextInputPlugin class]);
FlutterTextInputView* textInputView = OCMClassMock([FlutterTextInputView class]);

OCMStub([viewController engine]).andReturn(self.engine);
OCMStub([self.engine textInputPlugin]).andReturn(textInputPlugin);
OCMStub([textInputPlugin textInputView]).andReturn(textInputView);

FlutterMethodCall* setUndoStateCall =
[FlutterMethodCall methodCallWithMethodName:@"UndoManager.setUndoState"
arguments:@{@"canUndo" : @NO, @"canRedo" : @NO}];
[self.undoManagerPlugin handleMethodCall:setUndoStateCall
result:^(id _Nullable result){
}];

OCMVerify(never(), [textInputView inputDelegate]);
}

@end

0 comments on commit 2178248

Please sign in to comment.