| Index: sky/framework/layout.dart
|
| diff --git a/sky/framework/layout.dart b/sky/framework/layout.dart
|
| deleted file mode 100644
|
| index 2580942222ad0d4fdad1cbff456fd3ccf6a72268..0000000000000000000000000000000000000000
|
| --- a/sky/framework/layout.dart
|
| +++ /dev/null
|
| @@ -1,560 +0,0 @@
|
| -library layout;
|
| -
|
| -import 'node.dart';
|
| -import 'dart:sky' as sky;
|
| -import 'dart:collection';
|
| -
|
| -// UTILS
|
| -
|
| -// Bridge to legacy CSS-like style specification
|
| -// Eventually we'll replace this with something else
|
| -class Style {
|
| - final String _className;
|
| - static final Map<String, Style> _cache = new HashMap<String, Style>();
|
| -
|
| - static int _nextStyleId = 1;
|
| -
|
| - static String _getNextClassName() { return "style${_nextStyleId++}"; }
|
| -
|
| - Style extend(Style other) {
|
| - var className = "$_className ${other._className}";
|
| -
|
| - return _cache.putIfAbsent(className, () {
|
| - return new Style._construct(className);
|
| - });
|
| - }
|
| -
|
| - factory Style(String styles) {
|
| - assert(!styles.contains(new RegExp('\\b(display|flex|flex-direction)\\b')));
|
| - return new Style._addToCache(styles);
|
| - }
|
| -
|
| - factory Style._addToCache(String styles) {
|
| - return _cache.putIfAbsent(styles, () {
|
| - var className = _getNextClassName();
|
| - sky.Element styleNode = sky.document.createElement('style');
|
| - styleNode.setChild(new sky.Text(".$className { $styles }"));
|
| - sky.document.appendChild(styleNode);
|
| - return new Style._construct(className);
|
| - });
|
| - }
|
| -
|
| - Style._construct(this._className);
|
| -}
|
| -
|
| -class Rect {
|
| - const Rect(this.x, this.y, this.width, this.height);
|
| - final double x;
|
| - final double y;
|
| - final double width;
|
| - final double height;
|
| -}
|
| -
|
| -
|
| -// ABSTRACT LAYOUT
|
| -
|
| -class ParentData {
|
| - void detach() {
|
| - detachSiblings();
|
| - }
|
| - void detachSiblings() { } // workaround for lack of inter-class mixins in Dart
|
| - void merge(ParentData other) {
|
| - // override this in subclasses to merge in data from other into this
|
| - assert(other.runtimeType == this.runtimeType);
|
| - }
|
| -}
|
| -
|
| -abstract class RenderNode extends Node {
|
| -
|
| - // LAYOUT
|
| -
|
| - // parentData is only for use by the RenderNode that actually lays this
|
| - // node out, and any other nodes who happen to know exactly what
|
| - // kind of node that is.
|
| - ParentData parentData;
|
| - void setupPos(RenderNode child) {
|
| - // override this to setup .parentData correctly for your class
|
| - if (child.parentData is! ParentData)
|
| - child.parentData = new ParentData();
|
| - }
|
| -
|
| - void setAsChild(RenderNode child) { // only for use by subclasses
|
| - // call this whenever you decide a node is a child
|
| - assert(child != null);
|
| - setupPos(child);
|
| - super.setAsChild(child);
|
| - }
|
| - void dropChild(RenderNode child) { // only for use by subclasses
|
| - assert(child != null);
|
| - assert(child.parentData != null);
|
| - child.parentData.detach();
|
| - super.dropChild(child);
|
| - }
|
| -
|
| -}
|
| -
|
| -abstract class RenderBox extends RenderNode { }
|
| -
|
| -
|
| -// GENERIC MIXIN FOR RENDER NODES THAT TAKE A LIST OF CHILDREN
|
| -
|
| -abstract class ContainerParentDataMixin<ChildType extends RenderNode> {
|
| - ChildType previousSibling;
|
| - ChildType nextSibling;
|
| - void detachSiblings() {
|
| - if (previousSibling != null) {
|
| - assert(previousSibling.parentData is ContainerParentDataMixin<ChildType>);
|
| - assert(previousSibling != this);
|
| - assert(previousSibling.parentData.nextSibling == this);
|
| - previousSibling.parentData.nextSibling = nextSibling;
|
| - }
|
| - if (nextSibling != null) {
|
| - assert(nextSibling.parentData is ContainerParentDataMixin<ChildType>);
|
| - assert(nextSibling != this);
|
| - assert(nextSibling.parentData.previousSibling == this);
|
| - nextSibling.parentData.previousSibling = previousSibling;
|
| - }
|
| - previousSibling = null;
|
| - nextSibling = null;
|
| - }
|
| -}
|
| -
|
| -abstract class ContainerRenderNodeMixin<ChildType extends RenderNode, ParentDataType extends ContainerParentDataMixin<ChildType>> implements RenderNode {
|
| - // abstract class that has only InlineNode children
|
| -
|
| - bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType equals }) {
|
| - assert(child.parentData is ParentDataType);
|
| - while (child.parentData.previousSibling != null) {
|
| - assert(child.parentData.previousSibling != child);
|
| - child = child.parentData.previousSibling;
|
| - assert(child.parentData is ParentDataType);
|
| - }
|
| - return child == equals;
|
| - }
|
| - bool _debugUltimateNextSiblingOf(ChildType child, { ChildType equals }) {
|
| - assert(child.parentData is ParentDataType);
|
| - while (child.parentData.nextSibling != null) {
|
| - assert(child.parentData.nextSibling != child);
|
| - child = child.parentData.nextSibling;
|
| - assert(child.parentData is ParentDataType);
|
| - }
|
| - return child == equals;
|
| - }
|
| -
|
| - ChildType _firstChild;
|
| - ChildType _lastChild;
|
| - void add(ChildType child, { ChildType before }) {
|
| - assert(child != this);
|
| - assert(before != this);
|
| - assert(child != before);
|
| - assert(child != _firstChild);
|
| - assert(child != _lastChild);
|
| - setAsChild(child);
|
| - assert(child.parentData is ParentDataType);
|
| - assert(child.parentData.nextSibling == null);
|
| - assert(child.parentData.previousSibling == null);
|
| - if (before == null) {
|
| - // append at the end (_lastChild)
|
| - child.parentData.previousSibling = _lastChild;
|
| - if (_lastChild != null) {
|
| - assert(_lastChild.parentData is ParentDataType);
|
| - _lastChild.parentData.nextSibling = child;
|
| - }
|
| - _lastChild = child;
|
| - if (_firstChild == null)
|
| - _firstChild = child;
|
| - } else {
|
| - assert(_firstChild != null);
|
| - assert(_lastChild != null);
|
| - assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
|
| - assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
|
| - assert(before.parentData is ParentDataType);
|
| - if (before.parentData.previousSibling == null) {
|
| - // insert at the start (_firstChild); we'll end up with two or more children
|
| - assert(before == _firstChild);
|
| - child.parentData.nextSibling = before;
|
| - before.parentData.previousSibling = child;
|
| - _firstChild = child;
|
| - } else {
|
| - // insert in the middle; we'll end up with three or more children
|
| - // set up links from child to siblings
|
| - child.parentData.previousSibling = before.parentData.previousSibling;
|
| - child.parentData.nextSibling = before;
|
| - // set up links from siblings to child
|
| - assert(child.parentData.previousSibling.parentData is ParentDataType);
|
| - assert(child.parentData.nextSibling.parentData is ParentDataType);
|
| - child.parentData.previousSibling.parentData.nextSibling = child;
|
| - child.parentData.nextSibling.parentData.previousSibling = child;
|
| - assert(before.parentData.previousSibling == child);
|
| - }
|
| - }
|
| - markNeedsLayout();
|
| - }
|
| - void remove(ChildType child) {
|
| - assert(child.parentData is ParentDataType);
|
| - assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
|
| - assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
|
| - if (child.parentData.previousSibling == null) {
|
| - assert(_firstChild == child);
|
| - _firstChild = child.parentData.nextSibling;
|
| - } else {
|
| - assert(child.parentData.previousSibling.parentData is ParentDataType);
|
| - child.parentData.previousSibling.parentData.nextSibling = child.parentData.nextSibling;
|
| - }
|
| - if (child.parentData.nextSibling == null) {
|
| - assert(_lastChild == child);
|
| - _lastChild = child.parentData.previousSibling;
|
| - } else {
|
| - assert(child.parentData.nextSibling.parentData is ParentDataType);
|
| - child.parentData.nextSibling.parentData.previousSibling = child.parentData.previousSibling;
|
| - }
|
| - child.parentData.previousSibling = null;
|
| - child.parentData.nextSibling = null;
|
| - dropChild(child);
|
| - markNeedsLayout();
|
| - }
|
| - void redepthChildren() {
|
| - ChildType child = _firstChild;
|
| - while (child != null) {
|
| - redepthChild(child);
|
| - assert(child.parentData is ParentDataType);
|
| - child = child.parentData.nextSibling;
|
| - }
|
| - }
|
| - void attachChildren() {
|
| - ChildType child = _firstChild;
|
| - while (child != null) {
|
| - child.attach();
|
| - assert(child.parentData is ParentDataType);
|
| - child = child.parentData.nextSibling;
|
| - }
|
| - }
|
| - void detachChildren() {
|
| - ChildType child = _firstChild;
|
| - while (child != null) {
|
| - child.detach();
|
| - assert(child.parentData is ParentDataType);
|
| - child = child.parentData.nextSibling;
|
| - }
|
| - }
|
| -
|
| - ChildType get firstChild => _firstChild;
|
| - ChildType get lastChild => _lastChild;
|
| - ChildType childAfter(ChildType child) {
|
| - assert(child.parentData is ParentDataType);
|
| - return child.parentData.nextSibling;
|
| - }
|
| -
|
| -}
|
| -
|
| -
|
| -// CSS SHIMS
|
| -
|
| -abstract class RenderCSS extends RenderBox {
|
| -
|
| - dynamic debug;
|
| - sky.Element _skyElement;
|
| -
|
| - RenderCSS(this.debug) {
|
| - _skyElement = createSkyElement();
|
| - registerEventTarget(_skyElement, this);
|
| - }
|
| -
|
| - sky.Element createSkyElement();
|
| -
|
| - void updateStyles(List<Style> styles) {
|
| - _skyElement.setAttribute('class', stylesToClasses(styles));
|
| - }
|
| -
|
| - String stylesToClasses(List<Style> styles) {
|
| - return styles.map((s) => s._className).join(' ');
|
| - }
|
| -
|
| - String _inlineStyles = '';
|
| - String _additionalStylesFromParent = ''; // used internally to propagate parentData settings to the child
|
| -
|
| - void updateInlineStyle(String newStyle) {
|
| - assert(newStyle == null || !newStyle.contains(new RegExp('\\b(display|flex|flex-direction)\\b')));
|
| - _inlineStyles = newStyle != null ? newStyle : '';
|
| - _updateInlineStyleAttribute();
|
| - }
|
| -
|
| - void _updateInlineStyleAttribute() {
|
| - if ((_inlineStyles != '') && (_additionalStylesFromParent != ''))
|
| - _skyElement.setAttribute('style', "$_inlineStyles;$_additionalStylesFromParent");
|
| - else
|
| - _skyElement.setAttribute('style', "$_inlineStyles$_additionalStylesFromParent");
|
| - }
|
| -
|
| - double get width {
|
| - sky.ClientRect rect = _skyElement.getBoundingClientRect();
|
| - return rect.width;
|
| - }
|
| -
|
| - double get height {
|
| - sky.ClientRect rect = _skyElement.getBoundingClientRect();
|
| - return rect.height;
|
| - }
|
| -
|
| - Rect get rect {
|
| - sky.ClientRect rect = _skyElement.getBoundingClientRect();
|
| - return new Rect(rect.left, rect.top, rect.width, rect.height);
|
| - }
|
| -
|
| -}
|
| -
|
| -class CSSParentData extends ParentData with ContainerParentDataMixin<RenderCSS> { }
|
| -
|
| -class RenderCSSContainer extends RenderCSS with ContainerRenderNodeMixin<RenderCSS, CSSParentData> {
|
| -
|
| - RenderCSSContainer(debug) : super(debug);
|
| -
|
| - void setupPos(RenderNode child) {
|
| - if (child.parentData is! CSSParentData)
|
| - child.parentData = new CSSParentData();
|
| - }
|
| -
|
| - sky.Element createSkyElement() => sky.document.createElement('div')
|
| - ..setAttribute('debug', debug.toString());
|
| -
|
| - void markNeedsLayout() { }
|
| -
|
| - void add(RenderCSS child, { RenderCSS before }) {
|
| - if (before != null) {
|
| - assert(before._skyElement.parentNode != null);
|
| - assert(before._skyElement.parentNode == _skyElement);
|
| - }
|
| - super.add(child, before: before);
|
| - if (before != null) {
|
| - before._skyElement.insertBefore([child._skyElement]);
|
| - assert(child._skyElement.parentNode != null);
|
| - assert(child._skyElement.parentNode == _skyElement);
|
| - assert(child._skyElement.parentNode == before._skyElement.parentNode);
|
| - } else {
|
| - _skyElement.appendChild(child._skyElement);
|
| - }
|
| - }
|
| - void remove(RenderCSS child) {
|
| - child._skyElement.remove();
|
| - super.remove(child);
|
| - }
|
| -
|
| -}
|
| -
|
| -class FlexBoxParentData extends CSSParentData {
|
| - int flex;
|
| - void merge(FlexBoxParentData other) {
|
| - if (other.flex != null)
|
| - flex = other.flex;
|
| - super.merge(other);
|
| - }
|
| -}
|
| -
|
| -enum FlexDirection { Row, Column }
|
| -
|
| -class RenderCSSFlex extends RenderCSSContainer {
|
| -
|
| - RenderCSSFlex(debug, FlexDirection direction) : _direction = direction, super(debug);
|
| -
|
| - FlexDirection _direction;
|
| - FlexDirection get direction => _direction;
|
| - void set direction (FlexDirection value) {
|
| - _direction = value;
|
| - markNeedsLayout();
|
| - }
|
| -
|
| - void setupPos(RenderNode child) {
|
| - if (child.parentData is! FlexBoxParentData)
|
| - child.parentData = new FlexBoxParentData();
|
| - }
|
| -
|
| - static final Style _displayFlex = new Style._addToCache('display:flex');
|
| - static final Style _displayFlexRow = new Style._addToCache('flex-direction:row');
|
| - static final Style _displayFlexColumn = new Style._addToCache('flex-direction:column');
|
| -
|
| - String stylesToClasses(List<Style> styles) {
|
| - var settings = _displayFlex._className;
|
| - switch (_direction) {
|
| - case FlexDirection.Row: settings += ' ' + _displayFlexRow._className; break;
|
| - case FlexDirection.Column: settings += ' ' + _displayFlexColumn._className; break;
|
| - }
|
| - return super.stylesToClasses(styles) + ' ' + settings;
|
| - }
|
| -
|
| - void markNeedsLayout() {
|
| - super.markNeedsLayout();
|
| -
|
| - // pretend we did the layout:
|
| - RenderCSS child = _firstChild;
|
| - while (child != null) {
|
| - assert(child.parentData is FlexBoxParentData);
|
| - if (child.parentData.flex != null) {
|
| - child._additionalStylesFromParent = 'flex:${child.parentData.flex}';
|
| - child._updateInlineStyleAttribute();
|
| - }
|
| - child = child.parentData.nextSibling;
|
| - }
|
| - }
|
| -
|
| -}
|
| -
|
| -class StackParentData extends CSSParentData {
|
| - double top;
|
| - double left;
|
| - double right;
|
| - double bottom;
|
| - void merge(StackParentData other) {
|
| - if (other.top != null)
|
| - top = other.top;
|
| - if (other.left != null)
|
| - left = other.left;
|
| - if (other.right != null)
|
| - right = other.right;
|
| - if (other.bottom != null)
|
| - bottom = other.bottom;
|
| - super.merge(other);
|
| - }
|
| -}
|
| -
|
| -class RenderCSSStack extends RenderCSSContainer {
|
| -
|
| - RenderCSSStack(debug) : super(debug);
|
| -
|
| - void setupPos(RenderNode child) {
|
| - if (child.parentData is! StackParentData)
|
| - child.parentData = new StackParentData();
|
| - }
|
| -
|
| - static final Style _displayPosition = new Style._addToCache('transform:translateX(0);position:relative');
|
| -
|
| - String stylesToClasses(List<Style> styles) {
|
| - return super.stylesToClasses(styles) + ' ' + _displayPosition._className;
|
| - }
|
| -
|
| - void markNeedsLayout() {
|
| - super.markNeedsLayout();
|
| -
|
| - // pretend we did the layout:
|
| - RenderCSS child = _firstChild;
|
| - while (child != null) {
|
| - assert(child.parentData is StackParentData);
|
| - var style = 'position:absolute;';
|
| - if (child.parentData.top != null)
|
| - style += 'top:${child.parentData.top};';
|
| - if (child.parentData.left != null)
|
| - style += 'left:${child.parentData.left};';
|
| - if (child.parentData.right != null)
|
| - style += 'right:${child.parentData.right};';
|
| - if (child.parentData.bottom != null)
|
| - style += 'bottom:${child.parentData.bottom};';
|
| - child._additionalStylesFromParent = style;
|
| - child._updateInlineStyleAttribute();
|
| - child = child.parentData.nextSibling;
|
| - }
|
| - }
|
| -
|
| -}
|
| -
|
| -class RenderCSSParagraph extends RenderCSSContainer {
|
| -
|
| - RenderCSSParagraph(debug) : super(debug);
|
| -
|
| - static final Style _displayParagraph = new Style._addToCache('display:paragraph');
|
| -
|
| - String stylesToClasses(List<Style> styles) {
|
| - return super.stylesToClasses(styles) + ' ' + _displayParagraph._className;
|
| - }
|
| -
|
| -}
|
| -
|
| -class RenderCSSInline extends RenderCSS {
|
| -
|
| - RenderCSSInline(debug, String newData) : super(debug) {
|
| - data = newData;
|
| - }
|
| -
|
| - static final Style _displayInline = new Style._addToCache('display:inline');
|
| -
|
| - String stylesToClasses(List<Style> styles) {
|
| - return super.stylesToClasses(styles) + ' ' + _displayInline._className;
|
| - }
|
| -
|
| - sky.Element createSkyElement() {
|
| - return sky.document.createElement('div')
|
| - ..setChild(new sky.Text())
|
| - ..setAttribute('debug', debug.toString());
|
| - }
|
| -
|
| - void set data (String value) {
|
| - (_skyElement.firstChild as sky.Text).data = value;
|
| - }
|
| -
|
| -}
|
| -
|
| -class RenderCSSImage extends RenderCSS {
|
| -
|
| - RenderCSSImage(debug, String src, num width, num height) : super(debug) {
|
| - configure(src, width, height);
|
| - }
|
| -
|
| - sky.Element createSkyElement() {
|
| - return sky.document.createElement('img')
|
| - ..setAttribute('debug', debug.toString());
|
| - }
|
| -
|
| - void configure(String src, num width, num height) {
|
| - if (_skyElement.getAttribute('src') != src)
|
| - _skyElement.setAttribute('src', src);
|
| - _skyElement.style['width'] = '${width}px';
|
| - _skyElement.style['height'] = '${height}px';
|
| - }
|
| -
|
| -}
|
| -
|
| -class RenderCSSRoot extends RenderCSSContainer {
|
| - RenderCSSRoot(debug) : super(debug);
|
| - sky.Element createSkyElement() {
|
| - var result = super.createSkyElement();
|
| - assert(result != null);
|
| - sky.document.appendChild(result);
|
| - return result;
|
| - }
|
| -}
|
| -
|
| -
|
| -// legacy tools
|
| -Map<sky.EventTarget, RenderNode> _eventTargetRegistry = {};
|
| -void registerEventTarget(sky.EventTarget e, RenderNode n) {
|
| - _eventTargetRegistry[e] = n;
|
| -}
|
| -RenderNode bridgeEventTargetToRenderNode(sky.EventTarget e) {
|
| - return _eventTargetRegistry[e];
|
| -}
|
| -
|
| -
|
| -
|
| -
|
| -String _attributes(node) {
|
| - if (node is! sky.Element) return '';
|
| - var result = '';
|
| - var attrs = node.getAttributes();
|
| - for (var attr in attrs)
|
| - result += ' ${attr.name}="${attr.value}"';
|
| - return result;
|
| -}
|
| -
|
| -void _serialiseDOM(node, [String prefix = '']) {
|
| - if (node is sky.Text) {
|
| - print(prefix + 'text: "' + node.data.replaceAll('\n', '\\n') + '"');
|
| - return;
|
| - }
|
| - print(prefix + node.toString() + _attributes(node));
|
| - var children = node.getChildNodes();
|
| - prefix = prefix + ' ';
|
| - for (var child in children)
|
| - _serialiseDOM(child, prefix);
|
| -}
|
| -
|
| -void dumpState() {
|
| - _serialiseDOM(sky.document);
|
| -}
|
|
|