From 03fffdfef9f1f08aaf9b026bcb318332e688e620 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 27 Aug 2015 17:07:24 -0700 Subject: [PATCH] Add GestureArena In the GestureArena, gestures will fight it out to see which is the real ultimate gesture. --- sky/packages/sky/lib/gestures/arena.dart | 64 ++++++++++++++++++++++ sky/unit/test/gestures/arena_test.dart | 68 ++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 sky/packages/sky/lib/gestures/arena.dart create mode 100644 sky/unit/test/gestures/arena_test.dart diff --git a/sky/packages/sky/lib/gestures/arena.dart b/sky/packages/sky/lib/gestures/arena.dart new file mode 100644 index 00000000000..360177d6802 --- /dev/null +++ b/sky/packages/sky/lib/gestures/arena.dart @@ -0,0 +1,64 @@ +// 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. + +enum GestureDisposition { + accepted, + rejected +} + +abstract class GestureArenaMember { + /// Called when this member wins the arena. + void acceptGesture(Object key); + + /// Called when this member loses the arena. + void rejectGesture(Object key); +} + +class GestureArenaEntry { + GestureArenaEntry._(this._arena, this._key, this._member); + + final GestureArena _arena; + final Object _key; + final GestureArenaMember _member; + + /// Call this member to claim victory (with accepted) or admit defeat (with rejected). + void resolve(GestureDisposition disposition) { + _arena._resolve(_key, _member, disposition); + } +} + +/// The first member to accept or the last member to not to reject wins. +class GestureArena { + final Map> _arenas = new Map>(); + + GestureArenaEntry add(Object key, GestureArenaMember member) { + List members = _arenas.putIfAbsent(key, () => new List()); + members.add(member); + return new GestureArenaEntry._(this, key, member); + } + + void _resolve(Object key, GestureArenaMember member, GestureDisposition disposition) { + List members = _arenas[key]; + assert(members != null); + assert(members.contains(member)); + if (disposition == GestureDisposition.rejected) { + members.remove(member); + if (members.length == 1) { + _arenas.remove(key); + members.first.acceptGesture(key); + } else if (members.isEmpty) { + _arenas.remove(key); + } + } else { + assert(disposition == GestureDisposition.accepted); + List members = _arenas[key]; + _arenas.remove(key); + for (GestureArenaMember rejectedMember in members) { + if (rejectedMember != member) + rejectedMember.rejectGesture(key); + } + member.acceptGesture(key); + } + } +} diff --git a/sky/unit/test/gestures/arena_test.dart b/sky/unit/test/gestures/arena_test.dart new file mode 100644 index 00000000000..5b188c92078 --- /dev/null +++ b/sky/unit/test/gestures/arena_test.dart @@ -0,0 +1,68 @@ +import 'package:sky/gestures/arena.dart'; +import 'package:test/test.dart'; + +typedef void GestureArenaCallback(Object key); + +class TestGestureArenaMember extends GestureArenaMember { + TestGestureArenaMember({ this.onAcceptGesture, this.onRejectGesture }); + + final GestureArenaCallback onAcceptGesture; + final GestureArenaCallback onRejectGesture; + + void acceptGesture(Object key) { + onAcceptGesture(key); + } + + void rejectGesture(Object key) { + onRejectGesture(key); + } +} + +void main() { + test('Should win by accepting', () { + 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; + } + ); + + GestureArenaEntry firstEntry = arena.add(primaryKey, first); + arena.add(primaryKey, second); + + expect(firstAcceptRan, isFalse); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isFalse); + + firstEntry.resolve(GestureDisposition.accepted); + + expect(firstAcceptRan, isTrue); + expect(firstRejectRan, isFalse); + expect(secondAcceptRan, isFalse); + expect(secondRejectRan, isTrue); + }); +}