695 lines
23 KiB
Dart
695 lines
23 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter/gestures.dart' show PointerDeviceKind;
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
void main() {
|
|
int tapCount;
|
|
int singleTapUpCount;
|
|
int singleTapCancelCount;
|
|
int singleLongTapStartCount;
|
|
int doubleTapDownCount;
|
|
int forcePressStartCount;
|
|
int forcePressEndCount;
|
|
int dragStartCount;
|
|
int dragUpdateCount;
|
|
int dragEndCount;
|
|
const Offset forcePressOffset = Offset(400.0, 50.0);
|
|
|
|
void _handleTapDown(TapDownDetails details) { tapCount++; }
|
|
void _handleSingleTapUp(TapUpDetails details) { singleTapUpCount++; }
|
|
void _handleSingleTapCancel() { singleTapCancelCount++; }
|
|
void _handleSingleLongTapStart(LongPressStartDetails details) { singleLongTapStartCount++; }
|
|
void _handleDoubleTapDown(TapDownDetails details) { doubleTapDownCount++; }
|
|
void _handleForcePressStart(ForcePressDetails details) { forcePressStartCount++; }
|
|
void _handleForcePressEnd(ForcePressDetails details) { forcePressEndCount++; }
|
|
void _handleDragSelectionStart(DragStartDetails details) { dragStartCount++; }
|
|
void _handleDragSelectionUpdate(DragStartDetails _, DragUpdateDetails details) { dragUpdateCount++; }
|
|
void _handleDragSelectionEnd(DragEndDetails details) { dragEndCount++; }
|
|
|
|
setUp(() {
|
|
tapCount = 0;
|
|
singleTapUpCount = 0;
|
|
singleTapCancelCount = 0;
|
|
singleLongTapStartCount = 0;
|
|
doubleTapDownCount = 0;
|
|
forcePressStartCount = 0;
|
|
forcePressEndCount = 0;
|
|
dragStartCount = 0;
|
|
dragUpdateCount = 0;
|
|
dragEndCount = 0;
|
|
});
|
|
|
|
Future<void> pumpGestureDetector(WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
TextSelectionGestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTapDown: _handleTapDown,
|
|
onSingleTapUp: _handleSingleTapUp,
|
|
onSingleTapCancel: _handleSingleTapCancel,
|
|
onSingleLongTapStart: _handleSingleLongTapStart,
|
|
onDoubleTapDown: _handleDoubleTapDown,
|
|
onForcePressStart: _handleForcePressStart,
|
|
onForcePressEnd: _handleForcePressEnd,
|
|
onDragSelectionStart: _handleDragSelectionStart,
|
|
onDragSelectionUpdate: _handleDragSelectionUpdate,
|
|
onDragSelectionEnd: _handleDragSelectionEnd,
|
|
child: Container(),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> pumpTextSelectionGestureDetectorBuilder(
|
|
WidgetTester tester, {
|
|
bool forcePressEnabled = true,
|
|
bool selectionEnabled = true,
|
|
}) async {
|
|
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
|
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
|
editableTextKey: editableTextKey,
|
|
forcePressEnabled: forcePressEnabled,
|
|
selectionEnabled: selectionEnabled,
|
|
);
|
|
final TextSelectionGestureDetectorBuilder provider =
|
|
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: provider.buildGestureDetector(
|
|
behavior: HitTestBehavior.translucent,
|
|
child: FakeEditableText(key: editableTextKey),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
testWidgets('a series of taps all call onTaps', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.tapAt(const Offset(200, 200));
|
|
expect(tapCount, 6);
|
|
});
|
|
|
|
testWidgets('in a series of rapid taps, onTapDown and onDoubleTapDown alternate', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 1);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 1);
|
|
expect(doubleTapDownCount, 1);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 2);
|
|
expect(doubleTapDownCount, 1);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 2);
|
|
expect(doubleTapDownCount, 2);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 3);
|
|
expect(doubleTapDownCount, 2);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
expect(singleTapUpCount, 3);
|
|
expect(doubleTapDownCount, 3);
|
|
expect(tapCount, 6);
|
|
});
|
|
|
|
testWidgets('quick tap-tap-hold is a double tap down', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
await tester.tapAt(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
expect(singleTapUpCount, 1);
|
|
final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
expect(singleTapUpCount, 1);
|
|
// Every down is counted.
|
|
expect(tapCount, 2);
|
|
// No cancels because the second tap of the double tap is a second successful
|
|
// single tap behind the scene.
|
|
expect(singleTapCancelCount, 0);
|
|
expect(doubleTapDownCount, 1);
|
|
// The double tap down hold supersedes the single tap down.
|
|
expect(singleLongTapStartCount, 0);
|
|
|
|
await gesture.up();
|
|
// Nothing else happens on up.
|
|
expect(singleTapUpCount, 1);
|
|
expect(tapCount, 2);
|
|
expect(singleTapCancelCount, 0);
|
|
expect(doubleTapDownCount, 1);
|
|
expect(singleLongTapStartCount, 0);
|
|
});
|
|
|
|
testWidgets('a very quick swipe is ignored', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
await gesture.moveBy(const Offset(100, 100));
|
|
await tester.pump();
|
|
expect(singleTapUpCount, 0);
|
|
expect(tapCount, 0);
|
|
expect(singleTapCancelCount, 0);
|
|
expect(doubleTapDownCount, 0);
|
|
expect(singleLongTapStartCount, 0);
|
|
|
|
await gesture.up();
|
|
// Nothing else happens on up.
|
|
expect(singleTapUpCount, 0);
|
|
expect(tapCount, 0);
|
|
expect(singleTapCancelCount, 0);
|
|
expect(doubleTapDownCount, 0);
|
|
expect(singleLongTapStartCount, 0);
|
|
});
|
|
|
|
testWidgets('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(milliseconds: 120));
|
|
await gesture.moveBy(const Offset(100, 100));
|
|
await tester.pump();
|
|
expect(singleTapUpCount, 0);
|
|
expect(tapCount, 1);
|
|
expect(singleTapCancelCount, 1);
|
|
expect(doubleTapDownCount, 0);
|
|
expect(singleLongTapStartCount, 0);
|
|
});
|
|
|
|
testWidgets('a force press initiates a force press', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
|
|
final TestGesture gesture = await tester.createGesture();
|
|
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
|
|
await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1));
|
|
await gesture.up();
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1));
|
|
await gesture.up();
|
|
await tester.pump(const Duration(milliseconds: 20));
|
|
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.updateWithCustomEvent(const PointerMoveEvent(pointer: pointerValue, position: Offset(0.0, 0.0), pressure: 0.5, pressureMin: 0, pressureMax: 1));
|
|
await gesture.up();
|
|
|
|
expect(forcePressStartCount, 4);
|
|
});
|
|
|
|
testWidgets('a tap and then force press initiates a force press and not a double tap', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.createGesture();
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
|
|
);
|
|
// Initiate a quick tap.
|
|
await gesture.updateWithCustomEvent(
|
|
const PointerMoveEvent(
|
|
pointer: pointerValue,
|
|
position: Offset(0.0, 0.0),
|
|
pressure: 0.0,
|
|
pressureMin: 0,
|
|
pressureMax: 1,
|
|
),
|
|
);
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
await gesture.up();
|
|
|
|
// Initiate a force tap.
|
|
await gesture.downWithCustomEvent(
|
|
forcePressOffset,
|
|
const PointerDownEvent(
|
|
pointer: pointerValue,
|
|
position: forcePressOffset,
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.updateWithCustomEvent(const PointerMoveEvent(
|
|
pointer: pointerValue,
|
|
position: Offset(0.0, 0.0),
|
|
pressure: 0.5,
|
|
pressureMin: 0,
|
|
pressureMax: 1,
|
|
));
|
|
expect(forcePressStartCount, 1);
|
|
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(forcePressEndCount, 1);
|
|
expect(doubleTapDownCount, 0);
|
|
});
|
|
|
|
testWidgets('a long press from a touch device is recognized as a long single tap', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: pointerValue,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tapCount, 1);
|
|
expect(singleTapUpCount, 0);
|
|
expect(singleLongTapStartCount, 1);
|
|
});
|
|
|
|
testWidgets('a long press from a mouse is just a tap', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: pointerValue,
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tapCount, 1);
|
|
expect(singleTapUpCount, 1);
|
|
expect(singleLongTapStartCount, 0);
|
|
});
|
|
|
|
testWidgets('a touch drag is not recognized for text selection', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: pointerValue,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump();
|
|
await gesture.moveBy(const Offset(210.0, 200.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tapCount, 0);
|
|
expect(singleTapUpCount, 0);
|
|
expect(dragStartCount, 0);
|
|
expect(dragUpdateCount, 0);
|
|
expect(dragEndCount, 0);
|
|
});
|
|
|
|
testWidgets('a mouse drag is recognized for text selection', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: pointerValue,
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump();
|
|
await gesture.moveBy(const Offset(210.0, 200.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(tapCount, 0);
|
|
expect(singleTapUpCount, 0);
|
|
expect(dragStartCount, 1);
|
|
expect(dragUpdateCount, 1);
|
|
expect(dragEndCount, 1);
|
|
});
|
|
|
|
testWidgets('a slow mouse drag is still recognized for text selection', (WidgetTester tester) async {
|
|
await pumpGestureDetector(tester);
|
|
|
|
const int pointerValue = 1;
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: pointerValue,
|
|
kind: PointerDeviceKind.mouse,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.moveBy(const Offset(210.0, 200.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(dragStartCount, 1);
|
|
expect(dragUpdateCount, 1);
|
|
expect(dragEndCount, 1);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder long press', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester);
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: 0,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isTrue);
|
|
expect(renderEditable.selectPositionAtCalled, isTrue);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder tap', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester);
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: 0,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isFalse);
|
|
expect(renderEditable.selectWordEdgeCalled, isTrue);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder double tap', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester);
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: 0,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
await gesture.up();
|
|
await gesture.down(const Offset(200.0, 200.0));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isTrue);
|
|
expect(renderEditable.selectWordCalled, isTrue);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder forcePress enabled', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester);
|
|
final TestGesture gesture = await tester.createGesture();
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.downWithCustomEvent(
|
|
const Offset(200.0, 200.0),
|
|
const PointerDownEvent(
|
|
pointer: 0,
|
|
position: Offset(200.0, 200.0),
|
|
pressure: 3.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.updateWithCustomEvent(
|
|
const PointerUpEvent(
|
|
pointer: 0,
|
|
position: Offset(200.0, 200.0),
|
|
pressure: 0.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isTrue);
|
|
expect(renderEditable.selectWordsInRangeCalled, isTrue);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder selection disabled', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester, selectionEnabled: false);
|
|
final TestGesture gesture = await tester.startGesture(
|
|
const Offset(200.0, 200.0),
|
|
pointer: 0,
|
|
kind: PointerDeviceKind.touch,
|
|
);
|
|
addTearDown(gesture.removePointer);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pumpAndSettle();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isTrue);
|
|
expect(renderEditable.selectWordsInRangeCalled, isFalse);
|
|
});
|
|
|
|
testWidgets('test TextSelectionGestureDetectorBuilder forcePress disabled', (WidgetTester tester) async {
|
|
await pumpTextSelectionGestureDetectorBuilder(tester, forcePressEnabled: false);
|
|
final TestGesture gesture = await tester.createGesture();
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.downWithCustomEvent(
|
|
const Offset(200.0, 200.0),
|
|
const PointerDownEvent(
|
|
pointer: 0,
|
|
position: Offset(200.0, 200.0),
|
|
pressure: 3.0,
|
|
pressureMax: 6.0,
|
|
pressureMin: 0.0,
|
|
),
|
|
);
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
|
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
|
expect(state.showToolbarCalled, isFalse);
|
|
expect(renderEditable.selectWordsInRangeCalled, isFalse);
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/37032.
|
|
testWidgets("selection handle's GestureDetector should not cover the entire screen", (WidgetTester tester) async {
|
|
final TextEditingController controller = TextEditingController(text: 'a');
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: TextField(
|
|
autofocus: true,
|
|
controller: controller,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
final Finder gestureDetector = find.descendant(
|
|
of: find.byType(Visibility),
|
|
matching: find.descendant(
|
|
of: find.byType(FadeTransition),
|
|
matching: find.byType(GestureDetector),
|
|
),
|
|
);
|
|
|
|
expect(gestureDetector, findsOneWidget);
|
|
// The GestureDetector's size should not exceed that of the TextField.
|
|
final Rect hitRect = tester.getRect(gestureDetector);
|
|
final Rect textFieldRect = tester.getRect(find.byType(TextField));
|
|
|
|
expect(hitRect.size.width, lessThan(textFieldRect.size.width));
|
|
expect(hitRect.size.height, lessThan(textFieldRect.size.height));
|
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
|
}
|
|
|
|
class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGestureDetectorBuilderDelegate {
|
|
FakeTextSelectionGestureDetectorBuilderDelegate({
|
|
this.editableTextKey,
|
|
this.forcePressEnabled,
|
|
this.selectionEnabled,
|
|
});
|
|
|
|
@override
|
|
final GlobalKey<EditableTextState> editableTextKey;
|
|
|
|
@override
|
|
final bool forcePressEnabled;
|
|
|
|
@override
|
|
final bool selectionEnabled;
|
|
}
|
|
|
|
class FakeEditableText extends EditableText {
|
|
FakeEditableText({Key key}): super(
|
|
key: key,
|
|
controller: TextEditingController(),
|
|
focusNode: FocusNode(),
|
|
backgroundCursorColor: Colors.white,
|
|
cursorColor: Colors.white,
|
|
style: const TextStyle(),
|
|
);
|
|
|
|
@override
|
|
FakeEditableTextState createState() => FakeEditableTextState();
|
|
}
|
|
|
|
class FakeEditableTextState extends EditableTextState {
|
|
final GlobalKey _editableKey = GlobalKey();
|
|
bool showToolbarCalled = false;
|
|
|
|
@override
|
|
RenderEditable get renderEditable => _editableKey.currentContext.findRenderObject() as RenderEditable;
|
|
|
|
@override
|
|
bool showToolbar() {
|
|
showToolbarCalled = true;
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context);
|
|
return FakeEditable(this, key: _editableKey);
|
|
}
|
|
}
|
|
|
|
class FakeEditable extends LeafRenderObjectWidget {
|
|
const FakeEditable(
|
|
this.delegate, {
|
|
Key key,
|
|
}) : super(key: key);
|
|
final EditableTextState delegate;
|
|
|
|
@override
|
|
RenderEditable createRenderObject(BuildContext context) {
|
|
return FakeRenderEditable(delegate);
|
|
}
|
|
}
|
|
|
|
class FakeRenderEditable extends RenderEditable {
|
|
FakeRenderEditable(EditableTextState delegate) : super(
|
|
text: const TextSpan(
|
|
style: TextStyle(height: 1.0, fontSize: 10.0, fontFamily: 'Ahem'),
|
|
text: 'placeholder',
|
|
),
|
|
startHandleLayerLink: LayerLink(),
|
|
endHandleLayerLink: LayerLink(),
|
|
textAlign: TextAlign.start,
|
|
textDirection: TextDirection.ltr,
|
|
locale: const Locale('en', 'US'),
|
|
offset: ViewportOffset.fixed(10.0),
|
|
textSelectionDelegate: delegate,
|
|
selection: const TextSelection.collapsed(
|
|
offset: 0,
|
|
),
|
|
);
|
|
|
|
bool selectWordsInRangeCalled = false;
|
|
@override
|
|
void selectWordsInRange({ @required Offset from, Offset to, @required SelectionChangedCause cause }) {
|
|
selectWordsInRangeCalled = true;
|
|
}
|
|
|
|
bool selectWordEdgeCalled = false;
|
|
@override
|
|
void selectWordEdge({ @required SelectionChangedCause cause }) {
|
|
selectWordEdgeCalled = true;
|
|
}
|
|
|
|
bool selectPositionAtCalled = false;
|
|
@override
|
|
void selectPositionAt({ @required Offset from, Offset to, @required SelectionChangedCause cause }) {
|
|
selectPositionAtCalled = true;
|
|
}
|
|
|
|
bool selectWordCalled = false;
|
|
@override
|
|
void selectWord({ @required SelectionChangedCause cause }) {
|
|
selectWordCalled = true;
|
|
}
|
|
}
|