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

Side by Side Diff: client/view/view.dart

Issue 9382027: Move client/{base, observable, layout, touch, util, view} to samples/ui_lib . (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « client/view/resources/view.css ('k') | samples/isolate_html/IsolateSample.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 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #library("view");
6
7 #import('dart:html');
8 #import('../base/base.dart');
9 #import('../observable/observable.dart');
10 #import('../touch/touch.dart');
11 #import('../layout/layout.dart');
12
13 #source('CompositeView.dart');
14 #source('ConveyorView.dart');
15 #source('MeasureText.dart');
16 #source('PagedViews.dart');
17 #source('SliderMenu.dart');
18
19
20 // TODO(rnystrom): Note! This class is undergoing heavy construction. It will
21 // temporary support both some old and some new ways of doing things until all
22 // subclasses are refactored to use the new way. There will be some scaffolding
23 // and construction cones laying around. Try not to freak out.
24
25 /** A generic view. */
26 class View implements Positionable {
27 Element _node;
28 ViewLayout _layout;
29
30 // TODO(jmesserly): instead of tracking this on every View, we could have the
31 // App track the views that want to be notified of resize()
32 EventListener _resizeHandler;
33
34 /**
35 * Style properties configured for this view.
36 */
37 // TODO(jmesserly): We should be getting these from our CSS preprocessor.
38 // I'm not sure if this will stay as a Map, or just be a get method.
39 // TODO(jacobr): Consider returning a somewhat typed base.Style wrapper
40 // object instead, and integrating with built in CSS properties.
41 final Map<String, String> customStyle;
42
43 View()
44 : customStyle = new Map<String, String>();
45
46 View.fromNode(Element this._node)
47 : customStyle = new Map<String, String>();
48
49 View.html(String html)
50 : customStyle = new Map<String, String>(),
51 _node = new Element.html(html);
52
53 // TODO(rnystrom): Get rid of this when all views are refactored to not use
54 // it.
55 Element get node() {
56 // Lazy render.
57 if (_node === null) {
58 _render();
59 }
60
61 return _node;
62 }
63
64 /**
65 * A subclass that contains child views should override this to return those
66 * views. View uses this to ensure that child views are properly rendered
67 * and initialized when their parent view is without the parent having to
68 * manually handle that traversal.
69 */
70 Collection<View> get childViews() {
71 return const [];
72 }
73
74 /**
75 * View presumes the collection of views returned by childViews is more or
76 * less static after the view is first created. Subclasses should call this
77 * when that invariant doesn't hold to let View know that a new childView has
78 * appeared.
79 */
80 void childViewAdded(View child) {
81 if (isInDocument) {
82 child._enterDocument();
83
84 // TODO(jmesserly): is this too expensive?
85 doLayout();
86 }
87 }
88
89 /**
90 * View presumes the collection of views returned by childViews is more or
91 * less static after the view is first created. Subclasses should call this
92 * when that invariant doesn't hold to let View know that a childView has
93 * been removed.
94 */
95 void childViewRemoved(View child) {
96 if (isInDocument) {
97 child._exitDocument();
98 }
99 }
100
101 /** Gets whether this View has already been rendered or not. */
102 bool get isRendered() {
103 return _node !== null;
104 }
105
106 /**
107 * Gets whether this View (or one of its parents) has been added to the
108 * document or not.
109 */
110 bool get isInDocument() {
111 return _node !== null && node.document.contains(node);
112 }
113
114 /**
115 * Adds this view to the document as a child of the given node. This should
116 * generally only be called once for the top-level view.
117 */
118 void addToDocument(Element parentNode) {
119 assert(!isInDocument);
120
121 _render();
122 parentNode.nodes.add(_node);
123 _hookGlobalLayoutEvents();
124 _enterDocument();
125 }
126
127 void removeFromDocument() {
128 assert(isInDocument);
129
130 // Remove runs in reverse order of how we entered.
131 _exitDocument();
132 _unhookGlobalLayoutEvents();
133 _node.remove();
134 }
135
136 /**
137 * Override this to generate the DOM structure for the view.
138 */
139 // TODO(rnystrom): make this method abstract, see b/5015671
140 Element render() { throw 'abstract'; }
141
142 /**
143 * Override this to perform initialization behavior that requires access to
144 * the DOM associated with the View, such as event wiring.
145 */
146 void afterRender(Element node) {
147 // Do nothing by default.
148 }
149
150 /**
151 * Override this to perform behavior after this View has been added to the
152 * document. This is appropriate if you need access to state (such as the
153 * calculated size of an element) that's only available when the View is in
154 * the document.
155 *
156 * This will be called each time the View is added to the document, if it is
157 * added and removed multiple times.
158 */
159 void enterDocument() {}
160
161 /**
162 * Override this to perform behavior after this View has been removed from the
163 * document. This can be a convenient time to unregister event handlers bound
164 * in enterDocument().
165 *
166 * This will be called each time the View is removed from the document, if it
167 * is added and removed multiple times.
168 */
169 void exitDocument() {}
170
171 /** Override this to perform behavior after the window is resized. */
172 // TODO(jmesserly): this isn't really the event we want. Ideally we want to
173 // fire the event only if this particular View changed size. Also we should
174 // give a view the ability to measure itself when added to the document.
175 void windowResized() {}
176
177 /**
178 * Registers the given listener callback to the given observable. Also
179 * immedially invokes the callback once as if a change has just come in. This
180 * lets you define a render() method that renders the skeleton of a view, then
181 * register a bunch of listeners which all fire to populate the view with
182 * model data.
183 */
184 void watch(Observable observable, void watcher(EventSummary summary)) {
185 // Make a fake summary for the initial watch.
186 final summary = new EventSummary(observable);
187 watcher(summary);
188
189 attachWatch(observable, watcher);
190 }
191
192 /** Registers the given listener callback to the given observable. */
193 void attachWatch(Observable observable, void watcher(EventSummary summary)) {
194 observable.addChangeListener(watcher);
195
196 // TODO(rnystrom): Should keep track of this and unregister when the view
197 // is discarded.
198 }
199
200 void addOnClick(EventListener handler) {
201 _node.on.click.add(handler);
202 }
203
204 /**
205 * Gets whether the view is hidden.
206 */
207 bool get hidden() => _node.style.display == 'none';
208
209 /**
210 * Sets whether the view is hidden.
211 */
212 void set hidden(bool hidden) {
213 if (hidden) {
214 node.style.display = 'none';
215 } else {
216 node.style.display = '';
217 }
218 }
219
220 void addClass(String className) {
221 node.classes.add(className);
222 }
223
224 void removeClass(String className) {
225 node.classes.remove(className);
226 }
227
228 /** Sets the CSS3 transform applied to the view. */
229 set transform(String transform) {
230 node.style.transform = transform;
231 }
232
233 // TODO(rnystrom): Get rid of this, or move into a separate class?
234 /** Creates a View whose node is a <div> with the given class(es). */
235 static View div(String cssClass, [String body = null]) {
236 if (body == null) {
237 body = '';
238 }
239 return new View.html('<div class="$cssClass">$body</div>');
240 }
241
242 /**
243 * Internal render method that deals with traversing child views. Should not
244 * be overridden.
245 */
246 void _render() {
247 // TODO(rnystrom): Should render child views here. Not implemented yet.
248 // Instead, we rely on the parent accessing .node to implicitly cause the
249 // child to be rendered.
250
251 // Render this view.
252 if (_node == null) {
253 _node = render();
254 }
255
256 // Pass the node back to the derived view so it can register event
257 // handlers on it.
258 afterRender(_node);
259 }
260
261 /**
262 * Internal method that deals with traversing child views. Should not be
263 * overridden.
264 */
265 void _enterDocument() {
266 // Notify the children first.
267 for (final child in childViews) {
268 child._enterDocument();
269 }
270
271 enterDocument();
272 }
273
274 // Layout related methods
275
276 ViewLayout get layout() {
277 if (_layout == null) {
278 _layout = new ViewLayout.fromView(this);
279 }
280 return _layout;
281 }
282
283 /**
284 * Internal method that deals with traversing child views. Should not be
285 * overridden.
286 */
287 void _exitDocument() {
288 // Notify this View first so that it's children are still valid.
289 exitDocument();
290
291 // Notify the children.
292 for (final child in childViews) {
293 child._exitDocument();
294 }
295 }
296
297 /**
298 * If needed, starts a layout computation from the top level.
299 * Also hooks the relevant events like window resize, so we can layout on too
300 * demand.
301 */
302 void _hookGlobalLayoutEvents() {
303 if (_resizeHandler == null) {
304 _resizeHandler = EventBatch.wrap((e) => doLayout());
305 }
306 window.on.resize.add(_resizeHandler);
307
308 // Trigger the initial layout.
309 doLayout();
310 }
311
312 void _unhookGlobalLayoutEvents() {
313 if (_resizeHandler != null) {
314 window.on.resize.remove(_resizeHandler);
315 _resizeHandler = null;
316 }
317 }
318
319 void doLayout() {
320 _measureLayout().then((bool changed) {
321 if (changed) {
322 _applyLayoutToChildren();
323 }
324 });
325 }
326
327 Future<bool> _measureLayout() {
328 final changed = new Completer<bool>();
329 _measureLayoutHelper(changed);
330
331 window.requestLayoutFrame(() {
332 if (!changed.future.isComplete) {
333 changed.complete(false);
334 }
335 });
336 return changed.future;
337 }
338
339 void _measureLayoutHelper(Completer<bool> changed) {
340 windowResized();
341
342 // TODO(jmesserly): this logic is more complex than it needs to be because
343 // we're taking pains to not initialize _layout if it's not needed. Is that
344 // a good tradeoff?
345 if (ViewLayout.hasCustomLayout(this)) {
346 Completer sizeCompleter = new Completer<Size>();
347 _node.rect.then((ElementRect rect) {
348 sizeCompleter.complete(
349 new Size(rect.client.width, rect.client.height));
350 });
351 layout.measureLayout(sizeCompleter.future, changed);
352 } else {
353 for (final child in childViews) {
354 child._measureLayoutHelper(changed);
355 }
356 }
357 }
358
359 void _applyLayoutToChildren() {
360 for (final child in childViews) {
361 child._applyLayout();
362 }
363 }
364
365 void _applyLayout() {
366 if (_layout != null) {
367 _layout.applyLayout();
368 }
369 _applyLayoutToChildren();
370 }
371 }
OLDNEW
« no previous file with comments | « client/view/resources/view.css ('k') | samples/isolate_html/IsolateSample.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698