Index: client/view/view.dart |
=================================================================== |
--- client/view/view.dart (revision 4144) |
+++ client/view/view.dart (working copy) |
@@ -1,371 +0,0 @@ |
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-#library("view"); |
- |
-#import('dart:html'); |
-#import('../base/base.dart'); |
-#import('../observable/observable.dart'); |
-#import('../touch/touch.dart'); |
-#import('../layout/layout.dart'); |
- |
-#source('CompositeView.dart'); |
-#source('ConveyorView.dart'); |
-#source('MeasureText.dart'); |
-#source('PagedViews.dart'); |
-#source('SliderMenu.dart'); |
- |
- |
-// TODO(rnystrom): Note! This class is undergoing heavy construction. It will |
-// temporary support both some old and some new ways of doing things until all |
-// subclasses are refactored to use the new way. There will be some scaffolding |
-// and construction cones laying around. Try not to freak out. |
- |
-/** A generic view. */ |
-class View implements Positionable { |
- Element _node; |
- ViewLayout _layout; |
- |
- // TODO(jmesserly): instead of tracking this on every View, we could have the |
- // App track the views that want to be notified of resize() |
- EventListener _resizeHandler; |
- |
- /** |
- * Style properties configured for this view. |
- */ |
- // TODO(jmesserly): We should be getting these from our CSS preprocessor. |
- // I'm not sure if this will stay as a Map, or just be a get method. |
- // TODO(jacobr): Consider returning a somewhat typed base.Style wrapper |
- // object instead, and integrating with built in CSS properties. |
- final Map<String, String> customStyle; |
- |
- View() |
- : customStyle = new Map<String, String>(); |
- |
- View.fromNode(Element this._node) |
- : customStyle = new Map<String, String>(); |
- |
- View.html(String html) |
- : customStyle = new Map<String, String>(), |
- _node = new Element.html(html); |
- |
- // TODO(rnystrom): Get rid of this when all views are refactored to not use |
- // it. |
- Element get node() { |
- // Lazy render. |
- if (_node === null) { |
- _render(); |
- } |
- |
- return _node; |
- } |
- |
- /** |
- * A subclass that contains child views should override this to return those |
- * views. View uses this to ensure that child views are properly rendered |
- * and initialized when their parent view is without the parent having to |
- * manually handle that traversal. |
- */ |
- Collection<View> get childViews() { |
- return const []; |
- } |
- |
- /** |
- * View presumes the collection of views returned by childViews is more or |
- * less static after the view is first created. Subclasses should call this |
- * when that invariant doesn't hold to let View know that a new childView has |
- * appeared. |
- */ |
- void childViewAdded(View child) { |
- if (isInDocument) { |
- child._enterDocument(); |
- |
- // TODO(jmesserly): is this too expensive? |
- doLayout(); |
- } |
- } |
- |
- /** |
- * View presumes the collection of views returned by childViews is more or |
- * less static after the view is first created. Subclasses should call this |
- * when that invariant doesn't hold to let View know that a childView has |
- * been removed. |
- */ |
- void childViewRemoved(View child) { |
- if (isInDocument) { |
- child._exitDocument(); |
- } |
- } |
- |
- /** Gets whether this View has already been rendered or not. */ |
- bool get isRendered() { |
- return _node !== null; |
- } |
- |
- /** |
- * Gets whether this View (or one of its parents) has been added to the |
- * document or not. |
- */ |
- bool get isInDocument() { |
- return _node !== null && node.document.contains(node); |
- } |
- |
- /** |
- * Adds this view to the document as a child of the given node. This should |
- * generally only be called once for the top-level view. |
- */ |
- void addToDocument(Element parentNode) { |
- assert(!isInDocument); |
- |
- _render(); |
- parentNode.nodes.add(_node); |
- _hookGlobalLayoutEvents(); |
- _enterDocument(); |
- } |
- |
- void removeFromDocument() { |
- assert(isInDocument); |
- |
- // Remove runs in reverse order of how we entered. |
- _exitDocument(); |
- _unhookGlobalLayoutEvents(); |
- _node.remove(); |
- } |
- |
- /** |
- * Override this to generate the DOM structure for the view. |
- */ |
- // TODO(rnystrom): make this method abstract, see b/5015671 |
- Element render() { throw 'abstract'; } |
- |
- /** |
- * Override this to perform initialization behavior that requires access to |
- * the DOM associated with the View, such as event wiring. |
- */ |
- void afterRender(Element node) { |
- // Do nothing by default. |
- } |
- |
- /** |
- * Override this to perform behavior after this View has been added to the |
- * document. This is appropriate if you need access to state (such as the |
- * calculated size of an element) that's only available when the View is in |
- * the document. |
- * |
- * This will be called each time the View is added to the document, if it is |
- * added and removed multiple times. |
- */ |
- void enterDocument() {} |
- |
- /** |
- * Override this to perform behavior after this View has been removed from the |
- * document. This can be a convenient time to unregister event handlers bound |
- * in enterDocument(). |
- * |
- * This will be called each time the View is removed from the document, if it |
- * is added and removed multiple times. |
- */ |
- void exitDocument() {} |
- |
- /** Override this to perform behavior after the window is resized. */ |
- // TODO(jmesserly): this isn't really the event we want. Ideally we want to |
- // fire the event only if this particular View changed size. Also we should |
- // give a view the ability to measure itself when added to the document. |
- void windowResized() {} |
- |
- /** |
- * Registers the given listener callback to the given observable. Also |
- * immedially invokes the callback once as if a change has just come in. This |
- * lets you define a render() method that renders the skeleton of a view, then |
- * register a bunch of listeners which all fire to populate the view with |
- * model data. |
- */ |
- void watch(Observable observable, void watcher(EventSummary summary)) { |
- // Make a fake summary for the initial watch. |
- final summary = new EventSummary(observable); |
- watcher(summary); |
- |
- attachWatch(observable, watcher); |
- } |
- |
- /** Registers the given listener callback to the given observable. */ |
- void attachWatch(Observable observable, void watcher(EventSummary summary)) { |
- observable.addChangeListener(watcher); |
- |
- // TODO(rnystrom): Should keep track of this and unregister when the view |
- // is discarded. |
- } |
- |
- void addOnClick(EventListener handler) { |
- _node.on.click.add(handler); |
- } |
- |
- /** |
- * Gets whether the view is hidden. |
- */ |
- bool get hidden() => _node.style.display == 'none'; |
- |
- /** |
- * Sets whether the view is hidden. |
- */ |
- void set hidden(bool hidden) { |
- if (hidden) { |
- node.style.display = 'none'; |
- } else { |
- node.style.display = ''; |
- } |
- } |
- |
- void addClass(String className) { |
- node.classes.add(className); |
- } |
- |
- void removeClass(String className) { |
- node.classes.remove(className); |
- } |
- |
- /** Sets the CSS3 transform applied to the view. */ |
- set transform(String transform) { |
- node.style.transform = transform; |
- } |
- |
- // TODO(rnystrom): Get rid of this, or move into a separate class? |
- /** Creates a View whose node is a <div> with the given class(es). */ |
- static View div(String cssClass, [String body = null]) { |
- if (body == null) { |
- body = ''; |
- } |
- return new View.html('<div class="$cssClass">$body</div>'); |
- } |
- |
- /** |
- * Internal render method that deals with traversing child views. Should not |
- * be overridden. |
- */ |
- void _render() { |
- // TODO(rnystrom): Should render child views here. Not implemented yet. |
- // Instead, we rely on the parent accessing .node to implicitly cause the |
- // child to be rendered. |
- |
- // Render this view. |
- if (_node == null) { |
- _node = render(); |
- } |
- |
- // Pass the node back to the derived view so it can register event |
- // handlers on it. |
- afterRender(_node); |
- } |
- |
- /** |
- * Internal method that deals with traversing child views. Should not be |
- * overridden. |
- */ |
- void _enterDocument() { |
- // Notify the children first. |
- for (final child in childViews) { |
- child._enterDocument(); |
- } |
- |
- enterDocument(); |
- } |
- |
- // Layout related methods |
- |
- ViewLayout get layout() { |
- if (_layout == null) { |
- _layout = new ViewLayout.fromView(this); |
- } |
- return _layout; |
- } |
- |
- /** |
- * Internal method that deals with traversing child views. Should not be |
- * overridden. |
- */ |
- void _exitDocument() { |
- // Notify this View first so that it's children are still valid. |
- exitDocument(); |
- |
- // Notify the children. |
- for (final child in childViews) { |
- child._exitDocument(); |
- } |
- } |
- |
- /** |
- * If needed, starts a layout computation from the top level. |
- * Also hooks the relevant events like window resize, so we can layout on too |
- * demand. |
- */ |
- void _hookGlobalLayoutEvents() { |
- if (_resizeHandler == null) { |
- _resizeHandler = EventBatch.wrap((e) => doLayout()); |
- } |
- window.on.resize.add(_resizeHandler); |
- |
- // Trigger the initial layout. |
- doLayout(); |
- } |
- |
- void _unhookGlobalLayoutEvents() { |
- if (_resizeHandler != null) { |
- window.on.resize.remove(_resizeHandler); |
- _resizeHandler = null; |
- } |
- } |
- |
- void doLayout() { |
- _measureLayout().then((bool changed) { |
- if (changed) { |
- _applyLayoutToChildren(); |
- } |
- }); |
- } |
- |
- Future<bool> _measureLayout() { |
- final changed = new Completer<bool>(); |
- _measureLayoutHelper(changed); |
- |
- window.requestLayoutFrame(() { |
- if (!changed.future.isComplete) { |
- changed.complete(false); |
- } |
- }); |
- return changed.future; |
- } |
- |
- void _measureLayoutHelper(Completer<bool> changed) { |
- windowResized(); |
- |
- // TODO(jmesserly): this logic is more complex than it needs to be because |
- // we're taking pains to not initialize _layout if it's not needed. Is that |
- // a good tradeoff? |
- if (ViewLayout.hasCustomLayout(this)) { |
- Completer sizeCompleter = new Completer<Size>(); |
- _node.rect.then((ElementRect rect) { |
- sizeCompleter.complete( |
- new Size(rect.client.width, rect.client.height)); |
- }); |
- layout.measureLayout(sizeCompleter.future, changed); |
- } else { |
- for (final child in childViews) { |
- child._measureLayoutHelper(changed); |
- } |
- } |
- } |
- |
- void _applyLayoutToChildren() { |
- for (final child in childViews) { |
- child._applyLayout(); |
- } |
- } |
- |
- void _applyLayout() { |
- if (_layout != null) { |
- _layout.applyLayout(); |
- } |
- _applyLayoutToChildren(); |
- } |
-} |