| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 library fn; | 5 library fn; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:sky' as sky; | 9 import 'dart:sky' as sky; |
| 10 import 'reflect.dart' as reflect; | 10 import 'reflect.dart' as reflect; |
| 11 | 11 |
| 12 bool _initIsInCheckedMode() { | 12 bool _initIsInCheckedMode() { |
| 13 String testFn(i) { double d = i; return d.toString(); } | 13 String testFn(i) { double d = i; return d.toString(); } |
| 14 try { | 14 try { |
| 15 testFn('not a double'); | 15 testFn('not a double'); |
| 16 } catch (ex) { | 16 } catch (ex) { |
| 17 return true; | 17 return true; |
| 18 } | 18 } |
| 19 return false; | 19 return false; |
| 20 } | 20 } |
| 21 | 21 |
| 22 final bool _isInCheckedMode = _initIsInCheckedMode(); | 22 final bool _isInCheckedMode = _initIsInCheckedMode(); |
| 23 final bool _shouldLogRenderDuration = false; | 23 final bool _shouldLogRenderDuration = true; |
| 24 | |
| 25 class EventHandler { | |
| 26 final String type; | |
| 27 final sky.EventListener listener; | |
| 28 | |
| 29 EventHandler(this.type, this.listener); | |
| 30 } | |
| 31 | |
| 32 class EventMap { | |
| 33 final List<EventHandler> _handlers = new List<EventHandler>(); | |
| 34 | |
| 35 void listen(String type, sky.EventListener listener) { | |
| 36 assert(listener != null); | |
| 37 _handlers.add(new EventHandler(type, listener)); | |
| 38 } | |
| 39 | |
| 40 void addAll(EventMap events) { | |
| 41 _handlers.addAll(events._handlers); | |
| 42 } | |
| 43 } | |
| 44 | 24 |
| 45 class Style { | 25 class Style { |
| 46 final String _className; | 26 final String _className; |
| 47 static final Map<String, Style> _cache = new HashMap<String, Style>(); | 27 static final Map<String, Style> _cache = new HashMap<String, Style>(); |
| 48 | 28 |
| 49 static int _nextStyleId = 1; | 29 static int _nextStyleId = 1; |
| 50 | 30 |
| 51 static String _getNextClassName() { return "style${_nextStyleId++}"; } | 31 static String _getNextClassName() { return "style${_nextStyleId++}"; } |
| 52 | 32 |
| 53 Style extend(Style other) { | 33 Style extend(Style other) { |
| (...skipping 30 matching lines...) Expand all Loading... |
| 84 /* | 64 /* |
| 85 * All Effen nodes derive from Node. All nodes have a _parent, a _key and | 65 * All Effen nodes derive from Node. All nodes have a _parent, a _key and |
| 86 * can be sync'd. | 66 * can be sync'd. |
| 87 */ | 67 */ |
| 88 abstract class Node { | 68 abstract class Node { |
| 89 String _key; | 69 String _key; |
| 90 Node _parent; | 70 Node _parent; |
| 91 sky.Node _root; | 71 sky.Node _root; |
| 92 bool _defunct = false; | 72 bool _defunct = false; |
| 93 | 73 |
| 94 // TODO(abarth): Both Elements and Components have |events| but |Text| | |
| 95 // doesn't. Should we add a common base class to contain |events|? | |
| 96 final EventMap events = new EventMap(); | |
| 97 | |
| 98 Node({ Object key }) { | 74 Node({ Object key }) { |
| 99 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; | 75 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
| 100 } | 76 } |
| 101 | 77 |
| 102 // Subclasses which implements Nodes that become stateful may return true | 78 // Subclasses which implements Nodes that become stateful may return true |
| 103 // if the |old| node has become stateful and should be retained. | 79 // if the |old| node has become stateful and should be retained. |
| 104 bool _willSync(Node old) => false; | 80 bool _willSync(Node old) => false; |
| 105 | 81 |
| 106 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore); | 82 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore); |
| 107 | 83 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 131 } | 107 } |
| 132 | 108 |
| 133 node._parent = this; | 109 node._parent = this; |
| 134 node._sync(oldNode, host, insertBefore); | 110 node._sync(oldNode, host, insertBefore); |
| 135 if (oldNode != null) | 111 if (oldNode != null) |
| 136 oldNode._defunct = true; | 112 oldNode._defunct = true; |
| 137 | 113 |
| 138 assert(node._root is sky.Node); | 114 assert(node._root is sky.Node); |
| 139 return node; | 115 return node; |
| 140 } | 116 } |
| 141 | |
| 142 void _syncEvents(EventMap oldEventMap) { | |
| 143 List<EventHandler> newHandlers = events._handlers; | |
| 144 int newStartIndex = 0; | |
| 145 int newEndIndex = newHandlers.length; | |
| 146 | |
| 147 List<EventHandler> oldHandlers = oldEventMap._handlers; | |
| 148 int oldStartIndex = 0; | |
| 149 int oldEndIndex = oldHandlers.length; | |
| 150 | |
| 151 // Skip over leading handlers that match. | |
| 152 while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { | |
| 153 EventHandler newHandler = newHandlers[newStartIndex]; | |
| 154 EventHandler oldHandler = oldHandlers[oldStartIndex]; | |
| 155 if (newHandler.type != oldHandler.type | |
| 156 || newHandler.listener != oldHandler.listener) | |
| 157 break; | |
| 158 ++newStartIndex; | |
| 159 ++oldStartIndex; | |
| 160 } | |
| 161 | |
| 162 // Skip over trailing handlers that match. | |
| 163 while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { | |
| 164 EventHandler newHandler = newHandlers[newEndIndex - 1]; | |
| 165 EventHandler oldHandler = oldHandlers[oldEndIndex - 1]; | |
| 166 if (newHandler.type != oldHandler.type | |
| 167 || newHandler.listener != oldHandler.listener) | |
| 168 break; | |
| 169 --newEndIndex; | |
| 170 --oldEndIndex; | |
| 171 } | |
| 172 | |
| 173 sky.Element root = _root as sky.Element; | |
| 174 | |
| 175 for (int i = oldStartIndex; i < oldEndIndex; ++i) { | |
| 176 EventHandler oldHandler = oldHandlers[i]; | |
| 177 root.removeEventListener(oldHandler.type, oldHandler.listener); | |
| 178 } | |
| 179 | |
| 180 for (int i = newStartIndex; i < newEndIndex; ++i) { | |
| 181 EventHandler newHandler = newHandlers[i]; | |
| 182 root.addEventListener(newHandler.type, newHandler.listener); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 } | 117 } |
| 187 | 118 |
| 188 /* | 119 /* |
| 189 * RenderNodes correspond to a desired state of a sky.Node. They are fully | 120 * RenderNodes correspond to a desired state of a sky.Node. They are fully |
| 190 * immutable, with one exception: A Node which is a Component which lives within | 121 * immutable, with one exception: A Node which is a Component which lives within |
| 191 * an Element's children list, may be replaced with the "old" instance if it | 122 * an Element's children list, may be replaced with the "old" instance if it |
| 192 * has become stateful. | 123 * has become stateful. |
| 193 */ | 124 */ |
| 194 abstract class RenderNode extends Node { | 125 abstract class RenderNode extends Node { |
| 195 | 126 |
| 127 static final Map<sky.Node, RenderNode> _nodeMap = |
| 128 new HashMap<sky.Node, RenderNode>(); |
| 129 |
| 130 static RenderNode _getMounted(sky.Node node) => _nodeMap[node]; |
| 131 |
| 196 RenderNode({ Object key }) : super(key: key); | 132 RenderNode({ Object key }) : super(key: key); |
| 197 | 133 |
| 198 RenderNode get _emptyNode; | 134 RenderNode get _emptyNode; |
| 199 | 135 |
| 200 sky.Node _createNode(); | 136 sky.Node _createNode(); |
| 201 | 137 |
| 202 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { | 138 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { |
| 203 if (old == null) { | 139 if (old == null) { |
| 204 _root = _createNode(); | 140 _root = _createNode(); |
| 205 _parentInsertBefore(host, _root, insertBefore); | 141 _parentInsertBefore(host, _root, insertBefore); |
| 206 old = _emptyNode; | 142 old = _emptyNode; |
| 207 } else { | 143 } else { |
| 208 _root = old._root; | 144 _root = old._root; |
| 209 } | 145 } |
| 210 | 146 |
| 147 _nodeMap[_root] = this; |
| 211 _syncNode(old); | 148 _syncNode(old); |
| 212 } | 149 } |
| 213 | 150 |
| 214 void _syncNode(RenderNode old); | 151 void _syncNode(RenderNode old); |
| 215 | 152 |
| 216 void _remove() { | 153 void _remove() { |
| 217 assert(_root != null); | 154 assert(_root != null); |
| 218 _root.remove(); | 155 _root.remove(); |
| 156 _nodeMap.remove(_root); |
| 219 super._remove(); | 157 super._remove(); |
| 220 } | 158 } |
| 221 } | 159 } |
| 160 |
| 161 typedef GestureEventListener(sky.GestureEvent e); |
| 162 typedef PointerEventListener(sky.PointerEvent e); |
| 163 typedef EventListener(sky.Event e); |
| 164 |
| 165 class EventTarget extends Node { |
| 166 Node content; |
| 167 final Map<String, sky.EventListener> listeners; |
| 168 |
| 169 static final Set<String> _registeredEvents = new HashSet<String>(); |
| 170 |
| 171 static Map<String, sky.EventListener> _createListeners({ |
| 172 EventListener onWheel, |
| 173 GestureEventListener onGestureFlingCancel, |
| 174 GestureEventListener onGestureFlingStart, |
| 175 GestureEventListener onGestureScrollStart, |
| 176 GestureEventListener onGestureScrollUpdate, |
| 177 GestureEventListener onGestureTap, |
| 178 PointerEventListener onPointerCancel, |
| 179 PointerEventListener onPointerDown, |
| 180 PointerEventListener onPointerMove, |
| 181 PointerEventListener onPointerUp, |
| 182 Map<String, sky.EventListener> custom |
| 183 }) { |
| 184 var listeners = custom != null ? |
| 185 new HashMap<String, sky.EventListener>.from(custom) : |
| 186 new HashMap<String, sky.EventListener>(); |
| 187 |
| 188 if (onWheel != null) |
| 189 listeners['wheel'] = onWheel; |
| 190 if (onGestureFlingCancel != null) |
| 191 listeners['gestureflingcancel'] = onGestureFlingCancel; |
| 192 if (onGestureFlingStart != null) |
| 193 listeners['gestureflingstart'] = onGestureFlingStart; |
| 194 if (onGestureScrollStart != null) |
| 195 listeners['gesturescrollstart'] = onGestureScrollStart; |
| 196 if (onGestureScrollUpdate != null) |
| 197 listeners['gesturescrollupdate'] = onGestureScrollUpdate; |
| 198 if (onGestureTap != null) |
| 199 listeners['gesturetap'] = onGestureTap; |
| 200 if (onPointerCancel != null) |
| 201 listeners['pointercancel'] = onPointerCancel; |
| 202 if (onPointerDown != null) |
| 203 listeners['pointerdown'] = onPointerDown; |
| 204 if (onPointerMove != null) |
| 205 listeners['pointermove'] = onPointerMove; |
| 206 if (onPointerUp != null) |
| 207 listeners['pointerup'] = onPointerUp; |
| 208 |
| 209 return listeners; |
| 210 } |
| 211 |
| 212 EventTarget(Node content, { |
| 213 EventListener onWheel, |
| 214 GestureEventListener onGestureFlingCancel, |
| 215 GestureEventListener onGestureFlingStart, |
| 216 GestureEventListener onGestureScrollStart, |
| 217 GestureEventListener onGestureScrollUpdate, |
| 218 GestureEventListener onGestureTap, |
| 219 PointerEventListener onPointerCancel, |
| 220 PointerEventListener onPointerDown, |
| 221 PointerEventListener onPointerMove, |
| 222 PointerEventListener onPointerUp, |
| 223 Map<String, sky.EventListener> custom |
| 224 }) : this.content = content, |
| 225 listeners = _createListeners( |
| 226 onWheel: onWheel, |
| 227 onGestureFlingCancel: onGestureFlingCancel, |
| 228 onGestureFlingStart: onGestureFlingStart, |
| 229 onGestureScrollUpdate: onGestureScrollUpdate, |
| 230 onGestureScrollStart: onGestureScrollStart, |
| 231 onGestureTap: onGestureTap, |
| 232 onPointerCancel: onPointerCancel, |
| 233 onPointerDown: onPointerDown, |
| 234 onPointerMove: onPointerMove, |
| 235 onPointerUp: onPointerUp, |
| 236 custom: custom |
| 237 ), |
| 238 super(key: content._key); |
| 239 |
| 240 void _handleEvent(sky.Event e) { |
| 241 sky.EventListener listener = listeners[e.type]; |
| 242 if (listener != null) { |
| 243 listener(e); |
| 244 } |
| 245 } |
| 246 |
| 247 static void _dispatchEvent(sky.Event e) { |
| 248 Node target = RenderNode._getMounted(e.target); |
| 249 |
| 250 // TODO(rafaelw): StopPropagation? |
| 251 while (target != null) { |
| 252 if (target is EventTarget) { |
| 253 (target as EventTarget)._handleEvent(e); |
| 254 } |
| 255 |
| 256 target = target._parent; |
| 257 } |
| 258 } |
| 259 |
| 260 static void _ensureDocumentListener(String eventType) { |
| 261 if (_registeredEvents.add(eventType)) { |
| 262 sky.document.addEventListener(eventType, _dispatchEvent); |
| 263 } |
| 264 } |
| 265 |
| 266 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { |
| 267 for (var type in listeners.keys) { |
| 268 _ensureDocumentListener(type); |
| 269 } |
| 270 |
| 271 Node oldContent = old == null ? null : (old as EventTarget).content; |
| 272 content = _syncChild(content, oldContent , host, insertBefore); |
| 273 _root = content._root; |
| 274 } |
| 275 |
| 276 void _remove() { |
| 277 content._remove(); |
| 278 super._remove(); |
| 279 } |
| 280 } |
| 222 | 281 |
| 223 class Text extends RenderNode { | 282 class Text extends RenderNode { |
| 224 final String data; | 283 final String data; |
| 225 | 284 |
| 226 // Text nodes are special cases of having non-unique keys (which don't need | 285 // Text nodes are special cases of having non-unique keys (which don't need |
| 227 // to be assigned as part of the API). Since they are unique in not having | 286 // to be assigned as part of the API). Since they are unique in not having |
| 228 // children, there's little point to reordering, so we always just re-assign | 287 // children, there's little point to reordering, so we always just re-assign |
| 229 // the data. | 288 // the data. |
| 230 Text(this.data) : super(key:'*text*'); | 289 Text(this.data) : super(key:'*text*'); |
| 231 | 290 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 throw '''If multiple (non-Text) nodes of the same type exist as children | 351 throw '''If multiple (non-Text) nodes of the same type exist as children |
| 293 of another node, they must have unique keys.'''; | 352 of another node, they must have unique keys.'''; |
| 294 } | 353 } |
| 295 } | 354 } |
| 296 } | 355 } |
| 297 | 356 |
| 298 void _syncNode(RenderNode old) { | 357 void _syncNode(RenderNode old) { |
| 299 Element oldElement = old as Element; | 358 Element oldElement = old as Element; |
| 300 sky.Element root = _root as sky.Element; | 359 sky.Element root = _root as sky.Element; |
| 301 | 360 |
| 302 _syncEvents(oldElement.events); | |
| 303 | |
| 304 if (_class != oldElement._class) | 361 if (_class != oldElement._class) |
| 305 root.setAttribute('class', _class); | 362 root.setAttribute('class', _class); |
| 306 | 363 |
| 307 if (inlineStyle != oldElement.inlineStyle) | 364 if (inlineStyle != oldElement.inlineStyle) |
| 308 root.setAttribute('style', inlineStyle); | 365 root.setAttribute('style', inlineStyle); |
| 309 | 366 |
| 310 _syncChildren(oldElement); | 367 _syncChildren(oldElement); |
| 311 } | 368 } |
| 312 | 369 |
| 313 void _syncChildren(Element oldElement) { | 370 void _syncChildren(Element oldElement) { |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 void _scheduleComponentForRender(Component c) { | 631 void _scheduleComponentForRender(Component c) { |
| 575 assert(!_inRenderDirtyComponents); | 632 assert(!_inRenderDirtyComponents); |
| 576 _dirtyComponents.add(c); | 633 _dirtyComponents.add(c); |
| 577 | 634 |
| 578 if (!_buildScheduled) { | 635 if (!_buildScheduled) { |
| 579 _buildScheduled = true; | 636 _buildScheduled = true; |
| 580 new Future.microtask(_buildDirtyComponents); | 637 new Future.microtask(_buildDirtyComponents); |
| 581 } | 638 } |
| 582 } | 639 } |
| 583 | 640 |
| 584 EventMap _emptyEventMap = new EventMap(); | |
| 585 | |
| 586 abstract class Component extends Node { | 641 abstract class Component extends Node { |
| 587 bool get _isBuilding => _currentlyBuilding == this; | 642 bool get _isBuilding => _currentlyBuilding == this; |
| 588 bool _dirty = true; | 643 bool _dirty = true; |
| 589 | 644 |
| 590 Node _built; | 645 Node _built; |
| 591 final int _order; | 646 final int _order; |
| 592 static int _currentOrder = 0; | 647 static int _currentOrder = 0; |
| 593 bool _stateful; | 648 bool _stateful; |
| 594 static Component _currentlyBuilding; | 649 static Component _currentlyBuilding; |
| 595 | 650 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 659 int lastOrder = _currentOrder; | 714 int lastOrder = _currentOrder; |
| 660 _currentOrder = _order; | 715 _currentOrder = _order; |
| 661 _currentlyBuilding = this; | 716 _currentlyBuilding = this; |
| 662 _built = build(); | 717 _built = build(); |
| 663 _currentlyBuilding = null; | 718 _currentlyBuilding = null; |
| 664 _currentOrder = lastOrder; | 719 _currentOrder = lastOrder; |
| 665 | 720 |
| 666 _built = _syncChild(_built, oldBuilt, host, insertBefore); | 721 _built = _syncChild(_built, oldBuilt, host, insertBefore); |
| 667 _dirty = false; | 722 _dirty = false; |
| 668 _root = _built._root; | 723 _root = _built._root; |
| 669 | |
| 670 _built.events.addAll(events); | |
| 671 _syncEvents(oldComponent != null ? oldComponent.events : _emptyEventMap); | |
| 672 } | 724 } |
| 673 | 725 |
| 674 void _buildIfDirty() { | 726 void _buildIfDirty() { |
| 675 if (!_dirty || _defunct) | 727 if (!_dirty || _defunct) |
| 676 return; | 728 return; |
| 677 | 729 |
| 678 assert(_root != null); | 730 assert(_root != null); |
| 679 _sync(null, _root.parentNode, _root.nextSibling); | 731 _sync(null, _root.parentNode, _root.nextSibling); |
| 680 } | 732 } |
| 681 | 733 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 707 | 759 |
| 708 _sync(null, _host, null); | 760 _sync(null, _host, null); |
| 709 assert(_root is sky.Node); | 761 assert(_root is sky.Node); |
| 710 | 762 |
| 711 sw.stop(); | 763 sw.stop(); |
| 712 if (_shouldLogRenderDuration) | 764 if (_shouldLogRenderDuration) |
| 713 print("Initial build: ${sw.elapsedMicroseconds} microseconds"); | 765 print("Initial build: ${sw.elapsedMicroseconds} microseconds"); |
| 714 }); | 766 }); |
| 715 } | 767 } |
| 716 } | 768 } |
| OLD | NEW |