Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b94bf87c
Unverified
Commit
b94bf87c
authored
Mar 01, 2019
by
Mouad Debbar
Committed by
GitHub
Mar 01, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Text selection via mouse (#28290)
parent
1df28e8b
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
833 additions
and
51 deletions
+833
-51
eager.dart
packages/flutter/lib/src/gestures/eager.dart
+6
-1
force_press.dart
packages/flutter/lib/src/gestures/force_press.dart
+5
-2
long_press.dart
packages/flutter/lib/src/gestures/long_press.dart
+2
-0
monodrag.dart
packages/flutter/lib/src/gestures/monodrag.dart
+17
-4
multidrag.dart
packages/flutter/lib/src/gestures/multidrag.dart
+19
-6
multitap.dart
packages/flutter/lib/src/gestures/multitap.dart
+10
-4
recognizer.dart
packages/flutter/lib/src/gestures/recognizer.dart
+65
-6
scale.dart
packages/flutter/lib/src/gestures/scale.dart
+7
-2
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+26
-5
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+5
-1
platform_view.dart
packages/flutter/lib/src/rendering/platform_view.dart
+8
-4
text_selection.dart
packages/flutter/lib/src/widgets/text_selection.dart
+144
-11
drag_test.dart
packages/flutter/test/gestures/drag_test.dart
+80
-1
long_press_test.dart
packages/flutter/test/gestures/long_press_test.dart
+40
-0
multidrag_test.dart
packages/flutter/test/gestures/multidrag_test.dart
+28
-0
multitap_test.dart
packages/flutter/test/gestures/multitap_test.dart
+77
-0
scale_test.dart
packages/flutter/test/gestures/scale_test.dart
+95
-0
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+95
-1
text_selection_test.dart
packages/flutter/test/widgets/text_selection_test.dart
+98
-1
controller.dart
packages/flutter_test/lib/src/controller.dart
+6
-2
No files found.
packages/flutter/lib/src/gestures/eager.dart
View file @
b94bf87c
...
...
@@ -12,8 +12,13 @@ import 'recognizer.dart';
/// all touch events inside the view bounds to the embedded Android view.
/// See [AndroidView.gestureRecognizers] for more details.
class
EagerGestureRecognizer
extends
OneSequenceGestureRecognizer
{
/// Create an eager gesture recognizer.
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
EagerGestureRecognizer
({
PointerDeviceKind
kind
}):
super
(
kind:
kind
);
@override
void
addPointer
(
PointerDownEvent
event
)
{
void
add
Allowed
Pointer
(
PointerDownEvent
event
)
{
// We call startTrackingPointer as this is where OneSequenceGestureRecognizer joins the arena.
startTrackingPointer
(
event
.
pointer
);
resolve
(
GestureDisposition
.
accepted
);
...
...
packages/flutter/lib/src/gestures/force_press.dart
View file @
b94bf87c
...
...
@@ -116,16 +116,19 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
/// The [interpolation] callback must always return a value in the range 0.0
/// to 1.0 for values of `pressure` that are between `pressureMin` and
/// `pressureMax`.
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
ForcePressGestureRecognizer
({
this
.
startPressure
=
0.4
,
this
.
peakPressure
=
0.85
,
this
.
interpolation
=
_inverseLerp
,
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
assert
(
startPressure
!=
null
),
assert
(
peakPressure
!=
null
),
assert
(
interpolation
!=
null
),
assert
(
peakPressure
>
startPressure
),
super
(
debugOwner:
debugOwner
);
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// A pointer is in contact with the screen and has just pressed with a force
/// exceeding the [startPressure]. Consequently, if there were other gesture
...
...
@@ -205,7 +208,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
_ForceState
_state
=
_ForceState
.
ready
;
@override
void
addPointer
(
PointerEvent
event
)
{
void
add
Allowed
Pointer
(
PointerEvent
event
)
{
// If the device has a maximum pressure of less than or equal to 1, it
// doesn't have touch pressure sensing capabilities. Do not participate
// in the gesture arena.
...
...
packages/flutter/lib/src/gestures/long_press.dart
View file @
b94bf87c
...
...
@@ -123,10 +123,12 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
/// can be moved without limit once the long press is accepted.
LongPressGestureRecognizer
({
double
postAcceptSlopTolerance
,
PointerDeviceKind
kind
,
Object
debugOwner
,
})
:
super
(
deadline:
kLongPressTimeout
,
postAcceptSlopTolerance:
postAcceptSlopTolerance
,
kind:
kind
,
debugOwner:
debugOwner
,
);
...
...
packages/flutter/lib/src/gestures/monodrag.dart
View file @
b94bf87c
...
...
@@ -52,11 +52,14 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
/// Initialize the object.
///
/// [dragStartBehavior] must not be null.
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
DragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
})
:
assert
(
dragStartBehavior
!=
null
),
super
(
debugOwner:
debugOwner
);
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// Configure the behavior of offsets sent to [onStart].
///
...
...
@@ -147,7 +150,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
final
Map
<
int
,
VelocityTracker
>
_velocityTrackers
=
<
int
,
VelocityTracker
>{};
@override
void
addPointer
(
PointerEvent
event
)
{
void
add
Allowed
Pointer
(
PointerEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
_velocityTrackers
[
event
.
pointer
]
=
VelocityTracker
();
if
(
_state
==
_DragState
.
ready
)
{
...
...
@@ -296,7 +299,12 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
/// track each touch point independently.
class
VerticalDragGestureRecognizer
extends
DragGestureRecognizer
{
/// Create a gesture recognizer for interactions in the vertical axis.
VerticalDragGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
VerticalDragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
@override
bool
_isFlingGesture
(
VelocityEstimate
estimate
)
{
...
...
@@ -330,7 +338,12 @@ class VerticalDragGestureRecognizer extends DragGestureRecognizer {
/// track each touch point independently.
class
HorizontalDragGestureRecognizer
extends
DragGestureRecognizer
{
/// Create a gesture recognizer for interactions in the horizontal axis.
HorizontalDragGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
HorizontalDragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
@override
bool
_isFlingGesture
(
VelocityEstimate
estimate
)
{
...
...
packages/flutter/lib/src/gestures/multidrag.dart
View file @
b94bf87c
...
...
@@ -189,7 +189,10 @@ abstract class MultiDragPointerState {
/// start after a long-press gesture.
abstract
class
MultiDragGestureRecognizer
<
T
extends
MultiDragPointerState
>
extends
GestureRecognizer
{
/// Initialize the object.
MultiDragGestureRecognizer
({
@required
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
MultiDragGestureRecognizer
({
@required
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// Called when this class recognizes the start of a drag gesture.
///
...
...
@@ -200,7 +203,7 @@ abstract class MultiDragGestureRecognizer<T extends MultiDragPointerState> exten
Map
<
int
,
T
>
_pointers
=
<
int
,
T
>{};
@override
void
addPointer
(
PointerDownEvent
event
)
{
void
add
Allowed
Pointer
(
PointerDownEvent
event
)
{
assert
(
_pointers
!=
null
);
assert
(
event
.
pointer
!=
null
);
assert
(
event
.
position
!=
null
);
...
...
@@ -334,7 +337,10 @@ class _ImmediatePointerState extends MultiDragPointerState {
/// start after a long-press gesture.
class
ImmediateMultiDragGestureRecognizer
extends
MultiDragGestureRecognizer
<
_ImmediatePointerState
>
{
/// Create a gesture recognizer for tracking multiple pointers at once.
ImmediateMultiDragGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
ImmediateMultiDragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
@override
_ImmediatePointerState
createNewPointerState
(
PointerDownEvent
event
)
{
...
...
@@ -380,7 +386,10 @@ class _HorizontalPointerState extends MultiDragPointerState {
class
HorizontalMultiDragGestureRecognizer
extends
MultiDragGestureRecognizer
<
_HorizontalPointerState
>
{
/// Create a gesture recognizer for tracking multiple pointers at once
/// but only if they first move horizontally.
HorizontalMultiDragGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
HorizontalMultiDragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
@override
_HorizontalPointerState
createNewPointerState
(
PointerDownEvent
event
)
{
...
...
@@ -426,7 +435,10 @@ class _VerticalPointerState extends MultiDragPointerState {
class
VerticalMultiDragGestureRecognizer
extends
MultiDragGestureRecognizer
<
_VerticalPointerState
>
{
/// Create a gesture recognizer for tracking multiple pointers at once
/// but only if they first move vertically.
VerticalMultiDragGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
VerticalMultiDragGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
@override
_VerticalPointerState
createNewPointerState
(
PointerDownEvent
event
)
{
...
...
@@ -528,8 +540,9 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer<_Dela
DelayedMultiDragGestureRecognizer
({
this
.
delay
=
kLongPressTimeout
,
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
assert
(
delay
!=
null
),
super
(
debugOwner:
debugOwner
);
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// The amount of time the pointer must remain in the same place for the drag
/// to be recognized.
...
...
packages/flutter/lib/src/gestures/multitap.dart
View file @
b94bf87c
...
...
@@ -69,7 +69,12 @@ class _TapTracker {
/// quick succession.
class
DoubleTapGestureRecognizer
extends
GestureRecognizer
{
/// Create a gesture recognizer for double taps.
DoubleTapGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
DoubleTapGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
// Implementation notes:
// The double tap recognizer can be in one of four states. There's no
...
...
@@ -100,7 +105,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
final
Map
<
int
,
_TapTracker
>
_trackers
=
<
int
,
_TapTracker
>{};
@override
void
addPointer
(
PointerEvent
event
)
{
void
add
Allowed
Pointer
(
PointerEvent
event
)
{
// Ignore out-of-bounds second taps.
if
(
_firstTap
!=
null
&&
!
_firstTap
.
isWithinTolerance
(
event
,
kDoubleTapSlop
))
...
...
@@ -318,7 +323,8 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
MultiTapGestureRecognizer
({
this
.
longTapDelay
=
Duration
.
zero
,
Object
debugOwner
,
})
:
super
(
debugOwner:
debugOwner
);
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// A pointer that might cause a tap has contacted the screen at a particular
/// location.
...
...
@@ -345,7 +351,7 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
final
Map
<
int
,
_TapGesture
>
_gestureMap
=
<
int
,
_TapGesture
>{};
@override
void
addPointer
(
PointerEvent
event
)
{
void
add
Allowed
Pointer
(
PointerEvent
event
)
{
assert
(!
_gestureMap
.
containsKey
(
event
.
pointer
));
_gestureMap
[
event
.
pointer
]
=
_TapGesture
(
gestureRecognizer:
this
,
...
...
packages/flutter/lib/src/gestures/recognizer.dart
View file @
b94bf87c
...
...
@@ -33,7 +33,7 @@ typedef RecognizerCallback<T> = T Function();
///
/// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors.
enum
DragStartBehavior
{
/// Set the initial offset, at the position where the first down even was
/// Set the initial offset, at the position where the first down even
t
was
/// detected.
down
,
...
...
@@ -58,7 +58,13 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
///
/// The argument is optional and is only used for debug purposes (e.g. in the
/// [toString] serialization).
GestureRecognizer
({
this
.
debugOwner
});
///
/// {@template flutter.gestures.gestureRecognizer.kind}
/// It's possible to limit this recognizer to a specific [PointerDeviceKind]
/// by providing the optional [kind] argument. If [kind] is null,
/// the recognizer will accept pointer events from all device kinds.
/// {@endtemplate}
GestureRecognizer
({
this
.
debugOwner
,
PointerDeviceKind
kind
})
:
_kind
=
kind
;
/// The recognizer's owner.
///
...
...
@@ -66,6 +72,10 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
/// this gesture recognizer was created, to aid in debugging.
final
Object
debugOwner
;
/// The kind of device that's allowed to be recognized. If null, events from
/// all device kinds will be tracked and recognized.
final
PointerDeviceKind
_kind
;
/// Registers a new pointer that might be relevant to this gesture
/// detector.
///
...
...
@@ -78,7 +88,43 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
/// subsequent events for this pointer, and to add the pointer to
/// the global gesture arena manager (see [GestureArenaManager]) to track
/// that pointer.
void
addPointer
(
PointerDownEvent
event
);
///
/// This method is called for each and all pointers being added. In
/// most cases, you want to override [addAllowedPointer] instead.
void
addPointer
(
PointerDownEvent
event
)
{
if
(
isPointerAllowed
(
event
))
{
addAllowedPointer
(
event
);
}
else
{
handleNonAllowedPointer
(
event
);
}
}
/// Registers a new pointer that's been checked to be allowed by this gesture
/// recognizer.
///
/// Subclasses of [GestureRecognizer] are supposed to override this method
/// instead of [addPointer] because [addPointer] will be called for each
/// pointer being added while [addAllowedPointer] is only called for pointers
/// that are allowed by this recognizer.
@protected
void
addAllowedPointer
(
PointerDownEvent
event
)
{
}
/// Handles a pointer being added that's not allowed by this recognizer.
///
/// Subclasses can override this method and reject the gesture.
///
/// See:
/// - [OneSequenceGestureRecognizer.handleNonAllowedPointer].
@protected
void
handleNonAllowedPointer
(
PointerDownEvent
event
)
{
}
/// Checks whether or not a pointer is allowed to be tracked by this recognizer.
@protected
bool
isPointerAllowed
(
PointerDownEvent
event
)
{
// Currently, it only checks for device kind. But in the future we could check
// for other things e.g. mouse button.
return
_kind
==
null
||
_kind
==
event
.
kind
;
}
/// Releases any resources used by the object.
///
...
...
@@ -151,11 +197,21 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
/// simultaneous touches to each result in a separate tap.
abstract
class
OneSequenceGestureRecognizer
extends
GestureRecognizer
{
/// Initialize the object.
OneSequenceGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
OneSequenceGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
final
Map
<
int
,
GestureArenaEntry
>
_entries
=
<
int
,
GestureArenaEntry
>{};
final
Set
<
int
>
_trackedPointers
=
HashSet
<
int
>();
@override
void
handleNonAllowedPointer
(
PointerDownEvent
event
)
{
resolve
(
GestureDisposition
.
rejected
);
}
/// Called when a pointer event is routed to this recognizer.
@protected
void
handleEvent
(
PointerEvent
event
);
...
...
@@ -291,11 +347,14 @@ enum GestureRecognizerState {
/// in the gesture arena, the gesture will be rejected.
abstract
class
PrimaryPointerGestureRecognizer
extends
OneSequenceGestureRecognizer
{
/// Initializes the [deadline] field during construction of subclasses.
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
PrimaryPointerGestureRecognizer
({
this
.
deadline
,
this
.
preAcceptSlopTolerance
=
kTouchSlop
,
this
.
postAcceptSlopTolerance
=
kTouchSlop
,
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
assert
(
preAcceptSlopTolerance
==
null
||
preAcceptSlopTolerance
>=
0
,
'The preAcceptSlopTolerance must be positive or null'
,
...
...
@@ -304,7 +363,7 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
postAcceptSlopTolerance
==
null
||
postAcceptSlopTolerance
>=
0
,
'The postAcceptSlopTolerance must be positive or null'
,
),
super
(
debugOwner:
debugOwner
);
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// If non-null, the recognizer will call [didExceedDeadline] after this
/// amount of time has elapsed since starting to track the primary pointer.
...
...
@@ -346,7 +405,7 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
Timer
_timer
;
@override
void
addPointer
(
PointerDownEvent
event
)
{
void
add
Allowed
Pointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
if
(
state
==
GestureRecognizerState
.
ready
)
{
state
=
GestureRecognizerState
.
possible
;
...
...
packages/flutter/lib/src/gestures/scale.dart
View file @
b94bf87c
...
...
@@ -183,7 +183,12 @@ class _LineBetweenPointers{
/// are no longer in contact with the screen, the recognizer calls [onEnd].
class
ScaleGestureRecognizer
extends
OneSequenceGestureRecognizer
{
/// Create a gesture recognizer for interactions intended for scaling content.
ScaleGestureRecognizer
({
Object
debugOwner
})
:
super
(
debugOwner:
debugOwner
);
///
/// {@macro flutter.gestures.gestureRecognizer.kind}
ScaleGestureRecognizer
({
Object
debugOwner
,
PointerDeviceKind
kind
,
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
/// The pointers in contact with the screen have established a focal point and
/// initial scale of 1.0.
...
...
@@ -239,7 +244,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
}
@override
void
addPointer
(
PointerEvent
event
)
{
void
add
Allowed
Pointer
(
PointerEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
_velocityTrackers
[
event
.
pointer
]
=
VelocityTracker
();
if
(
_state
==
_ScaleState
.
ready
)
{
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
b94bf87c
...
...
@@ -599,12 +599,12 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
}
}
InteractiveInkFeature
_createInkFeature
(
TapDownDetails
details
)
{
InteractiveInkFeature
_createInkFeature
(
Offset
globalPosition
)
{
final
MaterialInkController
inkController
=
Material
.
of
(
context
);
final
ThemeData
themeData
=
Theme
.
of
(
context
);
final
BuildContext
editableContext
=
_editableTextKey
.
currentContext
;
final
RenderBox
referenceBox
=
InputDecorator
.
containerOf
(
editableContext
)
??
editableContext
.
findRenderObject
();
final
Offset
position
=
referenceBox
.
globalToLocal
(
details
.
globalPosition
);
final
Offset
position
=
referenceBox
.
globalToLocal
(
globalPosition
);
final
Color
color
=
themeData
.
splashColor
;
InteractiveInkFeature
splash
;
...
...
@@ -637,7 +637,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
void
_handleTapDown
(
TapDownDetails
details
)
{
_renderEditable
.
handleTapDown
(
details
);
_startSplash
(
details
);
_startSplash
(
details
.
globalPosition
);
}
void
_handleForcePressStarted
(
ForcePressDetails
details
)
{
...
...
@@ -723,10 +723,29 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
}
}
void
_startSplash
(
TapDownDetails
details
)
{
void
_handleDragSelectionStart
(
DragStartDetails
details
)
{
_renderEditable
.
selectPositionAt
(
from:
details
.
globalPosition
,
cause:
SelectionChangedCause
.
drag
,
);
_startSplash
(
details
.
globalPosition
);
}
void
_handleDragSelectionUpdate
(
DragStartDetails
startDetails
,
DragUpdateDetails
updateDetails
,
)
{
_renderEditable
.
selectPositionAt
(
from:
startDetails
.
globalPosition
,
to:
updateDetails
.
globalPosition
,
cause:
SelectionChangedCause
.
drag
,
);
}
void
_startSplash
(
Offset
globalPosition
)
{
if
(
_effectiveFocusNode
.
hasFocus
)
return
;
final
InteractiveInkFeature
splash
=
_createInkFeature
(
details
);
final
InteractiveInkFeature
splash
=
_createInkFeature
(
globalPosition
);
_splashes
??=
HashSet
<
InteractiveInkFeature
>();
_splashes
.
add
(
splash
);
_currentSplash
=
splash
;
...
...
@@ -888,6 +907,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
onSingleLongTapMoveUpdate:
_handleSingleLongTapMoveUpdate
,
onSingleLongTapEnd:
_handleSingleLongTapEnd
,
onDoubleTapDown:
_handleDoubleTapDown
,
onDragSelectionStart:
_handleDragSelectionStart
,
onDragSelectionUpdate:
_handleDragSelectionUpdate
,
behavior:
HitTestBehavior
.
translucent
,
child:
child
,
),
...
...
packages/flutter/lib/src/rendering/editable.dart
View file @
b94bf87c
...
...
@@ -55,6 +55,10 @@ enum SelectionChangedCause {
/// Keyboard-triggered selection changes may be caused by the IME as well as
/// by accessibility tools (e.g. TalkBack on Android).
keyboard
,
/// The user used the mouse to change the selection by dragging over a piece
/// of text.
drag
,
}
/// Signature for the callback that reports when the caret location changes.
...
...
@@ -1235,7 +1239,7 @@ class RenderEditable extends RenderBox {
}
/// If [ignorePointer] is false (the default) then this method is called by
/// the internal gesture recognizer's [LongPressRecognizer.onLongPress]
/// the internal gesture recognizer's [LongPress
Gesture
Recognizer.onLongPress]
/// callback.
///
/// When [ignorePointer] is true, an ancestor widget must respond to long
...
...
packages/flutter/lib/src/rendering/platform_view.dart
View file @
b94bf87c
...
...
@@ -367,7 +367,9 @@ class RenderUiKitView extends RenderBox {
// When the team wins a gesture the recognizer notifies the engine that it should release
// the touch sequence to the embedded UIView.
class
_UiKitViewGestureRecognizer
extends
OneSequenceGestureRecognizer
{
_UiKitViewGestureRecognizer
(
this
.
controller
,
this
.
gestureRecognizerFactories
)
{
_UiKitViewGestureRecognizer
(
this
.
controller
,
this
.
gestureRecognizerFactories
,
{
PointerDeviceKind
kind
,
}):
super
(
kind:
kind
)
{
team
=
GestureArenaTeam
();
team
.
captain
=
this
;
_gestureRecognizers
=
gestureRecognizerFactories
.
map
(
...
...
@@ -387,7 +389,7 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
final
UiKitViewController
controller
;
@override
void
addPointer
(
PointerDownEvent
event
)
{
void
add
Allowed
Pointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
for
(
OneSequenceGestureRecognizer
recognizer
in
_gestureRecognizers
)
{
recognizer
.
addPointer
(
event
);
...
...
@@ -427,7 +429,9 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
// When the team wins the recognizer sends all the cached point events to the embedded Android view, and
// sets itself to a "forwarding mode" where it will forward any new pointer event to the Android view.
class
_AndroidViewGestureRecognizer
extends
OneSequenceGestureRecognizer
{
_AndroidViewGestureRecognizer
(
this
.
dispatcher
,
this
.
gestureRecognizerFactories
)
{
_AndroidViewGestureRecognizer
(
this
.
dispatcher
,
this
.
gestureRecognizerFactories
,
{
PointerDeviceKind
kind
,
}):
super
(
kind:
kind
)
{
team
=
GestureArenaTeam
();
team
.
captain
=
this
;
_gestureRecognizers
=
gestureRecognizerFactories
.
map
(
...
...
@@ -456,7 +460,7 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
Set
<
OneSequenceGestureRecognizer
>
_gestureRecognizers
;
@override
void
addPointer
(
PointerDownEvent
event
)
{
void
add
Allowed
Pointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
for
(
OneSequenceGestureRecognizer
recognizer
in
_gestureRecognizers
)
{
recognizer
.
addPointer
(
event
);
...
...
packages/flutter/lib/src/widgets/text_selection.dart
View file @
b94bf87c
...
...
@@ -8,7 +8,7 @@ import 'package:flutter/gestures.dart' show kDoubleTapTimeout, kDoubleTapSlop;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
;
import
'package:flutter/gestures.dart'
;
import
'basic.dart'
;
import
'container.dart'
;
...
...
@@ -20,6 +20,10 @@ import 'transitions.dart';
export
'package:flutter/services.dart'
show
TextSelectionDelegate
;
/// A duration that controls how often the drag selection update callback is
/// called.
const
Duration
_kDragSelectionUpdateThrottle
=
Duration
(
milliseconds:
50
);
/// Which type of selection handle to be displayed.
///
/// With mixed-direction text, both handles may be the same type. Examples:
...
...
@@ -64,6 +68,19 @@ enum _TextSelectionHandlePosition { start, end }
/// Used by [TextSelectionOverlay.onSelectionOverlayChanged].
typedef
TextSelectionOverlayChanged
=
void
Function
(
TextEditingValue
value
,
Rect
caretRect
);
/// Signature for when a pointer that's dragging to select text has moved again.
///
/// The first argument [startDetails] contains the details of the event that
/// initiated the dragging.
///
/// The second argument [updateDetails] contains the details of the current
/// pointer movement. It's the same as the one passed to [DragGestureRecognizer.onUpdate].
///
/// This signature is different from [GestureDragUpdateCallback] to make it
/// easier for various text fields to use [TextSelectionGestureDetector] without
/// having to store the start position.
typedef
DragSelectionUpdateCallback
=
void
Function
(
DragStartDetails
startDetails
,
DragUpdateDetails
updateDetails
);
/// An interface for building the selection UI, to be provided by the
/// implementor of the toolbar widget.
///
...
...
@@ -620,6 +637,9 @@ class TextSelectionGestureDetector extends StatefulWidget {
this
.
onSingleLongTapMoveUpdate
,
this
.
onSingleLongTapEnd
,
this
.
onDoubleTapDown
,
this
.
onDragSelectionStart
,
this
.
onDragSelectionUpdate
,
this
.
onDragSelectionEnd
,
this
.
behavior
,
@required
this
.
child
,
})
:
assert
(
child
!=
null
),
...
...
@@ -664,6 +684,19 @@ class TextSelectionGestureDetector extends StatefulWidget {
/// time (within [kDoubleTapTimeout]) to a previous short tap.
final
GestureTapDownCallback
onDoubleTapDown
;
/// Called when a mouse starts dragging to select text.
final
GestureDragStartCallback
onDragSelectionStart
;
/// Called repeatedly as a mouse moves while dragging.
///
/// The frequency of calls is throttled to avoid excessive text layout
/// operations in text fields. The throttling is controlled by the constant
/// [_kDragSelectionUpdateThrottle].
final
DragSelectionUpdateCallback
onDragSelectionUpdate
;
/// Called when a mouse that was previously dragging is released.
final
GestureDragEndCallback
onDragSelectionEnd
;
/// How this gesture detector should behave during hit testing.
///
/// This defaults to [HitTestBehavior.deferToChild].
...
...
@@ -687,6 +720,7 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
@override
void
dispose
()
{
_doubleTapTimer
?.
cancel
();
_dragUpdateThrottleTimer
?.
cancel
();
super
.
dispose
();
}
...
...
@@ -730,6 +764,56 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
}
}
DragStartDetails
_lastDragStartDetails
;
DragUpdateDetails
_lastDragUpdateDetails
;
Timer
_dragUpdateThrottleTimer
;
void
_handleDragStart
(
DragStartDetails
details
)
{
assert
(
_lastDragStartDetails
==
null
);
_lastDragStartDetails
=
details
;
if
(
widget
.
onDragSelectionStart
!=
null
)
{
widget
.
onDragSelectionStart
(
details
);
}
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
_lastDragUpdateDetails
=
details
;
// Only schedule a new timer if there's no one pending.
_dragUpdateThrottleTimer
??=
Timer
(
_kDragSelectionUpdateThrottle
,
_handleDragUpdateThrottled
);
}
/// Drag updates are being throttled to avoid excessive text layouts in text
/// fields. The frequency of invocations is controlled by the constant
/// [_kDragSelectionUpdateThrottle].
///
/// Once the drag gesture ends, any pending drag update will be fired
/// immediately. See [_handleDragEnd].
void
_handleDragUpdateThrottled
()
{
assert
(
_lastDragStartDetails
!=
null
);
assert
(
_lastDragUpdateDetails
!=
null
);
if
(
widget
.
onDragSelectionUpdate
!=
null
)
{
widget
.
onDragSelectionUpdate
(
_lastDragStartDetails
,
_lastDragUpdateDetails
);
}
_dragUpdateThrottleTimer
=
null
;
_lastDragUpdateDetails
=
null
;
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
assert
(
_lastDragStartDetails
!=
null
);
if
(
_dragUpdateThrottleTimer
!=
null
)
{
// If there's already an update scheduled, trigger it immediately and
// cancel the timer.
_dragUpdateThrottleTimer
.
cancel
();
_handleDragUpdateThrottled
();
}
if
(
widget
.
onDragSelectionEnd
!=
null
)
{
widget
.
onDragSelectionEnd
(
details
);
}
_dragUpdateThrottleTimer
=
null
;
_lastDragStartDetails
=
null
;
_lastDragUpdateDetails
=
null
;
}
void
_forcePressStarted
(
ForcePressDetails
details
)
{
_doubleTapTimer
?.
cancel
();
_doubleTapTimer
=
null
;
...
...
@@ -754,7 +838,7 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
}
}
void
_handleLongPress
Up
(
LongPressEndDetails
details
)
{
void
_handleLongPress
End
(
LongPressEndDetails
details
)
{
if
(!
_isDoubleTap
&&
widget
.
onSingleLongTapEnd
!=
null
)
{
widget
.
onSingleLongTapEnd
(
details
);
}
...
...
@@ -778,15 +862,64 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec
@override
Widget
build
(
BuildContext
context
)
{
return
GestureDetector
(
onTapDown:
_handleTapDown
,
onTapUp:
_handleTapUp
,
onForcePressStart:
widget
.
onForcePressStart
!=
null
?
_forcePressStarted
:
null
,
onForcePressEnd:
widget
.
onForcePressEnd
!=
null
?
_forcePressEnded
:
null
,
onTapCancel:
_handleTapCancel
,
onLongPressStart:
_handleLongPressStart
,
onLongPressMoveUpdate:
_handleLongPressMoveUpdate
,
onLongPressEnd:
_handleLongPressUp
,
final
Map
<
Type
,
GestureRecognizerFactory
>
gestures
=
<
Type
,
GestureRecognizerFactory
>{};
gestures
[
TapGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
TapGestureRecognizer
>(
()
=>
TapGestureRecognizer
(
debugOwner:
this
),
(
TapGestureRecognizer
instance
)
{
instance
..
onTapDown
=
_handleTapDown
..
onTapUp
=
_handleTapUp
..
onTapCancel
=
_handleTapCancel
;
},
);
if
(
widget
.
onSingleLongTapStart
!=
null
||
widget
.
onSingleLongTapMoveUpdate
!=
null
||
widget
.
onSingleLongTapEnd
!=
null
)
{
gestures
[
LongPressGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
LongPressGestureRecognizer
>(
()
=>
LongPressGestureRecognizer
(
debugOwner:
this
,
kind:
PointerDeviceKind
.
touch
),
(
LongPressGestureRecognizer
instance
)
{
instance
..
onLongPressStart
=
_handleLongPressStart
..
onLongPressMoveUpdate
=
_handleLongPressMoveUpdate
..
onLongPressEnd
=
_handleLongPressEnd
;
},
);
}
if
(
widget
.
onDragSelectionStart
!=
null
||
widget
.
onDragSelectionUpdate
!=
null
||
widget
.
onDragSelectionEnd
!=
null
)
{
// TODO(mdebbar): Support dragging in any direction (for multiline text).
// https://github.com/flutter/flutter/issues/28676
gestures
[
HorizontalDragGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
HorizontalDragGestureRecognizer
>(
()
=>
HorizontalDragGestureRecognizer
(
debugOwner:
this
,
kind:
PointerDeviceKind
.
mouse
),
(
HorizontalDragGestureRecognizer
instance
)
{
instance
// Text selection should start from the position of the first pointer
// down event.
..
dragStartBehavior
=
DragStartBehavior
.
down
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
;
},
);
}
if
(
widget
.
onForcePressStart
!=
null
||
widget
.
onForcePressEnd
!=
null
)
{
gestures
[
ForcePressGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
ForcePressGestureRecognizer
>(
()
=>
ForcePressGestureRecognizer
(
debugOwner:
this
),
(
ForcePressGestureRecognizer
instance
)
{
instance
..
onStart
=
widget
.
onForcePressStart
!=
null
?
_forcePressStarted
:
null
..
onEnd
=
widget
.
onForcePressEnd
!=
null
?
_forcePressEnded
:
null
;
},
);
}
return
RawGestureDetector
(
gestures:
gestures
,
excludeFromSemantics:
true
,
behavior:
widget
.
behavior
,
child:
widget
.
child
,
...
...
packages/flutter/test/gestures/drag_test.dart
View file @
b94bf87c
...
...
@@ -517,4 +517,83 @@ void main() {
tester
.
route
(
pointer
.
up
());
drag
.
dispose
();
});
}
\ No newline at end of file
testGesture
(
'Can filter drags based on device kind'
,
(
GestureTester
tester
)
{
final
HorizontalDragGestureRecognizer
drag
=
HorizontalDragGestureRecognizer
(
kind:
PointerDeviceKind
.
mouse
,
)
..
dragStartBehavior
=
DragStartBehavior
.
down
;
bool
didStartDrag
=
false
;
drag
.
onStart
=
(
_
)
{
didStartDrag
=
true
;
};
double
updatedDelta
;
drag
.
onUpdate
=
(
DragUpdateDetails
details
)
{
updatedDelta
=
details
.
primaryDelta
;
};
bool
didEndDrag
=
false
;
drag
.
onEnd
=
(
DragEndDetails
details
)
{
didEndDrag
=
true
;
};
// Using a touch pointer to drag shouldn't be recognized.
final
TestPointer
touchPointer
=
TestPointer
(
5
,
PointerDeviceKind
.
touch
);
final
PointerDownEvent
touchDown
=
touchPointer
.
down
(
const
Offset
(
10.0
,
10.0
));
drag
.
addPointer
(
touchDown
);
tester
.
closeArena
(
5
);
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
touchDown
);
// Still doesn't recognize the drag because it's coming from a touch pointer.
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
touchPointer
.
move
(
const
Offset
(
20.0
,
25.0
)));
// Still doesn't recognize the drag because it's coming from a touch pointer.
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
touchPointer
.
up
());
// Still doesn't recognize the drag because it's coming from a touch pointer.
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
// Using a mouse pointer to drag should be recognized.
final
TestPointer
mousePointer
=
TestPointer
(
5
,
PointerDeviceKind
.
mouse
);
final
PointerDownEvent
mouseDown
=
mousePointer
.
down
(
const
Offset
(
10.0
,
10.0
));
drag
.
addPointer
(
mouseDown
);
tester
.
closeArena
(
5
);
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
mouseDown
);
expect
(
didStartDrag
,
isTrue
);
didStartDrag
=
false
;
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
mousePointer
.
move
(
const
Offset
(
20.0
,
25.0
)));
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
10.0
);
updatedDelta
=
null
;
expect
(
didEndDrag
,
isFalse
);
tester
.
route
(
mousePointer
.
up
());
expect
(
didStartDrag
,
isFalse
);
expect
(
updatedDelta
,
isNull
);
expect
(
didEndDrag
,
isTrue
);
didEndDrag
=
false
;
drag
.
dispose
();
});
}
packages/flutter/test/gestures/long_press_test.dart
View file @
b94bf87c
...
...
@@ -278,4 +278,44 @@ void main() {
longPressDrag
.
dispose
();
});
});
testGesture
(
'Can filter long press based on device kind'
,
(
GestureTester
tester
)
{
final
LongPressGestureRecognizer
mouseLongPress
=
LongPressGestureRecognizer
(
kind:
PointerDeviceKind
.
mouse
);
bool
mouseLongPressDown
=
false
;
mouseLongPress
.
onLongPress
=
()
{
mouseLongPressDown
=
true
;
};
const
PointerDownEvent
mouseDown
=
PointerDownEvent
(
pointer:
5
,
position:
Offset
(
10
,
10
),
kind:
PointerDeviceKind
.
mouse
,
);
const
PointerDownEvent
touchDown
=
PointerDownEvent
(
pointer:
5
,
position:
Offset
(
10
,
10
),
kind:
PointerDeviceKind
.
touch
,
);
// Touch events shouldn't be recognized.
mouseLongPress
.
addPointer
(
touchDown
);
tester
.
closeArena
(
5
);
expect
(
mouseLongPressDown
,
isFalse
);
tester
.
route
(
touchDown
);
expect
(
mouseLongPressDown
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
seconds:
2
));
expect
(
mouseLongPressDown
,
isFalse
);
// Mouse events are still recognized.
mouseLongPress
.
addPointer
(
mouseDown
);
tester
.
closeArena
(
5
);
expect
(
mouseLongPressDown
,
isFalse
);
tester
.
route
(
mouseDown
);
expect
(
mouseLongPressDown
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
seconds:
2
));
expect
(
mouseLongPressDown
,
isTrue
);
mouseLongPress
.
dispose
();
});
}
packages/flutter/test/gestures/multidrag_test.dart
View file @
b94bf87c
...
...
@@ -61,4 +61,32 @@ void main() {
expect
(
didStartDrag
,
isTrue
);
drag
.
dispose
();
});
testGesture
(
'MultiDrag: can filter based on device kind'
,
(
GestureTester
tester
)
{
final
DelayedMultiDragGestureRecognizer
drag
=
DelayedMultiDragGestureRecognizer
(
kind:
PointerDeviceKind
.
touch
);
bool
didStartDrag
=
false
;
drag
.
onStart
=
(
Offset
position
)
{
didStartDrag
=
true
;
return
TestDrag
();
};
final
TestPointer
mousePointer
=
TestPointer
(
5
,
PointerDeviceKind
.
mouse
);
final
PointerDownEvent
down
=
mousePointer
.
down
(
const
Offset
(
10.0
,
10.0
));
drag
.
addPointer
(
down
);
tester
.
closeArena
(
5
);
expect
(
didStartDrag
,
isFalse
);
tester
.
async
.
flushMicrotasks
();
expect
(
didStartDrag
,
isFalse
);
tester
.
route
(
mousePointer
.
move
(
const
Offset
(
20.0
,
20.0
)));
// move less than touch slop before delay expires
expect
(
didStartDrag
,
isFalse
);
tester
.
async
.
elapse
(
kLongPressTimeout
*
2
);
// expire delay
// Still false because it shouldn't recognize mouse events.
expect
(
didStartDrag
,
isFalse
);
tester
.
route
(
mousePointer
.
move
(
const
Offset
(
30.0
,
70.0
)));
// move more than touch slop after delay expires
// And still false.
expect
(
didStartDrag
,
isFalse
);
drag
.
dispose
();
});
}
packages/flutter/test/gestures/multitap_test.dart
View file @
b94bf87c
...
...
@@ -69,4 +69,81 @@ void main() {
tap
.
dispose
();
});
testGesture
(
'Can filter based on device kind'
,
(
GestureTester
tester
)
{
final
MultiTapGestureRecognizer
tap
=
MultiTapGestureRecognizer
(
longTapDelay:
kLongPressTimeout
,
kind:
PointerDeviceKind
.
touch
,
);
final
List
<
String
>
log
=
<
String
>[];
tap
.
onTapDown
=
(
int
pointer
,
TapDownDetails
details
)
{
log
.
add
(
'tap-down
$pointer
'
);
};
tap
.
onTapUp
=
(
int
pointer
,
TapUpDetails
details
)
{
log
.
add
(
'tap-up
$pointer
'
);
};
tap
.
onTap
=
(
int
pointer
)
{
log
.
add
(
'tap
$pointer
'
);
};
tap
.
onLongTapDown
=
(
int
pointer
,
TapDownDetails
details
)
{
log
.
add
(
'long-tap-down
$pointer
'
);
};
tap
.
onTapCancel
=
(
int
pointer
)
{
log
.
add
(
'tap-cancel
$pointer
'
);
};
final
TestPointer
touchPointer5
=
TestPointer
(
5
,
PointerDeviceKind
.
touch
);
final
PointerDownEvent
down5
=
touchPointer5
.
down
(
const
Offset
(
10.0
,
10.0
));
tap
.
addPointer
(
down5
);
tester
.
closeArena
(
5
);
expect
(
log
,
<
String
>[
'tap-down 5'
]);
log
.
clear
();
tester
.
route
(
down5
);
expect
(
log
,
isEmpty
);
final
TestPointer
mousePointer6
=
TestPointer
(
6
,
PointerDeviceKind
.
mouse
);
final
PointerDownEvent
down6
=
mousePointer6
.
down
(
const
Offset
(
20.0
,
20.0
));
tap
.
addPointer
(
down6
);
tester
.
closeArena
(
6
);
// Mouse down should be ignored by the recognizer.
expect
(
log
,
isEmpty
);
final
TestPointer
touchPointer7
=
TestPointer
(
7
,
PointerDeviceKind
.
touch
);
final
PointerDownEvent
down7
=
touchPointer7
.
down
(
const
Offset
(
15.0
,
15.0
));
tap
.
addPointer
(
down7
);
tester
.
closeArena
(
7
);
expect
(
log
,
<
String
>[
'tap-down 7'
]);
log
.
clear
();
tester
.
route
(
down7
);
expect
(
log
,
isEmpty
);
tester
.
route
(
touchPointer5
.
move
(
const
Offset
(
11.0
,
12.0
)));
expect
(
log
,
isEmpty
);
// Move within the [kTouchSlop] range.
tester
.
route
(
mousePointer6
.
move
(
const
Offset
(
21.0
,
18.0
)));
// Move beyond the slop range.
tester
.
route
(
mousePointer6
.
move
(
const
Offset
(
50.0
,
40.0
)));
// Neither triggers any event because they originate from a mouse.
expect
(
log
,
isEmpty
);
tester
.
route
(
touchPointer7
.
move
(
const
Offset
(
14.0
,
13.0
)));
expect
(
log
,
isEmpty
);
tester
.
route
(
touchPointer5
.
up
());
expect
(
log
,
<
String
>[
'tap-up 5'
,
'tap 5'
,
]);
log
.
clear
();
// Mouse up should be ignored.
tester
.
route
(
mousePointer6
.
up
());
expect
(
log
,
isEmpty
);
tester
.
async
.
elapse
(
kLongPressTimeout
+
kPressTimeout
);
// Only the touch pointer (7) triggers a long-tap, not the mouse pointer (6).
expect
(
log
,
<
String
>[
'long-tap-down 7'
]);
log
.
clear
();
tester
.
route
(
touchPointer7
.
move
(
const
Offset
(
40.0
,
30.0
)));
// move more than kTouchSlop from 15.0,15.0
expect
(
log
,
<
String
>[
'tap-cancel 7'
]);
log
.
clear
();
tap
.
dispose
();
});
}
packages/flutter/test/gestures/scale_test.dart
View file @
b94bf87c
...
...
@@ -220,6 +220,101 @@ void main() {
tap
.
dispose
();
});
testGesture
(
'Rejects scale gestures from unallowed device kinds'
,
(
GestureTester
tester
)
{
final
ScaleGestureRecognizer
scale
=
ScaleGestureRecognizer
(
kind:
PointerDeviceKind
.
touch
);
bool
didStartScale
=
false
;
scale
.
onStart
=
(
ScaleStartDetails
details
)
{
didStartScale
=
true
;
};
double
updatedScale
;
scale
.
onUpdate
=
(
ScaleUpdateDetails
details
)
{
updatedScale
=
details
.
scale
;
};
final
TestPointer
mousePointer
=
TestPointer
(
1
,
PointerDeviceKind
.
mouse
);
final
PointerDownEvent
down
=
mousePointer
.
down
(
const
Offset
(
0.0
,
0.0
));
scale
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
// One-finger panning
tester
.
route
(
down
);
expect
(
didStartScale
,
isFalse
);
expect
(
updatedScale
,
isNull
);
// Using a mouse, the scale gesture shouldn't even start.
tester
.
route
(
mousePointer
.
move
(
const
Offset
(
20.0
,
30.0
)));
expect
(
didStartScale
,
isFalse
);
expect
(
updatedScale
,
isNull
);
scale
.
dispose
();
});
testGesture
(
'Scale gestures starting from allowed device kinds cannot be ended from unallowed devices'
,
(
GestureTester
tester
)
{
final
ScaleGestureRecognizer
scale
=
ScaleGestureRecognizer
(
kind:
PointerDeviceKind
.
touch
);
bool
didStartScale
=
false
;
Offset
updatedFocalPoint
;
scale
.
onStart
=
(
ScaleStartDetails
details
)
{
didStartScale
=
true
;
updatedFocalPoint
=
details
.
focalPoint
;
};
double
updatedScale
;
scale
.
onUpdate
=
(
ScaleUpdateDetails
details
)
{
updatedScale
=
details
.
scale
;
updatedFocalPoint
=
details
.
focalPoint
;
};
bool
didEndScale
=
false
;
scale
.
onEnd
=
(
ScaleEndDetails
details
)
{
didEndScale
=
true
;
};
final
TestPointer
touchPointer
=
TestPointer
(
1
,
PointerDeviceKind
.
touch
);
final
PointerDownEvent
down
=
touchPointer
.
down
(
const
Offset
(
0.0
,
0.0
));
scale
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
// One-finger panning
tester
.
route
(
down
);
expect
(
didStartScale
,
isTrue
);
didStartScale
=
false
;
expect
(
updatedScale
,
isNull
);
expect
(
updatedFocalPoint
,
const
Offset
(
0.0
,
0.0
));
expect
(
didEndScale
,
isFalse
);
// The gesture can start using one touch finger.
tester
.
route
(
touchPointer
.
move
(
const
Offset
(
20.0
,
30.0
)));
expect
(
updatedFocalPoint
,
const
Offset
(
20.0
,
30.0
));
updatedFocalPoint
=
null
;
expect
(
updatedScale
,
1.0
);
updatedScale
=
null
;
expect
(
didEndScale
,
isFalse
);
// Two-finger scaling
final
TestPointer
mousePointer
=
TestPointer
(
2
,
PointerDeviceKind
.
mouse
);
final
PointerDownEvent
down2
=
mousePointer
.
down
(
const
Offset
(
10.0
,
20.0
));
scale
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
tester
.
route
(
down2
);
// Mouse-generated events are ignored.
expect
(
didEndScale
,
isFalse
);
expect
(
updatedScale
,
isNull
);
expect
(
didStartScale
,
isFalse
);
// Zoom in using a mouse doesn't work either.
tester
.
route
(
mousePointer
.
move
(
const
Offset
(
0.0
,
10.0
)));
expect
(
updatedScale
,
isNull
);
expect
(
didEndScale
,
isFalse
);
scale
.
dispose
();
});
testGesture
(
'Scale gesture competes with drag'
,
(
GestureTester
tester
)
{
final
ScaleGestureRecognizer
scale
=
ScaleGestureRecognizer
();
final
HorizontalDragGestureRecognizer
drag
=
HorizontalDragGestureRecognizer
();
...
...
packages/flutter/test/material/text_field_test.dart
View file @
b94bf87c
...
...
@@ -12,7 +12,7 @@ import 'package:flutter_test/flutter_test.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
,
PointerDeviceKind
;
import
'../widgets/semantics_tester.dart'
;
import
'feedback_tester.dart'
;
...
...
@@ -533,6 +533,37 @@ void main() {
expect
(
controller
.
selection
.
extentOffset
,
testValue
.
indexOf
(
'f'
)+
1
);
});
testWidgets
(
'Mouse long press is just like a tap'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
overlay
(
child:
TextField
(
controller:
controller
,
),
)
);
const
String
testValue
=
'abc def ghi'
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
testValue
);
expect
(
controller
.
value
.
text
,
testValue
);
await
skipPastScrollingAnimation
(
tester
);
expect
(
controller
.
selection
.
isCollapsed
,
true
);
// Long press the 'e' using a mouse device.
final
int
eIndex
=
testValue
.
indexOf
(
'e'
);
final
Offset
ePos
=
textOffsetToPosition
(
tester
,
eIndex
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
ePos
,
kind:
PointerDeviceKind
.
mouse
);
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
gesture
.
up
();
await
tester
.
pump
();
// The cursor is placed just like a regular tap.
expect
(
controller
.
selection
.
baseOffset
,
eIndex
);
expect
(
controller
.
selection
.
extentOffset
,
eIndex
);
});
testWidgets
(
'enableInteractiveSelection = false, long-press'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
...
...
@@ -564,6 +595,69 @@ void main() {
expect
(
controller
.
selection
.
extentOffset
,
-
1
);
});
testWidgets
(
'Can select text by dragging with a mouse'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Material
(
child:
TextField
(
dragStartBehavior:
DragStartBehavior
.
down
,
controller:
controller
,
),
),
),
);
const
String
testValue
=
'abc def ghi'
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
testValue
);
await
skipPastScrollingAnimation
(
tester
);
final
Offset
ePos
=
textOffsetToPosition
(
tester
,
testValue
.
indexOf
(
'e'
));
final
Offset
gPos
=
textOffsetToPosition
(
tester
,
testValue
.
indexOf
(
'g'
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
ePos
,
kind:
PointerDeviceKind
.
mouse
);
await
tester
.
pump
();
await
gesture
.
moveTo
(
gPos
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
expect
(
controller
.
selection
.
baseOffset
,
testValue
.
indexOf
(
'e'
));
expect
(
controller
.
selection
.
extentOffset
,
testValue
.
indexOf
(
'g'
));
});
testWidgets
(
'Slow mouse dragging also selects text'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Material
(
child:
TextField
(
dragStartBehavior:
DragStartBehavior
.
down
,
controller:
controller
,
),
),
),
);
const
String
testValue
=
'abc def ghi'
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
testValue
);
await
skipPastScrollingAnimation
(
tester
);
final
Offset
ePos
=
textOffsetToPosition
(
tester
,
testValue
.
indexOf
(
'e'
));
final
Offset
gPos
=
textOffsetToPosition
(
tester
,
testValue
.
indexOf
(
'g'
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
ePos
,
kind:
PointerDeviceKind
.
mouse
);
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
gesture
.
moveTo
(
gPos
);
await
tester
.
pump
();
await
gesture
.
up
();
expect
(
controller
.
selection
.
baseOffset
,
testValue
.
indexOf
(
'e'
));
expect
(
controller
.
selection
.
extentOffset
,
testValue
.
indexOf
(
'g'
));
});
testWidgets
(
'Can drag handles to change selection'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
();
...
...
packages/flutter/test/widgets/text_selection_test.dart
View file @
b94bf87c
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
show
PointerDeviceKind
;
import
'package:flutter/widgets.dart'
;
void
main
(
)
{
...
...
@@ -13,9 +14,11 @@ void main() {
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
++;
}
...
...
@@ -23,6 +26,9 @@ void main() {
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
;
...
...
@@ -32,6 +38,9 @@ void main() {
doubleTapDownCount
=
0
;
forcePressStartCount
=
0
;
forcePressEndCount
=
0
;
dragStartCount
=
0
;
dragUpdateCount
=
0
;
dragEndCount
=
0
;
});
Future
<
void
>
pumpGestureDetector
(
WidgetTester
tester
)
async
{
...
...
@@ -45,6 +54,9 @@ void main() {
onDoubleTapDown:
_handleDoubleTapDown
,
onForcePressStart:
_handleForcePressStart
,
onForcePressEnd:
_handleForcePressEnd
,
onDragSelectionStart:
_handleDragSelectionStart
,
onDragSelectionUpdate:
_handleDragSelectionUpdate
,
onDragSelectionEnd:
_handleDragSelectionEnd
,
child:
Container
(),
),
);
...
...
@@ -275,4 +287,89 @@ void main() {
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
);
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
);
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
);
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
);
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
);
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
);
});
}
packages/flutter_test/lib/src/controller.dart
View file @
b94bf87c
...
...
@@ -553,8 +553,12 @@ abstract class WidgetController {
///
/// You can use [createGesture] if your gesture doesn't begin with an initial
/// down gesture.
Future
<
TestGesture
>
startGesture
(
Offset
downLocation
,
{
int
pointer
})
async
{
final
TestGesture
result
=
await
createGesture
(
pointer:
pointer
);
Future
<
TestGesture
>
startGesture
(
Offset
downLocation
,
{
int
pointer
,
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
})
async
{
final
TestGesture
result
=
await
createGesture
(
pointer:
pointer
,
kind:
kind
);
await
result
.
down
(
downLocation
);
return
result
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment