mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[iOS TextInput] Avoid Unnecessary UndateEditingClient Calls (flutter/engine#21303)
This commit is contained in:
parent
73ae0bb94c
commit
1357779b57
@ -538,6 +538,8 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) {
|
||||
}
|
||||
|
||||
// Return true if the new input state needs to be synced back to the framework.
|
||||
// TODO(LongCatIsLooong): setTextInputState should never call updateEditingState. Sending the
|
||||
// editing value back may overwrite the framework's updated editing value.
|
||||
- (BOOL)setTextInputState:(NSDictionary*)state {
|
||||
NSString* newText = state[@"text"];
|
||||
BOOL textChanged = ![self.text isEqualToString:newText];
|
||||
@ -545,19 +547,14 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) {
|
||||
[self.inputDelegate textWillChange:self];
|
||||
[self.text setString:newText];
|
||||
}
|
||||
BOOL needsEditingStateUpdate = textChanged;
|
||||
NSInteger composingBase = [state[@"composingBase"] intValue];
|
||||
NSInteger composingExtent = [state[@"composingExtent"] intValue];
|
||||
NSRange composingRange = [self clampSelection:NSMakeRange(MIN(composingBase, composingExtent),
|
||||
ABS(composingBase - composingExtent))
|
||||
forText:self.text];
|
||||
FlutterTextRange* newMarkedRange =
|
||||
|
||||
self.markedTextRange =
|
||||
composingRange.length > 0 ? [FlutterTextRange rangeWithNSRange:composingRange] : nil;
|
||||
needsEditingStateUpdate =
|
||||
needsEditingStateUpdate ||
|
||||
(!newMarkedRange ? self.markedTextRange != nil
|
||||
: ![newMarkedRange isEqualTo:(FlutterTextRange*)self.markedTextRange]);
|
||||
self.markedTextRange = newMarkedRange;
|
||||
|
||||
NSRange selectedRange = [self clampSelectionFromBase:[state[@"selectionBase"] intValue]
|
||||
extent:[state[@"selectionExtent"] intValue]
|
||||
@ -565,7 +562,6 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) {
|
||||
|
||||
NSRange oldSelectedRange = [(FlutterTextRange*)self.selectedTextRange range];
|
||||
if (!NSEqualRanges(selectedRange, oldSelectedRange)) {
|
||||
needsEditingStateUpdate = YES;
|
||||
[self.inputDelegate selectionWillChange:self];
|
||||
|
||||
[self setSelectedTextRangeLocal:[FlutterTextRange rangeWithNSRange:selectedRange]];
|
||||
@ -580,8 +576,8 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) {
|
||||
[self.inputDelegate textDidChange:self];
|
||||
}
|
||||
|
||||
// For consistency with Android behavior, send an update to the framework if anything changed.
|
||||
return needsEditingStateUpdate;
|
||||
// For consistency with Android behavior, send an update to the framework if the text changed.
|
||||
return textChanged;
|
||||
}
|
||||
|
||||
// Extracts the selection information from the editing state dictionary.
|
||||
@ -788,6 +784,8 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) {
|
||||
}
|
||||
|
||||
- (void)unmarkText {
|
||||
if (!self.markedTextRange)
|
||||
return;
|
||||
self.markedTextRange = nil;
|
||||
[self updateEditingState];
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ FLUTTER_ASSERT_ARC
|
||||
XCTAssertFalse([inputView setTextInputState:@{@"text" : @"AFTER"}]);
|
||||
}
|
||||
|
||||
- (void)testSelectionChangeTriggersUpdateEditingClient {
|
||||
- (void)testSelectionChangeDoesNotTriggerUpdateEditingClient {
|
||||
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
|
||||
inputView.textInputDelegate = engine;
|
||||
|
||||
@ -236,15 +236,15 @@ FLUTTER_ASSERT_ARC
|
||||
|
||||
BOOL shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @3}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
// Don't send anything if there's nothing new.
|
||||
shouldUpdate = [inputView
|
||||
@ -252,7 +252,7 @@ FLUTTER_ASSERT_ARC
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
}
|
||||
|
||||
- (void)testComposingChangeTriggersUpdateEditingClient {
|
||||
- (void)testComposingChangeDoesNotTriggerUpdateEditingClient {
|
||||
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
|
||||
inputView.textInputDelegate = engine;
|
||||
|
||||
@ -263,20 +263,42 @@ FLUTTER_ASSERT_ARC
|
||||
|
||||
BOOL shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @0, @"composingExtent" : @3}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}];
|
||||
XCTAssertTrue(shouldUpdate);
|
||||
|
||||
// Don't send anything if there's nothing new.
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}];
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
|
||||
shouldUpdate = [inputView
|
||||
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}];
|
||||
XCTAssertFalse(shouldUpdate);
|
||||
}
|
||||
|
||||
- (void)testUITextInputAvoidUnnecessaryUndateEditingClientCalls {
|
||||
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
|
||||
inputView.textInputDelegate = engine;
|
||||
|
||||
__block int updateCount = 0;
|
||||
OCMStub([engine updateEditingClient:0 withState:[OCMArg isNotNil]])
|
||||
.andDo(^(NSInvocation* invocation) {
|
||||
updateCount++;
|
||||
});
|
||||
|
||||
[inputView unmarkText];
|
||||
// updateEditingClient shouldn't fire as the text is already unmarked.
|
||||
XCTAssertEqual(updateCount, 0);
|
||||
|
||||
[inputView setMarkedText:@"marked text" selectedRange:NSMakeRange(0, 1)];
|
||||
// updateEditingClient fires in response to setMarkedText.
|
||||
XCTAssertEqual(updateCount, 1);
|
||||
|
||||
[inputView unmarkText];
|
||||
// updateEditingClient fires in response to unmarkText.
|
||||
XCTAssertEqual(updateCount, 2);
|
||||
}
|
||||
|
||||
- (void)testUpdateEditingClientNegativeSelection {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user