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
52b5b3ea
Unverified
Commit
52b5b3ea
authored
Aug 24, 2020
by
Todd Volkert
Committed by
GitHub
Aug 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add GestureDetector.onDoubleTapDown() (#64431)
* Add GestureDetector.onDoubleTapDown() * Review comments
parent
8b52e6a8
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
211 additions
and
306 deletions
+211
-306
multitap.dart
packages/flutter/lib/src/gestures/multitap.dart
+88
-35
gesture_detector.dart
packages/flutter/lib/src/widgets/gesture_detector.dart
+28
-1
double_tap_test.dart
packages/flutter/test/gestures/double_tap_test.dart
+95
-270
No files found.
packages/flutter/lib/src/gestures/multitap.dart
View file @
52b5b3ea
...
...
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:ui'
show
Offset
;
import
'package:vector_math/vector_math_64.dart'
;
...
...
@@ -60,7 +59,7 @@ class _CountdownZoned {
class
_TapTracker
{
_TapTracker
({
required
PointerDownEvent
event
,
this
.
entry
,
required
this
.
entry
,
required
Duration
doubleTapMinTime
,
})
:
assert
(
doubleTapMinTime
!=
null
),
assert
(
event
!=
null
),
...
...
@@ -71,7 +70,7 @@ class _TapTracker {
_doubleTapMinTimeCountdown
=
_CountdownZoned
(
duration:
doubleTapMinTime
);
final
int
pointer
;
final
GestureArenaEntry
?
entry
;
final
GestureArenaEntry
entry
;
final
Offset
_initialGlobalPosition
;
final
int
initialButtons
;
final
_CountdownZoned
_doubleTapMinTimeCountdown
;
...
...
@@ -122,25 +121,45 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
})
:
super
(
debugOwner:
debugOwner
,
kind:
kind
);
// Implementation notes:
//
// The double tap recognizer can be in one of four states. There's no
// explicit enum for the states, because they are already captured by
// the state of existing fields. Specifically:
// Waiting on first tap: In this state, the _trackers list is empty, and
// _firstTap is null.
// First tap in progress: In this state, the _trackers list contains all
// the states for taps that have begun but not completed. This list can
// have more than one entry if two pointers begin to tap.
// Waiting on second tap: In this state, one of the in-progress taps has
// completed successfully. The _trackers list is again empty, and
// _firstTap records the successful tap.
// Second tap in progress: Much like the "first tap in progress" state, but
// _firstTap is non-null. If a tap completes successfully while in this
// state, the callback is called and the state is reset.
//
// 1. Waiting on first tap: In this state, the _trackers list is empty, and
// _firstTap is null.
// 2. First tap in progress: In this state, the _trackers list contains all
// the states for taps that have begun but not completed. This list can
// have more than one entry if two pointers begin to tap.
// 3. Waiting on second tap: In this state, one of the in-progress taps has
// completed successfully. The _trackers list is again empty, and
// _firstTap records the successful tap.
// 4. Second tap in progress: Much like the "first tap in progress" state, but
// _firstTap is non-null. If a tap completes successfully while in this
// state, the callback is called and the state is reset.
//
// There are various other scenarios that cause the state to reset:
//
// - All in-progress taps are rejected (by time, distance, pointercancel, etc)
// - The long timer between taps expires
// - The gesture arena decides we have been rejected wholesale
/// A pointer has contacted the screen with a primary button at the same
/// location twice in quick succession, which might be the start of a double
/// tap.
///
/// This triggers immediately after the down event of the second tap.
///
/// If this recognizer doesn't win the arena, [onDoubleTapCancel] is called
/// next. Otherwise, [onDoubleTap] is called next.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [TapDownDetails], which is passed as an argument to this callback.
/// * [GestureDetector.onDoubleTapDown], which exposes this callback.
GestureTapDownCallback
?
onDoubleTapDown
;
/// Called when the user has tapped the screen with a primary button at the
/// same location twice in quick succession.
///
...
...
@@ -150,47 +169,71 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [GestureDetector.onDoubleTap], which exposes this callback.
GestureDoubleTapCallback
?
onDoubleTap
;
/// A pointer that previously triggered [onDoubleTapDown] will not end up
/// causing a double tap.
///
/// This triggers once the gesture loses the arena if [onDoubleTapDown] has
/// previously been triggered.
///
/// If this recognizer wins the arena, [onDoubleTap] is called instead.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [GestureDetector.onDoubleTapCancel], which exposes this callback.
GestureTapCancelCallback
?
onDoubleTapCancel
;
Timer
?
_doubleTapTimer
;
_TapTracker
?
_firstTap
;
final
Map
<
int
,
_TapTracker
>
_trackers
=
<
int
,
_TapTracker
>{};
@override
bool
isPointerAllowed
(
PointerEvent
event
)
{
bool
isPointerAllowed
(
Pointer
Down
Event
event
)
{
if
(
_firstTap
==
null
)
{
switch
(
event
.
buttons
)
{
case
kPrimaryButton:
if
(
onDoubleTap
==
null
)
if
(
onDoubleTapDown
==
null
&&
onDoubleTap
==
null
&&
onDoubleTapCancel
==
null
)
return
false
;
break
;
default
:
return
false
;
}
}
return
super
.
isPointerAllowed
(
event
as
PointerDownEvent
);
return
super
.
isPointerAllowed
(
event
);
}
@override
void
addAllowedPointer
(
PointerEvent
event
)
{
void
addAllowedPointer
(
Pointer
Down
Event
event
)
{
if
(
_firstTap
!=
null
)
{
if
(!
_firstTap
!.
isWithinGlobalTolerance
(
event
,
kDoubleTapSlop
))
{
// Ignore out-of-bounds second taps.
return
;
}
else
if
(!
_firstTap
!.
hasElapsedMinTime
()
||
!
_firstTap
!.
hasSameButton
(
event
as
PointerDownEvent
))
{
// Restart when the second tap is too close to the first
, or when butto
ns
// mismatch.
}
else
if
(!
_firstTap
!.
hasElapsedMinTime
()
||
!
_firstTap
!.
hasSameButton
(
event
))
{
// Restart when the second tap is too close to the first
(touch scree
ns
//
often detect touches intermittently), or when buttons
mismatch.
_reset
();
return
_trackFirstTap
(
event
);
return
_trackTap
(
event
);
}
else
if
(
onDoubleTapDown
!=
null
)
{
final
TapDownDetails
details
=
TapDownDetails
(
globalPosition:
event
.
position
,
localPosition:
event
.
localPosition
,
kind:
getKindForPointer
(
event
.
pointer
),
);
invokeCallback
<
void
>(
'onDoubleTapDown'
,
()
=>
onDoubleTapDown
!(
details
));
}
}
_track
First
Tap
(
event
);
_trackTap
(
event
);
}
void
_track
FirstTap
(
Pointer
Event
event
)
{
void
_track
Tap
(
PointerDown
Event
event
)
{
_stopDoubleTapTimer
();
final
_TapTracker
tracker
=
_TapTracker
(
event:
event
as
PointerDownEvent
,
event:
event
,
entry:
GestureBinding
.
instance
!.
gestureArena
.
add
(
event
.
pointer
,
this
),
doubleTapMinTime:
kDoubleTapMinTime
,
);
...
...
@@ -231,14 +274,17 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
void
_reject
(
_TapTracker
tracker
)
{
_trackers
.
remove
(
tracker
.
pointer
);
tracker
.
entry
!
.
resolve
(
GestureDisposition
.
rejected
);
tracker
.
entry
.
resolve
(
GestureDisposition
.
rejected
);
_freezeTracker
(
tracker
);
// If the first tap is in progress, and we've run out of taps to track,
// reset won't have any work to do. But if we're in the second tap, we need
// to clear intermediate state.
if
(
_firstTap
!=
null
&&
(
_trackers
.
isEmpty
||
tracker
==
_firstTap
))
_reset
();
if
(
_firstTap
!=
null
)
{
if
(
tracker
==
_firstTap
)
{
_reset
();
}
else
{
_checkCancel
();
if
(
_trackers
.
isEmpty
)
_reset
();
}
}
}
@override
...
...
@@ -250,6 +296,8 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
void
_reset
()
{
_stopDoubleTapTimer
();
if
(
_firstTap
!=
null
)
{
if
(
_trackers
.
isNotEmpty
)
_checkCancel
();
// Note, order is important below in order for the resolve -> reject logic
// to work properly.
final
_TapTracker
tracker
=
_firstTap
!;
...
...
@@ -272,8 +320,8 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
}
void
_registerSecondTap
(
_TapTracker
tracker
)
{
_firstTap
!.
entry
!
.
resolve
(
GestureDisposition
.
accepted
);
tracker
.
entry
!
.
resolve
(
GestureDisposition
.
accepted
);
_firstTap
!.
entry
.
resolve
(
GestureDisposition
.
accepted
);
tracker
.
entry
.
resolve
(
GestureDisposition
.
accepted
);
_freezeTracker
(
tracker
);
_trackers
.
remove
(
tracker
.
pointer
);
_checkUp
(
tracker
.
initialButtons
);
...
...
@@ -306,6 +354,11 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
invokeCallback
<
void
>(
'onDoubleTap'
,
onDoubleTap
!);
}
void
_checkCancel
()
{
if
(
onDoubleTapCancel
!=
null
)
invokeCallback
<
void
>(
'onDoubleTapCancel'
,
onDoubleTapCancel
!);
}
@override
String
get
debugDescription
=>
'double tap'
;
}
...
...
@@ -381,7 +434,7 @@ class _TapGesture extends _TapTracker {
if
(
_wonArena
)
reject
();
else
entry
!
.
resolve
(
GestureDisposition
.
rejected
);
// eventually calls reject()
entry
.
resolve
(
GestureDisposition
.
rejected
);
// eventually calls reject()
}
void
_check
()
{
...
...
packages/flutter/lib/src/widgets/gesture_detector.dart
View file @
52b5b3ea
...
...
@@ -229,7 +229,9 @@ class GestureDetector extends StatelessWidget {
this
.
onTertiaryTapDown
,
this
.
onTertiaryTapUp
,
this
.
onTertiaryTapCancel
,
this
.
onDoubleTapDown
,
this
.
onDoubleTap
,
this
.
onDoubleTapCancel
,
this
.
onLongPress
,
this
.
onLongPressStart
,
this
.
onLongPressMoveUpdate
,
...
...
@@ -428,6 +430,20 @@ class GestureDetector extends StatelessWidget {
/// * [kTertiaryButton], the button this callback responds to.
final
GestureTapCancelCallback
onTertiaryTapCancel
;
/// A pointer that might cause a double tap has contacted the screen at a
/// particular location.
///
/// Triggered immediately after the down event of the second tap.
///
/// If the user completes the double tap and the gesture wins, [onDoubleTap]
/// will be called after this callback. Otherwise, [onDoubleTapCancel] will
/// be called after this callback.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
final
GestureTapDownCallback
onDoubleTapDown
;
/// The user has tapped the screen with a primary button at the same location
/// twice in quick succession.
///
...
...
@@ -436,6 +452,14 @@ class GestureDetector extends StatelessWidget {
/// * [kPrimaryButton], the button this callback responds to.
final
GestureTapCallback
onDoubleTap
;
/// The pointer that previously triggered [onDoubleTapDown] will not end up
/// causing a double tap.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
final
GestureTapCancelCallback
onDoubleTapCancel
;
/// Called when a long press gesture with a primary button has been recognized.
///
/// Triggered when a pointer has remained in contact with the screen at the
...
...
@@ -774,7 +798,10 @@ class GestureDetector extends StatelessWidget {
gestures
[
DoubleTapGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
DoubleTapGestureRecognizer
>(
()
=>
DoubleTapGestureRecognizer
(
debugOwner:
this
),
(
DoubleTapGestureRecognizer
instance
)
{
instance
.
onDoubleTap
=
onDoubleTap
;
instance
..
onDoubleTapDown
=
onDoubleTapDown
..
onDoubleTap
=
onDoubleTap
..
onDoubleTapCancel
=
onDoubleTapCancel
;
},
);
}
...
...
packages/flutter/test/gestures/double_tap_test.dart
View file @
52b5b3ea
...
...
@@ -26,7 +26,37 @@ class TestGestureArenaMember extends GestureArenaMember {
}
void
main
(
)
{
setUp
(
ensureGestureBinding
);
DoubleTapGestureRecognizer
tap
;
bool
doubleTapRecognized
;
TapDownDetails
doubleTapDownDetails
;
bool
doubleTapCanceled
;
setUp
(()
{
ensureGestureBinding
();
tap
=
DoubleTapGestureRecognizer
();
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
expect
(
doubleTapRecognized
,
isFalse
);
doubleTapRecognized
=
true
;
};
doubleTapDownDetails
=
null
;
tap
.
onDoubleTapDown
=
(
TapDownDetails
details
)
{
expect
(
doubleTapDownDetails
,
isNull
);
doubleTapDownDetails
=
details
;
};
doubleTapCanceled
=
false
;
tap
.
onDoubleTapCancel
=
()
{
expect
(
doubleTapCanceled
,
isFalse
);
doubleTapCanceled
=
true
;
};
});
tearDown
(()
{
tap
.
dispose
();
});
// Down/up pair 1: normal tap sequence
const
PointerDownEvent
down1
=
PointerDownEvent
(
...
...
@@ -101,524 +131,330 @@ void main() {
);
testGesture
(
'Should recognize double tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
100
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down2
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down2
.
localPosition
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isTrue
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Inter-tap distance cancels double tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
addPointer
(
down3
);
tester
.
closeArena
(
3
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down3
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up3
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
3
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Intra-tap distance cancels double tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down4
);
tester
.
closeArena
(
4
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down4
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
move4
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up4
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
4
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Inter-tap delay cancels double tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
5000
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Inter-tap delay resets double tap, allowing third tap to be a double-tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
5000
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
100
));
tap
.
addPointer
(
down5
);
tester
.
closeArena
(
5
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down5
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down5
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down5
.
localPosition
);
tester
.
route
(
up5
);
expect
(
doubleTapRecognized
,
isTrue
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
5
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Intra-tap delay does not cancel double tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
1000
));
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down2
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down2
.
localPosition
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isTrue
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Should not recognize two overlapping taps'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Should recognize one tap of group followed by second tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
100
));
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down1
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down1
.
localPosition
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isTrue
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Should cancel on arena reject during first tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
final
TestGestureArenaMember
member
=
TestGestureArenaMember
();
final
GestureArenaEntry
entry
=
GestureBinding
.
instance
.
gestureArena
.
add
(
1
,
member
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
entry
.
resolve
(
GestureDisposition
.
accepted
);
expect
(
member
.
accepted
,
isTrue
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Should cancel on arena reject between taps'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
final
TestGestureArenaMember
member
=
TestGestureArenaMember
();
final
GestureArenaEntry
entry
=
GestureBinding
.
instance
.
gestureArena
.
add
(
1
,
member
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
entry
.
resolve
(
GestureDisposition
.
accepted
);
expect
(
member
.
accepted
,
isTrue
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Should cancel on arena reject during last tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
final
TestGestureArenaMember
member
=
TestGestureArenaMember
();
final
GestureArenaEntry
entry
=
GestureBinding
.
instance
.
gestureArena
.
add
(
1
,
member
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
100
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down2
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down2
.
localPosition
);
expect
(
doubleTapCanceled
,
isFalse
);
entry
.
resolve
(
GestureDisposition
.
accepted
);
expect
(
member
.
accepted
,
isTrue
);
expect
(
doubleTapCanceled
,
isTrue
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
});
testGesture
(
'Passive gesture should trigger on double tap cancel'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
FakeAsync
().
run
((
FakeAsync
async
)
{
tap
.
addPointer
(
down1
);
final
TestGestureArenaMember
member
=
TestGestureArenaMember
();
GestureBinding
.
instance
.
gestureArena
.
add
(
1
,
member
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
member
.
accepted
,
isFalse
);
async
.
elapse
(
const
Duration
(
milliseconds:
5000
));
expect
(
member
.
accepted
,
isTrue
);
});
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
});
testGesture
(
'Should not recognize two over-rapid taps'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
10
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
();
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Over-rapid taps resets double tap, allowing third tap to be a double-tap'
,
(
GestureTester
tester
)
{
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up1
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
10
));
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down2
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
up2
);
expect
(
doubleTapRecognized
,
isFalse
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTap
Recognized
,
isFalse
);
expect
(
doubleTap
DownDetails
,
isNull
);
tester
.
async
.
elapse
(
const
Duration
(
milliseconds:
100
));
tap
.
addPointer
(
down5
);
tester
.
closeArena
(
5
);
expect
(
doubleTapRecognized
,
isFalse
);
tester
.
route
(
down5
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down5
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down5
.
localPosition
);
tester
.
route
(
up5
);
expect
(
doubleTapRecognized
,
isTrue
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
5
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
group
(
'Enforce consistent-button restriction:'
,
()
{
...
...
@@ -630,13 +466,6 @@ void main() {
assert
(
interval
*
2
<
kDoubleTapTimeout
);
assert
(
interval
>
kDoubleTapMinTime
);
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down1
);
tester
.
closeArena
(
1
);
tester
.
route
(
down1
);
...
...
@@ -661,8 +490,8 @@ void main() {
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isFalse
);
tap
.
dispose
(
);
expect
(
doubleTapDownDetails
,
isNull
);
expect
(
doubleTapCanceled
,
isFalse
);
});
testGesture
(
'Button change should start a valid sequence'
,
(
GestureTester
tester
)
{
...
...
@@ -672,13 +501,6 @@ void main() {
assert
(
interval
*
2
<
kDoubleTapTimeout
);
assert
(
interval
>
kDoubleTapMinTime
);
final
DoubleTapGestureRecognizer
tap
=
DoubleTapGestureRecognizer
();
bool
doubleTapRecognized
=
false
;
tap
.
onDoubleTap
=
()
{
doubleTapRecognized
=
true
;
};
tap
.
addPointer
(
down6
);
tester
.
closeArena
(
6
);
tester
.
route
(
down6
);
...
...
@@ -694,17 +516,20 @@ void main() {
GestureBinding
.
instance
.
gestureArena
.
sweep
(
1
);
expect
(
doubleTapRecognized
,
isFalse
);
expect
(
doubleTapDownDetails
,
isNull
);
tester
.
async
.
elapse
(
interval
);
tap
.
addPointer
(
down2
);
tester
.
closeArena
(
2
);
tester
.
route
(
down2
);
expect
(
doubleTapDownDetails
,
isNotNull
);
expect
(
doubleTapDownDetails
.
globalPosition
,
down2
.
position
);
expect
(
doubleTapDownDetails
.
localPosition
,
down2
.
localPosition
);
tester
.
route
(
up2
);
GestureBinding
.
instance
.
gestureArena
.
sweep
(
2
);
expect
(
doubleTapRecognized
,
isTrue
);
tap
.
dispose
();
expect
(
doubleTapCanceled
,
isFalse
);
});
});
...
...
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