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

Side by Side Diff: sky/examples/fn/lib/node.dart

Issue 971183002: Initial commit of Effen reactive framework experiment for Sky (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: 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
« no previous file with comments | « sky/examples/fn/lib/fn.dart ('k') | sky/examples/fn/lib/reflect.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 part of fn;
2
3 void parentInsertBefore(sky.ParentNode parent,
4 sky.Node node,
5 sky.Node ref) {
6 if (ref != null) {
7 ref.insertBefore([node]);
8 } else {
9 parent.appendChild(node);
10 }
11 }
12
13 abstract class Node {
14 String _key = null;
15 sky.Node _root = null;
16
17 Node({ Object key }) {
18 _key = key == null ? "$runtimeType" : "$runtimeType-$key";
19 }
20
21 // Return true IFF the old node has *become* the new node (should be
22 // retained because it is stateful)
23 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore);
24
25 void _remove() {
26 assert(_root != null);
27 _root.remove();
28 _root = null;
29 }
30 }
31
32 class Text extends Node {
33 String data;
34
35 // Text nodes are special cases of having non-unique keys (which don't need
36 // to be assigned as part of the API). Since they are unique in not having
37 // children, there's little point to reordering, so we always just re-assign
38 // the data.
39 Text(this.data) : super(key:'*text*');
40
41 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
42 if (old == null) {
43 _root = new sky.Text(data);
44 parentInsertBefore(host, _root, insertBefore);
45 return false;
46 }
47
48 _root = old._root;
49 (_root as sky.Text).data = data;
50 return false;
51 }
52 }
53
54 var _emptyList = new List<Node>();
55
56 abstract class Element extends Node {
57
58 String get _tagName;
59
60 Element get _emptyElement;
61
62 String inlineStyle;
63
64 sky.EventListener onClick;
65 sky.EventListener onFlingCancel;
66 sky.EventListener onFlingStart;
67 sky.EventListener onGestureTap;
68 sky.EventListener onPointerCancel;
69 sky.EventListener onPointerDown;
70 sky.EventListener onPointerMove;
71 sky.EventListener onPointerUp;
72 sky.EventListener onScrollEnd;
73 sky.EventListener onScrollStart;
74 sky.EventListener onScrollUpdate;
75 sky.EventListener onWheel;
76
77 List<Node> _children = null;
78 String _className = '';
79
80 Element({
81 Object key,
82 List<Node> children,
83 Style style,
84
85 this.inlineStyle,
86
87 // Events
88 this.onClick,
89 this.onFlingCancel,
90 this.onFlingStart,
91 this.onGestureTap,
92 this.onPointerCancel,
93 this.onPointerDown,
94 this.onPointerMove,
95 this.onPointerUp,
96 this.onScrollEnd,
97 this.onScrollStart,
98 this.onScrollUpdate,
99 this.onWheel
100 }) : super(key:key) {
101
102 _className = style == null ? '': style._className;
103 _children = children == null ? _emptyList : children;
104
105 if (debugWarnings()) {
106 _debugReportDuplicateIds();
107 }
108 }
109
110 void _remove() {
111 super._remove();
112 if (_children != null) {
113 for (var child in _children) {
114 child._remove();
115 }
116 }
117 _children = null;
118 }
119
120 void _debugReportDuplicateIds() {
121 var idSet = new HashSet<String>();
122 for (var child in _children) {
123 if (child is Text) {
124 continue; // Text nodes all have the same key and are never reordered.
125 }
126
127 if (!idSet.add(child._key)) {
128 throw '''If multiple (non-Text) nodes of the same type exist as children
129 of another node, they must have unique keys.''';
130 }
131 }
132 }
133
134 void _syncEvent(String eventName, sky.EventListener listener,
135 sky.EventListener oldListener) {
136 sky.Element root = _root as sky.Element;
137 if (listener == oldListener)
138 return;
139
140 if (oldListener != null) {
141 root.removeEventListener(eventName, oldListener);
142 }
143
144 if (listener != null) {
145 root.addEventListener(eventName, listener);
146 }
147 }
148
149 void _syncEvents([Element old]) {
150 _syncEvent('click', onClick, old.onClick);
151 _syncEvent('gestureflingcancel', onFlingCancel, old.onFlingCancel);
152 _syncEvent('gestureflingstart', onFlingStart, old.onFlingStart);
153 _syncEvent('gesturescrollend', onScrollEnd, old.onScrollEnd);
154 _syncEvent('gesturescrollstart', onScrollStart, old.onScrollStart);
155 _syncEvent('gesturescrollupdate', onScrollUpdate, old.onScrollUpdate);
156 _syncEvent('gesturetap', onGestureTap, old.onGestureTap);
157 _syncEvent('pointercancel', onPointerCancel, old.onPointerCancel);
158 _syncEvent('pointerdown', onPointerDown, old.onPointerDown);
159 _syncEvent('pointermove', onPointerMove, old.onPointerMove);
160 _syncEvent('pointerup', onPointerUp, old.onPointerUp);
161 _syncEvent('wheel', onWheel, old.onWheel);
162 }
163
164 void _syncNode([Element old]) {
165 if (old == null) {
166 old = _emptyElement;
167 }
168
169 _syncEvents(old);
170
171 sky.Element root = _root as sky.Element;
172 if (_className != old._className) {
173 root.setAttribute('class', _className);
174 }
175
176 if (inlineStyle != old.inlineStyle) {
177 root.setAttribute('style', inlineStyle);
178 }
179 }
180
181 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
182 // print("---Syncing children of $_key");
183
184 Element oldElement = old as Element;
185
186 if (oldElement == null) {
187 // print("...no oldElement, initial render");
188
189 _root = sky.document.createElement(_tagName);
190 _syncNode();
191
192 for (var child in _children) {
193 child._sync(null, _root, null);
194 assert(child._root is sky.Node);
195 }
196
197 parentInsertBefore(host, _root, insertBefore);
198 return false;
199 }
200
201 _root = oldElement._root;
202 oldElement._root = null;
203 sky.Element root = (_root as sky.Element);
204
205 _syncNode(oldElement);
206
207 var startIndex = 0;
208 var endIndex = _children.length;
209
210 var oldChildren = oldElement._children;
211 var oldStartIndex = 0;
212 var oldEndIndex = oldChildren.length;
213
214 sky.Node nextSibling = null;
215 Node currentNode = null;
216 Node oldNode = null;
217
218 void sync(int atIndex) {
219 if (currentNode._sync(oldNode, root, nextSibling)) {
220 // oldNode was stateful and must be retained.
221 assert(oldNode != null);
222 currentNode = oldNode;
223 _children[atIndex] = currentNode;
224 }
225 assert(currentNode._root is sky.Node);
226 }
227
228 // Scan backwards from end of list while nodes can be directly synced
229 // without reordering.
230 // print("...scanning backwards");
231 while (endIndex > startIndex && oldEndIndex > oldStartIndex) {
232 currentNode = _children[endIndex - 1];
233 oldNode = oldChildren[oldEndIndex - 1];
234
235 if (currentNode._key != oldNode._key) {
236 break;
237 }
238
239 // print('> syncing matched at: $endIndex : $oldEndIndex');
240 endIndex--;
241 oldEndIndex--;
242 sync(endIndex);
243 nextSibling = currentNode._root;
244 }
245
246 HashMap<String, Node> oldNodeIdMap = null;
247
248 bool oldNodeReordered(String key) {
249 return oldNodeIdMap != null &&
250 oldNodeIdMap.containsKey(key) &&
251 oldNodeIdMap[key] == null;
252 }
253
254 void advanceOldStartIndex() {
255 oldStartIndex++;
256 while (oldStartIndex < oldEndIndex &&
257 oldNodeReordered(oldChildren[oldStartIndex]._key)) {
258 oldStartIndex++;
259 }
260 }
261
262 void ensureOldIdMap() {
263 if (oldNodeIdMap != null)
264 return;
265
266 oldNodeIdMap = new HashMap<String, Node>();
267 for (int i = oldStartIndex; i < oldEndIndex; i++) {
268 var node = oldChildren[i];
269 if (node is! Text) {
270 oldNodeIdMap.putIfAbsent(node._key, () => node);
271 }
272 }
273 }
274
275 bool searchForOldNode() {
276 if (currentNode is Text)
277 return false; // Never re-order Text nodes.
278
279 ensureOldIdMap();
280 oldNode = oldNodeIdMap[currentNode._key];
281 if (oldNode == null)
282 return false;
283
284 oldNodeIdMap[currentNode._key] = null; // mark it reordered.
285 // print("Reparenting ${currentNode._key}");
286 parentInsertBefore(root, oldNode._root, nextSibling);
287 return true;
288 }
289
290 // Scan forwards, this time we may re-order;
291 // print("...scanning forward");
292 nextSibling = root.firstChild;
293 while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
294 currentNode = _children[startIndex];
295 oldNode = oldChildren[oldStartIndex];
296
297 if (currentNode._key == oldNode._key) {
298 // print('> syncing matched at: $startIndex : $oldStartIndex');
299 assert(currentNode.runtimeType == oldNode.runtimeType);
300 nextSibling = nextSibling.nextSibling;
301 sync(startIndex);
302 startIndex++;
303 advanceOldStartIndex();
304 continue;
305 }
306
307 oldNode = null;
308 if (searchForOldNode()) {
309 // print('> reordered to $startIndex');
310 } else {
311 // print('> inserting at $startIndex');
312 }
313
314 sync(startIndex);
315 startIndex++;
316 }
317
318 // New insertions
319 oldNode = null;
320 // print('...processing remaining insertions');
321 while (startIndex < endIndex) {
322 // print('> inserting at $startIndex');
323 currentNode = _children[startIndex];
324 sync(startIndex);
325 startIndex++;
326 }
327
328 // Removals
329 // print('...processing remaining removals');
330 currentNode = null;
331 while (oldStartIndex < oldEndIndex) {
332 oldNode = oldChildren[oldStartIndex];
333 // print('> ${oldNode._key} removing from $oldEndIndex');
334 oldNode._remove();
335 advanceOldStartIndex();
336 }
337
338 oldElement._children = null;
339 return false;
340 }
341 }
342
343 class Container extends Element {
344
345 String get _tagName => 'div';
346
347 static Container _emptyContainer = new Container();
348
349 Element get _emptyElement => _emptyContainer;
350
351 Container({
352 Object key,
353 List<Node> children,
354 Style style,
355 String inlineStyle,
356 sky.EventListener onClick,
357 sky.EventListener onFlingCancel,
358 sky.EventListener onFlingStart,
359 sky.EventListener onGestureTap,
360 sky.EventListener onPointerCancel,
361 sky.EventListener onPointerDown,
362 sky.EventListener onPointerMove,
363 sky.EventListener onPointerUp,
364 sky.EventListener onScrollEnd,
365 sky.EventListener onScrollStart,
366 sky.EventListener onScrollUpdate,
367 sky.EventListener onWheel
368 }) : super(
369 key: key,
370 children: children,
371 style: style,
372 inlineStyle: inlineStyle,
373 onClick: onClick,
374 onFlingCancel: onFlingCancel,
375 onFlingStart: onFlingStart,
376 onGestureTap: onGestureTap,
377 onPointerCancel: onPointerCancel,
378 onPointerDown: onPointerDown,
379 onPointerMove: onPointerMove,
380 onPointerUp: onPointerUp,
381 onScrollEnd: onScrollEnd,
382 onScrollStart: onScrollStart,
383 onScrollUpdate: onScrollUpdate,
384 onWheel: onWheel
385 );
386 }
387
388 class Image extends Element {
389
390 String get _tagName => 'img';
391
392 static Image _emptyImage = new Image();
393 Element get _emptyElement => _emptyImage;
394
395 String src;
396 int width;
397 int height;
398
399 Image({
400 Object key,
401 List<Node> children,
402 Style style,
403 String inlineStyle,
404 sky.EventListener onClick,
405 sky.EventListener onFlingCancel,
406 sky.EventListener onFlingStart,
407 sky.EventListener onGestureTap,
408 sky.EventListener onPointerCancel,
409 sky.EventListener onPointerDown,
410 sky.EventListener onPointerMove,
411 sky.EventListener onPointerUp,
412 sky.EventListener onScrollEnd,
413 sky.EventListener onScrollStart,
414 sky.EventListener onScrollUpdate,
415 sky.EventListener onWheel,
416 this.width,
417 this.height,
418 this.src
419 }) : super(
420 key: key,
421 children: children,
422 style: style,
423 inlineStyle: inlineStyle,
424 onClick: onClick,
425 onFlingCancel: onFlingCancel,
426 onFlingStart: onFlingStart,
427 onGestureTap: onGestureTap,
428 onPointerCancel: onPointerCancel,
429 onPointerDown: onPointerDown,
430 onPointerMove: onPointerMove,
431 onPointerUp: onPointerUp,
432 onScrollEnd: onScrollEnd,
433 onScrollStart: onScrollStart,
434 onScrollUpdate: onScrollUpdate,
435 onWheel: onWheel
436 );
437
438 void _syncNode([Element old]) {
439 super._syncNode(old);
440
441 Image oldImage = old != null ? old : _emptyImage;
442 sky.HTMLImageElement skyImage = _root as sky.HTMLImageElement;
443 if (src != oldImage.src) {
444 skyImage.src = src;
445 }
446
447 if (width != oldImage.width) {
448 skyImage.style['width'] = '${width}px';
449 }
450 if (height != oldImage.height) {
451 skyImage.style['height'] = '${height}px';
452 }
453 }
454 }
455
456 class Anchor extends Element {
457
458 String get _tagName => 'a';
459
460 static Anchor _emptyAnchor = new Anchor();
461
462 String href;
463
464 Anchor({
465 Object key,
466 List<Node> children,
467 Style style,
468 String inlineStyle,
469 sky.EventListener onClick,
470 sky.EventListener onFlingCancel,
471 sky.EventListener onFlingStart,
472 sky.EventListener onGestureTap,
473 sky.EventListener onPointerCancel,
474 sky.EventListener onPointerDown,
475 sky.EventListener onPointerMove,
476 sky.EventListener onPointerUp,
477 sky.EventListener onScrollEnd,
478 sky.EventListener onScrollStart,
479 sky.EventListener onScrollUpdate,
480 sky.EventListener onWheel,
481 this.width,
482 this.height,
483 this.href
484 }) : super(
485 key: key,
486 children: children,
487 style: style,
488 inlineStyle: inlineStyle,
489 onClick: onClick,
490 onFlingCancel: onFlingCancel,
491 onFlingStart: onFlingStart,
492 onGestureTap: onGestureTap,
493 onPointerCancel: onPointerCancel,
494 onPointerDown: onPointerDown,
495 onPointerMove: onPointerMove,
496 onPointerUp: onPointerUp,
497 onScrollEnd: onScrollEnd,
498 onScrollStart: onScrollStart,
499 onScrollUpdate: onScrollUpdate,
500 onWheel: onWheel
501 );
502
503 void _syncNode([Element old]) {
504 Anchor oldAnchor = old != null ? old as Anchor : _emptyAnchor;
505 super._syncNode(oldAnchor);
506
507 sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement;
508 if (href != oldAnchor.href) {
509 skyAnchor.href = href;
510 }
511 }
512 }
OLDNEW
« no previous file with comments | « sky/examples/fn/lib/fn.dart ('k') | sky/examples/fn/lib/reflect.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698