Skip to content

Commit 96be524

Browse files
nturgutgspencergoog
authored andcommitted
updating editing state after location change. focusing on the element (flutter#20620)
1 parent 8f36410 commit 96be524

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

lib/web_ui/lib/src/engine/text_editing/text_editing.dart

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,10 +614,15 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy {
614614

615615
@override
616616
void placeElement() {
617-
super.placeElement();
618617
if (hasAutofillGroup) {
619618
_geometry?.applyToDomElement(focusedFormElement!);
620619
placeForm();
620+
// Set the last editing state if it exists, this is critical for a
621+
// users ongoing work to continue uninterrupted when there is an update to
622+
// the transform.
623+
if (_lastEditingState != null) {
624+
_lastEditingState!.applyToDomElement(domElement);
625+
}
621626
// On Chrome, when a form is focused, it opens an autofill menu
622627
// immediately.
623628
// Flutter framework sends `setEditableSizeAndTransform` for informing
@@ -627,7 +632,9 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy {
627632
// `setEditableSizeAndTransform` method is called and focus on the form
628633
// only after placing it to the correct position. Hence autofill menu
629634
// does not appear on top-left of the page.
635+
// Refocus on the elements after applying the geometry.
630636
focusedFormElement!.focus();
637+
domElement.focus();
631638
} else {
632639
_geometry?.applyToDomElement(domElement);
633640
}
@@ -663,6 +670,12 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy {
663670
_geometry?.applyToDomElement(domElement);
664671
if (hasAutofillGroup) {
665672
placeForm();
673+
// Set the last editing state if it exists, this is critical for a
674+
// users ongoing work to continue uninterrupted when there is an update to
675+
// the transform.
676+
if (_lastEditingState != null) {
677+
_lastEditingState!.applyToDomElement(domElement);
678+
}
666679
// On Safari Desktop, when a form is focused, it opens an autofill menu
667680
// immediately.
668681
// Flutter framework sends `setEditableSizeAndTransform` for informing
@@ -1236,6 +1249,12 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
12361249
void placeElement() {
12371250
domElement.focus();
12381251
_geometry?.applyToDomElement(domElement);
1252+
// Set the last editing state if it exists, this is critical for a
1253+
// users ongoing work to continue uninterrupted when there is an update to
1254+
// the transform.
1255+
if (_lastEditingState != null) {
1256+
_lastEditingState!.applyToDomElement(domElement);
1257+
}
12391258
}
12401259
}
12411260

lib/web_ui/test/text_editing_test.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,55 @@ void testMain() {
10131013
expect(formsOnTheDom, hasLength(1));
10141014
});
10151015

1016+
test(
1017+
'singleTextField Autofill setEditableSizeAndTransform preserves'
1018+
'editing state', () {
1019+
// Create a configuration with focused element has autofil hint.
1020+
final Map<String, dynamic> flutterSingleAutofillElementConfig =
1021+
createFlutterConfig('text', autofillHint: 'username');
1022+
final MethodCall setClient = MethodCall('TextInput.setClient',
1023+
<dynamic>[123, flutterSingleAutofillElementConfig]);
1024+
sendFrameworkMessage(codec.encodeMethodCall(setClient));
1025+
1026+
const MethodCall setEditingState1 =
1027+
MethodCall('TextInput.setEditingState', <String, dynamic>{
1028+
'text': 'abcd',
1029+
'selectionBase': 2,
1030+
'selectionExtent': 3,
1031+
});
1032+
sendFrameworkMessage(codec.encodeMethodCall(setEditingState1));
1033+
1034+
const MethodCall show = MethodCall('TextInput.show');
1035+
sendFrameworkMessage(codec.encodeMethodCall(show));
1036+
1037+
// The second [setEditingState] should override the first one.
1038+
checkInputEditingState(
1039+
textEditing.editingElement.domElement, 'abcd', 2, 3);
1040+
1041+
// The transform is changed. For example after a validation error, red
1042+
// line appeared under the input field.
1043+
final MethodCall setSizeAndTransform =
1044+
configureSetSizeAndTransformMethodCall(150, 50,
1045+
Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList());
1046+
sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform));
1047+
1048+
// Check the element still has focus. User can keep editing.
1049+
expect(document.activeElement, textEditing.editingElement.domElement);
1050+
1051+
// Check the cursor location is the same.
1052+
checkInputEditingState(
1053+
textEditing.editingElement.domElement, 'abcd', 2, 3);
1054+
1055+
const MethodCall clearClient = MethodCall('TextInput.clearClient');
1056+
sendFrameworkMessage(codec.encodeMethodCall(clearClient));
1057+
1058+
// Confirm that [HybridTextEditing] didn't send any messages.
1059+
expect(spy.messages, isEmpty);
1060+
// Form stays on the DOM until autofill context is finalized.
1061+
expect(document.getElementsByTagName('form'), isNotEmpty);
1062+
expect(formsOnTheDom, hasLength(1));
1063+
});
1064+
10161065
test(
10171066
'multiTextField Autofill: setClient, setEditingState, show, '
10181067
'setEditingState, clearClient', () {

0 commit comments

Comments
 (0)