| OLD | NEW |
| 1 Sky Event Model | 1 Sky Event Model |
| 2 =============== | 2 =============== |
| 3 | 3 |
| 4 ```javascript | 4 ```dart |
| 5 // EVENTS | 5 SKY MODULE |
| 6 <!-- part of sky:core --> |
| 6 | 7 |
| 7 class Event { | 8 <script> |
| 8 constructor (String type, Boolean bubbles = true, any data = null); // O(1) | 9 abstract class Event { |
| 9 readonly attribute String type; // O(1) | 10 Event({bool bubbles}) : this._bubbles = bubbles; |
| 10 readonly attribute Boolean bubbles; // O(1) | |
| 11 attribute any data; // O(1) | |
| 12 | 11 |
| 13 readonly attribute EventTarget target; // O(1) | 12 bool _bubbles; |
| 14 readonly attribute EventTarget currentTarget; // O(1) | 13 bool get bubbles => _bubbles; |
| 15 attribute Boolean handled; // O(1) | |
| 16 attribute any result; // O(1) | |
| 17 | 14 |
| 18 // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events
when both sides are in the scope? | 15 EventTarget _target; |
| 19 // TODO(ianh): do events get retargetted, e.g. focus when leaving a custom e
lement? | 16 EventTarget get target => _target; |
| 17 |
| 18 EventTarget _currentTarget; |
| 19 EventTarget get currentTarget => _currentTarget; |
| 20 |
| 21 bool handled; // precise semantics depend on the event type, but in general, s
et this when you set result |
| 22 dynamic result; |
| 23 |
| 24 // TODO(ianh): abstract API for doing things at shadow tree boundaries |
| 25 // TODO(ianh): do events get blocked at scope boundaries, e.g. focus events wh
en both sides are in the scope? |
| 26 // TODO(ianh): do events get retargetted, e.g. focus when leaving a custom ele
ment? |
| 27 } |
| 28 |
| 29 class EventTarget { |
| 30 EventTarget() : _eventsController = new DispatcherController<Event>(); |
| 31 |
| 32 Dispatcher get events => _eventsController.dispatcher; |
| 33 EventTarget parentNode; |
| 34 |
| 35 List<EventTarget> getEventDispatchChain() { |
| 36 if (this.parentNode == null) { |
| 37 return [this]; |
| 38 } else { |
| 39 var result = this.parentNode.getEventDispatchChain(); |
| 40 result.add(this); |
| 41 return result; |
| 42 } |
| 20 } | 43 } |
| 21 | 44 |
| 22 // TODO(ianh): decide if we're using this generic Event class and | 45 final DispatcherController _eventsController; |
| 23 // allowing any arbitrary properties to be set on it, or if we're | |
| 24 // going to use subclasses (and drop "type"). If we use subclasses | |
| 25 // then how will declarative event handling work in frameworks? | |
| 26 // (consider that multiple modules can each have their own FooEvent | |
| 27 // class with the same name...) | |
| 28 // | |
| 29 // The advantage of this would be the ability to enforce (or at | |
| 30 // least better catch) incorrect uses of the API, e.g. to make sure | |
| 31 // people don't stomp on themselves with the return value. | |
| 32 | 46 |
| 33 callback EventListener any (Event event); | 47 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 |
| 34 // if the return value is not undefined: | 48 // note: this will throw if any of the listeners threw |
| 35 // assign it to event.result | 49 assert(event != null); // event must be non-null |
| 36 // set event.handled to true | 50 event.handled = false; |
| 37 | 51 event.result = defaultResult; |
| 38 abstract class EventTarget { | 52 event._target = this; |
| 39 any dispatchEvent(Event event, any defaultResult = null); // O(N) in total n
umber of listeners for this type in the chain | 53 var chain = this.getEventDispatchChain(); |
| 40 // sets event.handled to false and event.result to defaultResult | 54 var exceptions = new ExceptionListException<ExceptionListException<Exception
>>(); |
| 41 // makes a record of the event target chain by calling getEventDispatchCha
in() | 55 for (var link in chain) { |
| 42 // sets event.target to this | 56 try { |
| 43 // invokes all the handlers on the chain in turn, at each step setting cur
rentTarget to the EventTarget for that step | 57 link._dispatchEventLocally(event); |
| 44 // returns event.result | 58 } on ExceptionListException<Exception> catch (e) { |
| 45 virtual Array<EventTarget> getEventDispatchChain(); // O(1) // returns [] | 59 exceptions.add(e); |
| 46 void addEventListener(String type, EventListener listener); // O(1) | 60 } |
| 47 void removeEventListener(String type, EventListener listener); // O(N) in ev
ent listeners with that type | 61 } |
| 48 private Array<String> getRegisteredEventListenerTypes(); // O(N) | 62 if (exceptions.length > 0) |
| 49 private Array<EventListener> getRegisteredEventListenersForType(String type)
; // O(N) | 63 throw exceptions; |
| 64 return event.result; |
| 50 } | 65 } |
| 51 | 66 |
| 52 class CustomEventTarget : EventTarget { // implemented in JS | 67 void _dispatchEventLocally(Event event) { |
| 53 constructor (); // O(1) | 68 event._currentTarget = this; |
| 54 attribute EventTarget parentNode; // getter O(1), setter O(N) in height of t
ree, throws if this would make a loop | 69 _eventsController.add(event); |
| 55 | |
| 56 virtual Array<EventTarget> getEventDispatchChain(); // O(N) in height of tre
e // implements EventTarget.getEventDispatchChain() | |
| 57 // let result = []; | |
| 58 // let node = this; | |
| 59 // while (node) { | |
| 60 // result.push(node); | |
| 61 // node = node.parentNode; | |
| 62 // } | |
| 63 // return result; | |
| 64 | |
| 65 // you can inherit from this to make your object into an event target | |
| 66 // or you can inherit from EventTarget and implement your own getEventDispat
chChain() | |
| 67 } | 70 } |
| 71 } |
| 72 </script> |
| 68 ``` | 73 ``` |
| OLD | NEW |