Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: sky/framework/fn.dart

Issue 1019633004: Change how events are handled in Effen (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: fixed bug Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 = false;
24 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
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) {
54 var className = "$_className ${other._className}"; 34 var className = "$_className ${other._className}";
(...skipping 29 matching lines...) Expand all
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
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 Map<sky.Node, RenderNode> _nodeMap =
abarth-chromium 2015/03/19 14:19:59 final
rafaelw 2015/03/19 15:03:50 Done.
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 bool _docListenersRegistered = false;
abarth-chromium 2015/03/19 14:19:59 style nit: _docListenersRegistered -> _haveRegiste
rafaelw 2015/03/19 15:03:50 Removed this.
169
170 static final Set<String> _registeredEvents = new HashSet<String>();
171
172 static Map<String, sky.EventListener> _createListeners({
173 EventListener onWheel,
174 GestureEventListener onGestureFlingCancel,
175 GestureEventListener onGestureFlingStart,
176 GestureEventListener onGestureScrollStart,
177 GestureEventListener onGestureScrollUpdate,
178 GestureEventListener onGestureTap,
179 PointerEventListener onPointerCancel,
180 PointerEventListener onPointerDown,
181 PointerEventListener onPointerMove,
182 PointerEventListener onPointerUp,
183 Map<String, sky.EventListener> custom
184 }) {
185 var listeners = custom != null ? custom
186 : new HashMap<String, sky.EventListener>();
187
188 if (onWheel != null)
189 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.
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;
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.
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,
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
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 _ensureDocListener(String eventType) {
abarth-chromium 2015/03/19 14:19:59 style nit: s/Doc/Document/
rafaelw 2015/03/19 15:03:50 Done.
261 if (_registeredEvents.add(eventType)) {
262 sky.document.addEventListener(eventType, _dispatchEvent);
263 }
264 }
abarth-chromium 2015/03/19 14:19:59 Something is off with your indentation here.
rafaelw 2015/03/19 15:03:50 Done.
265
266 void _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
267 if (!_docListenersRegistered) {
268 for (var type in listeners.keys) {
269 _ensureDocListener(type);
270 }
271
272 _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
273 }
274
275 Node oldContent = old == null ? null : (old as EventTarget).content;
276 content = _syncChild(content, oldContent , host, insertBefore);
abarth-chromium 2015/03/19 14:19:59 s/ ,/,/
rafaelw 2015/03/19 15:03:51 Done.
277 _root = content._root;
278 }
279
280 void _remove() {
281 content._remove();
282 super._remove();
283 }
284 }
222 285
223 class Text extends RenderNode { 286 class Text extends RenderNode {
224 final String data; 287 final String data;
225 288
226 // Text nodes are special cases of having non-unique keys (which don't need 289 // 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 290 // 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 291 // children, there's little point to reordering, so we always just re-assign
229 // the data. 292 // the data.
230 Text(this.data) : super(key:'*text*'); 293 Text(this.data) : super(key:'*text*');
231 294
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 throw '''If multiple (non-Text) nodes of the same type exist as children 355 throw '''If multiple (non-Text) nodes of the same type exist as children
293 of another node, they must have unique keys.'''; 356 of another node, they must have unique keys.''';
294 } 357 }
295 } 358 }
296 } 359 }
297 360
298 void _syncNode(RenderNode old) { 361 void _syncNode(RenderNode old) {
299 Element oldElement = old as Element; 362 Element oldElement = old as Element;
300 sky.Element root = _root as sky.Element; 363 sky.Element root = _root as sky.Element;
301 364
302 _syncEvents(oldElement.events);
303
304 if (_class != oldElement._class) 365 if (_class != oldElement._class)
305 root.setAttribute('class', _class); 366 root.setAttribute('class', _class);
306 367
307 if (inlineStyle != oldElement.inlineStyle) 368 if (inlineStyle != oldElement.inlineStyle)
308 root.setAttribute('style', inlineStyle); 369 root.setAttribute('style', inlineStyle);
309 370
310 _syncChildren(oldElement); 371 _syncChildren(oldElement);
311 } 372 }
312 373
313 void _syncChildren(Element oldElement) { 374 void _syncChildren(Element oldElement) {
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
574 void _scheduleComponentForRender(Component c) { 635 void _scheduleComponentForRender(Component c) {
575 assert(!_inRenderDirtyComponents); 636 assert(!_inRenderDirtyComponents);
576 _dirtyComponents.add(c); 637 _dirtyComponents.add(c);
577 638
578 if (!_buildScheduled) { 639 if (!_buildScheduled) {
579 _buildScheduled = true; 640 _buildScheduled = true;
580 new Future.microtask(_buildDirtyComponents); 641 new Future.microtask(_buildDirtyComponents);
581 } 642 }
582 } 643 }
583 644
584 EventMap _emptyEventMap = new EventMap();
585
586 abstract class Component extends Node { 645 abstract class Component extends Node {
587 bool get _isBuilding => _currentlyBuilding == this; 646 bool get _isBuilding => _currentlyBuilding == this;
588 bool _dirty = true; 647 bool _dirty = true;
589 648
590 Node _built; 649 Node _built;
591 final int _order; 650 final int _order;
592 static int _currentOrder = 0; 651 static int _currentOrder = 0;
593 bool _stateful; 652 bool _stateful;
594 static Component _currentlyBuilding; 653 static Component _currentlyBuilding;
595 654
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
659 int lastOrder = _currentOrder; 718 int lastOrder = _currentOrder;
660 _currentOrder = _order; 719 _currentOrder = _order;
661 _currentlyBuilding = this; 720 _currentlyBuilding = this;
662 _built = build(); 721 _built = build();
663 _currentlyBuilding = null; 722 _currentlyBuilding = null;
664 _currentOrder = lastOrder; 723 _currentOrder = lastOrder;
665 724
666 _built = _syncChild(_built, oldBuilt, host, insertBefore); 725 _built = _syncChild(_built, oldBuilt, host, insertBefore);
667 _dirty = false; 726 _dirty = false;
668 _root = _built._root; 727 _root = _built._root;
669
670 _built.events.addAll(events);
671 _syncEvents(oldComponent != null ? oldComponent.events : _emptyEventMap);
672 } 728 }
673 729
674 void _buildIfDirty() { 730 void _buildIfDirty() {
675 if (!_dirty || _defunct) 731 if (!_dirty || _defunct)
676 return; 732 return;
677 733
678 assert(_root != null); 734 assert(_root != null);
679 _sync(null, _root.parentNode, _root.nextSibling); 735 _sync(null, _root.parentNode, _root.nextSibling);
680 } 736 }
681 737
(...skipping 25 matching lines...) Expand all
707 763
708 _sync(null, _host, null); 764 _sync(null, _host, null);
709 assert(_root is sky.Node); 765 assert(_root is sky.Node);
710 766
711 sw.stop(); 767 sw.stop();
712 if (_shouldLogRenderDuration) 768 if (_shouldLogRenderDuration)
713 print("Initial build: ${sw.elapsedMicroseconds} microseconds"); 769 print("Initial build: ${sw.elapsedMicroseconds} microseconds");
714 }); 770 });
715 } 771 }
716 } 772 }
OLDNEW
« sky/framework/components/button_base.dart ('K') | « sky/framework/components/scrollable.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698