diff --git a/packages/flutter/lib/src/gestures/arena.dart b/packages/flutter/lib/src/gestures/arena.dart index d9fee7f63e..184a314ac2 100644 --- a/packages/flutter/lib/src/gestures/arena.dart +++ b/packages/flutter/lib/src/gestures/arena.dart @@ -46,6 +46,7 @@ class _GestureArenaState { final List members = new List(); bool isOpen = true; bool isHeld = false; + bool hasPendingSweep = false; void add(GestureArenaMember member) { assert(isOpen); @@ -79,8 +80,11 @@ class GestureArena { if (state == null) return; // This arena either never existed or has been resolved. assert(!state.isOpen); - if (state.isHeld) + if (state.isHeld) { + state.hasPendingSweep = true; return; // This arena is being held for a long-lived member + } + _arenas.remove(key); if (!state.members.isEmpty) { // First member wins state.members.first.acceptGesture(key); @@ -88,7 +92,6 @@ class GestureArena { for (int i = 1; i < state.members.length; i++) state.members[i].rejectGesture(key); } - _arenas.remove(key); } /// Prevent the arena from being swept @@ -99,6 +102,18 @@ class GestureArena { state.isHeld = true; } + /// Release a hold, allowing the arena to be swept + /// If a sweep was attempted on a held arena, the sweep will be done + /// on release + void release(Object key) { + _GestureArenaState state = _arenas[key]; + if (state == null) + return; // This arena either never existed or has been resolved. + state.isHeld = false; + if (state.hasPendingSweep) + sweep(key); + } + void _tryToResolveArena(Object key, _GestureArenaState state) { assert(_arenas[key] == state); assert(!state.isOpen); diff --git a/packages/unit/test/gestures/arena_test.dart b/packages/unit/test/gestures/arena_test.dart index ccc861b50a..4f0d062fd6 100644 --- a/packages/unit/test/gestures/arena_test.dart +++ b/packages/unit/test/gestures/arena_test.dart @@ -66,4 +66,176 @@ void main() { expect(secondAcceptRan, isFalse); expect(secondRejectRan, isTrue); }); + + test('Should win by sweep', () { + GestureArena arena = new GestureArena(); + + int primaryKey = 4; + bool firstAcceptRan = false; + bool firstRejectRan = false; + bool secondAcceptRan = false; + bool secondRejectRan = false; + + TestGestureArenaMember first = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + firstAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + firstRejectRan = true; + } + ); + + TestGestureArenaMember second = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + secondAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + secondRejectRan = true; + } + ); + + arena.add(primaryKey, first); + arena.add(primaryKey, second); + arena.close(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.sweep(primaryKey); + + expect(firstAcceptRan, isTrue); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isTrue); + }); + + test('Should win on release after hold sweep release', () { + GestureArena arena = new GestureArena(); + + int primaryKey = 4; + bool firstAcceptRan = false; + bool firstRejectRan = false; + bool secondAcceptRan = false; + bool secondRejectRan = false; + + TestGestureArenaMember first = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + firstAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + firstRejectRan = true; + } + ); + + TestGestureArenaMember second = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + secondAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + secondRejectRan = true; + } + ); + + arena.add(primaryKey, first); + arena.add(primaryKey, second); + arena.close(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.hold(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.sweep(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.release(primaryKey); + + expect(firstAcceptRan, isTrue); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isTrue); + }); + + test('Should win on sweep after hold release sweep', () { + GestureArena arena = new GestureArena(); + + int primaryKey = 4; + bool firstAcceptRan = false; + bool firstRejectRan = false; + bool secondAcceptRan = false; + bool secondRejectRan = false; + + TestGestureArenaMember first = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + firstAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + firstRejectRan = true; + } + ); + + TestGestureArenaMember second = new TestGestureArenaMember( + onAcceptGesture: (int key) { + expect(key, equals(primaryKey)); + secondAcceptRan = true; + }, + onRejectGesture: (int key) { + expect(key, equals(primaryKey)); + secondRejectRan = true; + } + ); + + arena.add(primaryKey, first); + arena.add(primaryKey, second); + arena.close(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.hold(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.release(primaryKey); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + arena.sweep(primaryKey); + + expect(firstAcceptRan, isTrue); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isTrue); + }); }