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
ae23d4a4
Unverified
Commit
ae23d4a4
authored
May 31, 2019
by
Michael Goderbauer
Committed by
GitHub
May 31, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Transform PointerEvents to the local coordinate system of the event receiver (#32192)
parent
a9518da0
Changes
33
Show whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
3163 additions
and
157 deletions
+3163
-157
binding.dart
packages/flutter/lib/src/gestures/binding.dart
+1
-1
drag_details.dart
packages/flutter/lib/src/gestures/drag_details.dart
+51
-7
eager.dart
packages/flutter/lib/src/gestures/eager.dart
+1
-1
events.dart
packages/flutter/lib/src/gestures/events.dart
+532
-1
force_press.dart
packages/flutter/lib/src/gestures/force_press.dart
+18
-8
hit_test.dart
packages/flutter/lib/src/gestures/hit_test.dart
+95
-3
long_press.dart
packages/flutter/lib/src/gestures/long_press.dart
+37
-9
monodrag.dart
packages/flutter/lib/src/gestures/monodrag.dart
+61
-27
multitap.dart
packages/flutter/lib/src/gestures/multitap.dart
+28
-20
pointer_router.dart
packages/flutter/lib/src/gestures/pointer_router.dart
+43
-25
pointer_signal_resolver.dart
...ges/flutter/lib/src/gestures/pointer_signal_resolver.dart
+2
-2
recognizer.dart
packages/flutter/lib/src/gestures/recognizer.dart
+70
-11
tap.dart
packages/flutter/lib/src/gestures/tap.dart
+22
-8
box.dart
packages/flutter/lib/src/rendering/box.dart
+26
-14
layer.dart
packages/flutter/lib/src/rendering/layer.dart
+4
-1
list_wheel_viewport.dart
packages/flutter/lib/src/rendering/list_wheel_viewport.dart
+1
-3
platform_view.dart
packages/flutter/lib/src/rendering/platform_view.dart
+5
-6
sliver.dart
packages/flutter/lib/src/rendering/sliver.dart
+9
-3
texture.dart
packages/flutter/lib/src/rendering/texture.dart
+1
-3
drag_target.dart
packages/flutter/lib/src/widgets/drag_target.dart
+1
-1
events_test.dart
packages/flutter/test/gestures/events_test.dart
+342
-0
hit_test_test.dart
packages/flutter/test/gestures/hit_test_test.dart
+11
-1
pointer_router_test.dart
packages/flutter/test/gestures/pointer_router_test.dart
+50
-0
pointer_signal_resolver_test.dart
...s/flutter/test/gestures/pointer_signal_resolver_test.dart
+19
-0
recognizer_test.dart
packages/flutter/test/gestures/recognizer_test.dart
+24
-0
transformed_double_tap.dart
packages/flutter/test/gestures/transformed_double_tap.dart
+98
-0
transformed_long_press_test.dart
...es/flutter/test/gestures/transformed_long_press_test.dart
+206
-0
transformed_monodrag_test.dart
...ages/flutter/test/gestures/transformed_monodrag_test.dart
+668
-0
transformed_tap_test.dart
packages/flutter/test/gestures/transformed_tap_test.dart
+177
-0
box_test.dart
packages/flutter/test/rendering/box_test.dart
+10
-1
slivers_test.dart
packages/flutter/test/rendering/slivers_test.dart
+10
-1
listener_test.dart
packages/flutter/test/widgets/listener_test.dart
+322
-0
transformed_scrollable_test.dart
...ges/flutter/test/widgets/transformed_scrollable_test.dart
+218
-0
No files found.
packages/flutter/lib/src/gestures/binding.dart
View file @
ae23d4a4
...
...
@@ -195,7 +195,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
}
for
(
HitTestEntry
entry
in
hitTestResult
.
path
)
{
try
{
entry
.
target
.
handleEvent
(
event
,
entry
);
entry
.
target
.
handleEvent
(
event
.
transformed
(
entry
.
transform
)
,
entry
);
}
catch
(
exception
,
stack
)
{
FlutterError
.
reportError
(
FlutterErrorDetailsForPointerEventDispatcher
(
exception:
exception
,
...
...
packages/flutter/lib/src/gestures/drag_details.dart
View file @
ae23d4a4
...
...
@@ -20,14 +20,28 @@ class DragDownDetails {
/// Creates details for a [GestureDragDownCallback].
///
/// The [globalPosition] argument must not be null.
DragDownDetails
({
this
.
globalPosition
=
Offset
.
zero
})
:
assert
(
globalPosition
!=
null
);
DragDownDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the pointer contacted the screen.
///
/// Defaults to the origin if not specified in the constructor.
///
/// See also:
///
/// * [localPosition], which is the [globalPosition] transformed to the
/// coordinate space of the event receiver.
final
Offset
globalPosition
;
/// The local position in the coordinate system of the event receiver at
/// which the pointer contacted the screen.
///
/// Defaults to [globalPosition] if not specified in the constructor.
final
Offset
localPosition
;
@override
String
toString
()
=>
'
$runtimeType
(
$globalPosition
)'
;
}
...
...
@@ -52,8 +66,12 @@ class DragStartDetails {
/// Creates details for a [GestureDragStartCallback].
///
/// The [globalPosition] argument must not be null.
DragStartDetails
({
this
.
sourceTimeStamp
,
this
.
globalPosition
=
Offset
.
zero
})
:
assert
(
globalPosition
!=
null
);
DragStartDetails
({
this
.
sourceTimeStamp
,
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// Recorded timestamp of the source pointer event that triggered the drag
/// event.
...
...
@@ -64,8 +82,19 @@ class DragStartDetails {
/// The global position at which the pointer contacted the screen.
///
/// Defaults to the origin if not specified in the constructor.
///
/// See also:
///
/// * [localPosition], which is the [globalPosition] transformed to the
/// coordinate space of the event receiver.
final
Offset
globalPosition
;
/// The local position in the coordinate system of the event receiver at
/// which the pointer contacted the screen.
///
/// Defaults to [globalPosition] if not specified in the constructor.
final
Offset
localPosition
;
// TODO(ianh): Expose the current position, so that you can have a no-jump
// drag even when disambiguating (though of course it would lag the finger
// instead).
...
...
@@ -104,10 +133,12 @@ class DragUpdateDetails {
this
.
delta
=
Offset
.
zero
,
this
.
primaryDelta
,
@required
this
.
globalPosition
,
Offset
localPosition
,
})
:
assert
(
delta
!=
null
),
assert
(
primaryDelta
==
null
||
(
primaryDelta
==
delta
.
dx
&&
delta
.
dy
==
0.0
)
||
(
primaryDelta
==
delta
.
dy
&&
delta
.
dx
==
0.0
));
||
(
primaryDelta
==
delta
.
dy
&&
delta
.
dx
==
0.0
)),
localPosition
=
localPosition
??
globalPosition
;
/// Recorded timestamp of the source pointer event that triggered the drag
/// event.
...
...
@@ -115,7 +146,8 @@ class DragUpdateDetails {
/// Could be null if triggered from proxied events such as accessibility.
final
Duration
sourceTimeStamp
;
/// The amount the pointer has moved since the previous update.
/// The amount the pointer has moved in the coordinate space of the event
/// receiver since the previous update.
///
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
/// a horizontal or vertical drag), then this offset contains only the delta
...
...
@@ -124,7 +156,8 @@ class DragUpdateDetails {
/// Defaults to zero if not specified in the constructor.
final
Offset
delta
;
/// The amount the pointer has moved along the primary axis since the previous
/// The amount the pointer has moved along the primary axis in the coordinate
/// space of the event receiver since the previous
/// update.
///
/// If the [GestureDragUpdateCallback] is for a one-dimensional drag (e.g.,
...
...
@@ -137,8 +170,19 @@ class DragUpdateDetails {
final
double
primaryDelta
;
/// The pointer's global position when it triggered this update.
///
/// See also:
///
/// * [localPosition], which is the [globalPosition] transformed to the
/// coordinate space of the event receiver.
final
Offset
globalPosition
;
/// The local position in the coordinate system of the event receiver at
/// which the pointer contacted the screen.
///
/// Defaults to [globalPosition] if not specified in the constructor.
final
Offset
localPosition
;
@override
String
toString
()
=>
'
$runtimeType
(
$delta
)'
;
}
...
...
packages/flutter/lib/src/gestures/eager.dart
View file @
ae23d4a4
...
...
@@ -20,7 +20,7 @@ class EagerGestureRecognizer extends OneSequenceGestureRecognizer {
@override
void
addAllowedPointer
(
PointerDownEvent
event
)
{
// We call startTrackingPointer as this is where OneSequenceGestureRecognizer joins the arena.
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
resolve
(
GestureDisposition
.
accepted
);
stopTrackingPointer
(
event
.
pointer
);
}
...
...
packages/flutter/lib/src/gestures/events.dart
View file @
ae23d4a4
...
...
@@ -5,6 +5,7 @@
import
'dart:ui'
show
Offset
,
PointerDeviceKind
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
export
'dart:ui'
show
Offset
,
PointerDeviceKind
;
...
...
@@ -202,7 +203,9 @@ abstract class PointerEvent extends Diagnosticable {
this
.
kind
=
PointerDeviceKind
.
touch
,
this
.
device
=
0
,
this
.
position
=
Offset
.
zero
,
Offset
localPosition
,
this
.
delta
=
Offset
.
zero
,
Offset
localDelta
,
this
.
buttons
=
0
,
this
.
down
=
false
,
this
.
obscured
=
false
,
...
...
@@ -220,7 +223,11 @@ abstract class PointerEvent extends Diagnosticable {
this
.
tilt
=
0.0
,
this
.
platformData
=
0
,
this
.
synthesized
=
false
,
});
this
.
transform
,
this
.
original
,
})
:
localPosition
=
localPosition
??
position
,
localDelta
=
localDelta
??
delta
;
/// Time of event dispatch, relative to an arbitrary timeline.
final
Duration
timeStamp
;
...
...
@@ -237,14 +244,46 @@ abstract class PointerEvent extends Diagnosticable {
/// Coordinate of the position of the pointer, in logical pixels in the global
/// coordinate space.
///
/// See also:
///
/// * [localPosition], which is the [position] transformed into the local
/// coordinate system of the event receiver.
final
Offset
position
;
/// The [position] transformed into the event receiver's local coordinate
/// system according to [transform].
///
/// If this event has not been transformed, [position] is returned as-is.
///
/// See also:
///
/// * [globalPosition], which is the position in the global coordinate
/// system of the screen.
final
Offset
localPosition
;
/// Distance in logical pixels that the pointer moved since the last
/// [PointerMoveEvent] or [PointerHoverEvent].
///
/// This value is always 0.0 for down, up, and cancel events.
///
/// See also:
///
/// * [localDelta], which is the [delta] transformed into the local
/// coordinate space of the event receiver.
final
Offset
delta
;
/// The [delta] transformed into the event receiver's local coordinate
/// system according to [transform].
///
/// If this event has not been transformed, [delta] is returned as-is.
///
/// See also:
///
/// * [delta], which is the distance the pointer moved in the global
/// coordinate system of the screen.
final
Offset
localDelta
;
/// Bit field using the *Button constants such as [kPrimaryMouseButton],
/// [kSecondaryStylusButton], etc.
///
...
...
@@ -390,11 +429,56 @@ abstract class PointerEvent extends Diagnosticable {
/// the difference between the 2 events in that case.
final
bool
synthesized
;
/// The transformation used to transform this event from the global coordinate
/// space into the coordinate space of the event receiver.
///
/// This value affects what is returned by [localPosition] and [localDelta].
/// If this value is null, it is treated as the identity transformation.
///
/// Unlike a paint transform, this transform usually does not contain any
/// "perspective" components, meaning that the third row and the third column
/// of the matrix should be equal to "0, 0, 1, 0". This ensures that
/// [localPosition] describes the point in the local coordinate system of the
/// event receiver at which the user is actually touching the screen.
///
/// See also:
///
/// * [transformed], which transforms this event into a different coordinate
/// space.
final
Matrix4
transform
;
/// The original un-transformed [PointerEvent] before any [transform]s were
/// applied.
///
/// If [transform] is null or the identity transformation this may be null.
///
/// When multiple event receivers in different coordinate spaces receive an
/// event, they all receive the event transformed to their local coordinate
/// space. The [original] property can be used to determine if all those
/// transformed events actually originated from the same pointer interaction.
final
PointerEvent
original
;
/// Transforms the event from the global coordinate space into the coordinate
/// space of an event receiver.
///
/// The coordinate space of the event receiver is described by `transform`. A
/// null value for `transform` is treated as the identity transformation.
///
/// The method may return the same object instance if for example the
/// transformation has no effect on the event.
///
/// Transforms are not commutative. If this method is called on a
/// [PointerEvent] that has a non-null [transform] value, that value will be
/// overridden by the provided `transform`.
PointerEvent
transformed
(
Matrix4
transform
);
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'position'
,
position
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'localPosition'
,
localPosition
,
defaultValue:
position
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'delta'
,
delta
,
defaultValue:
Offset
.
zero
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'localDelta'
,
localDelta
,
defaultValue:
delta
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
Duration
>(
'timeStamp'
,
timeStamp
,
defaultValue:
Duration
.
zero
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
IntProperty
(
'pointer'
,
pointer
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
EnumProperty
<
PointerDeviceKind
>(
'kind'
,
kind
,
level:
DiagnosticLevel
.
debug
));
...
...
@@ -423,6 +507,61 @@ abstract class PointerEvent extends Diagnosticable {
String
toStringFull
()
{
return
toString
(
minLevel:
DiagnosticLevel
.
fine
);
}
/// Returns the transformation of `position` into the coordinate system
/// described by `transform`.
///
/// The z-value of `position` is assumed to be 0.0. If `transform` is null,
/// `position` is returned as-is.
static
Offset
transformPosition
(
Matrix4
transform
,
Offset
position
)
{
if
(
transform
==
null
)
{
return
position
;
}
final
Vector3
position3
=
Vector3
(
position
.
dx
,
position
.
dy
,
0.0
);
final
Vector3
transformed3
=
transform
.
perspectiveTransform
(
position3
);
return
Offset
(
transformed3
.
x
,
transformed3
.
y
);
}
/// Transforms `untransformedDelta` into the coordinate system described by
/// `transform`.
///
/// It uses the provided `untransformedEndPosition` and
/// `transformedEndPosition` of the provided delta to increase accuracy.
///
/// If `transform` is null, `untransformedDelta` is returned.
static
Offset
transformDeltaViaPositions
({
@required
Offset
untransformedEndPosition
,
Offset
transformedEndPosition
,
@required
Offset
untransformedDelta
,
@required
Matrix4
transform
,
})
{
if
(
transform
==
null
)
{
return
untransformedDelta
;
}
// We could transform the delta directly with the transformation matrix.
// While that is mathematically equivalent, in practice we are seeing a
// greater precision error with that approach. Instead, we are transforming
// start and end point of the delta separately and calculate the delta in
// the new space for greater accuracy.
transformedEndPosition
??=
transformPosition
(
transform
,
untransformedEndPosition
);
final
Offset
transformedStartPosition
=
transformPosition
(
transform
,
untransformedEndPosition
-
untransformedDelta
);
return
transformedEndPosition
-
transformedStartPosition
;
}
/// Removes the "perspective" component from `transform`.
///
/// When applying the resulting transform matrix to a point with a
/// z-coordinate of zero (which is generally assumed for all points
/// represented by an [Offset]), the other coordinates will get transformed as
/// before, but the new z-coordinate is going to be zero again. This is
/// achieved by setting the third column and third row of the matrix to
/// "0, 0, 1, 0".
static
Matrix4
removePerspectiveTransform
(
Matrix4
transform
)
{
final
Vector4
vector
=
Vector4
(
0
,
0
,
1
,
0
);
return
transform
.
clone
()
..
setColumn
(
2
,
vector
)
..
setRow
(
2
,
vector
);
}
}
/// The device has started tracking the pointer.
...
...
@@ -438,6 +577,7 @@ class PointerAddedEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
...
...
@@ -447,11 +587,14 @@ class PointerAddedEvent extends PointerEvent {
double
radiusMax
=
0.0
,
double
orientation
=
0.0
,
double
tilt
=
0.0
,
Matrix4
transform
,
PointerAddedEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
obscured:
obscured
,
pressure:
0.0
,
pressureMin:
pressureMin
,
...
...
@@ -462,7 +605,34 @@ class PointerAddedEvent extends PointerEvent {
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
,
);
@override
PointerAddedEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerAddedEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The device is no longer tracking the pointer.
...
...
@@ -478,17 +648,21 @@ class PointerRemovedEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distanceMax
=
0.0
,
double
radiusMin
=
0.0
,
double
radiusMax
=
0.0
,
Matrix4
transform
,
PointerRemovedEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
obscured:
obscured
,
pressure:
0.0
,
pressureMin:
pressureMin
,
...
...
@@ -496,7 +670,31 @@ class PointerRemovedEvent extends PointerEvent {
distanceMax:
distanceMax
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
transform:
transform
,
original:
original
,
);
@override
PointerRemovedEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerRemovedEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distanceMax:
distanceMax
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has moved with respect to the device while the pointer is not
...
...
@@ -518,7 +716,9 @@ class PointerHoverEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
Offset
delta
=
Offset
.
zero
,
Offset
localDelta
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
...
...
@@ -533,12 +733,16 @@ class PointerHoverEvent extends PointerEvent {
double
orientation
=
0.0
,
double
tilt
=
0.0
,
bool
synthesized
=
false
,
Matrix4
transform
,
PointerHoverEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
delta:
delta
,
localDelta:
localDelta
,
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
...
...
@@ -555,7 +759,47 @@ class PointerHoverEvent extends PointerEvent {
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
,
);
@override
PointerHoverEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
final
Offset
transformedPosition
=
PointerEvent
.
transformPosition
(
transform
,
position
);
return
PointerHoverEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
transformedPosition
,
delta:
delta
,
localDelta:
PointerEvent
.
transformDeltaViaPositions
(
transform:
transform
,
untransformedDelta:
delta
,
untransformedEndPosition:
position
,
transformedEndPosition:
transformedPosition
,
),
buttons:
buttons
,
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has moved with respect to the device while the pointer is not
...
...
@@ -577,7 +821,9 @@ class PointerEnterEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
Offset
delta
=
Offset
.
zero
,
Offset
localDelta
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
...
...
@@ -592,12 +838,16 @@ class PointerEnterEvent extends PointerEvent {
double
orientation
=
0.0
,
double
tilt
=
0.0
,
bool
synthesized
=
false
,
Matrix4
transform
,
PointerEnterEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
delta:
delta
,
localDelta:
localDelta
,
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
...
...
@@ -614,6 +864,8 @@ class PointerEnterEvent extends PointerEvent {
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
,
);
/// Creates an enter event from a [PointerHoverEvent].
...
...
@@ -630,7 +882,9 @@ class PointerEnterEvent extends PointerEvent {
kind:
event
?.
kind
,
device:
event
?.
device
,
position:
event
?.
position
,
localPosition:
event
?.
localPosition
,
delta:
event
?.
delta
,
localDelta:
event
?.
localDelta
,
buttons:
event
?.
buttons
,
obscured:
event
?.
obscured
,
pressureMin:
event
?.
pressureMin
,
...
...
@@ -645,7 +899,47 @@ class PointerEnterEvent extends PointerEvent {
orientation:
event
?.
orientation
,
tilt:
event
?.
tilt
,
synthesized:
event
?.
synthesized
,
transform:
event
?.
transform
,
original:
event
?.
original
,
);
@override
PointerEnterEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
final
Offset
transformedPosition
=
PointerEvent
.
transformPosition
(
transform
,
position
);
return
PointerEnterEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
transformedPosition
,
delta:
delta
,
localDelta:
PointerEvent
.
transformDeltaViaPositions
(
transform:
transform
,
untransformedDelta:
delta
,
untransformedEndPosition:
position
,
transformedEndPosition:
transformedPosition
,
),
buttons:
buttons
,
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has moved with respect to the device while the pointer is not
...
...
@@ -667,7 +961,9 @@ class PointerExitEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
Offset
delta
=
Offset
.
zero
,
Offset
localDelta
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
...
...
@@ -682,12 +978,16 @@ class PointerExitEvent extends PointerEvent {
double
orientation
=
0.0
,
double
tilt
=
0.0
,
bool
synthesized
=
false
,
Matrix4
transform
,
PointerExitEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
delta:
delta
,
localDelta:
localDelta
,
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
...
...
@@ -704,6 +1004,8 @@ class PointerExitEvent extends PointerEvent {
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
,
);
/// Creates an exit event from a [PointerHoverEvent].
...
...
@@ -720,7 +1022,9 @@ class PointerExitEvent extends PointerEvent {
kind:
event
?.
kind
,
device:
event
?.
device
,
position:
event
?.
position
,
localPosition:
event
?.
localPosition
,
delta:
event
?.
delta
,
localDelta:
event
?.
localDelta
,
buttons:
event
?.
buttons
,
obscured:
event
?.
obscured
,
pressureMin:
event
?.
pressureMin
,
...
...
@@ -735,7 +1039,47 @@ class PointerExitEvent extends PointerEvent {
orientation:
event
?.
orientation
,
tilt:
event
?.
tilt
,
synthesized:
event
?.
synthesized
,
transform:
event
?.
transform
,
original:
event
?.
original
,
);
@override
PointerExitEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
final
Offset
transformedPosition
=
PointerEvent
.
transformPosition
(
transform
,
position
);
return
PointerExitEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
transformedPosition
,
delta:
delta
,
localDelta:
PointerEvent
.
transformDeltaViaPositions
(
transform:
transform
,
untransformedDelta:
delta
,
untransformedEndPosition:
position
,
transformedEndPosition:
transformedPosition
,
),
buttons:
buttons
,
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has made contact with the device.
...
...
@@ -749,6 +1093,7 @@ class PointerDownEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
int
buttons
=
kPrimaryButton
,
bool
obscured
=
false
,
double
pressure
=
1.0
,
...
...
@@ -762,12 +1107,15 @@ class PointerDownEvent extends PointerEvent {
double
radiusMax
=
0.0
,
double
orientation
=
0.0
,
double
tilt
=
0.0
,
Matrix4
transform
,
PointerDownEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
buttons:
buttons
,
down:
true
,
obscured:
obscured
,
...
...
@@ -783,7 +1131,39 @@ class PointerDownEvent extends PointerEvent {
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
,
);
@override
PointerDownEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerDownEvent
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
buttons:
buttons
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has moved with respect to the device while the pointer is in
...
...
@@ -803,7 +1183,9 @@ class PointerMoveEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
Offset
delta
=
Offset
.
zero
,
Offset
localDelta
,
int
buttons
=
kPrimaryButton
,
bool
obscured
=
false
,
double
pressure
=
1.0
,
...
...
@@ -819,13 +1201,17 @@ class PointerMoveEvent extends PointerEvent {
double
tilt
=
0.0
,
int
platformData
=
0
,
bool
synthesized
=
false
,
Matrix4
transform
,
PointerMoveEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
delta:
delta
,
localDelta:
localDelta
,
buttons:
buttons
,
down:
true
,
obscured:
obscured
,
...
...
@@ -843,7 +1229,50 @@ class PointerMoveEvent extends PointerEvent {
tilt:
tilt
,
platformData:
platformData
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
,
);
@override
PointerMoveEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
final
Offset
transformedPosition
=
PointerEvent
.
transformPosition
(
transform
,
position
);
return
PointerMoveEvent
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
transformedPosition
,
delta:
delta
,
localDelta:
PointerEvent
.
transformDeltaViaPositions
(
transform:
transform
,
untransformedDelta:
delta
,
untransformedEndPosition:
position
,
transformedEndPosition:
transformedPosition
,
),
buttons:
buttons
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
platformData:
platformData
,
synthesized:
synthesized
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// The pointer has stopped making contact with the device.
...
...
@@ -857,6 +1286,7 @@ class PointerUpEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
int
buttons
=
0
,
bool
obscured
=
false
,
// Allow pressure customization here because PointerUpEvent can contain
...
...
@@ -873,12 +1303,15 @@ class PointerUpEvent extends PointerEvent {
double
radiusMax
=
0.0
,
double
orientation
=
0.0
,
double
tilt
=
0.0
,
Matrix4
transform
,
PointerUpEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
...
...
@@ -894,7 +1327,40 @@ class PointerUpEvent extends PointerEvent {
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
,
);
@override
PointerUpEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerUpEvent
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
buttons:
buttons
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
??
this
,
);
}
}
/// An event that corresponds to a discrete pointer signal.
...
...
@@ -911,12 +1377,18 @@ abstract class PointerSignalEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
mouse
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
Matrix4
transform
,
PointerSignalEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
transform:
transform
,
original:
original
,
);
}
...
...
@@ -933,7 +1405,10 @@ class PointerScrollEvent extends PointerSignalEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
mouse
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
this
.
scrollDelta
=
Offset
.
zero
,
Matrix4
transform
,
PointerScrollEvent
original
,
})
:
assert
(
timeStamp
!=
null
),
assert
(
kind
!=
null
),
assert
(
device
!=
null
),
...
...
@@ -944,11 +1419,31 @@ class PointerScrollEvent extends PointerSignalEvent {
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
transform:
transform
,
original:
original
,
);
/// The amount to scroll, in logical pixels.
final
Offset
scrollDelta
;
@override
PointerScrollEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerScrollEvent
(
timeStamp:
timeStamp
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
scrollDelta:
scrollDelta
,
transform:
transform
,
original:
original
??
this
,
);
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
@@ -967,6 +1462,7 @@ class PointerCancelEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
Offset
localPosition
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressureMin
=
1.0
,
...
...
@@ -980,12 +1476,15 @@ class PointerCancelEvent extends PointerEvent {
double
radiusMax
=
0.0
,
double
orientation
=
0.0
,
double
tilt
=
0.0
,
Matrix4
transform
,
PointerCancelEvent
original
,
})
:
super
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
localPosition
,
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
...
...
@@ -1001,5 +1500,37 @@ class PointerCancelEvent extends PointerEvent {
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
,
);
@override
PointerCancelEvent
transformed
(
Matrix4
transform
)
{
if
(
transform
==
null
||
transform
==
this
.
transform
)
{
return
this
;
}
return
PointerCancelEvent
(
timeStamp:
timeStamp
,
pointer:
pointer
,
kind:
kind
,
device:
device
,
position:
position
,
localPosition:
PointerEvent
.
transformPosition
(
transform
,
position
),
buttons:
buttons
,
obscured:
obscured
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
distanceMax:
distanceMax
,
size:
size
,
radiusMajor:
radiusMajor
,
radiusMinor:
radiusMinor
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
,
orientation:
orientation
,
tilt:
tilt
,
transform:
transform
,
original:
original
??
this
,
);
}
}
packages/flutter/lib/src/gestures/force_press.dart
View file @
ae23d4a4
...
...
@@ -51,13 +51,18 @@ class ForcePressDetails {
/// The [globalPosition] argument must not be null.
ForcePressDetails
({
@required
this
.
globalPosition
,
Offset
localPosition
,
@required
this
.
pressure
,
})
:
assert
(
globalPosition
!=
null
),
assert
(
pressure
!=
null
);
assert
(
pressure
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the function was called.
final
Offset
globalPosition
;
/// The local position at which the function was called.
final
Offset
localPosition
;
/// The pressure of the pointer on the screen.
final
double
pressure
;
}
...
...
@@ -203,7 +208,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
/// ```
final
GestureForceInterpolation
interpolation
;
Offset
_lastPosition
;
Offset
Pair
_lastPosition
;
double
_lastPressure
;
_ForceState
_state
=
_ForceState
.
ready
;
...
...
@@ -215,10 +220,10 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
if
(!(
event
is
PointerUpEvent
)
&&
event
.
pressureMax
<=
1.0
)
{
resolve
(
GestureDisposition
.
rejected
);
}
else
{
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
if
(
_state
==
_ForceState
.
ready
)
{
_state
=
_ForceState
.
possible
;
_lastPosition
=
event
.
position
;
_lastPosition
=
OffsetPair
.
fromEventPosition
(
event
)
;
}
}
}
...
...
@@ -242,7 +247,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
pressure
.
isNaN
// and interpolation may return NaN for values it doesn't want to support...
);
_lastPosition
=
event
.
position
;
_lastPosition
=
OffsetPair
.
fromEventPosition
(
event
)
;
_lastPressure
=
pressure
;
if
(
_state
==
_ForceState
.
possible
)
{
...
...
@@ -260,7 +265,8 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
if
(
onStart
!=
null
)
{
invokeCallback
<
void
>(
'onStart'
,
()
=>
onStart
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
_lastPosition
,
globalPosition:
_lastPosition
.
global
,
localPosition:
_lastPosition
.
local
,
)));
}
}
...
...
@@ -271,6 +277,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
invokeCallback
<
void
>(
'onPeak'
,
()
=>
onPeak
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
)));
}
}
...
...
@@ -280,6 +287,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
invokeCallback
<
void
>(
'onUpdate'
,
()
=>
onUpdate
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
)));
}
}
...
...
@@ -295,7 +303,8 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
if
(
onStart
!=
null
&&
_state
==
_ForceState
.
started
)
{
invokeCallback
<
void
>(
'onStart'
,
()
=>
onStart
(
ForcePressDetails
(
pressure:
_lastPressure
,
globalPosition:
_lastPosition
,
globalPosition:
_lastPosition
.
global
,
localPosition:
_lastPosition
.
local
,
)));
}
}
...
...
@@ -311,7 +320,8 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
if
(
onEnd
!=
null
)
{
invokeCallback
<
void
>(
'onEnd'
,
()
=>
onEnd
(
ForcePressDetails
(
pressure:
0.0
,
globalPosition:
_lastPosition
,
globalPosition:
_lastPosition
.
global
,
localPosition:
_lastPosition
.
local
,
)));
}
}
...
...
packages/flutter/lib/src/gestures/hit_test.dart
View file @
ae23d4a4
...
...
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:collection'
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'events.dart'
;
/// An object that can hit-test pointers.
...
...
@@ -43,19 +48,32 @@ abstract class HitTestTarget {
/// to the event propagation phase.
class
HitTestEntry
{
/// Creates a hit test entry.
const
HitTestEntry
(
this
.
target
);
HitTestEntry
(
this
.
target
);
/// The [HitTestTarget] encountered during the hit test.
final
HitTestTarget
target
;
@override
String
toString
()
=>
'
$target
'
;
/// Returns a matrix describing how [PointerEvent]s delivered to this
/// [HitTestEntry] should be transformed from the global coordinate space of
/// the screen to the local coordinate space of [target].
///
/// See also:
///
/// * [HitTestResult.addWithPaintTransform], which is used during hit testing
/// to build up the transform returned by this method.
Matrix4
get
transform
=>
_transform
;
Matrix4
_transform
;
}
/// The result of performing a hit test.
class
HitTestResult
{
/// Creates an empty hit test result.
HitTestResult
()
:
_path
=
<
HitTestEntry
>[];
HitTestResult
()
:
_path
=
<
HitTestEntry
>[],
_transforms
=
Queue
<
Matrix4
>();
/// Wraps `result` (usually a subtype of [HitTestResult]) to create a
/// generic [HitTestResult].
...
...
@@ -63,7 +81,9 @@ class HitTestResult {
/// The [HitTestEntry]s added to the returned [HitTestResult] are also
/// added to the wrapped `result` (both share the same underlying data
/// structure to store [HitTestEntry]s).
HitTestResult
.
wrap
(
HitTestResult
result
)
:
_path
=
result
.
_path
;
HitTestResult
.
wrap
(
HitTestResult
result
)
:
_path
=
result
.
_path
,
_transforms
=
result
.
_transforms
;
/// An unmodifiable list of [HitTestEntry] objects recorded during the hit test.
///
...
...
@@ -73,15 +93,87 @@ class HitTestResult {
Iterable
<
HitTestEntry
>
get
path
=>
_path
;
final
List
<
HitTestEntry
>
_path
;
final
Queue
<
Matrix4
>
_transforms
;
/// Add a [HitTestEntry] to the path.
///
/// The new entry is added at the end of the path, which means entries should
/// be added in order from most specific to least specific, typically during an
/// upward walk of the tree being hit tested.
void
add
(
HitTestEntry
entry
)
{
assert
(
entry
.
_transform
==
null
);
entry
.
_transform
=
_transforms
.
isEmpty
?
null
:
_transforms
.
last
;
_path
.
add
(
entry
);
}
/// Pushes a new transform matrix that is to be applied to all future
/// [HitTestEntry]s added via [add] until it is removed via [popTransform].
///
/// This method is only to be used by subclasses, which must provide
/// coordinate space specific public wrappers around this function for their
/// users (see [BoxHitTestResult.addWithPaintTransform] for such an example).
///
/// The provided `transform` matrix should describe how to transform
/// [PointerEvent]s from the coordinate space of the method caller to the
/// coordinate space of its children. In most cases `transform` is derived
/// from running the inverted result of [RenderObject.applyPaintTransform]
/// through [PointerEvent.removePerspectiveTransform] to remove
/// the perspective component.
///
/// [HitTestable]s need to call this method indirectly through a convenience
/// method defined on a subclass before hit testing a child that does not
/// have the same origin as the parent. After hit testing the child,
/// [popTransform] has to be called to remove the child-specific `transform`.
///
/// See also:
/// * [BoxHitTestResult.addWithPaintTransform], which is a public wrapper
/// around this function for hit testing on [RenderBox]s.
/// * [SliverHitTestResult.addWithAxisOffset], which is a public wrapper
/// around this function for hit testing on [RenderSlivers]s.
@protected
void
pushTransform
(
Matrix4
transform
)
{
assert
(
transform
!=
null
);
assert
(
_debugVectorMoreOrLessEquals
(
transform
.
getRow
(
2
),
Vector4
(
0
,
0
,
1
,
0
))
&&
_debugVectorMoreOrLessEquals
(
transform
.
getColumn
(
2
),
Vector4
(
0
,
0
,
1
,
0
)),
'The third row and third column of a transform matrix for pointer '
'events must be Vector4(0, 0, 1, 0) to ensure that a transformed '
'point is directly under the pointer device. Did you forget to run the paint '
'matrix through PointerEvent.removePerspectiveTransform?'
'The provided matrix is:
\n
$transform
'
);
_transforms
.
add
(
_transforms
.
isEmpty
?
transform
:
transform
*
_transforms
.
last
);
}
/// Removes the last transform added via [pushTransform].
///
/// This method is only to be used by subclasses, which must provide
/// coordinate space specific public wrappers around this function for their
/// users (see [BoxHitTestResult.addWithPaintTransform] for such an example).
///
/// This method must be called after hit testing is done on a child that
/// required a call to [pushTransform].
///
/// See also:
///
/// * [pushTransform], which describes the use case of this function pair in
/// more details.
@protected
void
popTransform
()
{
assert
(
_transforms
.
isNotEmpty
);
_transforms
.
removeLast
();
}
bool
_debugVectorMoreOrLessEquals
(
Vector4
a
,
Vector4
b
,
{
double
epsilon
=
precisionErrorTolerance
})
{
bool
result
=
true
;
assert
(()
{
final
Vector4
difference
=
a
-
b
;
result
=
difference
.
storage
.
every
((
double
component
)
=>
component
.
abs
()
<
epsilon
);
return
true
;
}());
return
result
;
}
@override
String
toString
()
=>
'HitTestResult(
${_path.isEmpty ? "<empty path>" : _path.join(", ")}
)'
;
}
packages/flutter/lib/src/gestures/long_press.dart
View file @
ae23d4a4
...
...
@@ -51,11 +51,17 @@ class LongPressStartDetails {
/// Creates the details for a [GestureLongPressStartCallback].
///
/// The [globalPosition] argument must not be null.
const
LongPressStartDetails
({
this
.
globalPosition
=
Offset
.
zero
})
:
assert
(
globalPosition
!=
null
);
const
LongPressStartDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the pointer contacted the screen.
final
Offset
globalPosition
;
/// The local position at which the pointer contacted the screen.
final
Offset
localPosition
;
}
/// Details for callbacks that use [GestureLongPressMoveUpdateCallback].
...
...
@@ -71,17 +77,29 @@ class LongPressMoveUpdateDetails {
/// The [globalPosition] and [offsetFromOrigin] arguments must not be null.
const
LongPressMoveUpdateDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
this
.
offsetFromOrigin
=
Offset
.
zero
,
Offset
localOffsetFromOrigin
,
})
:
assert
(
globalPosition
!=
null
),
assert
(
offsetFromOrigin
!=
null
);
assert
(
offsetFromOrigin
!=
null
),
localPosition
=
localPosition
??
globalPosition
,
localOffsetFromOrigin
=
localOffsetFromOrigin
??
offsetFromOrigin
;
/// The global position of the pointer when it triggered this update.
final
Offset
globalPosition
;
/// The local position of the pointer when it triggered this update.
final
Offset
localPosition
;
/// A delta offset from the point where the long press drag initially contacted
/// the screen to the point where the pointer is currently located (the
/// present [globalPosition]) when this callback is triggered.
final
Offset
offsetFromOrigin
;
/// A local delta offset from the point where the long press drag initially contacted
/// the screen to the point where the pointer is currently located (the
/// present [localPosition]) when this callback is triggered.
final
Offset
localOffsetFromOrigin
;
}
/// Details for callbacks that use [GestureLongPressEndCallback].
...
...
@@ -95,11 +113,17 @@ class LongPressEndDetails {
/// Creates the details for a [GestureLongPressEndCallback].
///
/// The [globalPosition] argument must not be null.
const
LongPressEndDetails
({
this
.
globalPosition
=
Offset
.
zero
})
:
assert
(
globalPosition
!=
null
);
const
LongPressEndDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the pointer lifted from the screen.
final
Offset
globalPosition
;
/// The local position at which the pointer contacted the screen.
final
Offset
localPosition
;
}
/// Recognizes when the user has pressed down at the same location for a long
...
...
@@ -137,7 +161,7 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
);
bool
_longPressAccepted
=
false
;
Offset
_longPressOrigin
;
Offset
Pair
_longPressOrigin
;
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
// different set of buttons, the gesture is canceled.
int
_initialButtons
;
...
...
@@ -230,7 +254,7 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
_reset
();
}
else
if
(
event
is
PointerDownEvent
)
{
// The first touch.
_longPressOrigin
=
event
.
position
;
_longPressOrigin
=
OffsetPair
.
fromEventPosition
(
event
)
;
_initialButtons
=
event
.
buttons
;
}
else
if
(
event
is
PointerMoveEvent
)
{
if
(
event
.
buttons
!=
_initialButtons
)
{
...
...
@@ -245,7 +269,8 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
void
_checkLongPressStart
()
{
assert
(
_initialButtons
==
kPrimaryButton
);
final
LongPressStartDetails
details
=
LongPressStartDetails
(
globalPosition:
_longPressOrigin
,
globalPosition:
_longPressOrigin
.
global
,
localPosition:
_longPressOrigin
.
local
,
);
if
(
onLongPressStart
!=
null
)
invokeCallback
<
void
>(
'onLongPressStart'
,
...
...
@@ -258,7 +283,9 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
assert
(
_initialButtons
==
kPrimaryButton
);
final
LongPressMoveUpdateDetails
details
=
LongPressMoveUpdateDetails
(
globalPosition:
event
.
position
,
offsetFromOrigin:
event
.
position
-
_longPressOrigin
,
localPosition:
event
.
localPosition
,
offsetFromOrigin:
event
.
position
-
_longPressOrigin
.
global
,
localOffsetFromOrigin:
event
.
localPosition
-
_longPressOrigin
.
local
,
);
if
(
onLongPressMoveUpdate
!=
null
)
invokeCallback
<
void
>(
'onLongPressMoveUpdate'
,
...
...
@@ -269,6 +296,7 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
assert
(
_initialButtons
==
kPrimaryButton
);
final
LongPressEndDetails
details
=
LongPressEndDetails
(
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
);
if
(
onLongPressEnd
!=
null
)
invokeCallback
<
void
>(
'onLongPressEnd'
,
()
=>
onLongPressEnd
(
details
));
...
...
packages/flutter/lib/src/gestures/monodrag.dart
View file @
ae23d4a4
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'arena.dart'
;
import
'constants.dart'
;
...
...
@@ -169,17 +170,24 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
double
maxFlingVelocity
;
_DragState
_state
=
_DragState
.
ready
;
Offset
_initialPosition
;
Offset
_pendingDragOffset
;
Offset
Pair
_initialPosition
;
Offset
Pair
_pendingDragOffset
;
Duration
_lastPendingEventTimestamp
;
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
// different set of buttons, the gesture is canceled.
int
_initialButtons
;
Matrix4
_lastTransform
;
/// Distance moved in the global coordinate space of the screen in drag direction.
///
/// If drag is only allowed along a defined axis, this value may be negative to
/// differentiate the direction of the drag.
double
_globalDistanceMoved
;
bool
_isFlingGesture
(
VelocityEstimate
estimate
);
Offset
_getDeltaForDetails
(
Offset
delta
);
double
_getPrimaryValueFromOffset
(
Offset
value
);
bool
get
_hasSufficient
PendingDragDelta
ToAccept
;
bool
get
_hasSufficient
GlobalDistance
ToAccept
;
final
Map
<
int
,
VelocityTracker
>
_velocityTrackers
=
<
int
,
VelocityTracker
>{};
...
...
@@ -209,14 +217,16 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
@override
void
addAllowedPointer
(
PointerEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
_velocityTrackers
[
event
.
pointer
]
=
VelocityTracker
();
if
(
_state
==
_DragState
.
ready
)
{
_state
=
_DragState
.
possible
;
_initialPosition
=
event
.
position
;
_initialPosition
=
OffsetPair
(
global:
event
.
position
,
local:
event
.
localPosition
)
;
_initialButtons
=
event
.
buttons
;
_pendingDragOffset
=
Offset
.
zero
;
_pendingDragOffset
=
OffsetPair
.
zero
;
_globalDistanceMoved
=
0.0
;
_lastPendingEventTimestamp
=
event
.
timeStamp
;
_lastTransform
=
event
.
transform
;
_checkDown
();
}
else
if
(
_state
==
_DragState
.
accepted
)
{
resolve
(
GestureDisposition
.
accepted
);
...
...
@@ -230,7 +240,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
&&
(
event
is
PointerDownEvent
||
event
is
PointerMoveEvent
))
{
final
VelocityTracker
tracker
=
_velocityTrackers
[
event
.
pointer
];
assert
(
tracker
!=
null
);
tracker
.
addPosition
(
event
.
timeStamp
,
event
.
p
osition
);
tracker
.
addPosition
(
event
.
timeStamp
,
event
.
localP
osition
);
}
if
(
event
is
PointerMoveEvent
)
{
...
...
@@ -239,18 +249,26 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
stopTrackingPointer
(
event
.
pointer
);
return
;
}
final
Offset
delta
=
event
.
delta
;
if
(
_state
==
_DragState
.
accepted
)
{
_checkUpdate
(
sourceTimeStamp:
event
.
timeStamp
,
delta:
_getDeltaForDetails
(
d
elta
),
primaryDelta:
_getPrimaryValueFromOffset
(
d
elta
),
delta:
_getDeltaForDetails
(
event
.
localD
elta
),
primaryDelta:
_getPrimaryValueFromOffset
(
event
.
localD
elta
),
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
);
}
else
{
_pendingDragOffset
+=
delta
;
_pendingDragOffset
+=
OffsetPair
(
local:
event
.
localDelta
,
global:
event
.
delta
)
;
_lastPendingEventTimestamp
=
event
.
timeStamp
;
if
(
_hasSufficientPendingDragDeltaToAccept
)
_lastTransform
=
event
.
transform
;
final
Offset
movedLocally
=
_getDeltaForDetails
(
event
.
localDelta
);
final
Matrix4
localToGlobalTransform
=
event
.
transform
==
null
?
null
:
Matrix4
.
tryInvert
(
event
.
transform
);
_globalDistanceMoved
+=
PointerEvent
.
transformDeltaViaPositions
(
transform:
localToGlobalTransform
,
untransformedDelta:
movedLocally
,
untransformedEndPosition:
event
.
localPosition
,
).
distance
*
(
_getPrimaryValueFromOffset
(
movedLocally
)
??
1
).
sign
;
if
(
_hasSufficientGlobalDistanceToAccept
)
resolve
(
GestureDisposition
.
accepted
);
}
}
...
...
@@ -261,27 +279,39 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
void
acceptGesture
(
int
pointer
)
{
if
(
_state
!=
_DragState
.
accepted
)
{
_state
=
_DragState
.
accepted
;
final
Offset
delta
=
_pendingDragOffset
;
final
Offset
Pair
delta
=
_pendingDragOffset
;
final
Duration
timestamp
=
_lastPendingEventTimestamp
;
Offset
updateDelta
;
final
Matrix4
transform
=
_lastTransform
;
Offset
localUpdateDelta
;
switch
(
dragStartBehavior
)
{
case
DragStartBehavior
.
start
:
_initialPosition
=
_initialPosition
+
delta
;
u
pdateDelta
=
Offset
.
zero
;
localU
pdateDelta
=
Offset
.
zero
;
break
;
case
DragStartBehavior
.
down
:
updateDelta
=
_getDeltaForDetails
(
delta
);
localUpdateDelta
=
_getDeltaForDetails
(
delta
.
local
);
break
;
}
_pendingDragOffset
=
Offset
.
zero
;
_pendingDragOffset
=
Offset
Pair
.
zero
;
_lastPendingEventTimestamp
=
null
;
_lastTransform
=
null
;
_checkStart
(
timestamp
);
if
(
updateDelta
!=
Offset
.
zero
)
{
if
(
localUpdateDelta
!=
Offset
.
zero
&&
onUpdate
!=
null
)
{
final
Matrix4
localToGlobal
=
transform
!=
null
?
Matrix4
.
tryInvert
(
transform
)
:
null
;
final
Offset
correctedLocalPosition
=
_initialPosition
.
local
+
localUpdateDelta
;
final
Offset
globalUpdateDelta
=
PointerEvent
.
transformDeltaViaPositions
(
untransformedEndPosition:
correctedLocalPosition
,
untransformedDelta:
localUpdateDelta
,
transform:
localToGlobal
,
);
final
OffsetPair
updateDelta
=
OffsetPair
(
local:
localUpdateDelta
,
global:
globalUpdateDelta
);
final
OffsetPair
correctedPosition
=
_initialPosition
+
updateDelta
;
// Only adds delta for down behaviour
_checkUpdate
(
sourceTimeStamp:
timestamp
,
delta:
updateDelta
,
primaryDelta:
_getPrimaryValueFromOffset
(
updateDelta
),
globalPosition:
_initialPosition
+
updateDelta
,
// Only adds delta for down behavior
delta:
localUpdateDelta
,
primaryDelta:
_getPrimaryValueFromOffset
(
localUpdateDelta
),
globalPosition:
correctedPosition
.
global
,
localPosition:
correctedPosition
.
local
,
);
}
}
...
...
@@ -316,7 +346,8 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
void
_checkDown
()
{
assert
(
_initialButtons
==
kPrimaryButton
);
final
DragDownDetails
details
=
DragDownDetails
(
globalPosition:
_initialPosition
,
globalPosition:
_initialPosition
.
global
,
localPosition:
_initialPosition
.
local
,
);
if
(
onDown
!=
null
)
invokeCallback
<
void
>(
'onDown'
,
()
=>
onDown
(
details
));
...
...
@@ -326,7 +357,8 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
assert
(
_initialButtons
==
kPrimaryButton
);
final
DragStartDetails
details
=
DragStartDetails
(
sourceTimeStamp:
timestamp
,
globalPosition:
_initialPosition
,
globalPosition:
_initialPosition
.
global
,
localPosition:
_initialPosition
.
local
,
);
if
(
onStart
!=
null
)
invokeCallback
<
void
>(
'onStart'
,
()
=>
onStart
(
details
));
...
...
@@ -337,6 +369,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
Offset
delta
,
double
primaryDelta
,
Offset
globalPosition
,
Offset
localPosition
,
})
{
assert
(
_initialButtons
==
kPrimaryButton
);
final
DragUpdateDetails
details
=
DragUpdateDetails
(
...
...
@@ -344,6 +377,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
delta:
delta
,
primaryDelta:
primaryDelta
,
globalPosition:
globalPosition
,
localPosition:
localPosition
,
);
if
(
onUpdate
!=
null
)
invokeCallback
<
void
>(
'onUpdate'
,
()
=>
onUpdate
(
details
));
...
...
@@ -430,7 +464,7 @@ class VerticalDragGestureRecognizer extends DragGestureRecognizer {
}
@override
bool
get
_hasSufficient
PendingDragDeltaToAccept
=>
_pendingDragOffset
.
dy
.
abs
()
>
kTouchSlop
;
bool
get
_hasSufficient
GlobalDistanceToAccept
=>
_globalDistanceMoved
.
abs
()
>
kTouchSlop
;
@override
Offset
_getDeltaForDetails
(
Offset
delta
)
=>
Offset
(
0.0
,
delta
.
dy
);
...
...
@@ -469,7 +503,7 @@ class HorizontalDragGestureRecognizer extends DragGestureRecognizer {
}
@override
bool
get
_hasSufficient
PendingDragDeltaToAccept
=>
_pendingDragOffset
.
dx
.
abs
()
>
kTouchSlop
;
bool
get
_hasSufficient
GlobalDistanceToAccept
=>
_globalDistanceMoved
.
abs
()
>
kTouchSlop
;
@override
Offset
_getDeltaForDetails
(
Offset
delta
)
=>
Offset
(
delta
.
dx
,
0.0
);
...
...
@@ -503,8 +537,8 @@ class PanGestureRecognizer extends DragGestureRecognizer {
}
@override
bool
get
_hasSufficient
PendingDragDelta
ToAccept
{
return
_
pendingDragOffset
.
distance
>
kPanSlop
;
bool
get
_hasSufficient
GlobalDistance
ToAccept
{
return
_
globalDistanceMoved
.
abs
()
>
kPanSlop
;
}
@override
...
...
packages/flutter/lib/src/gestures/multitap.dart
View file @
ae23d4a4
...
...
@@ -5,6 +5,7 @@
import
'dart:async'
;
import
'dart:ui'
show
Offset
;
import
'package:flutter/foundation.dart'
show
required
;
import
'package:vector_math/vector_math_64.dart'
;
import
'arena.dart'
;
import
'binding.dart'
;
...
...
@@ -66,22 +67,22 @@ class _TapTracker {
assert
(
event
!=
null
),
assert
(
event
.
buttons
!=
null
),
pointer
=
event
.
pointer
,
_initialPosition
=
event
.
position
,
_initial
Global
Position
=
event
.
position
,
initialButtons
=
event
.
buttons
,
_doubleTapMinTimeCountdown
=
_CountdownZoned
(
duration:
doubleTapMinTime
);
final
int
pointer
;
final
GestureArenaEntry
entry
;
final
Offset
_initialPosition
;
final
Offset
_initial
Global
Position
;
final
int
initialButtons
;
final
_CountdownZoned
_doubleTapMinTimeCountdown
;
bool
_isTrackingPointer
=
false
;
void
startTrackingPointer
(
PointerRoute
route
)
{
void
startTrackingPointer
(
PointerRoute
route
,
Matrix4
transform
)
{
if
(!
_isTrackingPointer
)
{
_isTrackingPointer
=
true
;
GestureBinding
.
instance
.
pointerRouter
.
addRoute
(
pointer
,
route
);
GestureBinding
.
instance
.
pointerRouter
.
addRoute
(
pointer
,
route
,
transform
);
}
}
...
...
@@ -92,8 +93,8 @@ class _TapTracker {
}
}
bool
isWithinTolerance
(
PointerEvent
event
,
double
tolerance
)
{
final
Offset
offset
=
event
.
position
-
_initialPosition
;
bool
isWithin
Global
Tolerance
(
PointerEvent
event
,
double
tolerance
)
{
final
Offset
offset
=
event
.
position
-
_initial
Global
Position
;
return
offset
.
distance
<=
tolerance
;
}
...
...
@@ -174,7 +175,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
@override
void
addAllowedPointer
(
PointerEvent
event
)
{
if
(
_firstTap
!=
null
)
{
if
(!
_firstTap
.
isWithinTolerance
(
event
,
kDoubleTapSlop
))
{
if
(!
_firstTap
.
isWithin
Global
Tolerance
(
event
,
kDoubleTapSlop
))
{
// Ignore out-of-bounds second taps.
return
;
}
else
if
(!
_firstTap
.
hasElapsedMinTime
()
||
!
_firstTap
.
hasSameButton
(
event
))
{
...
...
@@ -195,7 +196,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
doubleTapMinTime:
kDoubleTapMinTime
,
);
_trackers
[
event
.
pointer
]
=
tracker
;
tracker
.
startTrackingPointer
(
_handleEvent
);
tracker
.
startTrackingPointer
(
_handleEvent
,
event
.
transform
);
}
void
_handleEvent
(
PointerEvent
event
)
{
...
...
@@ -207,7 +208,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
else
_registerSecondTap
(
tracker
);
}
else
if
(
event
is
PointerMoveEvent
)
{
if
(!
tracker
.
isWithinTolerance
(
event
,
kDoubleTapTouchSlop
))
if
(!
tracker
.
isWithin
Global
Tolerance
(
event
,
kDoubleTapTouchSlop
))
_reject
(
tracker
);
}
else
if
(
event
is
PointerCancelEvent
)
{
_reject
(
tracker
);
...
...
@@ -320,13 +321,13 @@ class _TapGesture extends _TapTracker {
this
.
gestureRecognizer
,
PointerEvent
event
,
Duration
longTapDelay
,
})
:
_lastPosition
=
event
.
position
,
})
:
_lastPosition
=
OffsetPair
.
fromEventPosition
(
event
)
,
super
(
event:
event
,
entry:
GestureBinding
.
instance
.
gestureArena
.
add
(
event
.
pointer
,
gestureRecognizer
),
doubleTapMinTime:
kDoubleTapMinTime
,
)
{
startTrackingPointer
(
handleEvent
);
startTrackingPointer
(
handleEvent
,
event
.
transform
);
if
(
longTapDelay
>
Duration
.
zero
)
{
_timer
=
Timer
(
longTapDelay
,
()
{
_timer
=
null
;
...
...
@@ -340,21 +341,21 @@ class _TapGesture extends _TapTracker {
bool
_wonArena
=
false
;
Timer
_timer
;
Offset
_lastPosition
;
Offset
_finalPosition
;
Offset
Pair
_lastPosition
;
Offset
Pair
_finalPosition
;
void
handleEvent
(
PointerEvent
event
)
{
assert
(
event
.
pointer
==
pointer
);
if
(
event
is
PointerMoveEvent
)
{
if
(!
isWithinTolerance
(
event
,
kTouchSlop
))
if
(!
isWithin
Global
Tolerance
(
event
,
kTouchSlop
))
cancel
();
else
_lastPosition
=
event
.
position
;
_lastPosition
=
OffsetPair
.
fromEventPosition
(
event
)
;
}
else
if
(
event
is
PointerCancelEvent
)
{
cancel
();
}
else
if
(
event
is
PointerUpEvent
)
{
stopTrackingPointer
(
handleEvent
);
_finalPosition
=
event
.
position
;
_finalPosition
=
OffsetPair
.
fromEventPosition
(
event
)
;
_check
();
}
}
...
...
@@ -447,6 +448,7 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
invokeCallback
<
void
>(
'onTapDown'
,
()
{
onTapDown
(
event
.
pointer
,
TapDownDetails
(
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
kind:
event
.
kind
,
));
});
...
...
@@ -472,23 +474,29 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
invokeCallback
<
void
>(
'onTapCancel'
,
()
=>
onTapCancel
(
pointer
));
}
void
_dispatchTap
(
int
pointer
,
Offset
globalP
osition
)
{
void
_dispatchTap
(
int
pointer
,
Offset
Pair
p
osition
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
_gestureMap
.
remove
(
pointer
);
if
(
onTapUp
!=
null
)
invokeCallback
<
void
>(
'onTapUp'
,
()
=>
onTapUp
(
pointer
,
TapUpDetails
(
globalPosition:
globalPosition
)));
invokeCallback
<
void
>(
'onTapUp'
,
()
{
onTapUp
(
pointer
,
TapUpDetails
(
localPosition:
position
.
local
,
globalPosition:
position
.
global
,
));
});
if
(
onTap
!=
null
)
invokeCallback
<
void
>(
'onTap'
,
()
=>
onTap
(
pointer
));
}
void
_dispatchLongTap
(
int
pointer
,
Offset
lastPosition
)
{
void
_dispatchLongTap
(
int
pointer
,
Offset
Pair
lastPosition
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
if
(
onLongTapDown
!=
null
)
invokeCallback
<
void
>(
'onLongTapDown'
,
()
{
onLongTapDown
(
pointer
,
TapDownDetails
(
globalPosition:
lastPosition
,
globalPosition:
lastPosition
.
global
,
localPosition:
lastPosition
.
local
,
kind:
getKindForPointer
(
pointer
),
),
);
...
...
packages/flutter/lib/src/gestures/pointer_router.dart
View file @
ae23d4a4
...
...
@@ -5,6 +5,7 @@
import
'dart:collection'
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'events.dart'
;
...
...
@@ -13,8 +14,8 @@ typedef PointerRoute = void Function(PointerEvent event);
/// A routing table for [PointerEvent] events.
class
PointerRouter
{
final
Map
<
int
,
LinkedHashSet
<
PointerRoute
>>
_routeMap
=
<
int
,
LinkedHashSet
<
PointerRoute
>>{};
final
LinkedHashSet
<
PointerRoute
>
_globalRoutes
=
LinkedHashSet
<
PointerRoute
>();
final
Map
<
int
,
LinkedHashSet
<
_RouteEntry
>>
_routeMap
=
<
int
,
LinkedHashSet
<
_RouteEntry
>>{};
final
LinkedHashSet
<
_RouteEntry
>
_globalRoutes
=
LinkedHashSet
<
_RouteEntry
>();
/// Adds a route to the routing table.
///
...
...
@@ -23,10 +24,10 @@ class PointerRouter {
///
/// Routes added reentrantly within [PointerRouter.route] will take effect when
/// routing the next event.
void
addRoute
(
int
pointer
,
PointerRoute
route
)
{
final
LinkedHashSet
<
PointerRoute
>
routes
=
_routeMap
.
putIfAbsent
(
pointer
,
()
=>
LinkedHashSet
<
PointerRoute
>());
assert
(!
routes
.
contains
(
route
));
routes
.
add
(
route
);
void
addRoute
(
int
pointer
,
PointerRoute
route
,
[
Matrix4
transform
]
)
{
final
LinkedHashSet
<
_RouteEntry
>
routes
=
_routeMap
.
putIfAbsent
(
pointer
,
()
=>
LinkedHashSet
<
_RouteEntry
>());
assert
(!
routes
.
any
(
_RouteEntry
.
isRoutePredicate
(
route
)
));
routes
.
add
(
_RouteEntry
(
route:
route
,
transform:
transform
)
);
}
/// Removes a route from the routing table.
...
...
@@ -38,9 +39,9 @@ class PointerRouter {
/// immediately.
void
removeRoute
(
int
pointer
,
PointerRoute
route
)
{
assert
(
_routeMap
.
containsKey
(
pointer
));
final
LinkedHashSet
<
PointerRoute
>
routes
=
_routeMap
[
pointer
];
assert
(
routes
.
contains
(
route
));
routes
.
remove
(
route
);
final
LinkedHashSet
<
_RouteEntry
>
routes
=
_routeMap
[
pointer
];
assert
(
routes
.
any
(
_RouteEntry
.
isRoutePredicate
(
route
)
));
routes
.
remove
Where
(
_RouteEntry
.
isRoutePredicate
(
route
)
);
if
(
routes
.
isEmpty
)
_routeMap
.
remove
(
pointer
);
}
...
...
@@ -51,9 +52,9 @@ class PointerRouter {
///
/// Routes added reentrantly within [PointerRouter.route] will take effect when
/// routing the next event.
void
addGlobalRoute
(
PointerRoute
route
)
{
assert
(!
_globalRoutes
.
contains
(
route
));
_globalRoutes
.
add
(
route
);
void
addGlobalRoute
(
PointerRoute
route
,
[
Matrix4
transform
]
)
{
assert
(!
_globalRoutes
.
any
(
_RouteEntry
.
isRoutePredicate
(
route
)
));
_globalRoutes
.
add
(
_RouteEntry
(
route:
route
,
transform:
transform
)
);
}
/// Removes a route from the global entry in the routing table.
...
...
@@ -64,13 +65,14 @@ class PointerRouter {
/// Routes removed reentrantly within [PointerRouter.route] will take effect
/// immediately.
void
removeGlobalRoute
(
PointerRoute
route
)
{
assert
(
_globalRoutes
.
contains
(
route
));
_globalRoutes
.
remove
(
route
);
assert
(
_globalRoutes
.
any
(
_RouteEntry
.
isRoutePredicate
(
route
)
));
_globalRoutes
.
remove
Where
(
_RouteEntry
.
isRoutePredicate
(
route
)
);
}
void
_dispatch
(
PointerEvent
event
,
PointerRoute
route
)
{
void
_dispatch
(
PointerEvent
event
,
_RouteEntry
entry
)
{
try
{
route
(
event
);
event
=
event
.
transformed
(
entry
.
transform
);
entry
.
route
(
event
);
}
catch
(
exception
,
stack
)
{
FlutterError
.
reportError
(
FlutterErrorDetailsForPointerRouter
(
exception:
exception
,
...
...
@@ -78,7 +80,7 @@ class PointerRouter {
library
:
'gesture library'
,
context:
ErrorDescription
(
'while routing a pointer event'
),
router:
this
,
route:
route
,
route:
entry
.
route
,
event:
event
,
informationCollector:
()
sync
*
{
yield
DiagnosticsProperty
<
PointerEvent
>(
'Event'
,
event
,
style:
DiagnosticsTreeStyle
.
errorProperty
);
...
...
@@ -92,17 +94,17 @@ class PointerRouter {
/// Routes are called in the order in which they were added to the
/// PointerRouter object.
void
route
(
PointerEvent
event
)
{
final
LinkedHashSet
<
PointerRoute
>
routes
=
_routeMap
[
event
.
pointer
];
final
List
<
PointerRoute
>
globalRoutes
=
List
<
PointerRoute
>.
from
(
_globalRoutes
);
final
LinkedHashSet
<
_RouteEntry
>
routes
=
_routeMap
[
event
.
pointer
];
final
List
<
_RouteEntry
>
globalRoutes
=
List
<
_RouteEntry
>.
from
(
_globalRoutes
);
if
(
routes
!=
null
)
{
for
(
PointerRoute
route
in
List
<
PointerRoute
>.
from
(
routes
))
{
if
(
routes
.
contains
(
route
))
_dispatch
(
event
,
route
);
for
(
_RouteEntry
entry
in
List
<
_RouteEntry
>.
from
(
routes
))
{
if
(
routes
.
any
(
_RouteEntry
.
isRoutePredicate
(
entry
.
route
)
))
_dispatch
(
event
,
entry
);
}
}
for
(
PointerRoute
route
in
globalRoutes
)
{
if
(
_globalRoutes
.
contains
(
route
))
_dispatch
(
event
,
route
);
for
(
_RouteEntry
entry
in
globalRoutes
)
{
if
(
_globalRoutes
.
any
(
_RouteEntry
.
isRoutePredicate
(
entry
.
route
)
))
_dispatch
(
event
,
entry
);
}
}
}
...
...
@@ -149,3 +151,19 @@ class FlutterErrorDetailsForPointerRouter extends FlutterErrorDetails {
/// The pointer event that was being routed when the exception was raised.
final
PointerEvent
event
;
}
typedef
_RouteEntryPredicate
=
bool
Function
(
_RouteEntry
entry
);
class
_RouteEntry
{
const
_RouteEntry
({
@required
this
.
route
,
@required
this
.
transform
,
});
final
PointerRoute
route
;
final
Matrix4
transform
;
static
_RouteEntryPredicate
isRoutePredicate
(
PointerRoute
route
)
{
return
(
_RouteEntry
entry
)
=>
entry
.
route
==
route
;
}
}
packages/flutter/lib/src/gestures/pointer_signal_resolver.dart
View file @
ae23d4a4
...
...
@@ -47,9 +47,9 @@ class PointerSignalResolver {
assert
(
_currentEvent
==
null
);
return
;
}
assert
(
_currentEvent
==
event
);
assert
(
(
_currentEvent
.
original
??
_currentEvent
)
==
event
);
try
{
_firstRegisteredCallback
(
e
vent
);
_firstRegisteredCallback
(
_currentE
vent
);
}
catch
(
exception
,
stack
)
{
FlutterError
.
reportError
(
FlutterErrorDetails
(
exception:
exception
,
...
...
packages/flutter/lib/src/gestures/recognizer.dart
View file @
ae23d4a4
...
...
@@ -6,6 +6,7 @@ import 'dart:async';
import
'dart:collection'
;
import
'dart:ui'
show
Offset
;
import
'package:vector_math/vector_math_64.dart'
;
import
'package:flutter/foundation.dart'
;
import
'arena.dart'
;
...
...
@@ -293,12 +294,16 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
/// Causes events related to the given pointer ID to be routed to this recognizer.
///
/// The pointer events are delivered to [handleEvent].
/// The pointer events are transformed according to `transform` and then delivered
/// to [handleEvent]. The value for the `transform` argument is usually obtained
/// from [PointerDownEvent.transform] to transform the events from the global
/// coordinate space into the coordinate space of the event receiver. It may be
/// null if no transformation is necessary.
///
/// Use [stopTrackingPointer] to remove the route added by this function.
@protected
void
startTrackingPointer
(
int
pointer
)
{
GestureBinding
.
instance
.
pointerRouter
.
addRoute
(
pointer
,
handleEvent
);
void
startTrackingPointer
(
int
pointer
,
[
Matrix4
transform
]
)
{
GestureBinding
.
instance
.
pointerRouter
.
addRoute
(
pointer
,
handleEvent
,
transform
);
_trackedPointers
.
add
(
pointer
);
assert
(!
_entries
.
containsValue
(
pointer
));
_entries
[
pointer
]
=
_addPointerToArena
(
pointer
);
...
...
@@ -410,8 +415,8 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
/// The ID of the primary pointer this recognizer is tracking.
int
primaryPointer
;
/// The
global
location at which the primary pointer contacted the screen.
Offset
initialPosition
;
/// The location at which the primary pointer contacted the screen.
Offset
Pair
initialPosition
;
// Whether this pointer is accepted by winning the arena or as defined by
// a subclass calling acceptGesture.
...
...
@@ -420,11 +425,11 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
@override
void
addAllowedPointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
if
(
state
==
GestureRecognizerState
.
ready
)
{
state
=
GestureRecognizerState
.
possible
;
primaryPointer
=
event
.
pointer
;
initialPosition
=
event
.
position
;
initialPosition
=
OffsetPair
(
local:
event
.
localPosition
,
global:
event
.
position
)
;
if
(
deadline
!=
null
)
_timer
=
Timer
(
deadline
,
()
=>
didExceedDeadlineWithEvent
(
event
));
}
...
...
@@ -437,11 +442,11 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
final
bool
isPreAcceptSlopPastTolerance
=
!
_gestureAccepted
&&
preAcceptSlopTolerance
!=
null
&&
_getDistance
(
event
)
>
preAcceptSlopTolerance
;
_get
Global
Distance
(
event
)
>
preAcceptSlopTolerance
;
final
bool
isPostAcceptSlopPastTolerance
=
_gestureAccepted
&&
postAcceptSlopTolerance
!=
null
&&
_getDistance
(
event
)
>
postAcceptSlopTolerance
;
_get
Global
Distance
(
event
)
>
postAcceptSlopTolerance
;
if
(
event
is
PointerMoveEvent
&&
(
isPreAcceptSlopPastTolerance
||
isPostAcceptSlopPastTolerance
))
{
resolve
(
GestureDisposition
.
rejected
);
...
...
@@ -509,8 +514,8 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
}
}
double
_getDistance
(
PointerEvent
event
)
{
final
Offset
offset
=
event
.
position
-
initialPosition
;
double
_get
Global
Distance
(
PointerEvent
event
)
{
final
Offset
offset
=
event
.
position
-
initialPosition
.
global
;
return
offset
.
distance
;
}
...
...
@@ -520,3 +525,57 @@ abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecogni
properties
.
add
(
EnumProperty
<
GestureRecognizerState
>(
'state'
,
state
));
}
}
/// A container for a [local] and [global] [Offset] pair.
///
/// Usually, the [global] [Offset] is in the coordinate space of the screen
/// after conversion to logical pixels and the [local] offset is the same
/// [Offset], but transformed to a local coordinate space.
class
OffsetPair
{
/// Creates a [OffsetPair] combining a [local] and [global] [Offset].
const
OffsetPair
({
@required
this
.
local
,
@required
this
.
global
,
});
/// Creates a [OffsetPair] from [PointerEvent.localPosition] and
/// [PointerEvent.position].
factory
OffsetPair
.
fromEventPosition
(
PointerEvent
event
)
{
return
OffsetPair
(
local:
event
.
localPosition
,
global:
event
.
position
);
}
/// Creates a [OffsetPair] from [PointerEvent.localDelta] and
/// [PointerEvent.delta].
factory
OffsetPair
.
fromEventDelta
(
PointerEvent
event
)
{
return
OffsetPair
(
local:
event
.
localDelta
,
global:
event
.
delta
);
}
/// A [OffsetPair] where both [Offset]s are [Offset.zero].
static
const
OffsetPair
zero
=
OffsetPair
(
local:
Offset
.
zero
,
global:
Offset
.
zero
);
/// The [Offset] in the local coordinate space.
final
Offset
local
;
/// The [Offset] in the global coordinate space after conversion to logical
/// pixels.
final
Offset
global
;
/// Adds the `other.global` to [global] and `other.local` to [local].
OffsetPair
operator
+(
OffsetPair
other
)
{
return
OffsetPair
(
local:
local
+
other
.
local
,
global:
global
+
other
.
global
,
);
}
/// Subtracts the `other.global` from [global] and `other.local` from [local].
OffsetPair
operator
-(
OffsetPair
other
)
{
return
OffsetPair
(
local:
local
-
other
.
local
,
global:
global
-
other
.
global
,
);
}
@override
String
toString
()
=>
'
$runtimeType
(local:
$local
, global:
$global
)'
;
}
packages/flutter/lib/src/gestures/tap.dart
View file @
ae23d4a4
...
...
@@ -21,14 +21,19 @@ class TapDownDetails {
/// The [globalPosition] argument must not be null.
TapDownDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
this
.
kind
,
})
:
assert
(
globalPosition
!=
null
);
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the pointer contacted the screen.
final
Offset
globalPosition
;
/// The kind of the device that initiated the event.
final
PointerDeviceKind
kind
;
/// The local position at which the pointer contacted the screen.
final
Offset
localPosition
;
}
/// Signature for when a pointer that might cause a tap has contacted the
...
...
@@ -51,11 +56,17 @@ typedef GestureTapDownCallback = void Function(TapDownDetails details);
/// * [TapGestureRecognizer], which passes this information to one of its callbacks.
class
TapUpDetails
{
/// The [globalPosition] argument must not be null.
TapUpDetails
({
this
.
globalPosition
=
Offset
.
zero
})
:
assert
(
globalPosition
!=
null
);
TapUpDetails
({
this
.
globalPosition
=
Offset
.
zero
,
Offset
localPosition
,
})
:
assert
(
globalPosition
!=
null
),
localPosition
=
localPosition
??
globalPosition
;
/// The global position at which the pointer contacted the screen.
final
Offset
globalPosition
;
/// The local position at which the pointer contacted the screen.
final
Offset
localPosition
;
}
/// Signature for when a pointer that will trigger a tap has stopped contacting
...
...
@@ -221,7 +232,7 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
bool
_sentTapDown
=
false
;
bool
_wonArenaForPrimaryPointer
=
false
;
Offset
_finalPosition
;
Offset
Pair
_finalPosition
;
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
// different set of buttons, the gesture is canceled.
int
_initialButtons
;
...
...
@@ -260,7 +271,7 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
@override
void
handlePrimaryPointer
(
PointerEvent
event
)
{
if
(
event
is
PointerUpEvent
)
{
_finalPosition
=
event
.
position
;
_finalPosition
=
OffsetPair
(
global:
event
.
position
,
local:
event
.
localPosition
)
;
_checkUp
();
}
else
if
(
event
is
PointerCancelEvent
)
{
resolve
(
GestureDisposition
.
rejected
);
...
...
@@ -319,7 +330,8 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
return
;
}
final
TapDownDetails
details
=
TapDownDetails
(
globalPosition:
initialPosition
,
globalPosition:
initialPosition
.
global
,
localPosition:
initialPosition
.
local
,
kind:
getKindForPointer
(
pointer
),
);
switch
(
_initialButtons
)
{
...
...
@@ -342,7 +354,8 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
return
;
}
final
TapUpDetails
details
=
TapUpDetails
(
globalPosition:
_finalPosition
,
globalPosition:
_finalPosition
.
global
,
localPosition:
_finalPosition
.
local
,
);
switch
(
_initialButtons
)
{
case
kPrimaryButton:
...
...
@@ -390,7 +403,8 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
FlagProperty
(
'wonArenaForPrimaryPointer'
,
value:
_wonArenaForPrimaryPointer
,
ifTrue:
'won arena'
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'finalPosition'
,
_finalPosition
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'finalPosition'
,
_finalPosition
?.
global
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
Offset
>(
'finalLocalPosition'
,
_finalPosition
?.
local
,
defaultValue:
_finalPosition
?.
global
));
properties
.
add
(
FlagProperty
(
'sentTapDown'
,
value:
_sentTapDown
,
ifTrue:
'sent tap down'
));
// TODO(tongmu): Add property _initialButtons and update related tests
}
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
ae23d4a4
...
...
@@ -657,8 +657,10 @@ class BoxHitTestResult extends HitTestResult {
/// provided `hitTest` callback, which is invoked with the transformed
/// `position` as argument.
///
/// Since the provided paint `transform` describes the transform from the
/// child to the parent, the matrix is inverted before it is used to transform
/// The provided paint `transform` (which describes the transform from the
/// child to the parent in 3D) is processed by
/// [PointerEvent.removePerspectiveTransform] to remove the
/// perspective component and inverted before it is used to transform
/// `position` from the coordinate system of the parent to the system of the
/// child.
///
...
...
@@ -674,7 +676,8 @@ class BoxHitTestResult extends HitTestResult {
/// position is not required to do the actual hit testing in that protocol.
///
/// {@tool sample}
/// This method is used in [RenderBox.hitTestChildren]:
/// This method is used in [RenderBox.hitTestChildren] when the child and
/// parent don't share the same origin.
///
/// ```dart
/// abstract class Foo extends RenderBox {
...
...
@@ -713,7 +716,7 @@ class BoxHitTestResult extends HitTestResult {
})
{
assert
(
hitTest
!=
null
);
if
(
transform
!=
null
)
{
transform
=
Matrix4
.
tryInvert
(
transform
);
transform
=
Matrix4
.
tryInvert
(
PointerEvent
.
removePerspectiveTransform
(
transform
)
);
if
(
transform
==
null
)
{
// Objects are not visible on screen and cannot be hit-tested.
return
false
;
...
...
@@ -788,7 +791,14 @@ class BoxHitTestResult extends HitTestResult {
final
Offset
transformedPosition
=
position
==
null
||
transform
==
null
?
position
:
MatrixUtils
.
transformPoint
(
transform
,
position
);
return
hitTest
(
this
,
transformedPosition
);
if
(
transform
!=
null
)
{
pushTransform
(
transform
);
}
final
bool
isHit
=
hitTest
(
this
,
transformedPosition
);
if
(
transform
!=
null
)
{
popTransform
();
}
return
isHit
;
}
}
...
...
@@ -797,7 +807,7 @@ class BoxHitTestEntry extends HitTestEntry {
/// Creates a box hit test entry.
///
/// The [localPosition] argument must not be null.
const
BoxHitTestEntry
(
RenderBox
target
,
this
.
localPosition
)
BoxHitTestEntry
(
RenderBox
target
,
this
.
localPosition
)
:
assert
(
localPosition
!=
null
),
super
(
target
);
...
...
@@ -2057,10 +2067,11 @@ abstract class RenderBox extends RenderObject {
/// This [RenderBox] is responsible for checking whether the given position is
/// within its bounds.
///
/// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
/// [BoxHitTestResult.addWithPaintOffset], or
/// [BoxHitTestResult.addWithRawTransform] should be used to transform
/// `position` to the local coordinate system.
/// If transforming is necessary, [HitTestResult.addWithPaintTransform],
/// [HitTestResult.addWithPaintOffset], or [HitTestResult.addWithRawTransform] need
/// to be invoked by the caller to record the required transform operations
/// in the [HitTestResult]. These methods will also help with applying the
/// transform to `position`.
///
/// Hit testing requires layout to be up-to-date but does not require painting
/// to be up-to-date. That means a render object can rely upon [performLayout]
...
...
@@ -2130,10 +2141,11 @@ abstract class RenderBox extends RenderObject {
/// This [RenderBox] is responsible for checking whether the given position is
/// within its bounds.
///
/// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
/// [BoxHitTestResult.addWithPaintOffset], or
/// [BoxHitTestResult.addWithRawTransform] should be used to transform
/// `position` to the local coordinate system.
/// If transforming is necessary, [HitTestResult.addWithPaintTransform],
/// [HitTestResult.addWithPaintOffset], or [HitTestResult.addWithRawTransform] need
/// to be invoked by the caller to record the required transform operations
/// in the [HitTestResult]. These methods will also help with applying the
/// transform to `position`.
///
/// Used by [hitTest]. If you override [hitTest] and do not call this
/// function, then you don't need to implement this function.
...
...
packages/flutter/lib/src/rendering/layer.dart
View file @
ae23d4a4
...
...
@@ -8,6 +8,7 @@ import 'dart:ui' as ui show EngineLayer, Image, ImageFilter, PathMetric,
Picture
,
PictureRecorder
,
Scene
,
SceneBuilder
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/painting.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -1237,7 +1238,9 @@ class TransformLayer extends OffsetLayer {
Offset
_transformOffset
(
Offset
regionOffset
)
{
if
(
_inverseDirty
)
{
_invertedTransform
=
Matrix4
.
tryInvert
(
transform
);
_invertedTransform
=
Matrix4
.
tryInvert
(
PointerEvent
.
removePerspectiveTransform
(
transform
)
);
_inverseDirty
=
false
;
}
if
(
_invertedTransform
==
null
)
...
...
packages/flutter/lib/src/rendering/list_wheel_viewport.dart
View file @
ae23d4a4
...
...
@@ -984,9 +984,7 @@ class RenderListWheelViewport
}
@override
bool
hitTestChildren
(
BoxHitTestResult
result
,
{
Offset
position
})
{
return
false
;
}
bool
hitTestChildren
(
BoxHitTestResult
result
,
{
Offset
position
})
=>
false
;
@override
RevealedOffset
getOffsetToReveal
(
RenderObject
target
,
double
alignment
,
{
Rect
rect
})
{
...
...
packages/flutter/lib/src/rendering/platform_view.dart
View file @
ae23d4a4
...
...
@@ -381,7 +381,7 @@ class RenderUiKitView extends RenderBox {
return
;
}
_gestureRecognizer
.
addPointer
(
event
);
_lastPointerDownEvent
=
event
;
_lastPointerDownEvent
=
event
.
original
??
event
;
}
// This is registered as a global PointerRoute while the render object is attached.
...
...
@@ -389,11 +389,10 @@ class RenderUiKitView extends RenderBox {
if
(
event
is
!
PointerDownEvent
)
{
return
;
}
final
Offset
localOffset
=
globalToLocal
(
event
.
position
);
if
(!(
Offset
.
zero
&
size
).
contains
(
localOffset
))
{
if
(!(
Offset
.
zero
&
size
).
contains
(
event
.
localPosition
))
{
return
;
}
if
(
event
!=
_lastPointerDownEvent
)
{
if
(
(
event
.
original
??
event
)
!=
_lastPointerDownEvent
)
{
// The pointer event is in the bounds of this render box, but we didn't get it in handleEvent.
// This means that the pointer event was absorbed by a different render object.
// Since on the platform side the FlutterTouchIntercepting view is seeing all events that are
...
...
@@ -455,7 +454,7 @@ class _UiKitViewGestureRecognizer extends OneSequenceGestureRecognizer {
@override
void
addAllowedPointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
for
(
OneSequenceGestureRecognizer
recognizer
in
_gestureRecognizers
)
{
recognizer
.
addPointer
(
event
);
}
...
...
@@ -528,7 +527,7 @@ class _AndroidViewGestureRecognizer extends OneSequenceGestureRecognizer {
@override
void
addAllowedPointer
(
PointerDownEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
startTrackingPointer
(
event
.
pointer
,
event
.
transform
);
for
(
OneSequenceGestureRecognizer
recognizer
in
_gestureRecognizers
)
{
recognizer
.
addPointer
(
event
);
}
...
...
packages/flutter/lib/src/rendering/sliver.dart
View file @
ae23d4a4
...
...
@@ -846,12 +846,18 @@ class SliverHitTestResult extends HitTestResult {
assert
(
mainAxisPosition
!=
null
);
assert
(
crossAxisPosition
!=
null
);
assert
(
hitTest
!=
null
);
// TODO(goderbauer): use paintOffset when transforming pointer events is implemented.
return
hitTest
(
if
(
paintOffset
!=
null
)
{
pushTransform
(
Matrix4
.
translationValues
(
paintOffset
.
dx
,
paintOffset
.
dy
,
0
));
}
final
bool
isHit
=
hitTest
(
this
,
mainAxisPosition:
mainAxisPosition
-
mainAxisOffset
,
crossAxisPosition:
crossAxisPosition
-
crossAxisOffset
,
);
if
(
paintOffset
!=
null
)
{
popTransform
();
}
return
isHit
;
}
}
...
...
@@ -863,7 +869,7 @@ class SliverHitTestEntry extends HitTestEntry {
/// Creates a sliver hit test entry.
///
/// The [mainAxisPosition] and [crossAxisPosition] arguments must not be null.
const
SliverHitTestEntry
(
SliverHitTestEntry
(
RenderSliver
target
,
{
@required
this
.
mainAxisPosition
,
@required
this
.
crossAxisPosition
,
...
...
packages/flutter/lib/src/rendering/texture.dart
View file @
ae23d4a4
...
...
@@ -66,9 +66,7 @@ class TextureBox extends RenderBox {
}
@override
bool
hitTestSelf
(
Offset
position
)
{
return
true
;
}
bool
hitTestSelf
(
Offset
position
)
=>
true
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
...
...
packages/flutter/lib/src/widgets/drag_target.dart
View file @
ae23d4a4
...
...
@@ -663,7 +663,7 @@ class _DragAvatar<T> extends Drag {
_activeTarget
=
newTarget
;
}
Iterable
<
_DragTargetState
<
T
>>
_getDragTargets
(
List
<
HitTestEntry
>
path
)
sync
*
{
Iterable
<
_DragTargetState
<
T
>>
_getDragTargets
(
Iterable
<
HitTestEntry
>
path
)
sync
*
{
// Look for the RenderBoxes that corresponds to the hit target (the hit target
// widgets build RenderMetaData boxes for us for this purpose).
for
(
HitTestEntry
entry
in
path
)
{
...
...
packages/flutter/test/gestures/events_test.dart
View file @
ae23d4a4
...
...
@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'gesture_tester.dart'
;
...
...
@@ -124,4 +126,344 @@ void main() {
expect
(
event
.
buttons
,
kPrimaryButton
);
});
});
test
(
'paintTransformToPointerEventTransform'
,
()
{
Matrix4
original
=
Matrix4
.
identity
();
Matrix4
changed
=
PointerEvent
.
removePerspectiveTransform
(
original
);
expect
(
changed
,
original
);
original
=
Matrix4
.
identity
()..
scale
(
3.0
);
changed
=
PointerEvent
.
removePerspectiveTransform
(
original
);
expect
(
changed
,
isNot
(
original
));
original
..
setColumn
(
2
,
Vector4
(
0
,
0
,
1
,
0
))
..
setRow
(
2
,
Vector4
(
0
,
0
,
1
,
0
));
expect
(
changed
,
original
);
});
test
(
'transformPosition'
,
()
{
const
Offset
position
=
Offset
(
20
,
30
);
expect
(
PointerEvent
.
transformPosition
(
null
,
position
),
position
);
expect
(
PointerEvent
.
transformPosition
(
Matrix4
.
identity
(),
position
),
position
);
final
Matrix4
transform
=
Matrix4
.
translationValues
(
10
,
20
,
0
);
expect
(
PointerEvent
.
transformPosition
(
transform
,
position
),
const
Offset
(
20.0
+
10.0
,
30.0
+
20.0
));
});
test
(
'transformDeltaViaPositions'
,
()
{
Offset
transformedDelta
=
PointerEvent
.
transformDeltaViaPositions
(
untransformedEndPosition:
const
Offset
(
20
,
30
),
untransformedDelta:
const
Offset
(
5
,
5
),
transform:
Matrix4
.
identity
()..
scale
(
2.0
,
2.0
,
1.0
),
);
expect
(
transformedDelta
,
const
Offset
(
10.0
,
10.0
));
transformedDelta
=
PointerEvent
.
transformDeltaViaPositions
(
untransformedEndPosition:
const
Offset
(
20
,
30
),
transformedEndPosition:
const
Offset
(
40
,
60
),
untransformedDelta:
const
Offset
(
5
,
5
),
transform:
Matrix4
.
identity
()..
scale
(
2.0
,
2.0
,
1.0
),
);
expect
(
transformedDelta
,
const
Offset
(
10.0
,
10.0
));
transformedDelta
=
PointerEvent
.
transformDeltaViaPositions
(
untransformedEndPosition:
const
Offset
(
20
,
30
),
transformedEndPosition:
const
Offset
(
40
,
60
),
untransformedDelta:
const
Offset
(
5
,
5
),
transform:
null
,
);
expect
(
transformedDelta
,
const
Offset
(
5
,
5
));
});
test
(
'transforming events'
,
()
{
final
Matrix4
transform
=
(
Matrix4
.
identity
()..
scale
(
2.0
,
2.0
,
1.0
))
*
Matrix4
.
translationValues
(
10.0
,
20.0
,
0.0
);
const
Offset
localPosition
=
Offset
(
60
,
100
);
const
Offset
localDelta
=
Offset
(
10
,
10
);
const
PointerAddedEvent
added
=
PointerAddedEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
);
_expectTransformedEvent
(
original:
added
,
transform:
transform
,
localPosition:
localPosition
,
);
const
PointerCancelEvent
cancel
=
PointerCancelEvent
(
timeStamp:
Duration
(
seconds:
2
),
pointer:
45
,
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
buttons:
4
,
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
);
_expectTransformedEvent
(
original:
cancel
,
transform:
transform
,
localPosition:
localPosition
,
);
const
PointerDownEvent
down
=
PointerDownEvent
(
timeStamp:
Duration
(
seconds:
2
),
pointer:
45
,
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
buttons:
4
,
obscured:
true
,
pressure:
34
,
pressureMin:
10
,
pressureMax:
60
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
);
_expectTransformedEvent
(
original:
down
,
transform:
transform
,
localPosition:
localPosition
,
);
const
PointerEnterEvent
enter
=
PointerEnterEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
delta:
Offset
(
5
,
5
),
buttons:
4
,
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
synthesized:
true
,
);
_expectTransformedEvent
(
original:
enter
,
transform:
transform
,
localPosition:
localPosition
,
localDelta:
localDelta
,
);
const
PointerExitEvent
exit
=
PointerExitEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
delta:
Offset
(
5
,
5
),
buttons:
4
,
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
synthesized:
true
,
);
_expectTransformedEvent
(
original:
exit
,
transform:
transform
,
localPosition:
localPosition
,
localDelta:
localDelta
,
);
const
PointerHoverEvent
hover
=
PointerHoverEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
delta:
Offset
(
5
,
5
),
buttons:
4
,
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
synthesized:
true
,
);
_expectTransformedEvent
(
original:
hover
,
transform:
transform
,
localPosition:
localPosition
,
localDelta:
localDelta
,
);
const
PointerMoveEvent
move
=
PointerMoveEvent
(
timeStamp:
Duration
(
seconds:
2
),
pointer:
45
,
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
delta:
Offset
(
5
,
5
),
buttons:
4
,
obscured:
true
,
pressure:
34
,
pressureMin:
10
,
pressureMax:
60
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
platformData:
10
,
synthesized:
true
,
);
_expectTransformedEvent
(
original:
move
,
transform:
transform
,
localPosition:
localPosition
,
localDelta:
localDelta
,
);
const
PointerRemovedEvent
removed
=
PointerRemovedEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
obscured:
true
,
pressureMin:
10
,
pressureMax:
60
,
distanceMax:
24
,
radiusMin:
10
,
radiusMax:
50
,
);
_expectTransformedEvent
(
original:
removed
,
transform:
transform
,
localPosition:
localPosition
,
);
const
PointerScrollEvent
scroll
=
PointerScrollEvent
(
timeStamp:
Duration
(
seconds:
2
),
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
);
_expectTransformedEvent
(
original:
scroll
,
transform:
transform
,
localPosition:
localPosition
,
);
const
PointerUpEvent
up
=
PointerUpEvent
(
timeStamp:
Duration
(
seconds:
2
),
pointer:
45
,
kind:
PointerDeviceKind
.
mouse
,
device:
1
,
position:
Offset
(
20
,
30
),
buttons:
4
,
obscured:
true
,
pressure:
34
,
pressureMin:
10
,
pressureMax:
60
,
distance:
12
,
distanceMax:
24
,
size:
10
,
radiusMajor:
33
,
radiusMinor:
44
,
radiusMin:
10
,
radiusMax:
50
,
orientation:
2
,
tilt:
4
,
);
_expectTransformedEvent
(
original:
up
,
transform:
transform
,
localPosition:
localPosition
,
);
});
}
void
_expectTransformedEvent
(
{
@required
PointerEvent
original
,
@required
Matrix4
transform
,
Offset
localDelta
,
Offset
localPosition
,
})
{
expect
(
original
.
position
,
original
.
localPosition
);
expect
(
original
.
delta
,
original
.
localDelta
);
expect
(
original
.
original
,
isNull
);
expect
(
original
.
transform
,
isNull
);
final
PointerEvent
transformed
=
original
.
transformed
(
transform
);
expect
(
transformed
.
original
,
same
(
original
));
expect
(
transformed
.
transform
,
transform
);
expect
(
transformed
.
localDelta
,
localDelta
??
original
.
localDelta
);
expect
(
transformed
.
localPosition
,
localPosition
??
original
.
localPosition
);
expect
(
transformed
.
buttons
,
original
.
buttons
);
expect
(
transformed
.
delta
,
original
.
delta
);
expect
(
transformed
.
device
,
original
.
device
);
expect
(
transformed
.
distance
,
original
.
distance
);
expect
(
transformed
.
distanceMax
,
original
.
distanceMax
);
expect
(
transformed
.
distanceMin
,
original
.
distanceMin
);
expect
(
transformed
.
down
,
original
.
down
);
expect
(
transformed
.
kind
,
original
.
kind
);
expect
(
transformed
.
obscured
,
original
.
obscured
);
expect
(
transformed
.
orientation
,
original
.
orientation
);
expect
(
transformed
.
platformData
,
original
.
platformData
);
expect
(
transformed
.
pointer
,
original
.
pointer
);
expect
(
transformed
.
position
,
original
.
position
);
expect
(
transformed
.
pressure
,
original
.
pressure
);
expect
(
transformed
.
pressureMax
,
original
.
pressureMax
);
expect
(
transformed
.
pressureMin
,
original
.
pressureMin
);
expect
(
transformed
.
radiusMajor
,
original
.
radiusMajor
);
expect
(
transformed
.
radiusMax
,
original
.
radiusMax
);
expect
(
transformed
.
radiusMin
,
original
.
radiusMin
);
expect
(
transformed
.
radiusMinor
,
original
.
radiusMinor
);
expect
(
transformed
.
size
,
original
.
size
);
expect
(
transformed
.
synthesized
,
original
.
synthesized
);
expect
(
transformed
.
tilt
,
original
.
tilt
);
expect
(
transformed
.
timeStamp
,
original
.
timeStamp
);
}
packages/flutter/test/gestures/hit_test_test.dart
View file @
ae23d4a4
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'package:flutter/gestures.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'../flutter_test_alternative.dart'
;
...
...
@@ -11,10 +12,13 @@ void main() {
final
HitTestEntry
entry1
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry2
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry3
=
HitTestEntry
(
_DummyHitTestTarget
());
final
Matrix4
transform
=
Matrix4
.
translationValues
(
40.0
,
150.0
,
0.0
);
final
HitTestResult
wrapped
=
HitTestResult
();
final
HitTestResult
wrapped
=
MyHitTestResult
()
..
publicPushTransform
(
transform
);
wrapped
.
add
(
entry1
);
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
expect
(
entry1
.
transform
,
transform
);
final
HitTestResult
wrapping
=
HitTestResult
.
wrap
(
wrapped
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
...
...
@@ -23,10 +27,12 @@ void main() {
wrapping
.
add
(
entry2
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
entry2
.
transform
,
transform
);
wrapped
.
add
(
entry3
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
entry3
.
transform
,
transform
);
});
}
...
...
@@ -36,3 +42,7 @@ class _DummyHitTestTarget implements HitTestTarget {
// Nothing to do.
}
}
class
MyHitTestResult
extends
HitTestResult
{
void
publicPushTransform
(
Matrix4
transform
)
=>
pushTransform
(
transform
);
}
packages/flutter/test/gestures/pointer_router_test.dart
View file @
ae23d4a4
...
...
@@ -5,6 +5,7 @@
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
void
main
(
)
{
test
(
'Should route pointers'
,
()
{
...
...
@@ -149,4 +150,53 @@ void main() {
FlutterError
.
onError
=
previousErrorHandler
;
});
test
(
'Should transform events'
,
()
{
final
List
<
PointerEvent
>
events
=
<
PointerEvent
>[];
final
List
<
PointerEvent
>
globalEvents
=
<
PointerEvent
>[];
final
PointerRouter
router
=
PointerRouter
();
final
Matrix4
transform
=
(
Matrix4
.
identity
()..
scale
(
1
/
2.0
,
1
/
2.0
,
1.0
))
*
Matrix4
.
translationValues
(-
10
,
-
30
,
0
);
router
.
addRoute
(
1
,
(
PointerEvent
event
)
{
events
.
add
(
event
);
},
transform
);
router
.
addGlobalRoute
((
PointerEvent
event
)
{
globalEvents
.
add
(
event
);
},
transform
);
final
TestPointer
pointer1
=
TestPointer
(
1
);
const
Offset
firstPosition
=
Offset
(
16
,
36
);
router
.
route
(
pointer1
.
down
(
firstPosition
));
expect
(
events
.
single
.
transform
,
transform
);
expect
(
events
.
single
.
position
,
firstPosition
);
expect
(
events
.
single
.
delta
,
Offset
.
zero
);
expect
(
events
.
single
.
localPosition
,
const
Offset
(
3
,
3
));
expect
(
events
.
single
.
localDelta
,
Offset
.
zero
);
expect
(
globalEvents
.
single
.
transform
,
transform
);
expect
(
globalEvents
.
single
.
position
,
firstPosition
);
expect
(
globalEvents
.
single
.
delta
,
Offset
.
zero
);
expect
(
globalEvents
.
single
.
localPosition
,
const
Offset
(
3
,
3
));
expect
(
globalEvents
.
single
.
localDelta
,
Offset
.
zero
);
events
.
clear
();
globalEvents
.
clear
();
const
Offset
newPosition
=
Offset
(
20
,
40
);
router
.
route
(
pointer1
.
move
(
newPosition
));
expect
(
events
.
single
.
transform
,
transform
);
expect
(
events
.
single
.
position
,
newPosition
);
expect
(
events
.
single
.
delta
,
newPosition
-
firstPosition
);
expect
(
events
.
single
.
localPosition
,
const
Offset
(
5
,
5
));
expect
(
events
.
single
.
localDelta
,
const
Offset
(
2
,
2
));
expect
(
globalEvents
.
single
.
transform
,
transform
);
expect
(
globalEvents
.
single
.
position
,
newPosition
);
expect
(
globalEvents
.
single
.
delta
,
newPosition
-
firstPosition
);
expect
(
globalEvents
.
single
.
localPosition
,
const
Offset
(
5
,
5
));
expect
(
globalEvents
.
single
.
localDelta
,
const
Offset
(
2
,
2
));
});
}
packages/flutter/test/gestures/pointer_signal_resolver_test.dart
View file @
ae23d4a4
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'package:flutter/gestures.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
import
'../flutter_test_alternative.dart'
;
...
...
@@ -67,4 +68,22 @@ void main() {
expect
(
first
.
callbackRan
,
isTrue
);
expect
(
second
.
callbackRan
,
isFalse
);
});
test
(
'works with transformed events'
,
()
{
final
PointerSignalResolver
resolver
=
PointerSignalResolver
();
const
PointerSignalEvent
originalEvent
=
PointerScrollEvent
();
final
PointerSignalEvent
transformedEvent
=
originalEvent
.
transformed
(
Matrix4
.
translationValues
(
10.0
,
20.0
,
0.0
));
expect
(
originalEvent
,
isNot
(
same
(
transformedEvent
)));
expect
(
transformedEvent
.
original
,
same
(
originalEvent
));
final
List
<
PointerSignalEvent
>
events
=
<
PointerSignalEvent
>[];
resolver
.
register
(
transformedEvent
,
(
PointerSignalEvent
event
)
{
events
.
add
(
event
);
});
resolver
.
resolve
(
originalEvent
);
expect
(
events
.
single
,
same
(
transformedEvent
));
});
}
packages/flutter/test/gestures/recognizer_test.dart
View file @
ae23d4a4
...
...
@@ -26,4 +26,28 @@ void main() {
final
TestGestureRecognizer
recognizer
=
TestGestureRecognizer
(
debugOwner:
0
);
expect
(
recognizer
,
hasAGoodToStringDeep
);
});
test
(
'OffsetPair'
,
()
{
const
OffsetPair
offset1
=
OffsetPair
(
local:
Offset
(
10
,
20
),
global:
Offset
(
30
,
40
),
);
expect
(
offset1
.
local
,
const
Offset
(
10
,
20
));
expect
(
offset1
.
global
,
const
Offset
(
30
,
40
));
const
OffsetPair
offset2
=
OffsetPair
(
local:
Offset
(
50
,
60
),
global:
Offset
(
70
,
80
),
);
final
OffsetPair
sum
=
offset2
+
offset1
;
expect
(
sum
.
local
,
const
Offset
(
60
,
80
));
expect
(
sum
.
global
,
const
Offset
(
100
,
120
));
final
OffsetPair
difference
=
offset2
-
offset1
;
expect
(
difference
.
local
,
const
Offset
(
40
,
40
));
expect
(
difference
.
global
,
const
Offset
(
40
,
40
));
});
}
packages/flutter/test/gestures/transformed_double_tap.dart
0 → 100644
View file @
ae23d4a4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
void
main
(
)
{
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled up'
,
(
WidgetTester
tester
)
async
{
int
doubleTapCount
=
0
;
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
2.0
,
child:
GestureDetector
(
onDoubleTap:
()
{
doubleTapCount
++;
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
),
)
);
// Move just below kTouchSlop should recognize tap.
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
redContainer
));
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
up
();
await
tester
.
pump
(
kDoubleTapMinTime
);
gesture
=
await
tester
.
startGesture
(
center
+
const
Offset
(
kDoubleTapSlop
-
1
,
0
));
await
gesture
.
up
();
expect
(
doubleTapCount
,
1
);
doubleTapCount
=
0
;
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
up
();
await
tester
.
pump
(
kDoubleTapMinTime
);
gesture
=
await
tester
.
startGesture
(
center
+
const
Offset
(
kDoubleTapSlop
+
1
,
0
));
await
gesture
.
up
();
expect
(
doubleTapCount
,
0
);
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled down'
,
(
WidgetTester
tester
)
async
{
int
doubleTapCount
=
0
;
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
0.5
,
child:
GestureDetector
(
onDoubleTap:
()
{
doubleTapCount
++;
},
child:
Container
(
key:
redContainer
,
width:
500
,
height:
500
,
color:
Colors
.
red
,
)
),
),
)
);
// Move just below kTouchSlop should recognize tap.
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
redContainer
));
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
up
();
await
tester
.
pump
(
kDoubleTapMinTime
);
gesture
=
await
tester
.
startGesture
(
center
+
const
Offset
(
kDoubleTapSlop
-
1
,
0
));
await
gesture
.
up
();
expect
(
doubleTapCount
,
1
);
doubleTapCount
=
0
;
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
up
();
await
tester
.
pump
(
kDoubleTapMinTime
);
gesture
=
await
tester
.
startGesture
(
center
+
const
Offset
(
kDoubleTapSlop
+
1
,
0
));
await
gesture
.
up
();
expect
(
doubleTapCount
,
0
);
});
}
packages/flutter/test/gestures/transformed_long_press_test.dart
0 → 100644
View file @
ae23d4a4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
void
main
(
)
{
testWidgets
(
'gets local corrdinates'
,
(
WidgetTester
tester
)
async
{
int
longPressCount
=
0
;
int
longPressUpCount
=
0
;
final
List
<
LongPressEndDetails
>
endDetails
=
<
LongPressEndDetails
>[];
final
List
<
LongPressMoveUpdateDetails
>
moveDetails
=
<
LongPressMoveUpdateDetails
>[];
final
List
<
LongPressStartDetails
>
startDetails
=
<
LongPressStartDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
GestureDetector
(
onLongPress:
()
{
longPressCount
++;
},
onLongPressEnd:
(
LongPressEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onLongPressMoveUpdate:
(
LongPressMoveUpdateDetails
details
)
{
moveDetails
.
add
(
details
);
},
onLongPressStart:
(
LongPressStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onLongPressUp:
()
{
longPressUpCount
++;
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
)
);
await
tester
.
longPressAt
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
expect
(
longPressCount
,
1
);
expect
(
longPressUpCount
,
1
);
expect
(
moveDetails
,
isEmpty
);
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
endDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
});
testWidgets
(
'scaled up'
,
(
WidgetTester
tester
)
async
{
int
longPressCount
=
0
;
int
longPressUpCount
=
0
;
final
List
<
LongPressEndDetails
>
endDetails
=
<
LongPressEndDetails
>[];
final
List
<
LongPressMoveUpdateDetails
>
moveDetails
=
<
LongPressMoveUpdateDetails
>[];
final
List
<
LongPressStartDetails
>
startDetails
=
<
LongPressStartDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
2.0
,
child:
GestureDetector
(
onLongPress:
()
{
longPressCount
++;
},
onLongPressEnd:
(
LongPressEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onLongPressMoveUpdate:
(
LongPressMoveUpdateDetails
details
)
{
moveDetails
.
add
(
details
);
},
onLongPressStart:
(
LongPressStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onLongPressUp:
()
{
longPressUpCount
++;
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
),
)
);
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
10.0
));
await
tester
.
pump
(
kLongPressTimeout
);
await
gesture
.
up
();
expect
(
longPressCount
,
1
);
expect
(
longPressUpCount
,
1
);
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
10.0
/
2.0
));
expect
(
endDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
10.0
));
expect
(
moveDetails
,
isEmpty
);
// moved before long press was detected.
startDetails
.
clear
();
endDetails
.
clear
();
longPressCount
=
0
;
longPressUpCount
=
0
;
// Move after recognized.
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
tester
.
pump
(
kLongPressTimeout
);
await
gesture
.
moveBy
(
const
Offset
(
0
,
100
));
await
gesture
.
up
();
expect
(
longPressCount
,
1
);
expect
(
longPressUpCount
,
1
);
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
100.0
/
2.0
));
expect
(
endDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
100.0
));
expect
(
moveDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
100.0
/
2.0
));
expect
(
moveDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
100.0
));
expect
(
moveDetails
.
single
.
offsetFromOrigin
,
const
Offset
(
0
,
100.0
));
expect
(
moveDetails
.
single
.
localOffsetFromOrigin
,
const
Offset
(
0
,
100.0
/
2.0
));
});
testWidgets
(
'scaled down'
,
(
WidgetTester
tester
)
async
{
int
longPressCount
=
0
;
int
longPressUpCount
=
0
;
final
List
<
LongPressEndDetails
>
endDetails
=
<
LongPressEndDetails
>[];
final
List
<
LongPressMoveUpdateDetails
>
moveDetails
=
<
LongPressMoveUpdateDetails
>[];
final
List
<
LongPressStartDetails
>
startDetails
=
<
LongPressStartDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
0.5
,
child:
GestureDetector
(
onLongPress:
()
{
longPressCount
++;
},
onLongPressEnd:
(
LongPressEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onLongPressMoveUpdate:
(
LongPressMoveUpdateDetails
details
)
{
moveDetails
.
add
(
details
);
},
onLongPressStart:
(
LongPressStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onLongPressUp:
()
{
longPressUpCount
++;
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
),
)
);
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
10.0
));
await
tester
.
pump
(
kLongPressTimeout
);
await
gesture
.
up
();
expect
(
longPressCount
,
1
);
expect
(
longPressUpCount
,
1
);
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
10.0
*
2.0
));
expect
(
endDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
10.0
));
expect
(
moveDetails
,
isEmpty
);
// moved before long press was detected.
startDetails
.
clear
();
endDetails
.
clear
();
longPressCount
=
0
;
longPressUpCount
=
0
;
// Move after recognized.
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
tester
.
pump
(
kLongPressTimeout
);
await
gesture
.
moveBy
(
const
Offset
(
0
,
100
));
await
gesture
.
up
();
expect
(
longPressCount
,
1
);
expect
(
longPressUpCount
,
1
);
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
100.0
*
2.0
));
expect
(
endDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
100.0
));
expect
(
moveDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
100.0
*
2.0
));
expect
(
moveDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300.0
+
100.0
));
expect
(
moveDetails
.
single
.
offsetFromOrigin
,
const
Offset
(
0
,
100.0
));
expect
(
moveDetails
.
single
.
localOffsetFromOrigin
,
const
Offset
(
0
,
100.0
*
2.0
));
});
}
packages/flutter/test/gestures/transformed_monodrag_test.dart
0 → 100644
View file @
ae23d4a4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
void
main
(
)
{
group
(
'Horizontal'
,
()
{
testWidgets
(
'gets local corrdinates'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
GestureDetector
(
onHorizontalDragCancel:
()
{
dragCancelCount
++;
},
onHorizontalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onHorizontalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onHorizontalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onHorizontalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
);
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
100
,
0
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
updateDetails
.
last
.
localPosition
,
const
Offset
(
50
+
100.0
,
75
));
expect
(
updateDetails
.
last
.
globalPosition
,
const
Offset
(
400
+
100.0
,
300
));
expect
(
updateDetails
.
fold
(
Offset
.
zero
,
(
Offset
offset
,
DragUpdateDetails
details
)
=>
offset
+
details
.
delta
),
const
Offset
(
100
,
0
),
);
expect
(
updateDetails
.
fold
(
0.0
,
(
double
offset
,
DragUpdateDetails
details
)
=>
offset
+
details
.
primaryDelta
),
100.0
,
);
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled up'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
2.0
,
child:
GestureDetector
(
onHorizontalDragCancel:
()
{
dragCancelCount
++;
},
onHorizontalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onHorizontalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onHorizontalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onHorizontalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
kTouchSlop
+
1
,
0
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
(
kTouchSlop
+
1
)
/
2
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
(
kTouchSlop
+
1
),
300
));
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move just below kTouchSlop does not recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
kTouchSlop
-
1
,
0
));
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move in two separate movements
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
1
,
30
));
await
gesture
.
moveBy
(
const
Offset
(
100
,
10
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
(
kTouchSlop
+
1
)
/
2
,
75.0
+
30.0
/
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
(
kTouchSlop
+
1
),
300
+
30.0
));
expect
(
updateDetails
.
single
.
localPosition
,
startDetails
.
single
.
localPosition
+
const
Offset
(
100.0
/
2
,
10
/
2
));
expect
(
updateDetails
.
single
.
globalPosition
,
startDetails
.
single
.
globalPosition
+
const
Offset
(
100.0
,
10.0
));
expect
(
updateDetails
.
single
.
delta
,
const
Offset
(
100.0
/
2
,
0.0
));
expect
(
updateDetails
.
single
.
primaryDelta
,
100.0
/
2
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled down'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
0.5
,
child:
GestureDetector
(
onHorizontalDragCancel:
()
{
dragCancelCount
++;
},
onHorizontalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onHorizontalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onHorizontalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onHorizontalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
kTouchSlop
+
1
,
0
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
(
kTouchSlop
+
1
)
*
2
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
(
kTouchSlop
+
1
),
300
));
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move just below kTouchSlop does not recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
kTouchSlop
-
1
,
0
));
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move in two separate movements
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
1
,
30
));
await
gesture
.
moveBy
(
const
Offset
(
100
,
10
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
(
kTouchSlop
+
1
)
*
2
,
75.0
+
30.0
*
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
(
kTouchSlop
+
1
),
300
+
30.0
));
expect
(
updateDetails
.
single
.
localPosition
,
startDetails
.
single
.
localPosition
+
const
Offset
(
100.0
*
2
,
10.0
*
2.0
));
expect
(
updateDetails
.
single
.
globalPosition
,
startDetails
.
single
.
globalPosition
+
const
Offset
(
100.0
,
10.0
));
expect
(
updateDetails
.
single
.
delta
,
const
Offset
(
100.0
*
2.0
,
0.0
));
expect
(
updateDetails
.
single
.
primaryDelta
,
100.0
*
2
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when roateted 45 degrees'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
GestureDetector
(
onHorizontalDragCancel:
()
{
dragCancelCount
++;
},
onHorizontalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onHorizontalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onHorizontalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onHorizontalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just below kTouchSlop should not recognize drag.
const
Offset
moveBy1
=
Offset
(
kTouchSlop
/
2
,
kTouchSlop
/
2
);
expect
(
moveBy1
.
distance
,
lessThan
(
kTouchSlop
));
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
moveBy1
);
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
50
,
75
)));
expect
(
downDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
,
300
)));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move above kTouchSlop recognizes drag.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
,
kTouchSlop
));
await
gesture
.
moveBy
(
const
Offset
(
3
,
4
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
50
,
75
)));
expect
(
downDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
,
300
)));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
,
hasLength
(
1
));
expect
(
updateDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
+
kTouchSlop
+
3
,
300
+
kTouchSlop
+
4
)));
expect
(
updateDetails
.
single
.
delta
,
within
(
distance:
0.1
,
from:
const
Offset
(
5
,
0.0
)));
// sqrt(3^2 + 4^2)
expect
(
updateDetails
.
single
.
primaryDelta
,
within
(
distance:
0.1
,
from:
5.0
));
// sqrt(3^2 + 4^2)
});
});
group
(
'Vertical'
,
()
{
testWidgets
(
'gets local corrdinates'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
GestureDetector
(
onVerticalDragCancel:
()
{
dragCancelCount
++;
},
onVerticalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onVerticalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onVerticalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onVerticalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
);
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
0
,
100
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
updateDetails
.
last
.
localPosition
,
const
Offset
(
50
,
75
+
100.0
));
expect
(
updateDetails
.
last
.
globalPosition
,
const
Offset
(
400
,
300
+
100.0
));
expect
(
updateDetails
.
fold
(
Offset
.
zero
,
(
Offset
offset
,
DragUpdateDetails
details
)
=>
offset
+
details
.
delta
),
const
Offset
(
0
,
100
),
);
expect
(
updateDetails
.
fold
(
0.0
,
(
double
offset
,
DragUpdateDetails
details
)
=>
offset
+
details
.
primaryDelta
),
100.0
,
);
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled up'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
2.0
,
child:
GestureDetector
(
onVerticalDragCancel:
()
{
dragCancelCount
++;
},
onVerticalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onVerticalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onVerticalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onVerticalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
0
,
kTouchSlop
+
1
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
(
kTouchSlop
+
1
)
/
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
+
(
kTouchSlop
+
1
)));
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move just below kTouchSlop does not recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
0
,
kTouchSlop
-
1
));
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move in two separate movements
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
30
,
kTouchSlop
+
1
));
await
gesture
.
moveBy
(
const
Offset
(
10
,
100
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
30.0
/
2
,
75.0
+
(
kTouchSlop
+
1
)
/
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
30.0
,
300
+
(
kTouchSlop
+
1
)));
expect
(
updateDetails
.
single
.
localPosition
,
startDetails
.
single
.
localPosition
+
const
Offset
(
10.0
/
2
,
100.0
/
2
));
expect
(
updateDetails
.
single
.
globalPosition
,
startDetails
.
single
.
globalPosition
+
const
Offset
(
10.0
,
100.0
));
expect
(
updateDetails
.
single
.
delta
,
const
Offset
(
0.0
,
100.0
/
2
));
expect
(
updateDetails
.
single
.
primaryDelta
,
100.0
/
2
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled down'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
0.5
,
child:
GestureDetector
(
onVerticalDragCancel:
()
{
dragCancelCount
++;
},
onVerticalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onVerticalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onVerticalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onVerticalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just above kTouchSlop should recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
0
,
kTouchSlop
+
1
));
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
(
kTouchSlop
+
1
)
*
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
+
(
kTouchSlop
+
1
)));
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move just below kTouchSlop does not recognize drag.
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
const
Offset
(
0
,
kTouchSlop
-
1
));
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move in two separate movements
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
30
,
kTouchSlop
+
1
));
await
gesture
.
moveBy
(
const
Offset
(
10
,
100
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
.
single
.
localPosition
,
const
Offset
(
50
+
30.0
*
2
,
75.0
+
(
kTouchSlop
+
1
)
*
2
));
expect
(
startDetails
.
single
.
globalPosition
,
const
Offset
(
400
+
30.0
,
300
+
(
kTouchSlop
+
1
)));
expect
(
updateDetails
.
single
.
localPosition
,
startDetails
.
single
.
localPosition
+
const
Offset
(
10.0
*
2
,
100.0
*
2.0
));
expect
(
updateDetails
.
single
.
globalPosition
,
startDetails
.
single
.
globalPosition
+
const
Offset
(
10.0
,
100.0
));
expect
(
updateDetails
.
single
.
delta
,
const
Offset
(
0.0
,
100.0
*
2.0
));
expect
(
updateDetails
.
single
.
primaryDelta
,
100.0
*
2
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when roateted 45 degrees'
,
(
WidgetTester
tester
)
async
{
int
dragCancelCount
=
0
;
final
List
<
DragDownDetails
>
downDetails
=
<
DragDownDetails
>[];
final
List
<
DragEndDetails
>
endDetails
=
<
DragEndDetails
>[];
final
List
<
DragStartDetails
>
startDetails
=
<
DragStartDetails
>[];
final
List
<
DragUpdateDetails
>
updateDetails
=
<
DragUpdateDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
GestureDetector
(
onVerticalDragCancel:
()
{
dragCancelCount
++;
},
onVerticalDragDown:
(
DragDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onVerticalDragEnd:
(
DragEndDetails
details
)
{
endDetails
.
add
(
details
);
},
onVerticalDragStart:
(
DragStartDetails
details
)
{
startDetails
.
add
(
details
);
},
onVerticalDragUpdate:
(
DragUpdateDetails
details
)
{
updateDetails
.
add
(
details
);
},
onTap:
()
{
// Competing gesture detector.
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
),
),
),
),
);
// Move just below kTouchSlop should not recognize drag.
const
Offset
moveBy1
=
Offset
(
kTouchSlop
/
2
,
kTouchSlop
/
2
);
expect
(
moveBy1
.
distance
,
lessThan
(
kTouchSlop
));
await
tester
.
drag
(
find
.
byKey
(
redContainer
),
moveBy1
);
expect
(
dragCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
50
,
75
)));
expect
(
downDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
,
300
)));
expect
(
endDetails
,
isEmpty
);
expect
(
startDetails
,
isEmpty
);
expect
(
updateDetails
,
isEmpty
);
dragCancelCount
=
0
;
downDetails
.
clear
();
endDetails
.
clear
();
startDetails
.
clear
();
updateDetails
.
clear
();
// Move above kTouchSlop recognizes drag.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
,
kTouchSlop
));
await
gesture
.
moveBy
(
const
Offset
(-
4
,
3
));
await
gesture
.
up
();
expect
(
dragCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
50
,
75
)));
expect
(
downDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
,
300
)));
expect
(
endDetails
,
hasLength
(
1
));
expect
(
startDetails
,
hasLength
(
1
));
expect
(
updateDetails
.
single
.
globalPosition
,
within
(
distance:
0.0001
,
from:
const
Offset
(
400
+
kTouchSlop
-
4
,
300
+
kTouchSlop
+
3
)));
expect
(
updateDetails
.
single
.
delta
,
within
(
distance:
0.1
,
from:
const
Offset
(
0.0
,
5.0
)));
// sqrt(3^2 + 4^2)
expect
(
updateDetails
.
single
.
primaryDelta
,
within
(
distance:
0.1
,
from:
5.0
));
// sqrt(3^2 + 4^2)
});
});
}
packages/flutter/test/gestures/transformed_tap_test.dart
0 → 100644
View file @
ae23d4a4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
void
main
(
)
{
testWidgets
(
'gets local corrdinates'
,
(
WidgetTester
tester
)
async
{
int
tapCount
=
0
;
int
tapCancelCount
=
0
;
final
List
<
TapDownDetails
>
downDetails
=
<
TapDownDetails
>[];
final
List
<
TapUpDetails
>
upDetails
=
<
TapUpDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
GestureDetector
(
onTap:
()
{
tapCount
++;
},
onTapCancel:
()
{
tapCancelCount
++;
},
onTapDown:
(
TapDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onTapUp:
(
TapUpDetails
details
)
{
upDetails
.
add
(
details
);
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
)
);
await
tester
.
tapAt
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
expect
(
tapCount
,
1
);
expect
(
tapCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
upDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
upDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled up'
,
(
WidgetTester
tester
)
async
{
int
tapCount
=
0
;
int
tapCancelCount
=
0
;
final
List
<
TapDownDetails
>
downDetails
=
<
TapDownDetails
>[];
final
List
<
TapUpDetails
>
upDetails
=
<
TapUpDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
2.0
,
child:
GestureDetector
(
onTap:
()
{
tapCount
++;
},
onTapCancel:
()
{
tapCancelCount
++;
},
onTapDown:
(
TapDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onTapUp:
(
TapUpDetails
details
)
{
upDetails
.
add
(
details
);
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
),
)
);
// Move just below kTouchSlop should recognize tap.
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
kTouchSlop
-
1
));
await
gesture
.
up
();
expect
(
tapCount
,
1
);
expect
(
tapCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
upDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
(
kTouchSlop
-
1
)
/
2.0
));
expect
(
upDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
+
(
kTouchSlop
-
1
)));
downDetails
.
clear
();
upDetails
.
clear
();
tapCount
=
0
;
tapCancelCount
=
0
;
// Move more then kTouchSlop should cancel.
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
kTouchSlop
+
1
));
await
gesture
.
up
();
expect
(
tapCount
,
0
);
expect
(
tapCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
upDetails
,
isEmpty
);
});
testWidgets
(
'kTouchSlop is evaluated in the global coordinate space when scaled down'
,
(
WidgetTester
tester
)
async
{
int
tapCount
=
0
;
int
tapCancelCount
=
0
;
final
List
<
TapDownDetails
>
downDetails
=
<
TapDownDetails
>[];
final
List
<
TapUpDetails
>
upDetails
=
<
TapUpDetails
>[];
final
Key
redContainer
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
.
scale
(
scale:
0.5
,
child:
GestureDetector
(
onTap:
()
{
tapCount
++;
},
onTapCancel:
()
{
tapCancelCount
++;
},
onTapDown:
(
TapDownDetails
details
)
{
downDetails
.
add
(
details
);
},
onTapUp:
(
TapUpDetails
details
)
{
upDetails
.
add
(
details
);
},
child:
Container
(
key:
redContainer
,
width:
100
,
height:
150
,
color:
Colors
.
red
,
)
),
),
)
);
// Move just below kTouchSlop should recognize tap.
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
kTouchSlop
-
1
));
await
gesture
.
up
();
expect
(
tapCount
,
1
);
expect
(
tapCancelCount
,
0
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
upDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
+
(
kTouchSlop
-
1
)
*
2.0
));
expect
(
upDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
+
(
kTouchSlop
-
1
)));
downDetails
.
clear
();
upDetails
.
clear
();
tapCount
=
0
;
tapCancelCount
=
0
;
// Move more then kTouchSlop should cancel.
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byKey
(
redContainer
)));
await
gesture
.
moveBy
(
const
Offset
(
0
,
kTouchSlop
+
1
));
await
gesture
.
up
();
expect
(
tapCount
,
0
);
expect
(
tapCancelCount
,
1
);
expect
(
downDetails
.
single
.
localPosition
,
const
Offset
(
50
,
75
));
expect
(
downDetails
.
single
.
globalPosition
,
const
Offset
(
400
,
300
));
expect
(
upDetails
,
isEmpty
);
});
}
packages/flutter/test/rendering/box_test.dart
View file @
ae23d4a4
...
...
@@ -265,10 +265,13 @@ void main() {
final
HitTestEntry
entry1
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry2
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry3
=
HitTestEntry
(
_DummyHitTestTarget
());
final
Matrix4
transform
=
Matrix4
.
translationValues
(
40.0
,
150.0
,
0.0
);
final
HitTestResult
wrapped
=
HitTestResult
();
final
HitTestResult
wrapped
=
MyHitTestResult
()
..
publicPushTransform
(
transform
);
wrapped
.
add
(
entry1
);
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
expect
(
entry1
.
transform
,
transform
);
final
BoxHitTestResult
wrapping
=
BoxHitTestResult
.
wrap
(
wrapped
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
...
...
@@ -277,10 +280,12 @@ void main() {
wrapping
.
add
(
entry2
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
entry2
.
transform
,
transform
);
wrapped
.
add
(
entry3
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
entry3
.
transform
,
transform
);
});
test
(
'addWithPaintTransform'
,
()
{
...
...
@@ -517,3 +522,7 @@ class _DummyHitTestTarget implements HitTestTarget {
// Nothing to do.
}
}
class
MyHitTestResult
extends
HitTestResult
{
void
publicPushTransform
(
Matrix4
transform
)
=>
pushTransform
(
transform
);
}
packages/flutter/test/rendering/slivers_test.dart
View file @
ae23d4a4
...
...
@@ -834,10 +834,13 @@ void main() {
final
HitTestEntry
entry1
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry2
=
HitTestEntry
(
_DummyHitTestTarget
());
final
HitTestEntry
entry3
=
HitTestEntry
(
_DummyHitTestTarget
());
final
Matrix4
transform
=
Matrix4
.
translationValues
(
40.0
,
150.0
,
0.0
);
final
HitTestResult
wrapped
=
HitTestResult
();
final
HitTestResult
wrapped
=
MyHitTestResult
()
..
publicPushTransform
(
transform
);
wrapped
.
add
(
entry1
);
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
expect
(
entry1
.
transform
,
transform
);
final
SliverHitTestResult
wrapping
=
SliverHitTestResult
.
wrap
(
wrapped
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
]));
...
...
@@ -846,10 +849,12 @@ void main() {
wrapping
.
add
(
entry2
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
]));
expect
(
entry2
.
transform
,
transform
);
wrapped
.
add
(
entry3
);
expect
(
wrapping
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
entry3
.
transform
,
transform
);
});
test
(
'addWithAxisOffset'
,
()
{
...
...
@@ -924,3 +929,7 @@ class _DummyHitTestTarget implements HitTestTarget {
// Nothing to do.
}
}
class
MyHitTestResult
extends
HitTestResult
{
void
publicPushTransform
(
Matrix4
transform
)
=>
pushTransform
(
transform
);
}
packages/flutter/test/widgets/listener_test.dart
View file @
ae23d4a4
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
@@ -594,6 +596,7 @@ void main() {
await
gesture
.
removePointer
();
});
testWidgets
(
'Exit event when unplugging mouse should have a position'
,
(
WidgetTester
tester
)
async
{
final
List
<
PointerEnterEvent
>
enter
=
<
PointerEnterEvent
>[];
final
List
<
PointerHoverEvent
>
hover
=
<
PointerHoverEvent
>[];
...
...
@@ -640,4 +643,323 @@ void main() {
expect
(
exit
.
single
.
delta
,
const
Offset
(
0.0
,
0.0
));
});
});
group
(
'transformed events'
,
()
{
testWidgets
(
'simple offset for touch/signal'
,
(
WidgetTester
tester
)
async
{
final
List
<
PointerEvent
>
events
=
<
PointerEvent
>[];
final
Key
key
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Listener
(
onPointerDown:
(
PointerDownEvent
event
)
{
events
.
add
(
event
);
},
onPointerUp:
(
PointerUpEvent
event
)
{
events
.
add
(
event
);
},
onPointerMove:
(
PointerMoveEvent
event
)
{
events
.
add
(
event
);
},
onPointerSignal:
(
PointerSignalEvent
event
)
{
events
.
add
(
event
);
},
child:
Container
(
key:
key
,
color:
Colors
.
red
,
height:
100
,
width:
100
,
),
),
),
);
const
Offset
moved
=
Offset
(
20
,
30
);
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
key
));
final
Offset
topLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
key
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
moveBy
(
moved
);
await
gesture
.
up
();
expect
(
events
,
hasLength
(
3
));
final
PointerDownEvent
down
=
events
[
0
];
final
PointerMoveEvent
move
=
events
[
1
];
final
PointerUpEvent
up
=
events
[
2
];
final
Matrix4
expectedTransform
=
Matrix4
.
translationValues
(-
topLeft
.
dx
,
-
topLeft
.
dy
,
0
);
expect
(
center
,
isNot
(
const
Offset
(
50
,
50
)));
expect
(
down
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
down
.
position
,
center
);
expect
(
down
.
delta
,
Offset
.
zero
);
expect
(
down
.
localDelta
,
Offset
.
zero
);
expect
(
down
.
transform
,
expectedTransform
);
expect
(
move
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
);
expect
(
move
.
position
,
center
+
moved
);
expect
(
move
.
delta
,
moved
);
expect
(
move
.
localDelta
,
moved
);
expect
(
move
.
transform
,
expectedTransform
);
expect
(
up
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
);
expect
(
up
.
position
,
center
+
moved
);
expect
(
up
.
delta
,
Offset
.
zero
);
expect
(
up
.
localDelta
,
Offset
.
zero
);
expect
(
up
.
transform
,
expectedTransform
);
events
.
clear
();
await
scrollAt
(
center
,
tester
);
expect
(
events
.
single
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
events
.
single
.
position
,
center
);
expect
(
events
.
single
.
delta
,
Offset
.
zero
);
expect
(
events
.
single
.
localDelta
,
Offset
.
zero
);
expect
(
events
.
single
.
transform
,
expectedTransform
);
});
testWidgets
(
'scaled for touch/signal'
,
(
WidgetTester
tester
)
async
{
final
List
<
PointerEvent
>
events
=
<
PointerEvent
>[];
final
Key
key
=
UniqueKey
();
const
double
scaleFactor
=
2
;
await
tester
.
pumpWidget
(
Align
(
alignment:
Alignment
.
topLeft
,
child:
Transform
(
transform:
Matrix4
.
identity
()..
scale
(
scaleFactor
),
child:
Listener
(
onPointerDown:
(
PointerDownEvent
event
)
{
events
.
add
(
event
);
},
onPointerUp:
(
PointerUpEvent
event
)
{
events
.
add
(
event
);
},
onPointerMove:
(
PointerMoveEvent
event
)
{
events
.
add
(
event
);
},
onPointerSignal:
(
PointerSignalEvent
event
)
{
events
.
add
(
event
);
},
child:
Container
(
key:
key
,
color:
Colors
.
red
,
height:
100
,
width:
100
,
),
),
),
),
);
const
Offset
moved
=
Offset
(
20
,
30
);
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
key
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
moveBy
(
moved
);
await
gesture
.
up
();
expect
(
events
,
hasLength
(
3
));
final
PointerDownEvent
down
=
events
[
0
];
final
PointerMoveEvent
move
=
events
[
1
];
final
PointerUpEvent
up
=
events
[
2
];
final
Matrix4
expectedTransform
=
Matrix4
.
identity
()
..
scale
(
1
/
scaleFactor
,
1
/
scaleFactor
,
1.0
);
expect
(
center
,
isNot
(
const
Offset
(
50
,
50
)));
expect
(
down
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
down
.
position
,
center
);
expect
(
down
.
delta
,
Offset
.
zero
);
expect
(
down
.
localDelta
,
Offset
.
zero
);
expect
(
down
.
transform
,
expectedTransform
);
expect
(
move
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
/
scaleFactor
);
expect
(
move
.
position
,
center
+
moved
);
expect
(
move
.
delta
,
moved
);
expect
(
move
.
localDelta
,
moved
/
scaleFactor
);
expect
(
move
.
transform
,
expectedTransform
);
expect
(
up
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
/
scaleFactor
);
expect
(
up
.
position
,
center
+
moved
);
expect
(
up
.
delta
,
Offset
.
zero
);
expect
(
up
.
localDelta
,
Offset
.
zero
);
expect
(
up
.
transform
,
expectedTransform
);
events
.
clear
();
await
scrollAt
(
center
,
tester
);
expect
(
events
.
single
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
events
.
single
.
position
,
center
);
expect
(
events
.
single
.
delta
,
Offset
.
zero
);
expect
(
events
.
single
.
localDelta
,
Offset
.
zero
);
expect
(
events
.
single
.
transform
,
expectedTransform
);
await
gesture
.
removePointer
();
});
testWidgets
(
'scaled and offset for touch/signal'
,
(
WidgetTester
tester
)
async
{
final
List
<
PointerEvent
>
events
=
<
PointerEvent
>[];
final
Key
key
=
UniqueKey
();
const
double
scaleFactor
=
2
;
await
tester
.
pumpWidget
(
Center
(
child:
Transform
(
transform:
Matrix4
.
identity
()..
scale
(
scaleFactor
),
child:
Listener
(
onPointerDown:
(
PointerDownEvent
event
)
{
events
.
add
(
event
);
},
onPointerUp:
(
PointerUpEvent
event
)
{
events
.
add
(
event
);
},
onPointerMove:
(
PointerMoveEvent
event
)
{
events
.
add
(
event
);
},
onPointerSignal:
(
PointerSignalEvent
event
)
{
events
.
add
(
event
);
},
child:
Container
(
key:
key
,
color:
Colors
.
red
,
height:
100
,
width:
100
,
),
),
),
),
);
const
Offset
moved
=
Offset
(
20
,
30
);
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
key
));
final
Offset
topLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
key
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
gesture
.
moveBy
(
moved
);
await
gesture
.
up
();
expect
(
events
,
hasLength
(
3
));
final
PointerDownEvent
down
=
events
[
0
];
final
PointerMoveEvent
move
=
events
[
1
];
final
PointerUpEvent
up
=
events
[
2
];
final
Matrix4
expectedTransform
=
Matrix4
.
identity
()
..
scale
(
1
/
scaleFactor
,
1
/
scaleFactor
,
1.0
)
..
translate
(-
topLeft
.
dx
,
-
topLeft
.
dy
,
0
);
expect
(
center
,
isNot
(
const
Offset
(
50
,
50
)));
expect
(
down
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
down
.
position
,
center
);
expect
(
down
.
delta
,
Offset
.
zero
);
expect
(
down
.
localDelta
,
Offset
.
zero
);
expect
(
down
.
transform
,
expectedTransform
);
expect
(
move
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
/
scaleFactor
);
expect
(
move
.
position
,
center
+
moved
);
expect
(
move
.
delta
,
moved
);
expect
(
move
.
localDelta
,
moved
/
scaleFactor
);
expect
(
move
.
transform
,
expectedTransform
);
expect
(
up
.
localPosition
,
const
Offset
(
50
,
50
)
+
moved
/
scaleFactor
);
expect
(
up
.
position
,
center
+
moved
);
expect
(
up
.
delta
,
Offset
.
zero
);
expect
(
up
.
localDelta
,
Offset
.
zero
);
expect
(
up
.
transform
,
expectedTransform
);
events
.
clear
();
await
scrollAt
(
center
,
tester
);
expect
(
events
.
single
.
localPosition
,
const
Offset
(
50
,
50
));
expect
(
events
.
single
.
position
,
center
);
expect
(
events
.
single
.
delta
,
Offset
.
zero
);
expect
(
events
.
single
.
localDelta
,
Offset
.
zero
);
expect
(
events
.
single
.
transform
,
expectedTransform
);
await
gesture
.
removePointer
();
});
testWidgets
(
'rotated for touch/signal'
,
(
WidgetTester
tester
)
async
{
final
List
<
PointerEvent
>
events
=
<
PointerEvent
>[];
final
Key
key
=
UniqueKey
();
await
tester
.
pumpWidget
(
Center
(
child:
Transform
(
transform:
Matrix4
.
identity
()
..
rotateZ
(
math
.
pi
/
2
),
// 90 degrees clockwise around Container origin
child:
Listener
(
onPointerDown:
(
PointerDownEvent
event
)
{
events
.
add
(
event
);
},
onPointerUp:
(
PointerUpEvent
event
)
{
events
.
add
(
event
);
},
onPointerMove:
(
PointerMoveEvent
event
)
{
events
.
add
(
event
);
},
onPointerSignal:
(
PointerSignalEvent
event
)
{
events
.
add
(
event
);
},
child:
Container
(
key:
key
,
color:
Colors
.
red
,
height:
100
,
width:
100
,
),
),
),
),
);
const
Offset
moved
=
Offset
(
20
,
30
);
final
Offset
downPosition
=
tester
.
getCenter
(
find
.
byKey
(
key
))
+
const
Offset
(
10
,
5
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
downPosition
);
await
gesture
.
moveBy
(
moved
);
await
gesture
.
up
();
expect
(
events
,
hasLength
(
3
));
final
PointerDownEvent
down
=
events
[
0
];
final
PointerMoveEvent
move
=
events
[
1
];
final
PointerUpEvent
up
=
events
[
2
];
const
Offset
offset
=
Offset
((
800
-
100
)
/
2
,
(
600
-
100
)
/
2
);
final
Matrix4
expectedTransform
=
Matrix4
.
identity
()
..
rotateZ
(-
math
.
pi
/
2
)
..
translate
(-
offset
.
dx
,
-
offset
.
dy
,
0.0
);
final
Offset
localDownPosition
=
const
Offset
(
50
,
50
)
+
const
Offset
(
5
,
-
10
);
expect
(
down
.
localPosition
,
within
(
distance:
0.001
,
from:
localDownPosition
));
expect
(
down
.
position
,
downPosition
);
expect
(
down
.
delta
,
Offset
.
zero
);
expect
(
down
.
localDelta
,
Offset
.
zero
);
expect
(
down
.
transform
,
expectedTransform
);
const
Offset
localDelta
=
Offset
(
30
,
-
20
);
expect
(
move
.
localPosition
,
within
(
distance:
0.001
,
from:
localDownPosition
+
localDelta
));
expect
(
move
.
position
,
downPosition
+
moved
);
expect
(
move
.
delta
,
moved
);
expect
(
move
.
localDelta
,
localDelta
);
expect
(
move
.
transform
,
expectedTransform
);
expect
(
up
.
localPosition
,
within
(
distance:
0.001
,
from:
localDownPosition
+
localDelta
));
expect
(
up
.
position
,
downPosition
+
moved
);
expect
(
up
.
delta
,
Offset
.
zero
);
expect
(
up
.
localDelta
,
Offset
.
zero
);
expect
(
up
.
transform
,
expectedTransform
);
events
.
clear
();
await
scrollAt
(
downPosition
,
tester
);
expect
(
events
.
single
.
localPosition
,
within
(
distance:
0.001
,
from:
localDownPosition
));
expect
(
events
.
single
.
position
,
downPosition
);
expect
(
events
.
single
.
delta
,
Offset
.
zero
);
expect
(
events
.
single
.
localDelta
,
Offset
.
zero
);
expect
(
events
.
single
.
transform
,
expectedTransform
);
await
gesture
.
removePointer
();
});
});
}
Future
<
void
>
scrollAt
(
Offset
position
,
WidgetTester
tester
)
{
final
TestPointer
testPointer
=
TestPointer
(
1
,
PointerDeviceKind
.
mouse
);
// Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer
.
hover
(
position
);
final
HitTestResult
result
=
tester
.
hitTestOnBinding
(
position
);
return
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
20.0
)),
result
);
}
packages/flutter/test/widgets/transformed_scrollable_test.dart
0 → 100644
View file @
ae23d4a4
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/gestures.dart'
;
void
main
(
)
{
testWidgets
(
'Scrollable scaled up'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Transform
.
scale
(
scale:
2.0
,
child:
Center
(
child:
Container
(
width:
200
,
child:
ListView
.
builder
(
controller:
controller
,
cacheExtent:
0.0
,
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
Container
(
height:
100.0
,
color:
index
%
2
==
0
?
Colors
.
blue
:
Colors
.
red
,
child:
Text
(
'Tile
$index
'
),
);
},
),
),
),
),
),
);
expect
(
controller
.
offset
,
0.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
100.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
50.0
);
// 100.0 / 2.0
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
80.0
,
-
70.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
85.0
);
// 50.0 + (70.0 / 2)
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
100.0
,
0.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
85.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
85.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
42.5
);
// 85.0 - (85.0 / 2)
});
testWidgets
(
'Scrollable scaled down'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Transform
.
scale
(
scale:
0.5
,
child:
Center
(
child:
Container
(
width:
200
,
child:
ListView
.
builder
(
controller:
controller
,
cacheExtent:
0.0
,
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
Container
(
height:
100.0
,
color:
index
%
2
==
0
?
Colors
.
blue
:
Colors
.
red
,
child:
Text
(
'Tile
$index
'
),
);
},
),
),
),
),
),
);
expect
(
controller
.
offset
,
0.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
100.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
200.0
);
// 100.0 * 2.0
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
80.0
,
-
70.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
340.0
);
// 200.0 + (70.0 * 2)
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
100.0
,
0.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
340.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
170.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
0.0
);
// 340.0 - (170.0 * 2)
});
testWidgets
(
'Scrollable rotated 90 degrees'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Transform
.
rotate
(
angle:
math
.
pi
/
2
,
child:
Center
(
child:
Container
(
width:
200
,
child:
ListView
.
builder
(
controller:
controller
,
cacheExtent:
0.0
,
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
Container
(
height:
100.0
,
color:
index
%
2
==
0
?
Colors
.
blue
:
Colors
.
red
,
child:
Text
(
'Tile
$index
'
),
);
},
),
),
),
),
),
);
expect
(
controller
.
offset
,
0.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
100.0
,
0.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
100.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
100.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
100.0
);
await
tester
.
drag
(
find
.
byType
(
ListView
),
const
Offset
(-
70.0
,
-
50.0
));
await
tester
.
pump
();
expect
(
controller
.
offset
,
30.0
);
// 100.0 - 70.0
});
testWidgets
(
'Perspective transform on scrollable'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Transform
(
transform:
Matrix4
.
identity
()
..
setEntry
(
3
,
2
,
0.001
)
..
rotateX
(
math
.
pi
/
4
),
child:
Center
(
child:
Container
(
width:
200
,
child:
ListView
.
builder
(
controller:
controller
,
cacheExtent:
0.0
,
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
Container
(
height:
100.0
,
color:
index
%
2
==
0
?
Colors
.
blue
:
Colors
.
red
,
child:
Text
(
'Tile
$index
'
),
);
},
),
),
),
),
),
);
expect
(
controller
.
offset
,
0.0
);
// We want to test that the point in the ListView that the finger touches
// on the screen stays under the finger as the finger scrolls the ListView
// in vertical direction. For this, we pick a point in the ListView (here
// the center of Tile 5) and calculate its position in the coordinate space
// of the screen. We then place our finger on that point and drag that
// point up in vertical direction. After the scroll activity is done,
// we verify that - in the coordinate space of the screen (!) - the point
// has moved the same distance as the finger. Due to the perspective
// transform the point will have moved more distance in the *local*
// coordinate system of the ListView.
// Calculate where the center of Tile 5 is located in the coordinate
// space of the screen. We cannot use `tester.getCenter` because it
// does not properly remove the perspective component from the transform
// to give us the place on the screen at which we need to touch the screen
// to have the center of Tile 5 directly under our finger.
final
RenderBox
tile5
=
tester
.
renderObject
(
find
.
text
(
'Tile 5'
));
final
Offset
pointOnScreenStart
=
MatrixUtils
.
transformPoint
(
PointerEvent
.
removePerspectiveTransform
(
tile5
.
getTransformTo
(
null
)),
tile5
.
size
.
center
(
Offset
.
zero
),
);
// Place the finger on the tracked point and move the finger upwards for
// 50 pixels to scroll the ListView (the ListView's scroll offset will
// move more then 50 pixels due to the perspective transform).
await
tester
.
dragFrom
(
pointOnScreenStart
,
const
Offset
(
0.0
,
-
50.0
));
await
tester
.
pump
();
// Get the new position of the tracked point in the screen's coordinate
// system.
final
Offset
pointOnScreenEnd
=
MatrixUtils
.
transformPoint
(
PointerEvent
.
removePerspectiveTransform
(
tile5
.
getTransformTo
(
null
)),
tile5
.
size
.
center
(
Offset
.
zero
),
);
// The tracked point (in the coordinate space of the screen) and the finger
// should have moved the same vertical distance over the screen.
expect
(
pointOnScreenStart
.
dy
-
pointOnScreenEnd
.
dy
,
within
(
distance:
0.00001
,
from:
50.0
),
);
// While the point traveled the same distance as the finger in the
// coordinate space of the screen, the scroll view actually moved far more
// pixels in its local coordinate system due to the perspective transform.
expect
(
controller
.
offset
,
greaterThan
(
100
));
});
}
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