Commit bd038cfc authored by Adam Barth's avatar Adam Barth

Merge pull request #972 from abarth/parent_data_madness

Positioned 'remembers' things it shouldn't
parents 17476a68 64a78414
...@@ -9,10 +9,10 @@ import 'package:sky/rendering/object.dart'; ...@@ -9,10 +9,10 @@ import 'package:sky/rendering/object.dart';
export 'package:sky/rendering/object.dart' show EventDisposition; export 'package:sky/rendering/object.dart' show EventDisposition;
class FlexBoxParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> { class FlexParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {
int flex; int flex;
void merge(FlexBoxParentData other) { void merge(FlexParentData other) {
if (other.flex != null) if (other.flex != null)
flex = other.flex; flex = other.flex;
super.merge(other); super.merge(other);
...@@ -41,8 +41,8 @@ enum FlexAlignItems { ...@@ -41,8 +41,8 @@ enum FlexAlignItems {
typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints); typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexBoxParentData>, class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, FlexBoxParentData> { RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData> {
// lays out RenderBox children using flexible layout // lays out RenderBox children using flexible layout
RenderFlex({ RenderFlex({
...@@ -98,8 +98,8 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -98,8 +98,8 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
double _overflow; double _overflow;
void setupParentData(RenderBox child) { void setupParentData(RenderBox child) {
if (child.parentData is! FlexBoxParentData) if (child.parentData is! FlexParentData)
child.parentData = new FlexBoxParentData(); child.parentData = new FlexParentData();
} }
double _getIntrinsicSize({ BoxConstraints constraints, double _getIntrinsicSize({ BoxConstraints constraints,
...@@ -133,7 +133,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -133,7 +133,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
} else { } else {
inflexibleSpace += childSize(child, childConstraints); inflexibleSpace += childSize(child, childConstraints);
} }
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
child = child.parentData.nextSibling; child = child.parentData.nextSibling;
} }
double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace; double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace;
...@@ -196,7 +196,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -196,7 +196,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
inflexibleSpace += mainSize; inflexibleSpace += mainSize;
maxCrossSize = math.max(maxCrossSize, crossSize); maxCrossSize = math.max(maxCrossSize, crossSize);
} }
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
child = child.parentData.nextSibling; child = child.parentData.nextSibling;
} }
...@@ -224,7 +224,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -224,7 +224,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
} }
maxCrossSize = math.max(maxCrossSize, crossSize); maxCrossSize = math.max(maxCrossSize, crossSize);
} }
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
child = child.parentData.nextSibling; child = child.parentData.nextSibling;
} }
...@@ -276,7 +276,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -276,7 +276,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
} }
int _getFlex(RenderBox child) { int _getFlex(RenderBox child) {
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
return child.parentData.flex != null ? child.parentData.flex : 0; return child.parentData.flex != null ? child.parentData.flex : 0;
} }
...@@ -301,7 +301,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -301,7 +301,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
double freeSpace = canFlex ? mainSize : 0.0; double freeSpace = canFlex ? mainSize : 0.0;
RenderBox child = firstChild; RenderBox child = firstChild;
while (child != null) { while (child != null) {
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
totalChildren++; totalChildren++;
int flex = _getFlex(child); int flex = _getFlex(child);
if (flex > 0) { if (flex > 0) {
...@@ -392,7 +392,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -392,7 +392,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
if (distance != null) if (distance != null)
maxBaselineDistance = math.max(maxBaselineDistance, distance); maxBaselineDistance = math.max(maxBaselineDistance, distance);
} }
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
child = child.parentData.nextSibling; child = child.parentData.nextSibling;
} }
} }
...@@ -461,7 +461,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl ...@@ -461,7 +461,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
double childMainPosition = leadingSpace; double childMainPosition = leadingSpace;
child = firstChild; child = firstChild;
while (child != null) { while (child != null) {
assert(child.parentData is FlexBoxParentData); assert(child.parentData is FlexParentData);
double childCrossPosition; double childCrossPosition;
switch (_alignItems) { switch (_alignItems) {
case FlexAlignItems.stretch: case FlexAlignItems.stretch:
......
...@@ -485,22 +485,64 @@ class Stack extends MultiChildRenderObjectWrapper { ...@@ -485,22 +485,64 @@ class Stack extends MultiChildRenderObjectWrapper {
RenderStack createNode() => new RenderStack(); RenderStack createNode() => new RenderStack();
RenderStack get renderObject => super.renderObject; RenderStack get renderObject => super.renderObject;
void updateParentData(RenderObject child, Positioned positioned) {
if (positioned == null)
_updateParentDataWithValues(child, null, null, null, null);
else
_updateParentDataWithValues(child, positioned.top, positioned.right, positioned.bottom, positioned.left);
}
void _updateParentDataWithValues(RenderObject child, double top, double right, double bottom, double left) {
assert(child.parentData is StackParentData);
final StackParentData parentData = child.parentData;
bool needsLayout = false;
if (parentData.top != top) {
parentData.top = top;
needsLayout = true;
}
if (parentData.right != right) {
parentData.right = right;
needsLayout = true;
}
if (parentData.bottom != bottom) {
parentData.bottom = bottom;
needsLayout = true;
}
if (parentData.left != left) {
parentData.left = left;
needsLayout = true;
}
if (needsLayout)
renderObject.markNeedsLayout();
}
} }
class Positioned extends ParentDataNode { class Positioned extends ParentDataNode {
Positioned({ Positioned({
Key key, Key key,
Widget child, Widget child,
double top, this.top,
double right, this.right,
double bottom, this.bottom,
double left this.left
}) : super(child, }) : super(key: key, child: child);
new StackParentData()..top = top
..right = right final double top;
..bottom = bottom final double right;
..left = left, final double bottom;
key: key); final double left;
void debugValidateAncestor(Widget ancestor) {
assert(() {
'Positioned must placed directly inside a Stack';
return ancestor is Stack;
});
}
} }
class Grid extends MultiChildRenderObjectWrapper { class Grid extends MultiChildRenderObjectWrapper {
...@@ -548,6 +590,19 @@ class Flex extends MultiChildRenderObjectWrapper { ...@@ -548,6 +590,19 @@ class Flex extends MultiChildRenderObjectWrapper {
renderObject.alignItems = alignItems; renderObject.alignItems = alignItems;
renderObject.textBaseline = textBaseline; renderObject.textBaseline = textBaseline;
} }
void updateParentData(RenderObject child, Flexible flexible) {
_updateParentDataWithValues(child, flexible == null ? null : flexible.flex);
}
void _updateParentDataWithValues(RenderObject child, int flex) {
assert(child.parentData is FlexParentData);
final FlexParentData parentData = child.parentData;
if (parentData.flex != flex) {
parentData.flex = flex;
renderObject.markNeedsLayout();
}
}
} }
class Row extends Flex { class Row extends Flex {
...@@ -569,8 +624,17 @@ class Column extends Flex { ...@@ -569,8 +624,17 @@ class Column extends Flex {
} }
class Flexible extends ParentDataNode { class Flexible extends ParentDataNode {
Flexible({ Key key, int flex: 1, Widget child }) Flexible({ Key key, this.flex: 1, Widget child })
: super(child, new FlexBoxParentData()..flex = flex, key: key); : super(key: key, child: child);
final int flex;
void debugValidateAncestor(Widget ancestor) {
assert(() {
'Flexible must placed directly inside a Flex';
return ancestor is Flex;
});
}
} }
class Paragraph extends LeafRenderObjectWrapper { class Paragraph extends LeafRenderObjectWrapper {
......
...@@ -441,8 +441,10 @@ bool _canSync(Widget a, Widget b) { ...@@ -441,8 +441,10 @@ bool _canSync(Widget a, Widget b) {
// stylistic information, etc. // stylistic information, etc.
abstract class TagNode extends Widget { abstract class TagNode extends Widget {
TagNode(Widget child, { Key key }) TagNode({ Key key, Widget child })
: this.child = child, super(key: key); : this.child = child, super(key: key) {
assert(child != null);
}
// TODO(jackson): Remove this workaround for limitation of Dart mixins // TODO(jackson): Remove this workaround for limitation of Dart mixins
TagNode._withKey(Widget child, Key key) TagNode._withKey(Widget child, Key key)
...@@ -480,10 +482,15 @@ abstract class TagNode extends Widget { ...@@ -480,10 +482,15 @@ abstract class TagNode extends Widget {
} }
class ParentDataNode extends TagNode { abstract class ParentDataNode extends TagNode {
ParentDataNode(Widget child, this.parentData, { Key key }) ParentDataNode({ Key key, Widget child })
: super(child, key: key); : super(key: key, child: child);
final ParentData parentData;
/// Subclasses should override this function to ensure that they are placed
/// inside widgets that expect them.
///
/// The given ancestor is the first non-component ancestor of this widget.
void debugValidateAncestor(Widget ancestor);
} }
abstract class Inherited extends TagNode { abstract class Inherited extends TagNode {
...@@ -553,7 +560,7 @@ class Listener extends TagNode { ...@@ -553,7 +560,7 @@ class Listener extends TagNode {
onPointerUp: onPointerUp, onPointerUp: onPointerUp,
custom: custom custom: custom
), ),
super(child, key: key); super(key: key, child: child);
final Map<String, EventListener> listeners; final Map<String, EventListener> listeners;
...@@ -975,24 +982,30 @@ abstract class RenderObjectWrapper extends Widget { ...@@ -975,24 +982,30 @@ abstract class RenderObjectWrapper extends Widget {
// we give them their own slots for them to fit into us. // we give them their own slots for them to fit into us.
} }
/// Override this function if your RenderObjectWrapper uses a [ParentDataNode]
/// to program parent data into children.
void updateParentData(RenderObject child, ParentDataNode node) { }
void syncRenderObject(RenderObjectWrapper old) { void syncRenderObject(RenderObjectWrapper old) {
assert(old == null || old.renderObject == renderObject); assert(old == null || old.renderObject == renderObject);
ParentData parentData = null; ParentDataNode parentDataNode = null;
Widget ancestor = parent; for (Widget current = parent; current != null; current = current.parent) {
while (ancestor != null && ancestor is! RenderObjectWrapper) { assert(() {
if (ancestor is ParentDataNode && ancestor.parentData != null) { if (current is ParentDataNode) {
if (parentData != null) Widget ancestor = current.parent;
parentData.merge(ancestor.parentData); // this will throw if the types aren't the same while (ancestor != null && ancestor is Component)
else ancestor = ancestor.parent;
parentData = ancestor.parentData; current.debugValidateAncestor(ancestor);
}
return true;
});
if (current is ParentDataNode) {
assert(parentDataNode == null);
parentDataNode = current;
} else if (current is RenderObjectWrapper) {
current.updateParentData(renderObject, parentDataNode);
break;
} }
ancestor = ancestor.parent;
}
if (parentData != null) {
assert(renderObject.parentData != null);
renderObject.parentData.merge(parentData); // this will throw if the types aren't appropriate
if (ancestor != null && ancestor.renderObject != null)
ancestor.renderObject.markNeedsLayout();
} }
} }
......
import 'package:sky/widgets.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
void main() {
test('Can change position data', () {
WidgetTester tester = new WidgetTester();
tester.pumpFrame(() {
return new Stack([
new Positioned(
left: 10.0,
child: new Container(
width: 10.0,
height: 10.0
)
)
]);
});
Container container = tester.findWidget((Widget widget) => widget is Container);
expect(container.renderObject.parentData.top, isNull);
expect(container.renderObject.parentData.right, isNull);
expect(container.renderObject.parentData.bottom, isNull);
expect(container.renderObject.parentData.left, equals(10.0));
tester.pumpFrame(() {
return new Stack([
new Positioned(
right: 10.0,
child: new Container(
width: 10.0,
height: 10.0
)
)
]);
});
container = tester.findWidget((Widget widget) => widget is Container);
expect(container.renderObject.parentData.top, isNull);
expect(container.renderObject.parentData.right, equals(10.0));
expect(container.renderObject.parentData.bottom, isNull);
expect(container.renderObject.parentData.left, isNull);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment