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
98c0282f
Commit
98c0282f
authored
Nov 03, 2015
by
Hixie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move multitap out of tap.dart
Tap and the othes classes in tap.dart are not really related.
parent
04ee7133
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
330 additions
and
315 deletions
+330
-315
gestures.dart
packages/flutter/lib/gestures.dart
+1
-0
multitap.dart
packages/flutter/lib/src/gestures/multitap.dart
+329
-0
tap.dart
packages/flutter/lib/src/gestures/tap.dart
+0
-315
No files found.
packages/flutter/lib/gestures.dart
View file @
98c0282f
...
...
@@ -11,6 +11,7 @@ export 'src/gestures/drag.dart';
export
'src/gestures/events.dart'
;
export
'src/gestures/long_press.dart'
;
export
'src/gestures/lsq_solver.dart'
;
export
'src/gestures/multitap.dart'
;
export
'src/gestures/pointer_router.dart'
;
export
'src/gestures/recognizer.dart'
;
export
'src/gestures/scale.dart'
;
...
...
packages/flutter/lib/src/gestures/multitap.dart
0 → 100644
View file @
98c0282f
// Copyright 2015 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:async'
;
import
'dart:ui'
show
Point
,
Offset
;
import
'arena.dart'
;
import
'constants.dart'
;
import
'events.dart'
;
import
'pointer_router.dart'
;
import
'recognizer.dart'
;
import
'tap.dart'
show
GestureTapDownCallback
,
GestureTapDownCallback
,
GestureTapCallback
,
GestureTapCancelCallback
;
/// TapTracker helps track individual tap sequences as part of a
/// larger gesture.
class
_TapTracker
{
_TapTracker
({
PointerInputEvent
event
,
this
.
entry
})
:
pointer
=
event
.
pointer
,
_initialPosition
=
event
.
position
{
assert
(
event
.
type
==
'pointerdown'
);
}
final
int
pointer
;
final
GestureArenaEntry
entry
;
final
Point
_initialPosition
;
bool
_isTrackingPointer
=
false
;
void
startTrackingPointer
(
PointerRouter
router
,
PointerRoute
route
)
{
if
(!
_isTrackingPointer
)
{
_isTrackingPointer
=
true
;
router
.
addRoute
(
pointer
,
route
);
}
}
void
stopTrackingPointer
(
PointerRouter
router
,
PointerRoute
route
)
{
if
(
_isTrackingPointer
)
{
_isTrackingPointer
=
false
;
router
.
removeRoute
(
pointer
,
route
);
}
}
bool
isWithinTolerance
(
PointerInputEvent
event
,
double
tolerance
)
{
Offset
offset
=
event
.
position
-
_initialPosition
;
return
offset
.
distance
<=
tolerance
;
}
}
class
DoubleTapGestureRecognizer
extends
GestureArenaMember
{
DoubleTapGestureRecognizer
({
this
.
router
,
this
.
onDoubleTap
});
// 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 invoked 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
PointerRouter
router
;
GestureTapCallback
onDoubleTap
;
Timer
_doubleTapTimer
;
_TapTracker
_firstTap
;
final
Map
<
int
,
_TapTracker
>
_trackers
=
new
Map
<
int
,
_TapTracker
>();
void
addPointer
(
PointerInputEvent
event
)
{
// Ignore out-of-bounds second taps
if
(
_firstTap
!=
null
&&
!
_firstTap
.
isWithinTolerance
(
event
,
kDoubleTapTouchSlop
))
return
;
_stopDoubleTapTimer
();
_TapTracker
tracker
=
new
_TapTracker
(
event:
event
,
entry:
GestureArena
.
instance
.
add
(
event
.
pointer
,
this
)
);
_trackers
[
event
.
pointer
]
=
tracker
;
tracker
.
startTrackingPointer
(
router
,
handleEvent
);
}
void
handleEvent
(
PointerInputEvent
event
)
{
_TapTracker
tracker
=
_trackers
[
event
.
pointer
];
assert
(
tracker
!=
null
);
if
(
event
.
type
==
'pointerup'
)
{
if
(
_firstTap
==
null
)
_registerFirstTap
(
tracker
);
else
_registerSecondTap
(
tracker
);
}
else
if
(
event
.
type
==
'pointermove'
&&
!
tracker
.
isWithinTolerance
(
event
,
kTouchSlop
))
{
_reject
(
tracker
);
}
else
if
(
event
.
type
==
'pointercancel'
)
{
_reject
(
tracker
);
}
}
void
acceptGesture
(
int
pointer
)
{}
void
rejectGesture
(
int
pointer
)
{
_TapTracker
tracker
=
_trackers
[
pointer
];
// If tracker isn't in the list, check if this is the first tap tracker
if
(
tracker
==
null
&&
_firstTap
!=
null
&&
_firstTap
.
pointer
==
pointer
)
tracker
=
_firstTap
;
// If tracker is still null, we rejected ourselves already
if
(
tracker
!=
null
)
_reject
(
tracker
);
}
void
_reject
(
_TapTracker
tracker
)
{
_trackers
.
remove
(
tracker
.
pointer
);
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
();
}
void
dispose
()
{
_reset
();
router
=
null
;
}
void
_reset
()
{
_stopDoubleTapTimer
();
if
(
_firstTap
!=
null
)
{
// Note, order is important below in order for the resolve -> reject logic
// to work properly
_TapTracker
tracker
=
_firstTap
;
_firstTap
=
null
;
_reject
(
tracker
);
GestureArena
.
instance
.
release
(
tracker
.
pointer
);
}
_clearTrackers
();
}
void
_registerFirstTap
(
_TapTracker
tracker
)
{
_startDoubleTapTimer
();
GestureArena
.
instance
.
hold
(
tracker
.
pointer
);
// Note, order is important below in order for the clear -> reject logic to
// work properly.
_freezeTracker
(
tracker
);
_trackers
.
remove
(
tracker
.
pointer
);
_clearTrackers
();
_firstTap
=
tracker
;
}
void
_registerSecondTap
(
_TapTracker
tracker
)
{
_firstTap
.
entry
.
resolve
(
GestureDisposition
.
accepted
);
tracker
.
entry
.
resolve
(
GestureDisposition
.
accepted
);
_freezeTracker
(
tracker
);
_trackers
.
remove
(
tracker
.
pointer
);
if
(
onDoubleTap
!=
null
)
onDoubleTap
();
_reset
();
}
void
_clearTrackers
()
{
List
<
_TapTracker
>
localTrackers
=
new
List
<
_TapTracker
>.
from
(
_trackers
.
values
);
for
(
_TapTracker
tracker
in
localTrackers
)
_reject
(
tracker
);
assert
(
_trackers
.
isEmpty
);
}
void
_freezeTracker
(
_TapTracker
tracker
)
{
tracker
.
stopTrackingPointer
(
router
,
handleEvent
);
}
void
_startDoubleTapTimer
()
{
_doubleTapTimer
??=
new
Timer
(
kDoubleTapTimeout
,
()
=>
_reset
());
}
void
_stopDoubleTapTimer
()
{
if
(
_doubleTapTimer
!=
null
)
{
_doubleTapTimer
.
cancel
();
_doubleTapTimer
=
null
;
}
}
}
enum
_TapResolution
{
tap
,
cancel
}
/// TapGesture represents a full gesture resulting from a single tap sequence,
/// as part of a [MultiTapGestureRecognizer]. Tap gestures are passive, meaning
/// that they will not preempt any other arena member in play.
class
_TapGesture
extends
_TapTracker
{
_TapGesture
({
MultiTapGestureRecognizer
gestureRecognizer
,
PointerInputEvent
event
})
:
gestureRecognizer
=
gestureRecognizer
,
super
(
event:
event
,
entry:
GestureArena
.
instance
.
add
(
event
.
pointer
,
gestureRecognizer
))
{
startTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
}
final
MultiTapGestureRecognizer
gestureRecognizer
;
bool
_wonArena
=
false
;
Point
_finalPosition
;
void
handleEvent
(
PointerInputEvent
event
)
{
assert
(
event
.
pointer
==
pointer
);
if
(
event
.
type
==
'pointermove'
&&
!
isWithinTolerance
(
event
,
kTouchSlop
))
{
cancel
();
}
else
if
(
event
.
type
==
'pointercancel'
)
{
cancel
();
}
else
if
(
event
.
type
==
'pointerup'
)
{
stopTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
_finalPosition
=
event
.
position
;
_check
();
}
}
void
accept
()
{
_wonArena
=
true
;
_check
();
}
void
reject
()
{
stopTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
gestureRecognizer
.
_resolveTap
(
pointer
,
_TapResolution
.
cancel
,
null
);
}
void
cancel
()
{
// If we won the arena already, then entry is resolved, so resolving
// again is a no-op. But we still need to clean up our own state.
if
(
_wonArena
)
reject
();
else
entry
.
resolve
(
GestureDisposition
.
rejected
);
}
void
_check
()
{
if
(
_wonArena
&&
_finalPosition
!=
null
)
gestureRecognizer
.
_resolveTap
(
pointer
,
_TapResolution
.
tap
,
_finalPosition
);
}
}
/// MultiTapGestureRecognizer is a tap recognizer that treats taps
/// independently. That is, each pointer sequence that could resolve to a tap
/// does so independently of others: down-1, down-2, up-1, up-2 produces two
/// taps, on up-1 and up-2.
class
MultiTapGestureRecognizer
extends
GestureArenaMember
{
MultiTapGestureRecognizer
({
this
.
router
,
this
.
onTapDown
,
this
.
onTapUp
,
this
.
onTap
,
this
.
onTapCancel
});
PointerRouter
router
;
GestureTapDownCallback
onTapDown
;
GestureTapDownCallback
onTapUp
;
GestureTapCallback
onTap
;
GestureTapCancelCallback
onTapCancel
;
Map
<
int
,
_TapGesture
>
_gestureMap
=
new
Map
<
int
,
_TapGesture
>();
void
addPointer
(
PointerInputEvent
event
)
{
assert
(!
_gestureMap
.
containsKey
(
event
.
pointer
));
_gestureMap
[
event
.
pointer
]
=
new
_TapGesture
(
gestureRecognizer:
this
,
event:
event
);
if
(
onTapDown
!=
null
)
onTapDown
(
event
.
position
);
}
void
acceptGesture
(
int
pointer
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
_gestureMap
[
pointer
]?.
accept
();
}
void
rejectGesture
(
int
pointer
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
_gestureMap
[
pointer
]?.
reject
();
}
void
_resolveTap
(
int
pointer
,
_TapResolution
resolution
,
Point
globalPosition
)
{
_gestureMap
.
remove
(
pointer
);
if
(
resolution
==
_TapResolution
.
tap
)
{
if
(
onTapUp
!=
null
)
onTapUp
(
globalPosition
);
if
(
onTap
!=
null
)
onTap
();
}
else
{
if
(
onTapCancel
!=
null
)
onTapCancel
();
}
}
void
dispose
()
{
List
<
_TapGesture
>
localGestures
=
new
List
<
_TapGesture
>.
from
(
_gestureMap
.
values
);
for
(
_TapGesture
gesture
in
localGestures
)
gesture
.
cancel
();
// Rejection of each gesture should cause it to be removed from our map
assert
(
_gestureMap
.
isEmpty
);
router
=
null
;
}
}
packages/flutter/lib/src/gestures/tap.dart
View file @
98c0282f
...
...
@@ -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
Point
,
Offset
;
import
'arena.dart'
;
...
...
@@ -92,317 +91,3 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
_finalPosition
=
null
;
}
}
/// TapTracker helps track individual tap sequences as part of a
/// larger gesture.
class
_TapTracker
{
_TapTracker
({
PointerInputEvent
event
,
this
.
entry
})
:
pointer
=
event
.
pointer
,
_initialPosition
=
event
.
position
{
assert
(
event
.
type
==
'pointerdown'
);
}
final
int
pointer
;
final
GestureArenaEntry
entry
;
final
Point
_initialPosition
;
bool
_isTrackingPointer
=
false
;
void
startTrackingPointer
(
PointerRouter
router
,
PointerRoute
route
)
{
if
(!
_isTrackingPointer
)
{
_isTrackingPointer
=
true
;
router
.
addRoute
(
pointer
,
route
);
}
}
void
stopTrackingPointer
(
PointerRouter
router
,
PointerRoute
route
)
{
if
(
_isTrackingPointer
)
{
_isTrackingPointer
=
false
;
router
.
removeRoute
(
pointer
,
route
);
}
}
bool
isWithinTolerance
(
PointerInputEvent
event
,
double
tolerance
)
{
Offset
offset
=
event
.
position
-
_initialPosition
;
return
offset
.
distance
<=
tolerance
;
}
}
enum
_TapResolution
{
tap
,
cancel
}
/// TapGesture represents a full gesture resulting from a single tap sequence,
/// as part of a [MultiTapGestureRecognizer]. Tap gestures are passive, meaning
/// that they will not preempt any other arena member in play.
class
_TapGesture
extends
_TapTracker
{
_TapGesture
({
MultiTapGestureRecognizer
gestureRecognizer
,
PointerInputEvent
event
})
:
gestureRecognizer
=
gestureRecognizer
,
super
(
event:
event
,
entry:
GestureArena
.
instance
.
add
(
event
.
pointer
,
gestureRecognizer
))
{
startTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
}
final
MultiTapGestureRecognizer
gestureRecognizer
;
bool
_wonArena
=
false
;
Point
_finalPosition
;
void
handleEvent
(
PointerInputEvent
event
)
{
assert
(
event
.
pointer
==
pointer
);
if
(
event
.
type
==
'pointermove'
&&
!
isWithinTolerance
(
event
,
kTouchSlop
))
{
cancel
();
}
else
if
(
event
.
type
==
'pointercancel'
)
{
cancel
();
}
else
if
(
event
.
type
==
'pointerup'
)
{
stopTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
_finalPosition
=
event
.
position
;
_check
();
}
}
void
accept
()
{
_wonArena
=
true
;
_check
();
}
void
reject
()
{
stopTrackingPointer
(
gestureRecognizer
.
router
,
handleEvent
);
gestureRecognizer
.
_resolveTap
(
pointer
,
_TapResolution
.
cancel
,
null
);
}
void
cancel
()
{
// If we won the arena already, then entry is resolved, so resolving
// again is a no-op. But we still need to clean up our own state.
if
(
_wonArena
)
reject
();
else
entry
.
resolve
(
GestureDisposition
.
rejected
);
}
void
_check
()
{
if
(
_wonArena
&&
_finalPosition
!=
null
)
gestureRecognizer
.
_resolveTap
(
pointer
,
_TapResolution
.
tap
,
_finalPosition
);
}
}
/// MultiTapGestureRecognizer is a tap recognizer that treats taps
/// independently. That is, each pointer sequence that could resolve to a tap
/// does so independently of others: down-1, down-2, up-1, up-2 produces two
/// taps, on up-1 and up-2.
class
MultiTapGestureRecognizer
extends
GestureArenaMember
{
MultiTapGestureRecognizer
({
this
.
router
,
this
.
onTapDown
,
this
.
onTapUp
,
this
.
onTap
,
this
.
onTapCancel
});
PointerRouter
router
;
GestureTapDownCallback
onTapDown
;
GestureTapDownCallback
onTapUp
;
GestureTapCallback
onTap
;
GestureTapCancelCallback
onTapCancel
;
Map
<
int
,
_TapGesture
>
_gestureMap
=
new
Map
<
int
,
_TapGesture
>();
void
addPointer
(
PointerInputEvent
event
)
{
assert
(!
_gestureMap
.
containsKey
(
event
.
pointer
));
_gestureMap
[
event
.
pointer
]
=
new
_TapGesture
(
gestureRecognizer:
this
,
event:
event
);
if
(
onTapDown
!=
null
)
onTapDown
(
event
.
position
);
}
void
acceptGesture
(
int
pointer
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
_gestureMap
[
pointer
]?.
accept
();
}
void
rejectGesture
(
int
pointer
)
{
assert
(
_gestureMap
.
containsKey
(
pointer
));
_gestureMap
[
pointer
]?.
reject
();
}
void
_resolveTap
(
int
pointer
,
_TapResolution
resolution
,
Point
globalPosition
)
{
_gestureMap
.
remove
(
pointer
);
if
(
resolution
==
_TapResolution
.
tap
)
{
if
(
onTapUp
!=
null
)
onTapUp
(
globalPosition
);
if
(
onTap
!=
null
)
onTap
();
}
else
{
if
(
onTapCancel
!=
null
)
onTapCancel
();
}
}
void
dispose
()
{
List
<
_TapGesture
>
localGestures
=
new
List
<
_TapGesture
>.
from
(
_gestureMap
.
values
);
for
(
_TapGesture
gesture
in
localGestures
)
gesture
.
cancel
();
// Rejection of each gesture should cause it to be removed from our map
assert
(
_gestureMap
.
isEmpty
);
router
=
null
;
}
}
class
DoubleTapGestureRecognizer
extends
GestureArenaMember
{
DoubleTapGestureRecognizer
({
this
.
router
,
this
.
onDoubleTap
});
// 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 invoked 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
PointerRouter
router
;
GestureTapCallback
onDoubleTap
;
Timer
_doubleTapTimer
;
_TapTracker
_firstTap
;
final
Map
<
int
,
_TapTracker
>
_trackers
=
new
Map
<
int
,
_TapTracker
>();
void
addPointer
(
PointerInputEvent
event
)
{
// Ignore out-of-bounds second taps
if
(
_firstTap
!=
null
&&
!
_firstTap
.
isWithinTolerance
(
event
,
kDoubleTapTouchSlop
))
return
;
_stopDoubleTapTimer
();
_TapTracker
tracker
=
new
_TapTracker
(
event:
event
,
entry:
GestureArena
.
instance
.
add
(
event
.
pointer
,
this
)
);
_trackers
[
event
.
pointer
]
=
tracker
;
tracker
.
startTrackingPointer
(
router
,
handleEvent
);
}
void
handleEvent
(
PointerInputEvent
event
)
{
_TapTracker
tracker
=
_trackers
[
event
.
pointer
];
assert
(
tracker
!=
null
);
if
(
event
.
type
==
'pointerup'
)
{
if
(
_firstTap
==
null
)
_registerFirstTap
(
tracker
);
else
_registerSecondTap
(
tracker
);
}
else
if
(
event
.
type
==
'pointermove'
&&
!
tracker
.
isWithinTolerance
(
event
,
kTouchSlop
))
{
_reject
(
tracker
);
}
else
if
(
event
.
type
==
'pointercancel'
)
{
_reject
(
tracker
);
}
}
void
acceptGesture
(
int
pointer
)
{}
void
rejectGesture
(
int
pointer
)
{
_TapTracker
tracker
=
_trackers
[
pointer
];
// If tracker isn't in the list, check if this is the first tap tracker
if
(
tracker
==
null
&&
_firstTap
!=
null
&&
_firstTap
.
pointer
==
pointer
)
tracker
=
_firstTap
;
// If tracker is still null, we rejected ourselves already
if
(
tracker
!=
null
)
_reject
(
tracker
);
}
void
_reject
(
_TapTracker
tracker
)
{
_trackers
.
remove
(
tracker
.
pointer
);
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
();
}
void
dispose
()
{
_reset
();
router
=
null
;
}
void
_reset
()
{
_stopDoubleTapTimer
();
if
(
_firstTap
!=
null
)
{
// Note, order is important below in order for the resolve -> reject logic
// to work properly
_TapTracker
tracker
=
_firstTap
;
_firstTap
=
null
;
_reject
(
tracker
);
GestureArena
.
instance
.
release
(
tracker
.
pointer
);
}
_clearTrackers
();
}
void
_registerFirstTap
(
_TapTracker
tracker
)
{
_startDoubleTapTimer
();
GestureArena
.
instance
.
hold
(
tracker
.
pointer
);
// Note, order is important below in order for the clear -> reject logic to
// work properly.
_freezeTracker
(
tracker
);
_trackers
.
remove
(
tracker
.
pointer
);
_clearTrackers
();
_firstTap
=
tracker
;
}
void
_registerSecondTap
(
_TapTracker
tracker
)
{
_firstTap
.
entry
.
resolve
(
GestureDisposition
.
accepted
);
tracker
.
entry
.
resolve
(
GestureDisposition
.
accepted
);
_freezeTracker
(
tracker
);
_trackers
.
remove
(
tracker
.
pointer
);
if
(
onDoubleTap
!=
null
)
onDoubleTap
();
_reset
();
}
void
_clearTrackers
()
{
List
<
_TapTracker
>
localTrackers
=
new
List
<
_TapTracker
>.
from
(
_trackers
.
values
);
for
(
_TapTracker
tracker
in
localTrackers
)
_reject
(
tracker
);
assert
(
_trackers
.
isEmpty
);
}
void
_freezeTracker
(
_TapTracker
tracker
)
{
tracker
.
stopTrackingPointer
(
router
,
handleEvent
);
}
void
_startDoubleTapTimer
()
{
_doubleTapTimer
??=
new
Timer
(
kDoubleTapTimeout
,
()
=>
_reset
());
}
void
_stopDoubleTapTimer
()
{
if
(
_doubleTapTimer
!=
null
)
{
_doubleTapTimer
.
cancel
();
_doubleTapTimer
=
null
;
}
}
}
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