| // Copyright 2017 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 'arena.dart'; |
| import 'binding.dart'; |
| |
| 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 ??= _members[0]; |
| for (GestureArenaMember member in _members) { |
| if (member != _winner) |
| member.rejectGesture(pointer); |
| } |
| _winner.acceptGesture(pointer); |
| } |
| |
| @override |
| void rejectGesture(int pointer) { |
| assert(_pointer == pointer); |
| _close(); |
| for (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 new _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 ??= 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. |
| /// |
| /// When gesture recognizers are in a team together, 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. |
| /// |
| /// For example, [Slider] uses this 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. |
| /// |
| /// 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>{}; |
| |
| /// 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, () => new _CombiningGestureArenaMember(this, pointer)); |
| return combiner._add(pointer, member); |
| } |
| } |