Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -538,34 +538,30 @@ - (void)setTextInputClient:(int)client {
}

// 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];
if (textChanged) {
[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]
forText:self.text];

NSRange oldSelectedRange = [(FlutterTextRange*)self.selectedTextRange range];
if (!NSEqualRanges(selectedRange, oldSelectedRange)) {
needsEditingStateUpdate = YES;
[self.inputDelegate selectionWillChange:self];

[self setSelectedTextRangeLocal:[FlutterTextRange rangeWithNSRange:selectedRange]];
Expand All @@ -580,8 +576,8 @@ - (BOOL)setTextInputState:(NSDictionary*)state {
[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.
Expand Down Expand Up @@ -788,6 +784,8 @@ - (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelecte
}

- (void)unmarkText {
if (!self.markedTextRange)
return;
self.markedTextRange = nil;
[self updateEditingState];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ - (void)testTextChangesTriggerUpdateEditingClient {
XCTAssertFalse([inputView setTextInputState:@{@"text" : @"AFTER"}]);
}

- (void)testSelectionChangeTriggersUpdateEditingClient {
- (void)testSelectionChangeDoesNotTriggerUpdateEditingClient {
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
inputView.textInputDelegate = engine;

Expand All @@ -236,23 +236,23 @@ - (void)testSelectionChangeTriggersUpdateEditingClient {

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
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}];
XCTAssertFalse(shouldUpdate);
}

- (void)testComposingChangeTriggersUpdateEditingClient {
- (void)testComposingChangeDoesNotTriggerUpdateEditingClient {
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
inputView.textInputDelegate = engine;

Expand All @@ -263,22 +263,44 @@ - (void)testComposingChangeTriggersUpdateEditingClient {

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);
XCTAssertFalse(shouldUpdate);

// Don't send anything if there's nothing new.
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 {
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
inputView.textInputDelegate = engine;
Expand Down