Commit b9489678 authored by Hixie's avatar Hixie

fn3: MixedViewport

Also:
- Expose the slot of each Element.
- Minor improvements to HomogeneousViewport.
- Replace TestComponent with FlipComponent in tests.
parent 878775bb
......@@ -24,11 +24,12 @@ export 'fn3/focus.dart';
export 'fn3/framework.dart';
export 'fn3/gesture_detector.dart';
export 'fn3/homogeneous_viewport.dart';
export 'fn3/icon_button.dart';
export 'fn3/icon.dart';
export 'fn3/icon_button.dart';
export 'fn3/ink_well.dart';
export 'fn3/material_button.dart';
export 'fn3/material.dart';
export 'fn3/material_button.dart';
export 'fn3/mixed_viewport.dart';
export 'fn3/navigator.dart';
export 'fn3/popup_menu.dart';
export 'fn3/popup_menu_item.dart';
......
......@@ -419,6 +419,7 @@ abstract class Element<T extends Widget> implements BuildContext {
///
/// Subclasses of Element that only have one child should use null for
/// the slot for that child.
dynamic get slot => _slot;
dynamic _slot;
/// An integer that is guaranteed to be greater than the parent's, if any.
......@@ -488,12 +489,12 @@ abstract class Element<T extends Widget> implements BuildContext {
}
if (child != null) {
if (child.widget == newWidget) {
if (child._slot != newSlot)
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (_canUpdate(child.widget, newWidget)) {
if (child._slot != newSlot)
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
......@@ -517,7 +518,7 @@ abstract class Element<T extends Widget> implements BuildContext {
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._debugLifecycleState == _ElementLifecycle.mounted);
assert(_slot == null);
assert(slot == null);
assert(depth == null);
_parent = parent;
_slot = newSlot;
......@@ -654,12 +655,12 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
}
try {
_child = updateChild(_child, built, _slot);
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
_debugReportException('building $this', e, stack);
built = new ErrorWidget();
_child = updateChild(null, built, _slot);
_child = updateChild(null, built, slot);
}
}
......@@ -735,6 +736,7 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
StatefulComponentElement(StatefulComponent widget)
: _state = widget.createState(), super(widget) {
assert(_state._config == widget);
assert(_state._element == null);
_state._element = this;
_builder = _state.build;
}
......@@ -755,6 +757,7 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
void unmount() {
super.unmount();
_state.dispose();
_state._element = null;
_state = null;
}
}
......@@ -1016,6 +1019,7 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
void unmount() {
super.unmount();
assert(!renderObject.attached);
widget.didUnmountRenderObject(renderObject);
}
......@@ -1024,10 +1028,10 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
}
void _updateSlot(dynamic newSlot) {
assert(_slot != newSlot);
assert(slot != newSlot);
super._updateSlot(newSlot);
assert(_slot == newSlot);
_ancestorRenderObjectElement.moveChildRenderObject(renderObject, _slot);
assert(slot == newSlot);
_ancestorRenderObjectElement.moveChildRenderObject(renderObject, slot);
}
void detachRenderObject() {
......
......@@ -30,11 +30,11 @@ class HomogeneousViewport extends RenderObjectWidget {
final ScrollDirection direction;
final double startOffset;
RenderObjectElement createElement() => new HomogeneousViewportElement(this);
HomogeneousViewportElement createElement() => new HomogeneousViewportElement(this);
// we don't pass constructor arguments to the RenderBlockViewport() because until
// we know our children, the constructor arguments we could give have no effect
RenderObject createRenderObject() => new RenderBlockViewport();
RenderBlockViewport createRenderObject() => new RenderBlockViewport();
bool isLayoutDifferentThan(HomogeneousViewport oldWidget) {
return itemsWrap != oldWidget.itemsWrap ||
......@@ -163,7 +163,8 @@ class HomogeneousViewportElement extends RenderObjectElement<HomogeneousViewport
renderObject.add(child, before: nextSibling);
}
void moveChildRenderObject(RenderObject child, dynamic slot) {
void moveChildRenderObject(RenderObject child, Element slot) {
assert(child.parent == renderObject);
RenderObject nextSibling = slot?.renderObject;
renderObject.move(child, before: nextSibling);
}
......
This diff is collapsed.
......@@ -2,25 +2,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
class TestComponent extends StatefulComponent {
TestComponent(this.viewport);
final HomogeneousViewport viewport;
TestComponentState createState() => new TestComponentState(this);
}
class TestComponentState extends ComponentState<TestComponent> {
TestComponentState(TestComponent config): super(config);
bool _flag = true;
void go(bool flag) {
setState(() {
_flag = flag;
});
}
Widget build(BuildContext context) {
return _flag ? config.viewport : new Text('Not Today');
}
}
import 'test_widgets.dart';
void main() {
test('HomogeneousViewport mount/dismount smoke test', () {
......@@ -32,39 +14,42 @@ void main() {
// so if our widget is 100 pixels tall, it should fit exactly 6 times.
Widget builder() {
return new TestComponent(new HomogeneousViewport(
builder: (BuildContext context, int start, int count) {
List<Widget> result = <Widget>[];
for (int index = start; index < start + count; index += 1) {
callbackTracker.add(index);
result.add(new Container(
key: new ValueKey<int>(index),
height: 100.0,
child: new Text("$index")
));
}
return result;
},
startOffset: 0.0,
itemExtent: 100.0
));
return new FlipComponent(
left: new HomogeneousViewport(
builder: (BuildContext context, int start, int count) {
List<Widget> result = <Widget>[];
for (int index = start; index < start + count; index += 1) {
callbackTracker.add(index);
result.add(new Container(
key: new ValueKey<int>(index),
height: 100.0,
child: new Text("$index")
));
}
return result;
},
startOffset: 0.0,
itemExtent: 100.0
),
right: new Text('Not Today')
);
}
tester.pumpFrame(builder());
StatefulComponentElement testComponent = tester.findElement((element) => element.widget is TestComponent);
TestComponentState testComponentState = testComponent.state;
StatefulComponentElement element = tester.findElement((element) => element.widget is FlipComponent);
FlipComponentState testComponent = element.state;
expect(callbackTracker, equals([0, 1, 2, 3, 4, 5]));
callbackTracker.clear();
testComponentState.go(false);
testComponent.flip();
tester.pumpFrameWithoutChange();
expect(callbackTracker, equals([]));
callbackTracker.clear();
testComponentState.go(true);
testComponent.flip();
tester.pumpFrameWithoutChange();
expect(callbackTracker, equals([0, 1, 2, 3, 4, 5]));
......@@ -95,13 +80,16 @@ void main() {
return result;
};
TestComponent testComponent;
FlipComponent testComponent;
Widget builder() {
testComponent = new TestComponent(new HomogeneousViewport(
builder: itemBuilder,
startOffset: offset,
itemExtent: 200.0
));
testComponent = new FlipComponent(
left: new HomogeneousViewport(
builder: itemBuilder,
startOffset: offset,
itemExtent: 200.0
),
right: new Text('Not Today')
);
return testComponent;
}
......@@ -145,14 +133,17 @@ void main() {
return result;
};
TestComponent testComponent;
FlipComponent testComponent;
Widget builder() {
testComponent = new TestComponent(new HomogeneousViewport(
builder: itemBuilder,
startOffset: offset,
itemExtent: 200.0,
direction: ScrollDirection.horizontal
));
testComponent = new FlipComponent(
left: new HomogeneousViewport(
builder: itemBuilder,
startOffset: offset,
itemExtent: 200.0,
direction: ScrollDirection.horizontal
),
right: new Text('Not Today')
);
return testComponent;
}
......
import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
import 'test_widgets.dart';
void main() {
test('MixedViewport mount/dismount smoke test', () {
WidgetTester tester = new WidgetTester();
List<int> callbackTracker = <int>[];
// the root view is 800x600 in the test environment
// so if our widget is 100 pixels tall, it should fit exactly 6 times.
Widget builder() {
return new FlipComponent(
left: new MixedViewport(
builder: (BuildContext context, int i) {
callbackTracker.add(i);
return new Container(
key: new ValueKey<int>(i),
height: 100.0,
child: new Text("$i")
);
},
startOffset: 0.0
),
right: new Text('Not Today')
);
}
tester.pumpFrame(builder());
StatefulComponentElement element = tester.findElement((element) => element.widget is FlipComponent);
FlipComponentState testComponent = element.state;
expect(callbackTracker, equals([0, 1, 2, 3, 4, 5]));
callbackTracker.clear();
testComponent.flip();
tester.pumpFrameWithoutChange();
expect(callbackTracker, equals([]));
callbackTracker.clear();
testComponent.flip();
tester.pumpFrameWithoutChange();
expect(callbackTracker, equals([0, 1, 2, 3, 4, 5]));
});
test('MixedViewport vertical', () {
WidgetTester tester = new WidgetTester();
List<int> callbackTracker = <int>[];
// the root view is 800x600 in the test environment
// so if our widget is 200 pixels tall, it should fit exactly 3 times.
// but if we are offset by 300 pixels, there will be 4, numbered 1-4.
double offset = 300.0;
IndexedBuilder itemBuilder = (BuildContext context, int i) {
callbackTracker.add(i);
return new Container(
key: new ValueKey<int>(i),
width: 500.0, // this should be ignored
height: 200.0,
child: new Text("$i")
);
};
Widget builder() {
return new FlipComponent(
left: new MixedViewport(
builder: itemBuilder,
startOffset: offset
),
right: new Text('Not Today')
);
}
tester.pumpFrame(builder());
// 0 is built to find its width
expect(callbackTracker, equals([0, 1, 2, 3, 4]));
callbackTracker.clear();
offset = 400.0; // now only 3 should fit, numbered 2-4.
tester.pumpFrame(builder());
// 0 and 1 aren't built, we know their size and nothing else changed
expect(callbackTracker, equals([2, 3, 4]));
callbackTracker.clear();
});
test('MixedViewport horizontal', () {
WidgetTester tester = new WidgetTester();
List<int> callbackTracker = <int>[];
// the root view is 800x600 in the test environment
// so if our widget is 200 pixels wide, it should fit exactly 4 times.
// but if we are offset by 300 pixels, there will be 5, numbered 1-5.
double offset = 300.0;
IndexedBuilder itemBuilder = (BuildContext context, int i) {
callbackTracker.add(i);
return new Container(
key: new ValueKey<int>(i),
height: 500.0, // this should be ignored
width: 200.0,
child: new Text("$i")
);
};
Widget builder() {
return new FlipComponent(
left: new MixedViewport(
builder: itemBuilder,
startOffset: offset,
direction: ScrollDirection.horizontal
),
right: new Text('Not Today')
);
}
tester.pumpFrame(builder());
// 0 is built to find its width
expect(callbackTracker, equals([0, 1, 2, 3, 4, 5]));
callbackTracker.clear();
offset = 400.0; // now only 4 should fit, numbered 2-5.
tester.pumpFrame(builder());
// 0 and 1 aren't built, we know their size and nothing else changed
expect(callbackTracker, equals([2, 3, 4, 5]));
callbackTracker.clear();
});
}
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