OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 part of fn; | |
6 | |
7 List<Component> _dirtyComponents = new List<Component>(); | |
8 bool _renderScheduled = false; | |
9 | |
10 void _renderDirtyComponents() { | |
11 Stopwatch sw = new Stopwatch()..start(); | |
12 | |
13 _dirtyComponents.sort((a, b) => a._order - b._order); | |
14 for (var comp in _dirtyComponents) { | |
15 comp._renderIfDirty(); | |
16 } | |
17 | |
18 _dirtyComponents.clear(); | |
19 _renderScheduled = false; | |
20 sw.stop(); | |
21 print("Render took ${sw.elapsedMicroseconds} microseconds"); | |
22 } | |
23 | |
24 void _scheduleComponentForRender(Component c) { | |
25 _dirtyComponents.add(c); | |
26 | |
27 if (!_renderScheduled) { | |
28 _renderScheduled = true; | |
29 new Future.microtask(_renderDirtyComponents); | |
30 } | |
31 } | |
32 | |
33 abstract class Component extends Node { | |
34 bool _dirty = true; // components begin dirty because they haven't rendered. | |
35 Node _rendered = null; | |
36 bool _removed = false; | |
37 final int _order; | |
38 static int _currentOrder = 0; | |
39 bool _stateful; | |
40 static Component _currentlyRendering; | |
41 | |
42 Component({ Object key, bool stateful }) | |
43 : _stateful = stateful != null ? stateful : false, | |
44 _order = _currentOrder + 1, | |
45 super(key:key); | |
46 | |
47 void willUnmount() {} | |
48 | |
49 void _remove() { | |
50 assert(_rendered != null); | |
51 assert(_root != null); | |
52 willUnmount(); | |
53 _rendered._remove(); | |
54 _rendered = null; | |
55 _root = null; | |
56 _removed = true; | |
57 } | |
58 | |
59 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently | |
60 // needed to get sizing info. | |
61 sky.Node getRoot() => _root; | |
62 | |
63 bool _sync(Node old, sky.Node host, sky.Node insertBefore) { | |
64 Component oldComponent = old as Component; | |
65 | |
66 if (oldComponent == null || oldComponent == this) { | |
67 _renderInternal(host, insertBefore); | |
68 return false; | |
69 } | |
70 | |
71 assert(oldComponent != null); | |
72 assert(_dirty); | |
73 assert(_rendered == null); | |
74 | |
75 if (oldComponent._stateful) { | |
76 _stateful = false; // prevent iloop from _renderInternal below. | |
77 | |
78 reflect.copyPublicFields(this, oldComponent); | |
79 | |
80 oldComponent._dirty = true; | |
81 _dirty = false; | |
82 | |
83 oldComponent._renderInternal(host, insertBefore); | |
84 return true; // Must retain old component | |
85 } | |
86 | |
87 _rendered = oldComponent._rendered; | |
88 _renderInternal(host, insertBefore); | |
89 return false; | |
90 } | |
91 | |
92 void _renderInternal(sky.Node host, sky.Node insertBefore) { | |
93 if (!_dirty) { | |
94 assert(_rendered != null); | |
95 return; | |
96 } | |
97 | |
98 var oldRendered = _rendered; | |
99 int lastOrder = _currentOrder; | |
100 _currentOrder = _order; | |
101 _currentlyRendering = this; | |
102 _rendered = render(); | |
103 _currentlyRendering = null; | |
104 _currentOrder = lastOrder; | |
105 | |
106 _rendered.events.addAll(events); | |
107 | |
108 _dirty = false; | |
109 | |
110 // TODO(rafaelw): This prevents components from returning different node | |
111 // types as their root node at different times. Consider relaxing. | |
112 assert(oldRendered == null || | |
113 _rendered.runtimeType == oldRendered.runtimeType); | |
114 | |
115 if (_rendered._sync(oldRendered, host, insertBefore)) { | |
116 _rendered = oldRendered; // retain stateful component | |
117 } | |
118 _root = _rendered._root; | |
119 assert(_rendered._root is sky.Node); | |
120 } | |
121 | |
122 void _renderIfDirty() { | |
123 assert(_rendered != null); | |
124 assert(!_removed); | |
125 | |
126 var rendered = _rendered; | |
127 while (rendered is Component) { | |
128 rendered = rendered._rendered; | |
129 } | |
130 sky.Node root = rendered._root; | |
131 | |
132 _renderInternal(root.parentNode, root.nextSibling); | |
133 } | |
134 | |
135 void setState(Function fn()) { | |
136 assert(_rendered != null); // cannot setState before mounting. | |
137 _stateful = true; | |
138 fn(); | |
139 if (_currentlyRendering != this) { | |
140 _dirty = true; | |
141 _scheduleComponentForRender(this); | |
142 } | |
143 } | |
144 | |
145 Node render(); | |
146 } | |
147 | |
148 abstract class App extends Component { | |
149 sky.Node _host = null; | |
150 App() | |
151 : super(stateful: true) { | |
152 | |
153 _host = sky.document.createElement('div'); | |
154 sky.document.appendChild(_host); | |
155 | |
156 new Future.microtask(() { | |
157 Stopwatch sw = new Stopwatch()..start(); | |
158 _sync(null, _host, null); | |
159 assert(_root is sky.Node); | |
160 sw.stop(); | |
161 print("Initial render: ${sw.elapsedMicroseconds} microseconds"); | |
162 }); | |
163 } | |
164 } | |
OLD | NEW |