提交 e8ead8bb 编写于 作者: H Hixie

[Effen] Port fn.dart from the legacy sky.Node backend to the RenderNode...

[Effen] Port fn.dart from the legacy sky.Node backend to the RenderNode backend, which is currently just a sky.Node-backed shim, but will eventually be the core Sky interface for layout and painting.

- the custom layout class in fn is removed by this patch; a new class
  will be added in a later CL

- the version of layout.dart in this CL is a subset of what we're
  targetting on the long run with
     https://codereview.chromium.org/1093633002

- a couple of lines of dead code are removed in this CL also

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/1117143003
上级 77803d38
......@@ -6,7 +6,6 @@ import '../animation/scroll_behavior.dart';
import '../debug/tracing.dart';
import '../fn.dart';
import 'dart:math' as math;
import 'dart:sky' as sky;
import 'dart:async';
import 'scrollable.dart';
......@@ -52,14 +51,11 @@ abstract class FixedHeightScrollable extends Scrollable {
var item = root.firstChild.firstChild;
if (item == null)
return;
sky.ClientRect scrollRect = root.getBoundingClientRect();
sky.ClientRect itemRect = item.getBoundingClientRect();
assert(scrollRect.height > 0);
assert(itemRect.height > 0);
setState(() {
_height = scrollRect.height;
_itemHeight = itemRect.height;
_height = root.height;
assert(_height > 0);
_itemHeight = item.height;
assert(_itemHeight > 0);
scrollBehavior.containerHeight = _height;
scrollBehavior.contentsHeight = _itemHeight * _itemCount;
});
......@@ -82,7 +78,6 @@ abstract class FixedHeightScrollable extends Scrollable {
'transform: translateY(${(-scrollOffset).toStringAsFixed(2)}px)';
} else {
drawCount = (_height / _itemHeight).round() + 1;
double alignmentOffset = math.max(0.0, scrollOffset);
double alignmentDelta = -scrollOffset % _itemHeight;
if (alignmentDelta != 0.0)
alignmentDelta -= _itemHeight;
......
......@@ -8,16 +8,16 @@ import '../fn.dart';
import '../theme/view_configuration.dart' as config;
import 'dart:async';
import 'dart:math' as math;
import 'dart:sky' as sky;
import '../layout.dart';
const double _kSplashConfirmedDuration = 350.0;
const double _kSplashUnconfirmedDuration = config.kDefaultLongPressTimeout;
const double _kSplashAbortDuration = 100.0;
const double _kSplashInitialDelay = 0.0; // we could delay initially in case the user scrolls
double _getSplashTargetSize(sky.ClientRect rect, double x, double y) {
return 2.0 * math.max(math.max(x - rect.left, rect.right - x),
math.max(y - rect.top, rect.bottom - y));
double _getSplashTargetSize(Rect rect, double x, double y) {
return 2.0 * math.max(math.max(x - rect.x, rect.x + rect.width - x),
math.max(y - rect.y, rect.y + rect.height - y));
}
class SplashController {
......@@ -56,10 +56,10 @@ class SplashController {
_size.stop();
}
SplashController(sky.ClientRect rect, double x, double y,
SplashController(Rect rect, double x, double y,
{ this.pointer, Function onDone })
: _offsetX = x - rect.left,
_offsetY = y - rect.top,
: _offsetX = x - rect.x,
_offsetY = y - rect.y,
_targetSize = _getSplashTargetSize(rect, x, y) {
_styleStream = _size.onValueChanged.map((p) {
......
......@@ -45,14 +45,13 @@ class InkWell extends Component implements ScrollClient {
);
}
sky.ClientRect _getBoundingRect() => (getRoot() as sky.Element).getBoundingClientRect();
void _startSplash(sky.GestureEvent event) {
setState(() {
if (_splashes == null)
_splashes = new LinkedHashSet<SplashController>();
var splash;
splash = new SplashController(_getBoundingRect(), event.x, event.y,
var root = getRoot();
splash = new SplashController(root.rect, event.x, event.y,
pointer: event.primaryPointer,
onDone: () { _splashDone(splash); });
_splashes.add(splash);
......
......@@ -92,8 +92,8 @@ class PopupMenu extends AnimatedComponent {
void _measureSize() {
setState(() {
var root = getRoot();
_width = root.clientWidth;
_height = root.clientHeight;
_width = root.width.round();
_height = root.height.round();
});
}
......
......@@ -8,51 +8,15 @@ import 'dart:async';
import 'dart:collection';
import 'dart:sky' as sky;
import 'reflect.dart' as reflect;
import 'layout.dart';
export 'layout.dart' show Style;
final sky.Tracing _tracing = sky.window.tracing;
final bool _shouldLogRenderDuration = false;
final bool _shouldTrace = false;
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._internal(className);
});
}
factory Style(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._internal(className);
});
}
Style._internal(this._className);
}
void _parentInsertBefore(sky.ParentNode parent,
sky.Node node,
sky.Node ref) {
if (ref != null) {
ref.insertBefore([node]);
} else {
parent.appendChild(node);
}
}
enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL }
/*
......@@ -63,7 +27,7 @@ abstract class UINode {
String _key;
UINode _parent;
UINode get parent => _parent;
sky.Node _root;
RenderCSS _root;
bool _defunct = false;
UINode({ Object key }) {
......@@ -74,7 +38,7 @@ abstract class UINode {
// if the |old| node has become stateful and should be retained.
bool _willSync(UINode old) => false;
void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore);
void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore);
void _remove() {
_defunct = true;
......@@ -118,8 +82,8 @@ abstract class UINode {
}
// Returns the child which should be retained as the child of this node.
UINode _syncChild(UINode node, UINode oldNode, sky.ParentNode host,
sky.Node insertBefore) {
UINode _syncChild(UINode node, UINode oldNode, RenderCSSContainer host,
RenderCSS insertBefore) {
assert(oldNode == null || node._key == oldNode._key);
......@@ -139,7 +103,7 @@ abstract class UINode {
_traceSync(_SyncOperation.STATEFUL, node._key);
oldNode._sync(node, host, insertBefore);
node._defunct = true;
assert(oldNode._root is sky.Node);
assert(oldNode._root is RenderCSS);
return oldNode;
}
......@@ -154,7 +118,7 @@ abstract class UINode {
if (oldNode != null)
oldNode._defunct = true;
assert(node._root is sky.Node);
assert(node._root is RenderCSS);
return node;
}
}
......@@ -164,14 +128,16 @@ abstract class ContentNode extends UINode {
ContentNode(UINode content) : this.content = content, super(key: content._key);
void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
UINode oldContent = old == null ? null : (old as ContentNode).content;
content = _syncChild(content, oldContent, host, insertBefore);
assert(content._root != null);
_root = content._root;
}
void _remove() {
_removeChild(content);
if (content != null)
_removeChild(content);
super._remove();
}
}
......@@ -183,31 +149,33 @@ class StyleNode extends ContentNode {
}
/*
* SkyNodeWrappers correspond to a desired state of a sky.Node. They are fully
* SkyNodeWrappers correspond to a desired state of a RenderCSS. They are fully
* immutable, with one exception: A UINode which is a Component which lives within
* an SkyElementWrapper's children list, may be replaced with the "old" instance if it
* has become stateful.
*/
abstract class SkyNodeWrapper extends UINode {
static final Map<sky.Node, SkyNodeWrapper> _nodeMap =
new HashMap<sky.Node, SkyNodeWrapper>();
static final Map<RenderCSS, SkyNodeWrapper> _nodeMap =
new HashMap<RenderCSS, SkyNodeWrapper>();
static SkyNodeWrapper _getMounted(sky.Node node) => _nodeMap[node];
static SkyNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node];
SkyNodeWrapper({ Object key }) : super(key: key);
SkyNodeWrapper get _emptyNode;
sky.Node _createNode();
RenderCSS _createNode();
void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
if (old == null) {
_root = _createNode();
_parentInsertBefore(host, _root, insertBefore);
assert(_root != null);
host.add(_root, before: insertBefore);
old = _emptyNode;
} else {
_root = old._root;
assert(_root != null);
}
_nodeMap[_root] = this;
......@@ -216,9 +184,14 @@ abstract class SkyNodeWrapper extends UINode {
void _syncNode(SkyNodeWrapper old);
void _removeChild(UINode node) {
assert(_root is RenderCSSContainer);
_root.remove(node._root);
super._removeChild(node);
}
void _remove() {
assert(_root != null);
_root.remove();
_nodeMap.remove(_root);
super._remove();
}
......@@ -314,12 +287,12 @@ class EventListenerNode extends ContentNode {
}
static void _dispatchEvent(sky.Event e) {
UINode target = SkyNodeWrapper._getMounted(e.target);
UINode target = SkyNodeWrapper._getMounted(bridgeEventTargetToRenderNode(e.target));
// TODO(rafaelw): StopPropagation?
while (target != null) {
if (target is EventListenerNode) {
(target as EventListenerNode)._handleEvent(e);
target._handleEvent(e);
}
target = target._parent;
......@@ -332,7 +305,7 @@ class EventListenerNode extends ContentNode {
}
}
void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
for (var type in listeners.keys) {
_ensureDocumentListener(type);
}
......@@ -354,19 +327,15 @@ class Text extends SkyNodeWrapper {
SkyNodeWrapper get _emptyNode => _emptyText;
static final Style _displayParagraph = new Style('display:paragraph');
sky.Node _createNode() {
return sky.document.createElement('div')
..setChild(new sky.Text(this.data))
..setAttribute('class', _displayParagraph._className);
RenderCSSText _root;
RenderCSS _createNode() {
return new RenderCSSText(this, this.data);
}
void _syncNode(SkyNodeWrapper old) {
if (old == _emptyText)
return; // we set inside _createNode();
(_root.firstChild as sky.Text).data = data;
_root.data = data;
}
}
......@@ -374,16 +343,10 @@ final List<UINode> _emptyList = new List<UINode>();
abstract class SkyElementWrapper extends SkyNodeWrapper {
String get _tagName;
sky.Node _createNode() => sky.document.createElement(_tagName);
final List<UINode> children;
final Style style;
final String inlineStyle;
String _class;
SkyElementWrapper({
Object key,
List<UINode> children,
......@@ -396,17 +359,18 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
}
void _remove() {
super._remove();
if (children != null) {
for (var child in children) {
_removeChild(child);
}
assert(children != null);
for (var child in children) {
assert(child != null);
_removeChild(child);
}
super._remove();
}
bool _debugHasDuplicateIds() {
var idSet = new HashSet<String>();
for (var child in children) {
assert(child != null);
if (child is Text) {
continue; // Text nodes all have the same key and are never reordered.
}
......@@ -419,42 +383,28 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
return false;
}
void _ensureClass() {
if (_class == null) {
List<Style> styles = new List<Style>();
if (style != null) {
styles.add(style);
}
UINode parent = _parent;
while (parent != null && parent is! SkyNodeWrapper) {
if (parent is StyleNode && (parent as StyleNode).style != null)
styles.add((parent as StyleNode).style);
parent = parent._parent;
}
_class = styles.map((s) => s._className).join(' ');
}
}
void _syncNode(SkyNodeWrapper old) {
SkyElementWrapper oldSkyElementWrapper = old as SkyElementWrapper;
sky.Element root = _root as sky.Element;
_ensureClass();
if (_class != oldSkyElementWrapper._class && _class != '')
root.setAttribute('class', _class);
List<Style> styles = new List<Style>();
if (style != null)
styles.add(style);
UINode parent = _parent;
while (parent != null && parent is! SkyNodeWrapper) {
if (parent is StyleNode && parent.style != null)
styles.add(parent.style);
parent = parent._parent;
}
_root.updateStyles(styles);
if (inlineStyle != oldSkyElementWrapper.inlineStyle)
root.setAttribute('style', inlineStyle);
_root.updateInlineStyle(inlineStyle);
_syncChildren(oldSkyElementWrapper);
}
void _syncChildren(SkyElementWrapper oldSkyElementWrapper) {
sky.Element root = _root as sky.Element;
assert(root != null);
if (_root is! RenderCSSContainer)
return;
var startIndex = 0;
var endIndex = children.length;
......@@ -463,12 +413,13 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
var oldStartIndex = 0;
var oldEndIndex = oldChildren.length;
sky.Node nextSibling = null;
RenderCSS nextSibling = null;
UINode currentNode = null;
UINode oldNode = null;
void sync(int atIndex) {
children[atIndex] = _syncChild(currentNode, oldNode, _root, nextSibling);
assert(children[atIndex] != null);
}
// Scan backwards from end of list while nodes can be directly synced
......@@ -484,7 +435,6 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
endIndex--;
oldEndIndex--;
sync(endIndex);
nextSibling = currentNode._root;
}
HashMap<String, UINode> oldNodeIdMap = null;
......@@ -526,19 +476,22 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
return false;
oldNodeIdMap[currentNode._key] = null; // mark it reordered.
_parentInsertBefore(root, oldNode._root, nextSibling);
assert(_root is RenderCSSContainer);
assert(oldNode._root is RenderCSSContainer);
oldSkyElementWrapper._root.remove(oldNode._root);
_root.add(oldNode._root, before: nextSibling);
return true;
}
// Scan forwards, this time we may re-order;
nextSibling = root.firstChild;
nextSibling = _root.firstChild;
while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
currentNode = children[startIndex];
oldNode = oldChildren[oldStartIndex];
if (currentNode._key == oldNode._key) {
assert(currentNode.runtimeType == oldNode.runtimeType);
nextSibling = nextSibling.nextSibling;
nextSibling = _root.childAfter(nextSibling);
sync(startIndex);
startIndex++;
advanceOldStartIndex();
......@@ -571,7 +524,7 @@ abstract class SkyElementWrapper extends SkyNodeWrapper {
class Container extends SkyElementWrapper {
String get _tagName => 'div';
RenderCSS _createNode() => new RenderCSSContainer(this);
static final Container _emptyContainer = new Container();
......@@ -590,49 +543,10 @@ class Container extends SkyElementWrapper {
);
}
abstract class LayoutContainer extends Container {
LayoutContainer({
Object key,
List<UINode> children,
Style style,
String inlineStyle
}) : super(
key: key,
children: children,
style: style,
inlineStyle: inlineStyle
);
sky.Node _createNode() {
var result = super._createNode();
result.setLayoutManager(() => layout(_root));
return result;
}
// If we ever reuse sky nodes for different classes, then we should
// call _root.setLayoutManager(null) during _remove() here.
void _syncNode(SkyNodeWrapper old) {
super._syncNode(old);
_root.setLayoutManager(() => layout(_root));
_root.setNeedsLayout();
}
void layout(sky.Element skyNode);
// set skyNode.width (e.g., set it to skyNode.parentNode.width)
// for each skyNode.getChildNodes()[i]:
// call .layout()
// set .x, .y
// set .width if you want to force a width
// set .height if you want to force a height
// set skyNode.height
}
class Image extends SkyElementWrapper {
String get _tagName => 'img';
RenderCSSImage _root;
RenderCSSImage _createNode() => new RenderCSSImage(this, this.src, this.width, this.height);
static final Image _emptyImage = new Image();
......@@ -659,56 +573,7 @@ class Image extends SkyElementWrapper {
void _syncNode(UINode old) {
super._syncNode(old);
Image oldImage = old as Image;
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 SkyElementWrapper {
String get _tagName => 'a';
static final Anchor _emptyAnchor = new Anchor();
UINode get _emptyNode => _emptyAnchor;
final String href;
final int width;
final int height;
Anchor({
Object key,
List<UINode> children,
Style style,
String inlineStyle,
this.width,
this.height,
this.href
}) : super(
key: key,
children: children,
style: style,
inlineStyle: inlineStyle
);
void _syncNode(UINode old) {
super._syncNode(old);
Anchor oldAnchor = old as Anchor;
sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement;
if (href != oldAnchor.href)
skyAnchor.href = href;
_root.configure(this.src, this.width, this.height);
}
}
......@@ -789,9 +654,6 @@ abstract class Component extends UINode {
bool get _isBuilding => _currentlyBuilding == this;
bool _dirty = true;
sky.Node get _host => _root.parentNode;
sky.Node get _insertionPoint => _root == null ? _root : _root.nextSibling;
UINode _built;
final int _order;
static int _currentOrder = 0;
......@@ -835,7 +697,7 @@ abstract class Component extends UINode {
// TODO(rafaelw): It seems wrong to expose DOM at all. This is presently
// needed to get sizing info.
sky.Node getRoot() => _root;
RenderCSS getRoot() => _root;
void _remove() {
assert(_built != null);
......@@ -871,7 +733,7 @@ abstract class Component extends UINode {
* 3) Syncing against an old version
* assert(_built == null && old != null)
*/
void _sync(UINode old, sky.ParentNode host, sky.Node insertBefore) {
void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
assert(!_defunct);
assert(_built == null || old == null);
......@@ -898,15 +760,15 @@ abstract class Component extends UINode {
_built = _syncChild(_built, oldBuilt, host, insertBefore);
_dirty = false;
_root = _built._root;
assert(_root != null);
}
void _buildIfDirty() {
if (!_dirty || _defunct)
return;
assert(_host != null);
_trace('$_key rebuilding...');
_sync(null, _host, _insertionPoint);
_sync(null, null, null); // TODO(ianh): figure out how passing "null, null, null" here is ok
}
void scheduleBuild() {
......@@ -927,11 +789,18 @@ abstract class Component extends UINode {
}
abstract class App extends Component {
sky.Node _host;
RenderCSS _host;
App() : super(stateful: true) {
_host = sky.document.createElement('div');
sky.document.appendChild(_host);
_host = new RenderCSSRoot(this);
_scheduleComponentForRender(this);
}
void _buildIfDirty() {
if (!_dirty || _defunct)
return;
_trace('$_key rebuilding...');
_sync(null, _host, _root);
}
}
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._internal(className);
});
}
factory Style(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._internal(className);
});
}
Style._internal(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
}
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', styles.map((s) => s._className).join(' '));
}
void updateInlineStyle(String newStyle) {
_skyElement.setAttribute('style', newStyle);
}
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 RenderCSSText extends RenderCSS {
RenderCSSText(debug, String newData) : super(debug) {
data = newData;
}
static final Style _displayParagraph = new Style('display:paragraph');
sky.Element createSkyElement() {
return sky.document.createElement('div')
..setChild(new sky.Text())
..setAttribute('class', _displayParagraph._className)
..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);
}
library node;
class Node {
// Nodes always have a 'depth' greater than their ancestors'.
// There's no guarantee regarding depth between siblings. The depth
// of a node is used to ensure that nodes are processed in depth
// order. The 'depth' of a child can be more than one greater than
// the 'depth' of the parent, because the 'depth' values are never
// decreased: all that matters is that it's greater than the parent.
// Consider a tree with a root node A, a child B, and a grandchild
// C. Initially, A will have 'depth' 0, B 'depth' 1, and C 'depth'
// 2. If C is moved to be a child of A, sibling of B, then the
// numbers won't change. C's 'depth' will still be 2.
int _depth = 0;
int get depth => _depth;
void redepthChild(Node child) { // internal, do not call
assert(child._attached == _attached);
if (child._depth <= _depth) {
child._depth = _depth + 1;
child.redepthChildren();
}
}
void redepthChildren() { // internal, do not call
// override this in subclasses with child nodes
// simply call redepthChild(child) for each child
}
bool _attached = false;
bool get attached => _attached;
void attach() {
// override this in subclasses with child nodes
// simply call attach() for each child then call your superclass
_attached = true;
attachChildren();
}
attachChildren() { } // workaround for lack of inter-class mixins in Dart
void detach() {
// override this in subclasses with child nodes
// simply call detach() for each child then call your superclass
_attached = false;
detachChildren();
}
detachChildren() { } // workaround for lack of inter-class mixins in Dart
void setAsChild(Node child) { // only for use by subclasses
assert(child != null);
if (attached)
child.attach();
redepthChild(child);
}
void dropChild(Node child) { // only for use by subclasses
assert(child != null);
assert(child.attached == attached);
if (attached)
child.detach();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册