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
6f4c4b3c
Unverified
Commit
6f4c4b3c
authored
Jul 07, 2020
by
Tong Mu
Committed by
GitHub
Jul 07, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor mouse hit testing system: Direct mouse hit test (#59883)
parent
4b120501
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
441 additions
and
163 deletions
+441
-163
events.dart
packages/flutter/lib/src/gestures/events.dart
+2
-2
hit_test.dart
packages/flutter/lib/src/gestures/hit_test.dart
+122
-14
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+6
-6
box.dart
packages/flutter/lib/src/rendering/box.dart
+11
-5
layer.dart
packages/flutter/lib/src/rendering/layer.dart
+0
-39
platform_view.dart
packages/flutter/lib/src/rendering/platform_view.dart
+17
-21
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+9
-19
sliver.dart
packages/flutter/lib/src/rendering/sliver.dart
+1
-1
view.dart
packages/flutter/lib/src/rendering/view.dart
+12
-3
hit_test_test.dart
packages/flutter/test/gestures/hit_test_test.dart
+93
-0
basic_test.dart
packages/flutter/test/widgets/basic_test.dart
+157
-0
listener_deprecated_test.dart
packages/flutter/test/widgets/listener_deprecated_test.dart
+3
-16
mouse_region_test.dart
packages/flutter/test/widgets/mouse_region_test.dart
+8
-37
No files found.
packages/flutter/lib/src/gestures/events.dart
View file @
6f4c4b3c
...
...
@@ -908,7 +908,7 @@ class PointerEnterEvent extends PointerEvent {
down:
event
?.
down
,
synthesized:
event
?.
synthesized
,
transform:
event
?.
transform
,
original:
event
?.
original
as
PointerEnterEvent
,
original:
null
,
);
@override
...
...
@@ -1054,7 +1054,7 @@ class PointerExitEvent extends PointerEvent {
down:
event
?.
down
,
synthesized:
event
?.
synthesized
,
transform:
event
?.
transform
,
original:
event
?.
original
as
PointerExitEvent
,
original:
null
,
);
@override
...
...
packages/flutter/lib/src/gestures/hit_test.dart
View file @
6f4c4b3c
...
...
@@ -4,8 +4,6 @@
// @dart = 2.8
import
'dart:collection'
;
import
'package:flutter/foundation.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -73,12 +71,51 @@ class HitTestEntry {
Matrix4
_transform
;
}
// A type of data that can be applied to a matrix by left-multiplication.
@immutable
abstract
class
_TransformPart
{
const
_TransformPart
();
// Apply this transform part to `rhs` from the left.
//
// This should work as if this transform part is first converted to a matrix
// and then left-multiplied to `rhs`.
//
// For example, if this transform part is a vector `v1`, whose corresponding
// matrix is `m1 = Matrix4.translation(v1)`, then the result of
// `_VectorTransformPart(v1).multiply(rhs)` should equal to `m1 * rhs`.
Matrix4
multiply
(
Matrix4
rhs
);
}
class
_MatrixTransformPart
extends
_TransformPart
{
const
_MatrixTransformPart
(
this
.
matrix
);
final
Matrix4
matrix
;
@override
Matrix4
multiply
(
Matrix4
rhs
)
{
return
matrix
*
rhs
as
Matrix4
;
}
}
class
_OffsetTransformPart
extends
_TransformPart
{
const
_OffsetTransformPart
(
this
.
offset
);
final
Offset
offset
;
@override
Matrix4
multiply
(
Matrix4
rhs
)
{
return
rhs
.
clone
()..
leftTranslate
(
offset
.
dx
,
offset
.
dy
);
}
}
/// The result of performing a hit test.
class
HitTestResult
{
/// Creates an empty hit test result.
HitTestResult
()
:
_path
=
<
HitTestEntry
>[],
_transforms
=
Queue
<
Matrix4
>();
_transforms
=
<
Matrix4
>[
Matrix4
.
identity
()],
_localTransforms
=
<
_TransformPart
>[];
/// Wraps `result` (usually a subtype of [HitTestResult]) to create a
/// generic [HitTestResult].
...
...
@@ -88,7 +125,8 @@ class HitTestResult {
/// structure to store [HitTestEntry]s).
HitTestResult
.
wrap
(
HitTestResult
result
)
:
_path
=
result
.
_path
,
_transforms
=
result
.
_transforms
;
_transforms
=
result
.
_transforms
,
_localTransforms
=
result
.
_localTransforms
;
/// An unmodifiable list of [HitTestEntry] objects recorded during the hit test.
///
...
...
@@ -98,7 +136,40 @@ class HitTestResult {
Iterable
<
HitTestEntry
>
get
path
=>
_path
;
final
List
<
HitTestEntry
>
_path
;
final
Queue
<
Matrix4
>
_transforms
;
// A stack of transform parts.
//
// The transform part stack leading from global to the current object is stored
// in 2 parts:
//
// * `_transforms` are globalized matrices, meaning they have been multiplied
// by the ancestors and are thus relative to the global coordinate space.
// * `_localTransforms` are local transform parts, which are relative to the
// parent's coordinate space.
//
// When new transform parts are added they're appended to `_localTransforms`,
// and are converted to global ones and moved to `_transforms` only when used.
final
List
<
Matrix4
>
_transforms
;
final
List
<
_TransformPart
>
_localTransforms
;
// Globalize all transform parts in `_localTransforms` and move them to
// _transforms.
void
_globalizeTransforms
()
{
if
(
_localTransforms
.
isEmpty
)
{
return
;
}
Matrix4
last
=
_transforms
.
last
;
for
(
final
_TransformPart
part
in
_localTransforms
)
{
last
=
part
.
multiply
(
last
);
_transforms
.
add
(
last
);
}
_localTransforms
.
clear
();
}
Matrix4
get
_lastTransform
{
_globalizeTransforms
();
assert
(
_localTransforms
.
isEmpty
);
return
_transforms
.
last
;
}
/// Add a [HitTestEntry] to the path.
///
...
...
@@ -107,7 +178,7 @@ class HitTestResult {
/// upward walk of the tree being hit tested.
void
add
(
HitTestEntry
entry
)
{
assert
(
entry
.
_transform
==
null
);
entry
.
_transform
=
_
transforms
.
isEmpty
?
null
:
_transforms
.
last
;
entry
.
_transform
=
_
lastTransform
;
_path
.
add
(
entry
);
}
...
...
@@ -125,6 +196,9 @@ class HitTestResult {
/// through [PointerEvent.removePerspectiveTransform] to remove
/// the perspective component.
///
/// If the provided `transform` is a translation matrix, it is much faster
/// to use [pushOffset] with the translation offset instead.
///
/// [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,
...
...
@@ -132,10 +206,10 @@ class HitTestResult {
///
/// See also:
///
/// * [pushOffset], which is similar to [pushTransform] but is limited to
/// translations, and is faster in such cases.
/// * [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 [RenderSliver]s.
@protected
void
pushTransform
(
Matrix4
transform
)
{
assert
(
transform
!=
null
);
...
...
@@ -148,26 +222,60 @@ class HitTestResult {
'matrix through PointerEvent.removePerspectiveTransform? '
'The provided matrix is:
\n
$transform
'
);
_transforms
.
add
(
_transforms
.
isEmpty
?
transform
:
(
transform
*
_transforms
.
last
as
Matrix4
));
_localTransforms
.
add
(
_MatrixTransformPart
(
transform
));
}
/// Pushes a new translation offset 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.addWithPaintOffset] for such an example).
///
/// The provided `offset` should describe how to transform [PointerEvent]s from
/// the coordinate space of the method caller to the coordinate space of its
/// children. Usually `offset` is the inverse of the offset of the child
/// relative to the parent.
///
/// [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:
///
/// * [pushTransform], which is similar to [pushOffset] but allows general
/// transform besides translation.
/// * [BoxHitTestResult.addWithPaintOffset], 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 [RenderSliver]s.
@protected
void
pushOffset
(
Offset
offset
)
{
assert
(
offset
!=
null
);
_localTransforms
.
add
(
_OffsetTransformPart
(
offset
));
}
/// Removes the last transform added via [pushTransform].
/// Removes the last transform added via [pushTransform]
or [pushOffset]
.
///
/// 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].
/// required a call to [pushTransform]
or [pushOffset]
.
///
/// See also:
///
/// * [pushTransform]
, which describes the use case of this function pair in
/// more details.
/// * [pushTransform]
and [pushOffset], which describes the use case of this
///
function pair in
more details.
@protected
void
popTransform
()
{
if
(
_localTransforms
.
isNotEmpty
)
_localTransforms
.
removeLast
();
else
_transforms
.
removeLast
();
assert
(
_transforms
.
isNotEmpty
);
_transforms
.
removeLast
();
}
bool
_debugVectorMoreOrLessEquals
(
Vector4
a
,
Vector4
b
,
{
double
epsilon
=
precisionErrorTolerance
})
{
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
6f4c4b3c
...
...
@@ -1148,12 +1148,12 @@ class _TextFieldState extends State<TextField> implements TextSelectionGestureDe
},
);
return
IgnorePointer
(
ignoring:
!
_isEnabled
,
child:
MouseRegion
(
cursor:
effectiveMouseCursor
,
onEnter:
(
PointerEnterEvent
event
)
=>
_handleHover
(
true
),
onExit:
(
PointerExitEvent
event
)
=>
_handleHover
(
false
)
,
return
MouseRegion
(
cursor:
effectiveMouseCursor
,
onEnter:
(
PointerEnterEvent
event
)
=>
_handleHover
(
true
),
onExit:
(
PointerExitEvent
event
)
=>
_handleHover
(
false
)
,
child:
IgnorePointer
(
ignoring:
!
_isEnabled
,
child:
AnimatedBuilder
(
animation:
controller
,
// changes the _currentLength
builder:
(
BuildContext
context
,
Widget
child
)
{
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
6f4c4b3c
...
...
@@ -764,11 +764,17 @@ class BoxHitTestResult extends HitTestResult {
@required
BoxHitTest
hitTest
,
})
{
assert
(
hitTest
!=
null
);
return
addWithRawTransform
(
transform:
offset
!=
null
?
Matrix4
.
translationValues
(-
offset
.
dx
,
-
offset
.
dy
,
0.0
)
:
null
,
position:
position
,
hitTest:
hitTest
,
);
final
Offset
transformedPosition
=
position
==
null
||
offset
==
null
?
position
:
position
-
offset
;
if
(
offset
!=
null
)
{
pushOffset
(-
offset
);
}
final
bool
isHit
=
hitTest
(
this
,
transformedPosition
);
if
(
offset
!=
null
)
{
popTransform
();
}
return
isHit
;
}
/// Transforms `position` to the local coordinate system of a child for
...
...
packages/flutter/lib/src/rendering/layer.dart
View file @
6f4c4b3c
...
...
@@ -14,7 +14,6 @@ import 'package:flutter/painting.dart';
import
'package:vector_math/vector_math_64.dart'
;
import
'debug.dart'
;
import
'mouse_tracking.dart'
;
/// Information collected for an annotation that is found in the layer tree.
///
...
...
@@ -633,7 +632,6 @@ class PlatformViewLayer extends Layer {
PlatformViewLayer
({
@required
this
.
rect
,
@required
this
.
viewId
,
this
.
hoverAnnotation
,
})
:
assert
(
rect
!=
null
),
assert
(
viewId
!=
null
);
...
...
@@ -645,25 +643,6 @@ class PlatformViewLayer extends Layer {
/// A UIView with this identifier must have been created by [PlatformViewsServices.initUiKitView].
final
int
viewId
;
/// [MouseTrackerAnnotation] that handles mouse events for this layer.
///
/// If [hoverAnnotation] is non-null, [PlatformViewLayer] will annotate the
/// region of this platform view such that annotation callbacks will receive
/// mouse events, including mouse enter, exit, and hover, but not including
/// mouse down, move, and up. The layer will be treated as opaque during an
/// annotation search, which will prevent layers behind it from receiving
/// these events.
///
/// By default, [hoverAnnotation] is null, and [PlatformViewLayer] will not
/// receive mouse events, and will therefore appear translucent during the
/// annotation search.
///
/// See also:
///
/// * [MouseRegion], which explains more about the mouse events and opacity
/// during annotation search.
final
MouseTrackerAnnotation
hoverAnnotation
;
@override
void
addToScene
(
ui
.
SceneBuilder
builder
,
[
Offset
layerOffset
=
Offset
.
zero
])
{
final
Rect
shiftedRect
=
layerOffset
==
Offset
.
zero
?
rect
:
rect
.
shift
(
layerOffset
);
...
...
@@ -674,24 +653,6 @@ class PlatformViewLayer extends Layer {
height:
shiftedRect
.
height
,
);
}
@override
@protected
bool
findAnnotations
<
S
>(
AnnotationResult
<
S
>
result
,
Offset
localPosition
,
{
@required
bool
onlyFirst
})
{
if
(
hoverAnnotation
==
null
||
!
rect
.
contains
(
localPosition
))
{
return
false
;
}
if
(
S
==
MouseTrackerAnnotation
)
{
final
Object
untypedValue
=
hoverAnnotation
;
final
S
typedValue
=
untypedValue
as
S
;
result
.
add
(
AnnotationEntry
<
S
>(
annotation:
typedValue
,
localPosition:
localPosition
,
));
return
true
;
}
return
false
;
}
}
/// A layer that indicates to the compositor that it should display
...
...
packages/flutter/lib/src/rendering/platform_view.dart
View file @
6f4c4b3c
...
...
@@ -622,7 +622,6 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
context
.
addLayer
(
PlatformViewLayer
(
rect:
offset
&
size
,
viewId:
_controller
.
viewId
,
hoverAnnotation:
_hoverAnnotation
,
));
}
...
...
@@ -636,32 +635,13 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin {
}
/// The Mixin handling the pointer events and gestures of a platform view render box.
mixin
_PlatformViewGestureMixin
on
RenderBox
{
mixin
_PlatformViewGestureMixin
on
RenderBox
implements
MouseTrackerAnnotation
{
/// How to behave during hit testing.
// The implicit setter is enough here as changing this value will just affect
// any newly arriving events there's nothing we need to invalidate.
PlatformViewHitTestBehavior
hitTestBehavior
;
/// [MouseTrackerAnnotation] associated with the platform view layer.
///
/// Gesture recognizers don't receive hover events due to the performance
/// cost associated with hit testing a sequence of potentially thousands of
/// events -- move events only hit-test the down event, then cache the result
/// and apply it to all subsequent move events, but there is no down event
/// for a hover. To support native hover gesture handling by platform views,
/// we attach/detach this layer annotation as necessary.
MouseTrackerAnnotation
get
_hoverAnnotation
{
return
_cachedHoverAnnotation
??=
MouseTrackerAnnotation
(
onHover:
(
PointerHoverEvent
event
)
{
if
(
_handlePointerEvent
!=
null
)
_handlePointerEvent
(
event
);
},
cursor:
MouseCursor
.
uncontrolled
,
);
}
MouseTrackerAnnotation
_cachedHoverAnnotation
;
_HandlePointerEvent
_handlePointerEvent
;
/// {@macro flutter.rendering.platformView.updateGestureRecognizers}
...
...
@@ -696,6 +676,22 @@ mixin _PlatformViewGestureMixin on RenderBox {
@override
bool
hitTestSelf
(
Offset
position
)
=>
hitTestBehavior
!=
PlatformViewHitTestBehavior
.
transparent
;
@override
PointerEnterEventListener
get
onEnter
=>
null
;
@override
PointerHoverEventListener
get
onHover
=>
_handleHover
;
void
_handleHover
(
PointerHoverEvent
event
)
{
if
(
_handlePointerEvent
!=
null
)
_handlePointerEvent
(
event
);
}
@override
PointerExitEventListener
get
onExit
=>
null
;
@override
MouseCursor
get
cursor
=>
MouseCursor
.
uncontrolled
;
@override
void
handleEvent
(
PointerEvent
event
,
HitTestEntry
entry
)
{
if
(
event
is
PointerDownEvent
)
{
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
6f4c4b3c
...
...
@@ -2720,6 +2720,15 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
_annotationIsActive
=
false
,
super
(
child
);
@protected
@override
bool
hitTestSelf
(
Offset
position
)
=>
true
;
@override
bool
hitTest
(
BoxHitTestResult
result
,
{
@required
Offset
position
})
{
return
super
.
hitTest
(
result
,
position:
position
)
&&
_opaque
;
}
/// Whether this object should prevent [RenderMouseRegion]s visually behind it
/// from detecting the pointer, thus affecting how their [onHover], [onEnter],
/// and [onExit] behave.
...
...
@@ -2838,25 +2847,6 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
super
.
detach
();
}
@override
bool
get
needsCompositing
=>
super
.
needsCompositing
||
_annotationIsActive
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_annotationIsActive
)
{
// Annotated region layers are not retained because they do not create engine layers.
final
AnnotatedRegionLayer
<
MouseTrackerAnnotation
>
layer
=
AnnotatedRegionLayer
<
MouseTrackerAnnotation
>(
this
,
size:
size
,
offset:
offset
,
opaque:
opaque
,
);
context
.
pushLayer
(
layer
,
super
.
paint
,
offset
);
}
else
{
super
.
paint
(
context
,
offset
);
}
}
@override
void
performResize
()
{
size
=
constraints
.
biggest
;
...
...
packages/flutter/lib/src/rendering/sliver.dart
View file @
6f4c4b3c
...
...
@@ -861,7 +861,7 @@ class SliverHitTestResult extends HitTestResult {
assert
(
crossAxisPosition
!=
null
);
assert
(
hitTest
!=
null
);
if
(
paintOffset
!=
null
)
{
push
Transform
(
Matrix4
.
translationValues
(-
paintOffset
.
dx
,
-
paintOffset
.
dy
,
0
)
);
push
Offset
(-
paintOffset
);
}
final
bool
isHit
=
hitTest
(
this
,
...
...
packages/flutter/lib/src/rendering/view.dart
View file @
6f4c4b3c
...
...
@@ -9,6 +9,7 @@ import 'dart:io' show Platform;
import
'dart:ui'
as
ui
show
Scene
,
SceneBuilder
,
Window
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/services.dart'
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -201,9 +202,17 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
// Layer hit testing is done using device pixels, so we have to convert
// the logical coordinates of the event location back to device pixels
// here.
return
layer
.
findAllAnnotations
<
MouseTrackerAnnotation
>(
position
*
configuration
.
devicePixelRatio
).
annotations
;
final
BoxHitTestResult
result
=
BoxHitTestResult
();
if
(
child
!=
null
)
child
.
hitTest
(
result
,
position:
position
);
result
.
add
(
HitTestEntry
(
this
));
final
List
<
MouseTrackerAnnotation
>
annotations
=
<
MouseTrackerAnnotation
>[];
for
(
final
HitTestEntry
entry
in
result
.
path
)
{
if
(
entry
.
target
is
MouseTrackerAnnotation
)
{
annotations
.
add
(
entry
.
target
as
MouseTrackerAnnotation
);
}
}
return
annotations
;
}
@override
...
...
packages/flutter/test/gestures/hit_test_test.dart
View file @
6f4c4b3c
...
...
@@ -36,6 +36,94 @@ void main() {
expect
(
wrapped
.
path
,
equals
(<
HitTestEntry
>[
entry1
,
entry2
,
entry3
]));
expect
(
entry3
.
transform
,
transform
);
});
test
(
'HitTestResult should correctly push and pop transforms'
,
()
{
Matrix4
currentTransform
(
HitTestResult
targetResult
)
{
final
HitTestEntry
entry
=
HitTestEntry
(
_DummyHitTestTarget
());
targetResult
.
add
(
entry
);
return
entry
.
transform
;
}
final
MyHitTestResult
result
=
MyHitTestResult
();
final
Matrix4
m1
=
Matrix4
.
translationValues
(
10
,
20
,
0
);
final
Matrix4
m2
=
Matrix4
.
rotationZ
(
1
);
final
Matrix4
m3
=
Matrix4
.
diagonal3Values
(
1.1
,
1.2
,
1.0
);
result
.
publicPushTransform
(
m1
);
expect
(
currentTransform
(
result
),
equals
(
m1
));
result
.
publicPushTransform
(
m2
);
expect
(
currentTransform
(
result
),
equals
(
m2
*
m1
));
expect
(
currentTransform
(
result
),
equals
(
m2
*
m1
));
// Test repeated add
// The `wrapped` is wrapped at [m1, m2]
final
MyHitTestResult
wrapped
=
MyHitTestResult
.
wrap
(
result
);
expect
(
currentTransform
(
wrapped
),
equals
(
m2
*
m1
));
result
.
publicPushTransform
(
m3
);
expect
(
currentTransform
(
result
),
equals
(
m3
*
m2
*
m1
));
expect
(
currentTransform
(
wrapped
),
equals
(
m3
*
m2
*
m1
));
result
.
publicPopTransform
();
result
.
publicPopTransform
();
expect
(
currentTransform
(
result
),
equals
(
m1
));
result
.
publicPopTransform
();
result
.
publicPushTransform
(
m3
);
expect
(
currentTransform
(
result
),
equals
(
m3
));
result
.
publicPushTransform
(
m2
);
expect
(
currentTransform
(
result
),
equals
(
m2
*
m3
));
});
test
(
'HitTestResult should correctly push and pop offsets'
,
()
{
Matrix4
currentTransform
(
HitTestResult
targetResult
)
{
final
HitTestEntry
entry
=
HitTestEntry
(
_DummyHitTestTarget
());
targetResult
.
add
(
entry
);
return
entry
.
transform
;
}
final
MyHitTestResult
result
=
MyHitTestResult
();
final
Matrix4
m1
=
Matrix4
.
rotationZ
(
1
);
final
Matrix4
m2
=
Matrix4
.
diagonal3Values
(
1.1
,
1.2
,
1.0
);
const
Offset
o3
=
Offset
(
10
,
20
);
final
Matrix4
m3
=
Matrix4
.
translationValues
(
o3
.
dx
,
o3
.
dy
,
0.0
);
// Test pushing offset as the first element
result
.
publicPushOffset
(
o3
);
expect
(
currentTransform
(
result
),
equals
(
m3
));
result
.
publicPopTransform
();
result
.
publicPushOffset
(
o3
);
result
.
publicPushTransform
(
m1
);
expect
(
currentTransform
(
result
),
equals
(
m1
*
m3
));
expect
(
currentTransform
(
result
),
equals
(
m1
*
m3
));
// Test repeated add
// The `wrapped` is wrapped at [m1, m2]
final
MyHitTestResult
wrapped
=
MyHitTestResult
.
wrap
(
result
);
expect
(
currentTransform
(
wrapped
),
equals
(
m1
*
m3
));
result
.
publicPushTransform
(
m2
);
expect
(
currentTransform
(
result
),
equals
(
m2
*
m1
*
m3
));
expect
(
currentTransform
(
wrapped
),
equals
(
m2
*
m1
*
m3
));
result
.
publicPopTransform
();
result
.
publicPopTransform
();
result
.
publicPopTransform
();
expect
(
currentTransform
(
result
),
equals
(
Matrix4
.
identity
()));
result
.
publicPushTransform
(
m2
);
result
.
publicPushOffset
(
o3
);
result
.
publicPushTransform
(
m1
);
expect
(
currentTransform
(
result
),
equals
(
m1
*
m3
*
m2
));
result
.
publicPopTransform
();
expect
(
currentTransform
(
result
),
equals
(
m3
*
m2
));
});
}
class
_DummyHitTestTarget
implements
HitTestTarget
{
...
...
@@ -46,5 +134,10 @@ class _DummyHitTestTarget implements HitTestTarget {
}
class
MyHitTestResult
extends
HitTestResult
{
MyHitTestResult
();
MyHitTestResult
.
wrap
(
HitTestResult
result
)
:
super
.
wrap
(
result
);
void
publicPushTransform
(
Matrix4
transform
)
=>
pushTransform
(
transform
);
void
publicPushOffset
(
Offset
offset
)
=>
pushOffset
(
offset
);
void
publicPopTransform
()
=>
popTransform
();
}
packages/flutter/test/widgets/basic_test.dart
View file @
6f4c4b3c
...
...
@@ -5,6 +5,7 @@
// @dart = 2.8
import
'dart:math'
as
math
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -398,6 +399,162 @@ void main() {
);
// TODO(Piinks): Remove skip once web goldens are supported, https://github.com/flutter/flutter/issues/40297
},
skip:
isBrowser
);
testWidgets
(
'IgnorePointer ignores pointers'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
logs
=
<
String
>[];
Widget
target
({
bool
ignoring
})
=>
Align
(
alignment:
Alignment
.
topLeft
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
SizedBox
(
width:
100
,
height:
100
,
child:
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down1'
);
},
child:
MouseRegion
(
onEnter:
(
_
)
{
logs
.
add
(
'enter1'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit1'
);
},
cursor:
SystemMouseCursors
.
forbidden
,
child:
Stack
(
children:
<
Widget
>[
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down2'
);
},
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
click
,
onEnter:
(
_
)
{
logs
.
add
(
'enter2'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit2'
);
},
),
),
IgnorePointer
(
ignoring:
ignoring
,
child:
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down3'
);
},
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
text
,
onEnter:
(
_
)
{
logs
.
add
(
'enter3'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit3'
);
},
),
),
)
],
),
),
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
pointer:
1
,
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
const
Offset
(
200
,
200
));
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pumpWidget
(
target
(
ignoring:
true
));
expect
(
logs
,
isEmpty
);
await
gesture
.
moveTo
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'enter1'
,
'enter2'
]);
logs
.
clear
();
await
gesture
.
down
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'down2'
,
'down1'
]);
logs
.
clear
();
await
gesture
.
up
();
expect
(
logs
,
isEmpty
);
await
tester
.
pumpWidget
(
target
(
ignoring:
false
));
expect
(
logs
,
<
String
>[
'exit2'
,
'enter3'
]);
logs
.
clear
();
await
gesture
.
down
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'down3'
,
'down1'
]);
logs
.
clear
();
await
gesture
.
up
();
expect
(
logs
,
isEmpty
);
await
tester
.
pumpWidget
(
target
(
ignoring:
true
));
expect
(
logs
,
<
String
>[
'exit3'
,
'enter2'
]);
logs
.
clear
();
});
testWidgets
(
'AbsorbPointer absorbs pointers'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
logs
=
<
String
>[];
Widget
target
({
bool
absorbing
})
=>
Align
(
alignment:
Alignment
.
topLeft
,
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
SizedBox
(
width:
100
,
height:
100
,
child:
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down1'
);
},
child:
MouseRegion
(
onEnter:
(
_
)
{
logs
.
add
(
'enter1'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit1'
);
},
cursor:
SystemMouseCursors
.
forbidden
,
child:
Stack
(
children:
<
Widget
>[
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down2'
);
},
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
click
,
onEnter:
(
_
)
{
logs
.
add
(
'enter2'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit2'
);
},
),
),
AbsorbPointer
(
absorbing:
absorbing
,
child:
Listener
(
onPointerDown:
(
_
)
{
logs
.
add
(
'down3'
);
},
child:
MouseRegion
(
cursor:
SystemMouseCursors
.
text
,
onEnter:
(
_
)
{
logs
.
add
(
'enter3'
);
},
onExit:
(
_
)
{
logs
.
add
(
'exit3'
);
},
),
),
)
],
),
),
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
pointer:
1
,
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
const
Offset
(
200
,
200
));
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pumpWidget
(
target
(
absorbing:
true
));
expect
(
logs
,
isEmpty
);
await
gesture
.
moveTo
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'enter1'
]);
logs
.
clear
();
await
gesture
.
down
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'down1'
]);
logs
.
clear
();
await
gesture
.
up
();
expect
(
logs
,
isEmpty
);
await
tester
.
pumpWidget
(
target
(
absorbing:
false
));
expect
(
logs
,
<
String
>[
'enter3'
]);
logs
.
clear
();
await
gesture
.
down
(
const
Offset
(
50
,
50
));
expect
(
logs
,
<
String
>[
'down3'
,
'down1'
]);
logs
.
clear
();
await
gesture
.
up
();
expect
(
logs
,
isEmpty
);
await
tester
.
pumpWidget
(
target
(
absorbing:
true
));
expect
(
logs
,
<
String
>[
'exit3'
]);
logs
.
clear
();
});
}
HitsRenderBox
hits
(
RenderBox
renderBox
)
=>
HitsRenderBox
(
renderBox
);
...
...
packages/flutter/test/widgets/listener_deprecated_test.dart
View file @
6f4c4b3c
...
...
@@ -436,7 +436,7 @@ void main() {
events
.
clear
();
});
testWidgets
(
'needsCompositing
updates correctly and is respected
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'needsCompositing
is always false
'
,
(
WidgetTester
tester
)
async
{
// Pretend that we have a mouse connected.
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
...
...
@@ -467,22 +467,9 @@ void main() {
),
),
);
expect
(
listener
.
needsCompositing
,
isTrue
);
// Compositing is required, therefore a dedicated TransformLayer for
// `Transform.scale` is added.
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
2
));
await
tester
.
pumpWidget
(
Transform
.
scale
(
scale:
2.0
,
child:
Listener
(
onPointerDown:
(
PointerDownEvent
_
)
{
},
),
),
);
expect
(
listener
.
needsCompositing
,
isFalse
);
//
TransformLayer for `Transform.scale` is removed again as transform is
//
executed directly on the canvas
.
//
If compositing was required, a dedicated TransformLayer for
//
`Transform.scale` would be added
.
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
1
));
});
...
...
packages/flutter/test/widgets/mouse_region_test.dart
View file @
6f4c4b3c
...
...
@@ -168,10 +168,6 @@ void main() {
await
gesture
.
addPointer
(
location:
const
Offset
(
400
,
300
));
addTearDown
(
gesture
.
removePointer
);
expect
(
move
,
isNull
);
expect
(
enter
,
isNull
);
expect
(
exit
,
isNull
);
await
tester
.
pump
();
expect
(
move
,
isNull
);
expect
(
enter
,
isNotNull
);
expect
(
enter
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
expect
(
exit
,
isNull
);
...
...
@@ -585,13 +581,13 @@ void main() {
}
await
tester
.
pumpWidget
(
hoverableContainer
(
onEnter:
(
PointerEnterEvent
details
)
=>
logs
.
add
(
'enter1'
)
,
onHover:
(
PointerHoverEvent
details
)
=>
logs
.
add
(
'hover1'
)
,
onExit:
(
PointerExitEvent
details
)
=>
logs
.
add
(
'exit1'
)
,
onEnter:
(
PointerEnterEvent
details
)
{
logs
.
add
(
'enter1'
);
}
,
onHover:
(
PointerHoverEvent
details
)
{
logs
.
add
(
'hover1'
);
}
,
onExit:
(
PointerExitEvent
details
)
{
logs
.
add
(
'exit1'
);
}
,
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
await
gesture
.
addPointer
(
location:
const
Offset
(
150.0
,
150.0
)
);
addTearDown
(
gesture
.
removePointer
);
// Start outside, move inside, then move outside
...
...
@@ -709,7 +705,7 @@ void main() {
events
.
clear
();
});
testWidgets
(
'needsCompositing
updates correctly and is respected
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'needsCompositing
is always false
'
,
(
WidgetTester
tester
)
async
{
// Pretend that we have a mouse connected.
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
...
...
@@ -729,7 +725,7 @@ void main() {
// transform.)
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
1
));
// Test that needsCompositing
updates correctly
with callback change
// Test that needsCompositing
stays false
with callback change
await
tester
.
pumpWidget
(
Transform
.
scale
(
scale:
2.0
,
...
...
@@ -739,35 +735,10 @@ void main() {
),
),
);
expect
(
mouseRegion
.
needsCompositing
,
isTrue
);
// Compositing is required, therefore a dedicated TransformLayer for
// `Transform.scale` is added.
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
2
));
await
tester
.
pumpWidget
(
Transform
.
scale
(
scale:
2.0
,
child:
const
MouseRegion
(
opaque:
false
),
),
);
expect
(
mouseRegion
.
needsCompositing
,
isFalse
);
//
TransformLayer for `Transform.scale` is removed again as transform is
//
executed directly on the canvas
.
//
If compositing was required, a dedicated TransformLayer for
//
`Transform.scale` would be added
.
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
1
));
// Test that needsCompositing updates correctly with `opaque` change
await
tester
.
pumpWidget
(
Transform
.
scale
(
scale:
2.0
,
child:
const
MouseRegion
(
opaque:
true
,
),
),
);
expect
(
mouseRegion
.
needsCompositing
,
isTrue
);
// Compositing is required, therefore a dedicated TransformLayer for
// `Transform.scale` is added.
expect
(
tester
.
layers
.
whereType
<
TransformLayer
>(),
hasLength
(
2
));
});
testWidgets
(
"Callbacks aren't called during build"
,
(
WidgetTester
tester
)
async
{
...
...
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