[web] Don't show handles when selection change is caused by keyboard (#65127)

This commit is contained in:
Mouad Debbar 2020-09-09 15:40:06 -07:00 committed by GitHub
parent 389b5b6c3a
commit 0d8de393aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 132 additions and 1 deletions

View File

@ -1643,7 +1643,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
_formatAndSetValue(value);
if (_isSelectionOnlyChange(value)) {
_handleSelectionChanged(value.selection, renderEditable, SelectionChangedCause.keyboard);
} else {
_formatAndSetValue(value);
}
if (_hasInputConnection) {
// To keep the cursor from blinking while typing, we want to restart the
@ -1653,6 +1657,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
bool _isSelectionOnlyChange(TextEditingValue value) {
return value.text == _value.text && value.composing == _value.composing;
}
@override
void performAction(TextInputAction action) {
switch (action) {

View File

@ -7795,6 +7795,48 @@ void main() {
},
);
testWidgets('Does not show handles when updated from the web engine', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'abc def ghi',
);
await tester.pumpWidget(
MaterialApp(
home: Material(
child: TextField(controller: controller),
),
),
);
// Interact with the text field to establish the input connection.
final Offset topLeft = tester.getTopLeft(find.byType(EditableText));
final TestGesture gesture = await tester.startGesture(
topLeft + const Offset(0.0, 5.0),
kind: PointerDeviceKind.mouse,
);
addTearDown(gesture.removePointer);
await tester.pump(const Duration(milliseconds: 50));
await gesture.up();
await tester.pumpAndSettle();
final EditableTextState state = tester.state(find.byType(EditableText));
expect(state.selectionOverlay.handlesAreVisible, isFalse);
expect(controller.selection, const TextSelection.collapsed(offset: 0));
if (kIsWeb) {
tester.testTextInput.updateEditingValue(const TextEditingValue(
selection: TextSelection(baseOffset: 2, extentOffset: 7),
));
// Wait for all the `setState` calls to be flushed.
await tester.pumpAndSettle();
expect(
state.currentTextEditingValue.selection,
const TextSelection(baseOffset: 2, extentOffset: 7),
);
expect(state.selectionOverlay.handlesAreVisible, isFalse);
}
});
testWidgets('Tapping selection handles toggles the toolbar', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'abc def ghi',

View File

@ -1398,6 +1398,46 @@ void main() {
}
});
testWidgets('Read-only fields do not format text', (WidgetTester tester) async {
SelectionChangedCause selectionCause;
final TextEditingController controller =
TextEditingController(text: 'Lorem ipsum dolor sit amet');
await tester.pumpWidget(
MaterialApp(
home: EditableText(
readOnly: true,
controller: controller,
backgroundCursorColor: Colors.grey,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
selectionControls: materialTextSelectionControls,
onSelectionChanged: (TextSelection selection, SelectionChangedCause cause) {
selectionCause = cause;
},
),
),
);
// Interact with the field to establish the input connection.
final Offset topLeft = tester.getTopLeft(find.byType(EditableText));
await tester.tapAt(topLeft + const Offset(0.0, 5.0));
await tester.pump();
expect(tester.testTextInput.hasAnyClients, kIsWeb ? isTrue : isFalse);
if (kIsWeb) {
tester.testTextInput.updateEditingValue(const TextEditingValue(
text: 'Foo bar',
selection: TextSelection(baseOffset: 0, extentOffset: 3),
));
// On web, the only way a text field can be updated from the engine is if
// a keyboard is used.
expect(selectionCause, SelectionChangedCause.keyboard);
}
});
testWidgets('Fires onChanged when text changes via TextSelectionOverlay', (WidgetTester tester) async {
String changedValue;
final Widget widget = MaterialApp(

View File

@ -3971,4 +3971,45 @@ void main() {
TargetPlatform.windows,
}),
);
testWidgets('Does not show handles when updated from the web engine', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: SelectableText('abc def ghi'),
),
),
);
// Interact with the selectable text to establish the input connection.
final Offset topLeft = tester.getTopLeft(find.byType(EditableText));
final TestGesture gesture = await tester.startGesture(
topLeft + const Offset(0.0, 5.0),
kind: PointerDeviceKind.mouse,
);
addTearDown(gesture.removePointer);
await tester.pump(const Duration(milliseconds: 50));
await gesture.up();
await tester.pumpAndSettle();
final EditableTextState state = tester.state(find.byType(EditableText));
expect(state.selectionOverlay.handlesAreVisible, isFalse);
expect(
state.currentTextEditingValue.selection,
const TextSelection.collapsed(offset: 0),
);
if (kIsWeb) {
tester.testTextInput.updateEditingValue(const TextEditingValue(
selection: TextSelection(baseOffset: 2, extentOffset: 7),
));
// Wait for all the `setState` calls to be flushed.
await tester.pumpAndSettle();
expect(
state.currentTextEditingValue.selection,
const TextSelection(baseOffset: 2, extentOffset: 7),
);
expect(state.selectionOverlay.handlesAreVisible, isFalse);
}
});
}