| OLD | NEW |
| 1 Sky Event Model | 1 Sky Event Model |
| 2 =============== | 2 =============== |
| 3 | 3 |
| 4 ```dart | 4 ```dart |
| 5 SKY MODULE | 5 SKY MODULE |
| 6 <!-- part of sky:core --> | 6 <!-- part of sky:core --> |
| 7 | 7 |
| 8 <script> | 8 <script> |
| 9 abstract class Event<ReturnType> { | 9 abstract class Event<ReturnType> { |
| 10 Event({bool this.bubbles}); | 10 Event() { init(); } |
| 11 void init() { } |
| 11 | 12 |
| 12 final bool bubbles; | 13 bool get bubbles; |
| 13 | 14 |
| 14 EventTarget _target; | 15 EventTarget _target; |
| 15 EventTarget get target => _target; | 16 EventTarget get target => _target; |
| 16 | 17 |
| 17 EventTarget _currentTarget; | 18 EventTarget _currentTarget; |
| 18 EventTarget get currentTarget => _currentTarget; | 19 EventTarget get currentTarget => _currentTarget; |
| 19 | 20 |
| 20 bool handled; // precise semantics depend on the event type, but in general, s
et this when you set result | 21 bool handled; // precise semantics depend on the event type, but in general, s
et this when you set result |
| 21 ReturnType result; | 22 ReturnType result; |
| 22 | 23 |
| 24 bool resultIsCompatible(dynamic candidate) => candidate is ReturnType; |
| 25 |
| 23 // TODO(ianh): abstract API for doing things at shadow tree boundaries | 26 // TODO(ianh): abstract API for doing things at shadow tree boundaries |
| 24 // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events wh
en both sides are in the scope? | 27 // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events wh
en both sides are in the scope? |
| 25 // TODO(ianh): do events get retargetted, e.g. focus when leaving a custom ele
ment? | 28 // TODO(ianh): do events get retargetted, e.g. focus when leaving a custom ele
ment? |
| 26 } | 29 } |
| 27 | 30 |
| 28 class EventTarget { | 31 class EventTarget { |
| 29 EventTarget() : _eventsController = new DispatcherController<Event>(); | 32 EventTarget() : _eventsController = new DispatcherController<Event>(); |
| 30 | 33 |
| 31 Dispatcher get events => _eventsController.dispatcher; | 34 Dispatcher get events => _eventsController.dispatcher; |
| 32 EventTarget parentNode; | 35 EventTarget parentNode; |
| 33 | 36 |
| 34 List<EventTarget> getEventDispatchChain() { | 37 List<EventTarget> getEventDispatchChain() { |
| 35 if (this.parentNode == null) { | 38 if (this.parentNode == null) { |
| 36 return [this]; | 39 return [this]; |
| 37 } else { | 40 } else { |
| 38 var result = this.parentNode.getEventDispatchChain(); | 41 var result = this.parentNode.getEventDispatchChain(); |
| 39 result.add(this); | 42 result.insert(0, this); |
| 40 return result; | 43 return result; |
| 41 } | 44 } |
| 42 } | 45 } |
| 43 | 46 |
| 44 final DispatcherController _eventsController; | 47 final DispatcherController _eventsController; |
| 45 | 48 |
| 46 dynamic dispatchEvent(Event event, { defaultResult: null }) { // O(N*M) where
N is the length of the chain and M is the average number of listeners per link i
n the chain | 49 dynamic dispatchEvent(Event event, { defaultResult: null }) { // O(N*M) where
N is the length of the chain and M is the average number of listeners per link i
n the chain |
| 47 // note: this will throw an ExceptionListException<ExceptionListException> i
f any of the listeners threw | 50 // note: this will throw an ExceptionListException<ExceptionListException> i
f any of the listeners threw |
| 48 assert(event != null); // event must be non-null | 51 assert(event != null); // event must be non-null |
| 49 event.handled = false; | 52 event.handled = false; |
| 53 assert(event.resultIsCompatible(defaultResult)); |
| 50 event.result = defaultResult; | 54 event.result = defaultResult; |
| 51 event._target = this; | 55 event._target = this; |
| 52 var chain = this.getEventDispatchChain(); | 56 var chain; |
| 57 if (event.bubbles) |
| 58 chain = this.getEventDispatchChain(); |
| 59 else |
| 60 chain = [this]; |
| 53 var exceptions = new ExceptionListException<ExceptionListException>(); | 61 var exceptions = new ExceptionListException<ExceptionListException>(); |
| 54 for (var link in chain) { | 62 for (var link in chain) { |
| 55 try { | 63 try { |
| 56 link._dispatchEventLocally(event); | 64 link._dispatchEventLocally(event); |
| 57 } on ExceptionListException catch (e) { | 65 } on ExceptionListException catch (e) { |
| 58 exceptions.add(e); | 66 exceptions.add(e); |
| 59 } | 67 } |
| 60 } | 68 } |
| 61 if (exceptions.length > 0) | 69 if (exceptions.length > 0) |
| 62 throw exceptions; | 70 throw exceptions; |
| 63 return event.result; | 71 return event.result; |
| 64 } | 72 } |
| 65 | 73 |
| 66 void _dispatchEventLocally(Event event) { | 74 void _dispatchEventLocally(Event event) { |
| 67 event._currentTarget = this; | 75 event._currentTarget = this; |
| 68 _eventsController.add(event); | 76 _eventsController.add(event); |
| 69 } | 77 } |
| 70 } | 78 } |
| 71 </script> | 79 </script> |
| 72 ``` | 80 ``` |
| OLD | NEW |