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 |