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

Unified 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, 10 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sky/examples/fn/lib/node.dart
diff --git a/sky/examples/fn/lib/node.dart b/sky/examples/fn/lib/node.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f7ea1a301428b6fd3156f878ab8a6d139fdfa7ea
--- /dev/null
+++ b/sky/examples/fn/lib/node.dart
@@ -0,0 +1,512 @@
+part of fn;
+
+void parentInsertBefore(sky.ParentNode parent,
+ sky.Node node,
+ sky.Node ref) {
+ if (ref != null) {
+ ref.insertBefore([node]);
+ } else {
+ parent.appendChild(node);
+ }
+}
+
+abstract class Node {
+ String _key = null;
+ sky.Node _root = null;
+
+ Node({ Object key }) {
+ _key = key == null ? "$runtimeType" : "$runtimeType-$key";
+ }
+
+ // Return true IFF the old node has *become* the new node (should be
+ // retained because it is stateful)
+ bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore);
+
+ void _remove() {
+ assert(_root != null);
+ _root.remove();
+ _root = null;
+ }
+}
+
+class Text extends Node {
+ String data;
+
+ // Text nodes are special cases of having non-unique keys (which don't need
+ // to be assigned as part of the API). Since they are unique in not having
+ // children, there's little point to reordering, so we always just re-assign
+ // the data.
+ Text(this.data) : super(key:'*text*');
+
+ bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
+ if (old == null) {
+ _root = new sky.Text(data);
+ parentInsertBefore(host, _root, insertBefore);
+ return false;
+ }
+
+ _root = old._root;
+ (_root as sky.Text).data = data;
+ return false;
+ }
+}
+
+var _emptyList = new List<Node>();
+
+abstract class Element extends Node {
+
+ String get _tagName;
+
+ Element get _emptyElement;
+
+ String inlineStyle;
+
+ sky.EventListener onClick;
+ sky.EventListener onFlingCancel;
+ sky.EventListener onFlingStart;
+ sky.EventListener onGestureTap;
+ sky.EventListener onPointerCancel;
+ sky.EventListener onPointerDown;
+ sky.EventListener onPointerMove;
+ sky.EventListener onPointerUp;
+ sky.EventListener onScrollEnd;
+ sky.EventListener onScrollStart;
+ sky.EventListener onScrollUpdate;
+ sky.EventListener onWheel;
+
+ List<Node> _children = null;
+ String _className = '';
+
+ Element({
+ Object key,
+ List<Node> children,
+ Style style,
+
+ this.inlineStyle,
+
+ // Events
+ this.onClick,
+ this.onFlingCancel,
+ this.onFlingStart,
+ this.onGestureTap,
+ this.onPointerCancel,
+ this.onPointerDown,
+ this.onPointerMove,
+ this.onPointerUp,
+ this.onScrollEnd,
+ this.onScrollStart,
+ this.onScrollUpdate,
+ this.onWheel
+ }) : super(key:key) {
+
+ _className = style == null ? '': style._className;
+ _children = children == null ? _emptyList : children;
+
+ if (debugWarnings()) {
+ _debugReportDuplicateIds();
+ }
+ }
+
+ void _remove() {
+ super._remove();
+ if (_children != null) {
+ for (var child in _children) {
+ child._remove();
+ }
+ }
+ _children = null;
+ }
+
+ void _debugReportDuplicateIds() {
+ var idSet = new HashSet<String>();
+ for (var child in _children) {
+ if (child is Text) {
+ continue; // Text nodes all have the same key and are never reordered.
+ }
+
+ if (!idSet.add(child._key)) {
+ throw '''If multiple (non-Text) nodes of the same type exist as children
+ of another node, they must have unique keys.''';
+ }
+ }
+ }
+
+ void _syncEvent(String eventName, sky.EventListener listener,
+ sky.EventListener oldListener) {
+ sky.Element root = _root as sky.Element;
+ if (listener == oldListener)
+ return;
+
+ if (oldListener != null) {
+ root.removeEventListener(eventName, oldListener);
+ }
+
+ if (listener != null) {
+ root.addEventListener(eventName, listener);
+ }
+ }
+
+ void _syncEvents([Element old]) {
+ _syncEvent('click', onClick, old.onClick);
+ _syncEvent('gestureflingcancel', onFlingCancel, old.onFlingCancel);
+ _syncEvent('gestureflingstart', onFlingStart, old.onFlingStart);
+ _syncEvent('gesturescrollend', onScrollEnd, old.onScrollEnd);
+ _syncEvent('gesturescrollstart', onScrollStart, old.onScrollStart);
+ _syncEvent('gesturescrollupdate', onScrollUpdate, old.onScrollUpdate);
+ _syncEvent('gesturetap', onGestureTap, old.onGestureTap);
+ _syncEvent('pointercancel', onPointerCancel, old.onPointerCancel);
+ _syncEvent('pointerdown', onPointerDown, old.onPointerDown);
+ _syncEvent('pointermove', onPointerMove, old.onPointerMove);
+ _syncEvent('pointerup', onPointerUp, old.onPointerUp);
+ _syncEvent('wheel', onWheel, old.onWheel);
+ }
+
+ void _syncNode([Element old]) {
+ if (old == null) {
+ old = _emptyElement;
+ }
+
+ _syncEvents(old);
+
+ sky.Element root = _root as sky.Element;
+ if (_className != old._className) {
+ root.setAttribute('class', _className);
+ }
+
+ if (inlineStyle != old.inlineStyle) {
+ root.setAttribute('style', inlineStyle);
+ }
+ }
+
+ bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
+ // print("---Syncing children of $_key");
+
+ Element oldElement = old as Element;
+
+ if (oldElement == null) {
+ // print("...no oldElement, initial render");
+
+ _root = sky.document.createElement(_tagName);
+ _syncNode();
+
+ for (var child in _children) {
+ child._sync(null, _root, null);
+ assert(child._root is sky.Node);
+ }
+
+ parentInsertBefore(host, _root, insertBefore);
+ return false;
+ }
+
+ _root = oldElement._root;
+ oldElement._root = null;
+ sky.Element root = (_root as sky.Element);
+
+ _syncNode(oldElement);
+
+ var startIndex = 0;
+ var endIndex = _children.length;
+
+ var oldChildren = oldElement._children;
+ var oldStartIndex = 0;
+ var oldEndIndex = oldChildren.length;
+
+ sky.Node nextSibling = null;
+ Node currentNode = null;
+ Node oldNode = null;
+
+ void sync(int atIndex) {
+ if (currentNode._sync(oldNode, root, nextSibling)) {
+ // oldNode was stateful and must be retained.
+ assert(oldNode != null);
+ currentNode = oldNode;
+ _children[atIndex] = currentNode;
+ }
+ assert(currentNode._root is sky.Node);
+ }
+
+ // Scan backwards from end of list while nodes can be directly synced
+ // without reordering.
+ // print("...scanning backwards");
+ while (endIndex > startIndex && oldEndIndex > oldStartIndex) {
+ currentNode = _children[endIndex - 1];
+ oldNode = oldChildren[oldEndIndex - 1];
+
+ if (currentNode._key != oldNode._key) {
+ break;
+ }
+
+ // print('> syncing matched at: $endIndex : $oldEndIndex');
+ endIndex--;
+ oldEndIndex--;
+ sync(endIndex);
+ nextSibling = currentNode._root;
+ }
+
+ HashMap<String, Node> oldNodeIdMap = null;
+
+ bool oldNodeReordered(String key) {
+ return oldNodeIdMap != null &&
+ oldNodeIdMap.containsKey(key) &&
+ oldNodeIdMap[key] == null;
+ }
+
+ void advanceOldStartIndex() {
+ oldStartIndex++;
+ while (oldStartIndex < oldEndIndex &&
+ oldNodeReordered(oldChildren[oldStartIndex]._key)) {
+ oldStartIndex++;
+ }
+ }
+
+ void ensureOldIdMap() {
+ if (oldNodeIdMap != null)
+ return;
+
+ oldNodeIdMap = new HashMap<String, Node>();
+ for (int i = oldStartIndex; i < oldEndIndex; i++) {
+ var node = oldChildren[i];
+ if (node is! Text) {
+ oldNodeIdMap.putIfAbsent(node._key, () => node);
+ }
+ }
+ }
+
+ bool searchForOldNode() {
+ if (currentNode is Text)
+ return false; // Never re-order Text nodes.
+
+ ensureOldIdMap();
+ oldNode = oldNodeIdMap[currentNode._key];
+ if (oldNode == null)
+ return false;
+
+ oldNodeIdMap[currentNode._key] = null; // mark it reordered.
+ // print("Reparenting ${currentNode._key}");
+ parentInsertBefore(root, oldNode._root, nextSibling);
+ return true;
+ }
+
+ // Scan forwards, this time we may re-order;
+ // print("...scanning forward");
+ nextSibling = root.firstChild;
+ while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
+ currentNode = _children[startIndex];
+ oldNode = oldChildren[oldStartIndex];
+
+ if (currentNode._key == oldNode._key) {
+ // print('> syncing matched at: $startIndex : $oldStartIndex');
+ assert(currentNode.runtimeType == oldNode.runtimeType);
+ nextSibling = nextSibling.nextSibling;
+ sync(startIndex);
+ startIndex++;
+ advanceOldStartIndex();
+ continue;
+ }
+
+ oldNode = null;
+ if (searchForOldNode()) {
+ // print('> reordered to $startIndex');
+ } else {
+ // print('> inserting at $startIndex');
+ }
+
+ sync(startIndex);
+ startIndex++;
+ }
+
+ // New insertions
+ oldNode = null;
+ // print('...processing remaining insertions');
+ while (startIndex < endIndex) {
+ // print('> inserting at $startIndex');
+ currentNode = _children[startIndex];
+ sync(startIndex);
+ startIndex++;
+ }
+
+ // Removals
+ // print('...processing remaining removals');
+ currentNode = null;
+ while (oldStartIndex < oldEndIndex) {
+ oldNode = oldChildren[oldStartIndex];
+ // print('> ${oldNode._key} removing from $oldEndIndex');
+ oldNode._remove();
+ advanceOldStartIndex();
+ }
+
+ oldElement._children = null;
+ return false;
+ }
+}
+
+class Container extends Element {
+
+ String get _tagName => 'div';
+
+ static Container _emptyContainer = new Container();
+
+ Element get _emptyElement => _emptyContainer;
+
+ Container({
+ Object key,
+ List<Node> children,
+ Style style,
+ String inlineStyle,
+ sky.EventListener onClick,
+ sky.EventListener onFlingCancel,
+ sky.EventListener onFlingStart,
+ sky.EventListener onGestureTap,
+ sky.EventListener onPointerCancel,
+ sky.EventListener onPointerDown,
+ sky.EventListener onPointerMove,
+ sky.EventListener onPointerUp,
+ sky.EventListener onScrollEnd,
+ sky.EventListener onScrollStart,
+ sky.EventListener onScrollUpdate,
+ sky.EventListener onWheel
+ }) : super(
+ key: key,
+ children: children,
+ style: style,
+ inlineStyle: inlineStyle,
+ onClick: onClick,
+ onFlingCancel: onFlingCancel,
+ onFlingStart: onFlingStart,
+ onGestureTap: onGestureTap,
+ onPointerCancel: onPointerCancel,
+ onPointerDown: onPointerDown,
+ onPointerMove: onPointerMove,
+ onPointerUp: onPointerUp,
+ onScrollEnd: onScrollEnd,
+ onScrollStart: onScrollStart,
+ onScrollUpdate: onScrollUpdate,
+ onWheel: onWheel
+ );
+}
+
+class Image extends Element {
+
+ String get _tagName => 'img';
+
+ static Image _emptyImage = new Image();
+ Element get _emptyElement => _emptyImage;
+
+ String src;
+ int width;
+ int height;
+
+ Image({
+ Object key,
+ List<Node> children,
+ Style style,
+ String inlineStyle,
+ sky.EventListener onClick,
+ sky.EventListener onFlingCancel,
+ sky.EventListener onFlingStart,
+ sky.EventListener onGestureTap,
+ sky.EventListener onPointerCancel,
+ sky.EventListener onPointerDown,
+ sky.EventListener onPointerMove,
+ sky.EventListener onPointerUp,
+ sky.EventListener onScrollEnd,
+ sky.EventListener onScrollStart,
+ sky.EventListener onScrollUpdate,
+ sky.EventListener onWheel,
+ this.width,
+ this.height,
+ this.src
+ }) : super(
+ key: key,
+ children: children,
+ style: style,
+ inlineStyle: inlineStyle,
+ onClick: onClick,
+ onFlingCancel: onFlingCancel,
+ onFlingStart: onFlingStart,
+ onGestureTap: onGestureTap,
+ onPointerCancel: onPointerCancel,
+ onPointerDown: onPointerDown,
+ onPointerMove: onPointerMove,
+ onPointerUp: onPointerUp,
+ onScrollEnd: onScrollEnd,
+ onScrollStart: onScrollStart,
+ onScrollUpdate: onScrollUpdate,
+ onWheel: onWheel
+ );
+
+ void _syncNode([Element old]) {
+ super._syncNode(old);
+
+ Image oldImage = old != null ? old : _emptyImage;
+ sky.HTMLImageElement skyImage = _root as sky.HTMLImageElement;
+ if (src != oldImage.src) {
+ skyImage.src = src;
+ }
+
+ if (width != oldImage.width) {
+ skyImage.style['width'] = '${width}px';
+ }
+ if (height != oldImage.height) {
+ skyImage.style['height'] = '${height}px';
+ }
+ }
+}
+
+class Anchor extends Element {
+
+ String get _tagName => 'a';
+
+ static Anchor _emptyAnchor = new Anchor();
+
+ String href;
+
+ Anchor({
+ Object key,
+ List<Node> children,
+ Style style,
+ String inlineStyle,
+ sky.EventListener onClick,
+ sky.EventListener onFlingCancel,
+ sky.EventListener onFlingStart,
+ sky.EventListener onGestureTap,
+ sky.EventListener onPointerCancel,
+ sky.EventListener onPointerDown,
+ sky.EventListener onPointerMove,
+ sky.EventListener onPointerUp,
+ sky.EventListener onScrollEnd,
+ sky.EventListener onScrollStart,
+ sky.EventListener onScrollUpdate,
+ sky.EventListener onWheel,
+ this.width,
+ this.height,
+ this.href
+ }) : super(
+ key: key,
+ children: children,
+ style: style,
+ inlineStyle: inlineStyle,
+ onClick: onClick,
+ onFlingCancel: onFlingCancel,
+ onFlingStart: onFlingStart,
+ onGestureTap: onGestureTap,
+ onPointerCancel: onPointerCancel,
+ onPointerDown: onPointerDown,
+ onPointerMove: onPointerMove,
+ onPointerUp: onPointerUp,
+ onScrollEnd: onScrollEnd,
+ onScrollStart: onScrollStart,
+ onScrollUpdate: onScrollUpdate,
+ onWheel: onWheel
+ );
+
+ void _syncNode([Element old]) {
+ Anchor oldAnchor = old != null ? old as Anchor : _emptyAnchor;
+ super._syncNode(oldAnchor);
+
+ sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement;
+ if (href != oldAnchor.href) {
+ skyAnchor.href = href;
+ }
+ }
+}
« 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