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
0450e7c0
Commit
0450e7c0
authored
Jan 24, 2020
by
Tong Mu
Committed by
Flutter GitHub Bot
Jan 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Trigger MouseRegion.toHover only on hover events (#47403)
parent
3ae0345e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
212 additions
and
22 deletions
+212
-22
mouse_tracking.dart
packages/flutter/lib/src/gestures/mouse_tracking.dart
+13
-13
listener_deprecated_test.dart
packages/flutter/test/widgets/listener_deprecated_test.dart
+2
-2
mouse_region_test.dart
packages/flutter/test/widgets/mouse_region_test.dart
+197
-7
No files found.
packages/flutter/lib/src/gestures/mouse_tracking.dart
View file @
0450e7c0
...
...
@@ -289,7 +289,6 @@ class MouseTracker extends ChangeNotifier {
return
;
final
PointerEvent
previousEvent
=
existingState
?.
latestEvent
;
final
Offset
lastHoverPosition
=
previousEvent
is
!
PointerHoverEvent
?
null
:
previousEvent
.
position
;
_updateDevices
(
targetEvent:
event
,
handleUpdatedDevice:
(
_MouseState
mouseState
,
LinkedHashSet
<
MouseTrackerAnnotation
>
previousAnnotations
)
{
...
...
@@ -297,7 +296,7 @@ class MouseTracker extends ChangeNotifier {
_dispatchDeviceCallbacks
(
lastAnnotations:
previousAnnotations
,
nextAnnotations:
mouseState
.
annotations
,
lastHoverPosition:
lastHoverPosition
,
previousEvent:
previousEvent
,
unhandledEvent:
event
,
trackedAnnotations:
_trackedAnnotations
,
);
...
...
@@ -328,13 +327,11 @@ class MouseTracker extends ChangeNotifier {
void
_updateAllDevices
()
{
_updateDevices
(
handleUpdatedDevice:
(
_MouseState
mouseState
,
LinkedHashSet
<
MouseTrackerAnnotation
>
previousAnnotations
)
{
final
PointerEvent
latestEvent
=
mouseState
.
latestEvent
;
final
Offset
lastHoverPosition
=
latestEvent
is
PointerHoverEvent
?
latestEvent
.
position
:
null
;
_dispatchDeviceCallbacks
(
lastAnnotations:
previousAnnotations
,
nextAnnotations:
mouseState
.
annotations
,
lastHoverPosition:
lastHoverPosition
,
unhandledEvent:
mouseState
.
latestEvent
,
previousEvent:
mouseState
.
latestEvent
,
unhandledEvent:
null
,
trackedAnnotations:
_trackedAnnotations
,
);
}
...
...
@@ -427,20 +424,22 @@ class MouseTracker extends ChangeNotifier {
// Dispatch callbacks related to a device after all necessary information
// has been collected.
//
// The `lastHoverPosition` can be null, which means the last event is not a
// hover. Other arguments must not be null.
// The `previousEvent` is the latest event before `unhandledEvent`. It might be
// null, which means the update is triggered by a new event.
// The `unhandledEvent` can be null, which means the update is not triggered
// by an event.
static
void
_dispatchDeviceCallbacks
({
@required
LinkedHashSet
<
MouseTrackerAnnotation
>
lastAnnotations
,
@required
LinkedHashSet
<
MouseTrackerAnnotation
>
nextAnnotations
,
@required
Offset
lastHoverPosition
,
@required
PointerEvent
previousEvent
,
@required
PointerEvent
unhandledEvent
,
@required
Set
<
MouseTrackerAnnotation
>
trackedAnnotations
,
})
{
assert
(
lastAnnotations
!=
null
);
assert
(
nextAnnotations
!=
null
);
// lastHoverPosition can be null
assert
(
unhandledEvent
!=
null
);
assert
(
trackedAnnotations
!=
null
);
final
PointerEvent
latestEvent
=
unhandledEvent
??
previousEvent
;
assert
(
latestEvent
!=
null
);
// Order is important for mouse event callbacks. The `findAnnotations`
// returns annotations in the visual order from front to back. We call
// it the "visual order", and the opposite one "reverse visual order".
...
...
@@ -456,7 +455,7 @@ class MouseTracker extends ChangeNotifier {
// trigger may cause exceptions and has safer alternatives. See
// [MouseRegion.onExit] for details.
if
(
annotation
.
onExit
!=
null
&&
attached
)
{
annotation
.
onExit
(
PointerExitEvent
.
fromMouseEvent
(
unhandled
Event
));
annotation
.
onExit
(
PointerExitEvent
.
fromMouseEvent
(
latest
Event
));
}
}
...
...
@@ -466,7 +465,7 @@ class MouseTracker extends ChangeNotifier {
for
(
final
MouseTrackerAnnotation
annotation
in
enteringAnnotations
)
{
assert
(
trackedAnnotations
.
contains
(
annotation
));
if
(
annotation
.
onEnter
!=
null
)
{
annotation
.
onEnter
(
PointerEnterEvent
.
fromMouseEvent
(
unhandled
Event
));
annotation
.
onEnter
(
PointerEnterEvent
.
fromMouseEvent
(
latest
Event
));
}
}
...
...
@@ -476,6 +475,7 @@ class MouseTracker extends ChangeNotifier {
if
(
unhandledEvent
is
PointerHoverEvent
)
{
final
Iterable
<
MouseTrackerAnnotation
>
hoveringAnnotations
=
nextAnnotations
.
toList
().
reversed
;
final
Offset
lastHoverPosition
=
previousEvent
is
PointerHoverEvent
?
previousEvent
.
position
:
null
;
for
(
final
MouseTrackerAnnotation
annotation
in
hoveringAnnotations
)
{
// Deduplicate: Trigger hover if it's a newly hovered annotation
// or the position has changed
...
...
packages/flutter/test/widgets/listener_deprecated_test.dart
View file @
0450e7c0
...
...
@@ -107,8 +107,8 @@ void main() {
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
await
tester
.
pump
();
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
expect
(
move
,
isNotNull
);
expect
(
move
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
expect
(
enter
,
isNotNull
);
...
...
@@ -593,8 +593,8 @@ void main() {
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(()
=>
gesture
?.
removePointer
());
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byType
(
Container
)));
await
tester
.
pumpAndSettle
();
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byType
(
Container
)));
expect
(
enter
.
length
,
1
);
expect
(
enter
.
single
.
position
,
const
Offset
(
400.0
,
300.0
));
...
...
packages/flutter/test/widgets/mouse_region_test.dart
View file @
0450e7c0
...
...
@@ -101,8 +101,11 @@ void main() {
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
await
tester
.
pump
();
move
=
null
;
enter
=
null
;
exit
=
null
;
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
expect
(
move
,
isNotNull
);
expect
(
move
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
expect
(
enter
,
isNotNull
);
...
...
@@ -132,15 +135,15 @@ void main() {
await
tester
.
pump
();
move
=
null
;
enter
=
null
;
exit
=
null
;
await
gesture
.
moveTo
(
const
Offset
(
1.0
,
1.0
));
await
tester
.
pump
();
expect
(
move
,
isNull
);
expect
(
enter
,
isNull
);
expect
(
exit
,
isNotNull
);
expect
(
exit
.
position
,
equals
(
const
Offset
(
1.0
,
1.0
)));
});
testWidgets
(
'
detects pointer exit when widget disappears
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'
triggers pointer enter when a mouse is connected
'
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
...
...
@@ -155,27 +158,214 @@ void main() {
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
final
RenderMouseRegion
renderListener
=
tester
.
renderObject
(
find
.
byType
(
MouseRegion
));
await
tester
.
pump
();
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
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
);
});
testWidgets
(
'triggers pointer exit when a mouse is disconnected'
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
await
tester
.
pumpWidget
(
Center
(
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
await
tester
.
pump
();
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
const
Offset
(
400
,
300
));
addTearDown
(()
=>
gesture
?.
removePointer
);
await
tester
.
pump
();
move
=
null
;
enter
=
null
;
exit
=
null
;
await
gesture
.
removePointer
();
gesture
=
null
;
expect
(
move
,
isNull
);
expect
(
enter
,
isNull
);
expect
(
exit
,
isNotNull
);
expect
(
exit
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
exit
=
null
;
await
tester
.
pump
();
expect
(
move
,
isNull
);
expect
(
enter
,
isNull
);
expect
(
exit
,
isNull
);
});
testWidgets
(
'triggers pointer enter when widget appears'
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
await
tester
.
pumpWidget
(
Center
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
await
tester
.
pump
();
expect
(
move
,
isNotNull
);
expect
(
move
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
expect
(
enter
,
isNull
);
expect
(
move
,
isNull
);
expect
(
exit
,
isNull
);
await
tester
.
pumpWidget
(
Center
(
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
await
tester
.
pump
();
expect
(
move
,
isNull
);
expect
(
enter
,
isNotNull
);
expect
(
enter
.
position
,
equals
(
const
Offset
(
400.0
,
300.0
)));
expect
(
exit
,
isNull
);
});
testWidgets
(
"doesn't trigger pointer exit when widget disappears"
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
await
tester
.
pumpWidget
(
Center
(
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
final
RenderMouseRegion
renderListener
=
tester
.
renderObject
(
find
.
byType
(
MouseRegion
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
const
Offset
(
400.0
,
300.0
));
await
tester
.
pump
();
move
=
null
;
enter
=
null
;
exit
=
null
;
await
tester
.
pumpWidget
(
Center
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
));
expect
(
enter
,
isNull
);
expect
(
move
,
isNull
);
expect
(
exit
,
isNull
);
expect
(
tester
.
binding
.
mouseTracker
.
isAnnotationAttached
(
renderListener
.
hoverAnnotation
),
isFalse
);
});
testWidgets
(
'triggers pointer enter when widget moves in'
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
center
,
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
const
Offset
(
1.0
,
1.0
));
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
expect
(
enter
,
isNull
);
expect
(
move
,
isNull
);
expect
(
exit
,
isNull
);
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
topLeft
,
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
await
tester
.
pump
();
expect
(
enter
,
isNotNull
);
expect
(
enter
.
position
,
equals
(
const
Offset
(
1.0
,
1.0
)));
expect
(
move
,
isNull
);
expect
(
exit
,
isNull
);
});
testWidgets
(
'triggers pointer exit when widget moves out'
,
(
WidgetTester
tester
)
async
{
PointerEnterEvent
enter
;
PointerHoverEvent
move
;
PointerExitEvent
exit
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
center
,
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
const
Offset
(
400
,
300
));
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
enter
=
null
;
move
=
null
;
exit
=
null
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
topLeft
,
child:
MouseRegion
(
child:
Container
(
width:
100.0
,
height:
100.0
,
),
onEnter:
(
PointerEnterEvent
details
)
=>
enter
=
details
,
onHover:
(
PointerHoverEvent
details
)
=>
move
=
details
,
onExit:
(
PointerExitEvent
details
)
=>
exit
=
details
,
),
));
await
tester
.
pump
();
expect
(
enter
,
isNull
);
expect
(
move
,
isNull
);
expect
(
exit
,
isNotNull
);
expect
(
exit
.
position
,
equals
(
const
Offset
(
400
,
300
)));
});
testWidgets
(
'Hover works with nested listeners'
,
(
WidgetTester
tester
)
async
{
final
UniqueKey
key1
=
UniqueKey
();
final
UniqueKey
key2
=
UniqueKey
();
...
...
@@ -680,8 +870,8 @@ void main() {
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
(
location:
Offset
.
zero
);
addTearDown
(()
=>
gesture
?.
removePointer
());
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byType
(
Container
)));
await
tester
.
pumpAndSettle
();
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byType
(
Container
)));
expect
(
enter
.
length
,
1
);
expect
(
enter
.
single
.
position
,
const
Offset
(
400.0
,
300.0
));
...
...
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