Commit fb4a87ad authored by Adam Barth's avatar Adam Barth

Add ParentDataWidget and InheritedWidget to fn3

parent 5fb2cb32
......@@ -467,11 +467,59 @@ class Stack extends MultiChildRenderObjectWidget {
void updateRenderObject(RenderStack renderObject, Stack oldWidget) {
// Nothing to update
}
// TODO(abarth): Update parent data
}
// TODO(abarth): Positioned
class Positioned extends ParentDataWidget {
Positioned({
Key key,
Widget child,
this.top,
this.right,
this.bottom,
this.left
}) : super(key: key, child: child);
final double top;
final double right;
final double bottom;
final double left;
void debugValidateAncestor(Widget ancestor) {
assert(() {
'Positioned must placed inside a Stack';
return ancestor is Stack;
});
}
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is StackParentData);
final StackParentData parentData = renderObject.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 Grid extends MultiChildRenderObjectWidget {
Grid(List<Widget> children, { Key key, this.maxChildExtent })
......@@ -514,8 +562,6 @@ class Flex extends MultiChildRenderObjectWidget {
renderObject.alignItems = alignItems;
renderObject.textBaseline = textBaseline;
}
// TODO(abarth): Update parent data
}
class Row extends Flex {
......@@ -536,7 +582,28 @@ class Column extends Flex {
}) : super(children, key: key, direction: FlexDirection.vertical, justifyContent: justifyContent, alignItems: alignItems, textBaseline: textBaseline);
}
// TODO(abarth): Flexible
class Flexible extends ParentDataWidget {
Flexible({ Key key, this.flex: 1, Widget child })
: super(key: key, child: child);
final int flex;
void debugValidateAncestor(Widget ancestor) {
assert(() {
'Flexible must placed inside a Flex';
return ancestor is Flex;
});
}
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is FlexParentData);
final FlexParentData parentData = renderObject.parentData;
if (parentData.flex != flex) {
parentData.flex = flex;
renderObject.markNeedsLayout();
}
}
}
class Paragraph extends LeafRenderObjectWidget {
Paragraph({ Key key, this.text }) : super(key: key) {
......@@ -579,7 +646,51 @@ class StyledText extends StatelessComponent {
}
}
// TODO(abarth): Text
class DefaultTextStyle extends InheritedWidget {
DefaultTextStyle({
Key key,
this.style,
Widget child
}) : super(key: key, child: child) {
assert(style != null);
assert(child != null);
}
final TextStyle style;
static TextStyle of(BuildContext context) {
DefaultTextStyle result = context.inheritedWidgetOfType(DefaultTextStyle);
return result?.style;
}
bool updateShouldNotify(DefaultTextStyle old) => style != old.style;
}
class Text extends StatelessComponent {
Text(this.data, { Key key, TextStyle this.style }) : super(key: key) {
assert(data != null);
}
final String data;
final TextStyle style;
Widget build(BuildContext context) {
TextSpan text = new PlainTextSpan(data);
TextStyle defaultStyle = DefaultTextStyle.of(context);
TextStyle combinedStyle;
if (defaultStyle != null) {
if (style != null)
combinedStyle = defaultStyle.merge(style);
else
combinedStyle = defaultStyle;
} else {
combinedStyle = style;
}
if (combinedStyle != null)
text = new StyledTextSpan(combinedStyle, [text]);
return new Paragraph(text: text);
}
}
class Image extends LeafRenderObjectWidget {
Image({
......
......@@ -105,7 +105,11 @@ abstract class StatelessComponent extends Widget {
/// Returns another Widget out of which this StatelessComponent is built.
/// Typically that Widget will have been configured with further children,
/// such that really this function returns a tree of configuration.
Widget build();
///
/// The given build context object contains information about the location in
/// the tree at which this component is being built. For example, the context
/// provides the set of inherited widgets for this location in the tree.
Widget build(BuildContext context);
}
/// StatefulComponents provide the configuration for
......@@ -162,7 +166,41 @@ abstract class ComponentState<T extends StatefulComponent> {
/// Returns another Widget out of which this StatefulComponent is built.
/// Typically that Widget will have been configured with further children,
/// such that really this function returns a tree of configuration.
Widget build();
///
/// The given build context object contains information about the location in
/// the tree at which this component is being built. For example, the context
/// provides the set of inherited widgets for this location in the tree.
Widget build(BuildContext context);
}
abstract class ProxyWidget extends StatelessComponent {
const ProxyWidget({ Key key, Widget this.child }) : super(key: key);
final Widget child;
Widget build(BuildContext context) => child;
}
abstract class ParentDataWidget extends ProxyWidget {
ParentDataWidget({ Key key, Widget child })
: super(key: key, child: child);
/// Subclasses should override this function to ensure that they are placed
/// inside widgets that expect them.
///
/// The given ancestor is the first RenderObjectWidget ancestor of this widget.
void debugValidateAncestor(RenderObjectWidget ancestor);
void applyParentData(RenderObject renderObject);
}
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
InheritedElement createElement() => new InheritedElement(this);
bool updateShouldNotify(InheritedWidget oldWidget);
}
bool _canUpdate(Widget oldWidget, Widget newWidget) {
......@@ -180,11 +218,15 @@ typedef void ElementVisitor(Element element);
const Object _uniqueChild = const Object();
abstract class BuildContext {
InheritedWidget inheritedWidgetOfType(Type targetType);
}
/// Elements are the instantiations of Widget configurations.
///
/// Elements can, in principle, have children. Only subclasses of
/// RenderObjectElement are allowed to have more than one child.
abstract class Element<T extends Widget> {
abstract class Element<T extends Widget> implements BuildContext {
Element(T widget) : _widget = widget {
assert(_widget != null);
}
......@@ -355,9 +397,24 @@ abstract class Element<T extends Widget> {
assert(_depth != null);
assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; });
}
Set<Type> _dependencies;
InheritedWidget inheritedWidgetOfType(Type targetType) {
if (_dependencies == null)
_dependencies = new Set<Type>();
_dependencies.add(targetType);
Element ancestor = _parent;
while (ancestor != null && ancestor._widget.runtimeType != targetType)
ancestor = ancestor._parent;
return ancestor._widget;
}
void dependenciesChanged() {
assert(false);
}
}
typedef Widget WidgetBuilder();
typedef Widget WidgetBuilder(BuildContext context);
typedef void BuildScheduler(BuildableElement element);
/// Base class for the instantiation of StatelessComponent and StatefulComponent
......@@ -393,7 +450,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
_dirty = false;
Widget built;
try {
built = _builder();
built = _builder(this);
assert(built != null);
} catch (e, stack) {
_debugReportException('building $this', e, stack);
......@@ -428,10 +485,14 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
super.unmount();
_dirty = false; // so that we don't get rebuilt even if we're already marked dirty
}
void dependenciesChanged() {
markNeedsBuild();
}
}
/// Instantiation of StatelessComponent widgets.
class StatelessComponentElement extends BuildableElement<StatelessComponent> {
class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> {
StatelessComponentElement(StatelessComponent widget) : super(widget) {
_builder = _widget.build;
}
......@@ -474,6 +535,52 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
}
}
class ParentDataElement extends StatelessComponentElement<ParentDataWidget> {
ParentDataElement(ParentDataWidget widget) : super(widget);
void update(ParentDataWidget newWidget) {
ParentDataWidget oldWidget = _widget;
super.update(newWidget);
assert(_widget == newWidget);
if (_widget != oldWidget)
_notifyDescendants();
}
void _notifyDescendants() {
void notifyChildren(Element child) {
if (child is RenderObjectElement)
child.updateParentData(_widget);
else if (child is! ParentDataElement)
child.visitChildren(notifyChildren);
}
visitChildren(notifyChildren);
}
}
class InheritedElement extends StatelessComponentElement<InheritedWidget> {
InheritedElement(InheritedWidget widget) : super(widget);
void update(StatelessComponent newWidget) {
InheritedWidget oldWidget = _widget;
super.update(newWidget);
assert(_widget == newWidget);
if (_widget.updateShouldNotify(oldWidget))
_notifyDescendants();
}
void _notifyDescendants() {
final Type ourRuntimeType = runtimeType;
void notifyChildren(Element child) {
if (child._dependencies != null &&
child._dependencies.contains(ourRuntimeType))
child.dependenciesChanged();
if (child.runtimeType != ourRuntimeType)
child.visitChildren(notifyChildren);
}
visitChildren(notifyChildren);
}
}
/// Base class for instantiations of RenderObjectWidget subclasses
abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
RenderObjectElement(T widget)
......@@ -490,6 +597,16 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
return ancestor;
}
ParentDataElement _findAncestorParentDataElement() {
Element ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement) {
if (ancestor is ParentDataElement)
return ancestor;
ancestor = ancestor._parent;
}
return null;
}
static Map<RenderObject, RenderObjectElement> _registry = new Map<RenderObject, RenderObjectElement>();
static Iterable<RenderObjectElement> getElementsForRenderObject(RenderObject renderObject) sync* {
Element target = _registry[renderObject];
......@@ -507,10 +624,13 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
assert(_ancestorRenderObjectElement == null);
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, slot);
ParentDataElement parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
updateParentData(parentDataElement._widget);
}
void update(T newWidget) {
Widget oldWidget = _widget;
RenderObjectWidget oldWidget = _widget;
super.update(newWidget);
assert(_widget == newWidget);
_widget.updateRenderObject(renderObject, oldWidget);
......@@ -522,6 +642,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
_registry.remove(renderObject);
}
void updateParentData(ParentDataWidget parentData) {
assert(() {
parentData.debugValidateAncestor(_ancestorRenderObjectElement._widget);
return true;
});
parentData.applyParentData(renderObject);
}
void detachRenderObject() {
if (_ancestorRenderObjectElement != null) {
_ancestorRenderObjectElement.removeChildRenderObject(renderObject);
......@@ -590,7 +718,15 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends
class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> extends RenderObjectElement<T> {
MultiChildRenderObjectElement(T widget) : super(widget);
// TODO(ianh): implement
void insertChildRenderObject(RenderObject child, dynamic slot) {
// TODO(ianh): implement
assert(false);
}
void removeChildRenderObject(RenderObject child) {
// TODO(ianh): implement
assert(false);
}
}
typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack);
......
......@@ -11,7 +11,7 @@ final BoxDecoration kBoxDecorationC = new BoxDecoration();
class TestComponent extends StatelessComponent {
const TestComponent({ this.child });
final Widget child;
Widget build() => child;
Widget build(BuildContext context) => child;
}
void main() {
......
......@@ -23,7 +23,7 @@ class TestComponentState extends ComponentState<TestComponentConfig> {
});
}
Widget build() {
Widget build(BuildContext context) {
return _showLeft ? config.left : config.right;
}
}
......@@ -34,7 +34,7 @@ final BoxDecoration kBoxDecorationB = new BoxDecoration();
class TestBuildCounter extends StatelessComponent {
static int buildCount = 0;
Widget build() {
Widget build(BuildContext context) {
++buildCount;
return new DecoratedBox(decoration: kBoxDecorationA);
}
......
......@@ -15,7 +15,7 @@ class RootComponentState extends ComponentState<RootComponent> {
});
}
}
Widget build() => child;
Widget build(BuildContext context) => child;
}
const Object _rootSlot = const Object();
......
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