Skip to content

Commit

Permalink
[web] - fix text editing IME composition interruption on geometry upd…
Browse files Browse the repository at this point in the history
…ates (flutter#51323)

This change fixes an issue where IME composition gets interrupted when the `setEditableSizeAndTransform` platform message is received mid-composition.  This occurs when a multiline input expands and needs to inform the underlying `textarea` to update its size.  

Fixes flutter/flutter#134797
Fixes flutter/flutter#98817
  • Loading branch information
htoor3 authored Mar 14, 2024
1 parent 14d712d commit 71dfa10
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
11 changes: 10 additions & 1 deletion lib/web_ui/lib/src/engine/text_editing/text_editing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,16 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements
void updateElementPlacement(EditableTextGeometry textGeometry) {
geometry = textGeometry;
if (isEnabled) {
placeElement();
// On updates, we shouldn't go through the entire placeElement() flow if
// we are in the middle of IME composition, otherwise we risk interrupting it.
// Geometry updates occur when a multiline input expands or contracts. If
// we are in the middle of composition, we should just update the geometry.
// See: https://github.com/flutter/flutter/issues/98817
if (composingText != null) {
geometry?.applyToDomElement(activeDomElement);
} else {
placeElement();
}
}
}

Expand Down
70 changes: 70 additions & 0 deletions lib/web_ui/test/engine/text_editing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,64 @@ Future<void> testMain() async {
expect(editingStrategy!.domElement!.style.width, '13px');
expect(editingStrategy!.domElement!.style.height, '12px');
});

test('updateElementPlacement() should not call placeElement() when in mid-composition', () {
final HybridTextEditing testTextEditing = HybridTextEditing();
final GlobalTextEditingStrategySpy editingStrategy = GlobalTextEditingStrategySpy(testTextEditing);
testTextEditing.debugTextEditingStrategyOverride = editingStrategy;
testTextEditing.configuration = singlelineConfig;

editingStrategy.enable(
singlelineConfig,
onChange: trackEditingState,
onAction: trackInputAction,
);
expect(editingStrategy.isEnabled, isTrue);

// placeElement() called from enable()
expect(editingStrategy.placeElementCount, 1);

// No geometry should be set until setEditableSizeAndTransform is called.
expect(editingStrategy.domElement!.style.transform, '');
expect(editingStrategy.domElement!.style.width, '');
expect(editingStrategy.domElement!.style.height, '');

// set some composing text.
editingStrategy.composingText = '뮤';

testTextEditing.acceptCommand(TextInputSetEditableSizeAndTransform(geometry: EditableTextGeometry(
width: 13,
height: 12,
globalTransform: Matrix4.translationValues(14, 15, 0).storage,
)), () {});

// placeElement() should not be called again.
expect(editingStrategy.placeElementCount, 1);

// geometry should be applied.
expect(editingStrategy.domElement!.style.transform,
'matrix(1, 0, 0, 1, 14, 15)');
expect(editingStrategy.domElement!.style.width, '13px');
expect(editingStrategy.domElement!.style.height, '12px');

// set composing text to null.
editingStrategy.composingText = null;

testTextEditing.acceptCommand(TextInputSetEditableSizeAndTransform(geometry: EditableTextGeometry(
width: 10,
height: 10,
globalTransform: Matrix4.translationValues(11, 12, 0).storage,
)), () {});

// placeElement() should be called again.
expect(editingStrategy.placeElementCount, 2);

// geometry should be updated.
expect(editingStrategy.domElement!.style.transform,
'matrix(1, 0, 0, 1, 11, 12)');
expect(editingStrategy.domElement!.style.width, '10px');
expect(editingStrategy.domElement!.style.height, '10px');
});
});

group('$HybridTextEditing', () {
Expand Down Expand Up @@ -3408,3 +3466,15 @@ Future<void> waitForDesktopSafariFocus() async {
await Future<void>.delayed(Duration.zero);
}
}

class GlobalTextEditingStrategySpy extends GloballyPositionedTextEditingStrategy {
GlobalTextEditingStrategySpy(super.owner);

int placeElementCount = 0;

@override
void placeElement() {
placeElementCount++;
super.placeElement();
}
}

0 comments on commit 71dfa10

Please sign in to comment.