1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2014 The Flutter 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 'arena.dart';
import 'binding.dart';
export 'arena.dart' show GestureArenaEntry, GestureArenaMember;
class _CombiningGestureArenaEntry implements GestureArenaEntry {
_CombiningGestureArenaEntry(this._combiner, this._member);
final _CombiningGestureArenaMember _combiner;
final GestureArenaMember _member;
@override
void resolve(GestureDisposition disposition) {
_combiner._resolve(_member, disposition);
}
}
class _CombiningGestureArenaMember extends GestureArenaMember {
_CombiningGestureArenaMember(this._owner, this._pointer);
final GestureArenaTeam _owner;
final List<GestureArenaMember> _members = <GestureArenaMember>[];
final int _pointer;
bool _resolved = false;
GestureArenaMember? _winner;
GestureArenaEntry? _entry;
@override
void acceptGesture(int pointer) {
assert(_pointer == pointer);
assert(_winner != null || _members.isNotEmpty);
_close();
_winner ??= _owner.captain ?? _members[0];
for (final GestureArenaMember member in _members) {
if (member != _winner) {
member.rejectGesture(pointer);
}
}
_winner!.acceptGesture(pointer);
}
@override
void rejectGesture(int pointer) {
assert(_pointer == pointer);
_close();
for (final GestureArenaMember member in _members) {
member.rejectGesture(pointer);
}
}
void _close() {
assert(!_resolved);
_resolved = true;
final _CombiningGestureArenaMember? combiner = _owner._combiners.remove(_pointer);
assert(combiner == this);
}
GestureArenaEntry _add(int pointer, GestureArenaMember member) {
assert(!_resolved);
assert(_pointer == pointer);
_members.add(member);
_entry ??= GestureBinding.instance.gestureArena.add(pointer, this);
return _CombiningGestureArenaEntry(this, member);
}
void _resolve(GestureArenaMember member, GestureDisposition disposition) {
if (_resolved) {
return;
}
if (disposition == GestureDisposition.rejected) {
_members.remove(member);
member.rejectGesture(_pointer);
if (_members.isEmpty) {
_entry!.resolve(disposition);
}
} else {
assert(disposition == GestureDisposition.accepted);
_winner ??= _owner.captain ?? member;
_entry!.resolve(disposition);
}
}
}
/// A group of [GestureArenaMember] objects that are competing as a unit in the
/// [GestureArenaManager].
///
/// Normally, a recognizer competes directly in the [GestureArenaManager] to
/// recognize a sequence of pointer events as a gesture. With a
/// [GestureArenaTeam], recognizers can compete in the arena in a group with
/// other recognizers. Arena teams may have a captain which wins the arena on
/// behalf of its team.
///
/// When gesture recognizers are in a team together without a captain, then once
/// there are no other competing gestures in the arena, the first gesture to
/// have been added to the team automatically wins, instead of the gestures
/// continuing to compete against each other.
///
/// When gesture recognizers are in a team with a captain, then once one of the
/// team members claims victory or there are no other competing gestures in the
/// arena, the captain wins the arena, and all other team members lose.
///
/// For example, [Slider] uses a team without a captain to support both a
/// [HorizontalDragGestureRecognizer] and a [TapGestureRecognizer], but without
/// the drag recognizer having to wait until the user has dragged outside the
/// slop region of the tap gesture before triggering. Since they compete as a
/// team, as soon as any other recognizers are out of the arena, the drag
/// recognizer wins, even if the user has not actually dragged yet. On the other
/// hand, if the tap can win outright, before the other recognizers are taken
/// out of the arena (e.g. if the slider is in a vertical scrolling list and the
/// user places their finger on the touch surface then lifts it, so that neither
/// the horizontal nor vertical drag recognizers can claim victory) the tap
/// recognizer still actually wins, despite being in the team.
///
/// [AndroidView] uses a team with a captain to decide which gestures are
/// forwarded to the native view. For example if we want to forward taps and
/// vertical scrolls to a native Android view, [TapGestureRecognizer]s and
/// [VerticalDragGestureRecognizer] are added to a team with a captain (the
/// captain is set to be a gesture recognizer that never explicitly claims the
/// gesture).
///
/// The captain allows [AndroidView] to know when any gestures in the team has
/// been recognized (or all other arena members are out), once the captain wins
/// the gesture is forwarded to the Android view.
///
/// To assign a gesture recognizer to a team, set
/// [OneSequenceGestureRecognizer.team] to an instance of [GestureArenaTeam].
class GestureArenaTeam {
final Map<int, _CombiningGestureArenaMember> _combiners = <int, _CombiningGestureArenaMember>{};
/// A member that wins on behalf of the entire team.
///
/// If not null, when any one of the [GestureArenaTeam] members claims victory
/// the captain accepts the gesture.
/// If null, the member that claims a victory accepts the gesture.
GestureArenaMember? captain;
/// Adds a new member to the arena on behalf of this team.
///
/// Used by [GestureRecognizer] subclasses that wish to compete in the arena
/// using this team.
///
/// To assign a gesture recognizer to a team, see
/// [OneSequenceGestureRecognizer.team].
GestureArenaEntry add(int pointer, GestureArenaMember member) {
final _CombiningGestureArenaMember combiner = _combiners.putIfAbsent(
pointer,
() => _CombiningGestureArenaMember(this, pointer),
);
return combiner._add(pointer, member);
}
}