Index: sky/framework/fn.dart |
diff --git a/sky/framework/fn.dart b/sky/framework/fn.dart |
index 4a0f980a049295cd7f4bc2d7866185907ed38b4a..8288d83862620abbfe8f53252b2f94bf66a2d6aa 100644 |
--- a/sky/framework/fn.dart |
+++ b/sky/framework/fn.dart |
@@ -22,26 +22,6 @@ bool _initIsInCheckedMode() { |
final bool _isInCheckedMode = _initIsInCheckedMode(); |
final bool _shouldLogRenderDuration = false; |
-class EventHandler { |
- final String type; |
- final sky.EventListener listener; |
- |
- EventHandler(this.type, this.listener); |
-} |
- |
-class EventMap { |
- final List<EventHandler> _handlers = new List<EventHandler>(); |
- |
- void listen(String type, sky.EventListener listener) { |
- assert(listener != null); |
- _handlers.add(new EventHandler(type, listener)); |
- } |
- |
- void addAll(EventMap events) { |
- _handlers.addAll(events._handlers); |
- } |
-} |
- |
class Style { |
final String _className; |
static final Map<String, Style> _cache = new HashMap<String, Style>(); |
@@ -91,10 +71,6 @@ abstract class Node { |
sky.Node _root; |
bool _defunct = false; |
- // TODO(abarth): Both Elements and Components have |events| but |Text| |
- // doesn't. Should we add a common base class to contain |events|? |
- final EventMap events = new EventMap(); |
- |
Node({ Object key }) { |
_key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
} |
@@ -138,51 +114,6 @@ abstract class Node { |
assert(node._root is sky.Node); |
return node; |
} |
- |
- void _syncEvents(EventMap oldEventMap) { |
- List<EventHandler> newHandlers = events._handlers; |
- int newStartIndex = 0; |
- int newEndIndex = newHandlers.length; |
- |
- List<EventHandler> oldHandlers = oldEventMap._handlers; |
- int oldStartIndex = 0; |
- int oldEndIndex = oldHandlers.length; |
- |
- // Skip over leading handlers that match. |
- while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { |
- EventHandler newHandler = newHandlers[newStartIndex]; |
- EventHandler oldHandler = oldHandlers[oldStartIndex]; |
- if (newHandler.type != oldHandler.type |
- || newHandler.listener != oldHandler.listener) |
- break; |
- ++newStartIndex; |
- ++oldStartIndex; |
- } |
- |
- // Skip over trailing handlers that match. |
- while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { |
- EventHandler newHandler = newHandlers[newEndIndex - 1]; |
- EventHandler oldHandler = oldHandlers[oldEndIndex - 1]; |
- if (newHandler.type != oldHandler.type |
- || newHandler.listener != oldHandler.listener) |
- break; |
- --newEndIndex; |
- --oldEndIndex; |
- } |
- |
- sky.Element root = _root as sky.Element; |
- |
- for (int i = oldStartIndex; i < oldEndIndex; ++i) { |
- EventHandler oldHandler = oldHandlers[i]; |
- root.removeEventListener(oldHandler.type, oldHandler.listener); |
- } |
- |
- for (int i = newStartIndex; i < newEndIndex; ++i) { |
- EventHandler newHandler = newHandlers[i]; |
- root.addEventListener(newHandler.type, newHandler.listener); |
- } |
- } |
- |
} |
/* |
@@ -193,6 +124,11 @@ abstract class Node { |
*/ |
abstract class RenderNode extends Node { |
+ static Map<sky.Node, RenderNode> _nodeMap = |
abarth-chromium
2015/03/19 14:19:59
final
rafaelw
2015/03/19 15:03:50
Done.
|
+ new HashMap<sky.Node, RenderNode>(); |
+ |
+ static RenderNode _getMounted(sky.Node node) => _nodeMap[node]; |
+ |
RenderNode({ Object key }) : super(key: key); |
RenderNode get _emptyNode; |
@@ -208,6 +144,7 @@ abstract class RenderNode extends Node { |
_root = old._root; |
} |
+ _nodeMap[_root] = this; |
_syncNode(old); |
} |
@@ -216,6 +153,132 @@ abstract class RenderNode extends Node { |
void _remove() { |
assert(_root != null); |
_root.remove(); |
+ _nodeMap.remove(_root); |
+ super._remove(); |
+ } |
+} |
+ |
+typedef GestureEventListener(sky.GestureEvent e); |
+typedef PointerEventListener(sky.PointerEvent e); |
+typedef EventListener(sky.Event e); |
+ |
+class EventTarget extends Node { |
+ Node content; |
+ final Map<String, sky.EventListener> listeners; |
+ bool _docListenersRegistered = false; |
abarth-chromium
2015/03/19 14:19:59
style nit: _docListenersRegistered -> _haveRegiste
rafaelw
2015/03/19 15:03:50
Removed this.
|
+ |
+ static final Set<String> _registeredEvents = new HashSet<String>(); |
+ |
+ static Map<String, sky.EventListener> _createListeners({ |
+ EventListener onWheel, |
+ GestureEventListener onGestureFlingCancel, |
+ GestureEventListener onGestureFlingStart, |
+ GestureEventListener onGestureScrollStart, |
+ GestureEventListener onGestureScrollUpdate, |
+ GestureEventListener onGestureTap, |
+ PointerEventListener onPointerCancel, |
+ PointerEventListener onPointerDown, |
+ PointerEventListener onPointerMove, |
+ PointerEventListener onPointerUp, |
+ Map<String, sky.EventListener> custom |
+ }) { |
+ var listeners = custom != null ? custom |
+ : new HashMap<String, sky.EventListener>(); |
+ |
+ if (onWheel != null) |
+ listeners['wheel'] = onWheel; |
abarth-chromium
2015/03/19 14:19:59
This if-cascade is kind of ugly, but I think you'r
rafaelw
2015/03/19 15:03:50
Yeah. We could also do this with reflection (i.e.
|
+ if (onGestureFlingCancel != null) |
+ listeners['gestureflingcancel'] = onGestureFlingCancel; |
+ if (onGestureFlingStart != null) |
+ listeners['gestureflingstart'] = onGestureFlingStart; |
+ if (onGestureScrollStart != null) |
+ listeners['gesturescrollstart'] = onGestureScrollStart; |
+ if (onGestureScrollUpdate != null) |
+ listeners['gesturescrollupdate'] = onGestureScrollUpdate; |
+ if (onGestureTap != null) |
+ listeners['gesturetap'] = onGestureTap; |
+ if (onPointerCancel != null) |
+ listeners['pointercancel'] = onPointerCancel; |
+ if (onPointerDown != null) |
+ listeners['pointerdown'] = onPointerDown; |
+ if (onPointerMove != null) |
+ listeners['pointermove'] = onPointerMove; |
+ if (onPointerUp != null) |
+ listeners['pointerup'] = onPointerUp; |
abarth-chromium
2015/03/19 14:19:59
Do you actually want to mutate the |custom| argume
rafaelw
2015/03/19 15:03:50
Done.
|
+ |
+ return listeners; |
+ } |
+ |
+ EventTarget(Node content, { |
+ EventListener onWheel, |
+ GestureEventListener onGestureFlingCancel, |
+ GestureEventListener onGestureFlingStart, |
+ GestureEventListener onGestureScrollStart, |
+ GestureEventListener onGestureScrollUpdate, |
+ GestureEventListener onGestureTap, |
+ PointerEventListener onPointerCancel, |
+ PointerEventListener onPointerDown, |
+ PointerEventListener onPointerMove, |
+ PointerEventListener onPointerUp, |
+ Map<String, sky.EventListener> custom |
+ }) : this.content = content, |
abarth-chromium
2015/03/19 14:19:59
You can just write |this.content| in the argument
rafaelw
2015/03/19 15:03:50
Not if you want to use content in the initializer
|
+ listeners = _createListeners( |
+ onWheel: onWheel, |
+ onGestureFlingCancel: onGestureFlingCancel, |
+ onGestureFlingStart: onGestureFlingStart, |
+ onGestureScrollUpdate: onGestureScrollUpdate, |
+ onGestureScrollStart: onGestureScrollStart, |
+ onGestureTap: onGestureTap, |
+ onPointerCancel: onPointerCancel, |
+ onPointerDown: onPointerDown, |
+ onPointerMove: onPointerMove, |
+ onPointerUp: onPointerUp, |
+ custom: custom |
+ ), |
+ super(key: content._key); |
+ |
+ void _handleEvent(sky.Event e) { |
+ sky.EventListener listener = listeners[e.type]; |
+ if (listener != null) { |
+ listener(e); |
+ } |
+ } |
+ |
+ static void _dispatchEvent(sky.Event e) { |
+ Node target = RenderNode._getMounted(e.target); |
+ |
+ // TODO(rafaelw): StopPropagation? |
+ while (target != null) { |
+ if (target is EventTarget) { |
+ (target as EventTarget)._handleEvent(e); |
+ } |
+ |
+ target = target._parent; |
+ } |
+ } |
+ |
+ static void _ensureDocListener(String eventType) { |
abarth-chromium
2015/03/19 14:19:59
style nit: s/Doc/Document/
rafaelw
2015/03/19 15:03:50
Done.
|
+ if (_registeredEvents.add(eventType)) { |
+ sky.document.addEventListener(eventType, _dispatchEvent); |
+ } |
+ } |
abarth-chromium
2015/03/19 14:19:59
Something is off with your indentation here.
rafaelw
2015/03/19 15:03:50
Done.
|
+ |
+ void _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { |
+ if (!_docListenersRegistered) { |
+ for (var type in listeners.keys) { |
+ _ensureDocListener(type); |
+ } |
+ |
+ _docListenersRegistered = true; |
abarth-chromium
2015/03/19 14:19:59
Do we ever sync the same Node twice? It seems lik
rafaelw
2015/03/19 15:03:50
No, I think you're right. This buys us very little
|
+ } |
+ |
+ Node oldContent = old == null ? null : (old as EventTarget).content; |
+ content = _syncChild(content, oldContent , host, insertBefore); |
abarth-chromium
2015/03/19 14:19:59
s/ ,/,/
rafaelw
2015/03/19 15:03:51
Done.
|
+ _root = content._root; |
+ } |
+ |
+ void _remove() { |
+ content._remove(); |
super._remove(); |
} |
} |
@@ -299,8 +362,6 @@ abstract class Element extends RenderNode { |
Element oldElement = old as Element; |
sky.Element root = _root as sky.Element; |
- _syncEvents(oldElement.events); |
- |
if (_class != oldElement._class) |
root.setAttribute('class', _class); |
@@ -581,8 +642,6 @@ void _scheduleComponentForRender(Component c) { |
} |
} |
-EventMap _emptyEventMap = new EventMap(); |
- |
abstract class Component extends Node { |
bool get _isBuilding => _currentlyBuilding == this; |
bool _dirty = true; |
@@ -666,9 +725,6 @@ abstract class Component extends Node { |
_built = _syncChild(_built, oldBuilt, host, insertBefore); |
_dirty = false; |
_root = _built._root; |
- |
- _built.events.addAll(events); |
- _syncEvents(oldComponent != null ? oldComponent.events : _emptyEventMap); |
} |
void _buildIfDirty() { |