mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
iOS spell check cursor placement (#124875)
Fixes the cursor location after selecting a spell check result on iOS.
This commit is contained in:
parent
f7245b647e
commit
39becb7770
@ -98,10 +98,16 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
|
||||
// Replacement cannot be performed if the text is read only or obscured.
|
||||
assert(!editableTextState.widget.readOnly && !editableTextState.widget.obscureText);
|
||||
|
||||
final TextEditingValue newValue = editableTextState.textEditingValue.replaced(
|
||||
replacementRange,
|
||||
text,
|
||||
);
|
||||
final TextEditingValue newValue = editableTextState.textEditingValue
|
||||
.replaced(
|
||||
replacementRange,
|
||||
text,
|
||||
)
|
||||
.copyWith(
|
||||
selection: TextSelection.collapsed(
|
||||
offset: replacementRange.start + text.length,
|
||||
),
|
||||
);
|
||||
editableTextState.userUpdateTextEditingValue(newValue,SelectionChangedCause.toolbar);
|
||||
|
||||
// Schedule a call to bringIntoView() after renderEditable updates.
|
||||
@ -111,7 +117,6 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
|
||||
}
|
||||
});
|
||||
editableTextState.hideToolbar();
|
||||
editableTextState.renderEditable.selectWordEdge(cause: SelectionChangedCause.toolbar);
|
||||
}
|
||||
|
||||
/// Builds the toolbar buttons based on the [buttonItems].
|
||||
|
||||
@ -15294,6 +15294,118 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async
|
||||
expect(state.currentTextEditingValue.selection.baseOffset, equals(0));
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('replacing puts cursor at the end of the word', (WidgetTester tester) async {
|
||||
tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue =
|
||||
true;
|
||||
controller.value = const TextEditingValue(
|
||||
// All misspellings of "test". One the same length, one shorter, and one
|
||||
// longer.
|
||||
text: 'tset tst testt',
|
||||
selection: TextSelection(affinity: TextAffinity.upstream, baseOffset: 0, extentOffset: 4),
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: EditableText(
|
||||
backgroundCursorColor: Colors.grey,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
style: textStyle,
|
||||
cursorColor: cursorColor,
|
||||
selectionControls: materialTextSelectionControls,
|
||||
spellCheckConfiguration:
|
||||
const SpellCheckConfiguration(
|
||||
misspelledTextStyle: CupertinoTextField.cupertinoMisspelledTextStyle,
|
||||
spellCheckSuggestionsToolbarBuilder: CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableTextState state =
|
||||
tester.state<EditableTextState>(find.byType(EditableText));
|
||||
|
||||
state.spellCheckResults = SpellCheckResults(
|
||||
controller.value.text,
|
||||
const <SuggestionSpan>[
|
||||
SuggestionSpan(TextRange(start: 0, end: 4), <String>['test']),
|
||||
SuggestionSpan(TextRange(start: 5, end: 8), <String>['test']),
|
||||
SuggestionSpan(TextRange(start: 9, end: 13), <String>['test']),
|
||||
]);
|
||||
await tester.tapAt(textOffsetToPosition(tester, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
|
||||
// Replacing a word of the same length as the replacement puts the cursor
|
||||
// at the end of the new word.
|
||||
await tester.tap(find.text('test'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
controller.value,
|
||||
equals(const TextEditingValue(
|
||||
text: 'test tst testt',
|
||||
selection: TextSelection.collapsed(
|
||||
offset: 4,
|
||||
),
|
||||
)),
|
||||
);
|
||||
|
||||
state.spellCheckResults = SpellCheckResults(
|
||||
controller.value.text,
|
||||
const <SuggestionSpan>[
|
||||
SuggestionSpan(TextRange(start: 5, end: 8), <String>['test']),
|
||||
SuggestionSpan(TextRange(start: 9, end: 13), <String>['test']),
|
||||
]);
|
||||
await tester.tapAt(textOffsetToPosition(tester, 5));
|
||||
await tester.pumpAndSettle();
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
|
||||
// Replacing a word of less length as the replacement puts the cursor at
|
||||
// the end of the new word.
|
||||
await tester.tap(find.text('test'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
controller.value,
|
||||
equals(const TextEditingValue(
|
||||
text: 'test test testt',
|
||||
selection: TextSelection.collapsed(
|
||||
offset: 9,
|
||||
),
|
||||
)),
|
||||
);
|
||||
|
||||
state.spellCheckResults = SpellCheckResults(
|
||||
controller.value.text,
|
||||
const <SuggestionSpan>[
|
||||
SuggestionSpan(TextRange(start: 10, end: 15), <String>['test']),
|
||||
]);
|
||||
await tester.tapAt(textOffsetToPosition(tester, 10));
|
||||
await tester.pumpAndSettle();
|
||||
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
|
||||
// Replacing a word of greater length as the replacement puts the cursor
|
||||
// at the end of the new word.
|
||||
await tester.tap(find.text('test'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
controller.value,
|
||||
equals(const TextEditingValue(
|
||||
text: 'test test test',
|
||||
selection: TextSelection.collapsed(
|
||||
offset: 14,
|
||||
),
|
||||
)),
|
||||
);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }),
|
||||
skip: kIsWeb, // [intended]
|
||||
);
|
||||
});
|
||||
|
||||
group('magnifier', () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user