Commit de70bd95 authored by Adam Barth's avatar Adam Barth

Merge pull request #1291 from abarth/fn2_tag_widgets

Add TagWidget to fn3
parents d9cb5251 fb4a87ad
...@@ -467,11 +467,59 @@ class Stack extends MultiChildRenderObjectWidget { ...@@ -467,11 +467,59 @@ class Stack extends MultiChildRenderObjectWidget {
void updateRenderObject(RenderStack renderObject, Stack oldWidget) { void updateRenderObject(RenderStack renderObject, Stack oldWidget) {
// Nothing to update // 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 { class Grid extends MultiChildRenderObjectWidget {
Grid(List<Widget> children, { Key key, this.maxChildExtent }) Grid(List<Widget> children, { Key key, this.maxChildExtent })
...@@ -514,8 +562,6 @@ class Flex extends MultiChildRenderObjectWidget { ...@@ -514,8 +562,6 @@ class Flex extends MultiChildRenderObjectWidget {
renderObject.alignItems = alignItems; renderObject.alignItems = alignItems;
renderObject.textBaseline = textBaseline; renderObject.textBaseline = textBaseline;
} }
// TODO(abarth): Update parent data
} }
class Row extends Flex { class Row extends Flex {
...@@ -536,7 +582,28 @@ class Column extends Flex { ...@@ -536,7 +582,28 @@ class Column extends Flex {
}) : super(children, key: key, direction: FlexDirection.vertical, justifyContent: justifyContent, alignItems: alignItems, textBaseline: textBaseline); }) : 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 { class Paragraph extends LeafRenderObjectWidget {
Paragraph({ Key key, this.text }) : super(key: key) { Paragraph({ Key key, this.text }) : super(key: key) {
...@@ -579,7 +646,51 @@ class StyledText extends StatelessComponent { ...@@ -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 { class Image extends LeafRenderObjectWidget {
Image({ Image({
......
...@@ -105,7 +105,11 @@ abstract class StatelessComponent extends Widget { ...@@ -105,7 +105,11 @@ abstract class StatelessComponent extends Widget {
/// Returns another Widget out of which this StatelessComponent is built. /// Returns another Widget out of which this StatelessComponent is built.
/// Typically that Widget will have been configured with further children, /// Typically that Widget will have been configured with further children,
/// such that really this function returns a tree of configuration. /// 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 /// StatefulComponents provide the configuration for
...@@ -162,7 +166,41 @@ abstract class ComponentState<T extends StatefulComponent> { ...@@ -162,7 +166,41 @@ abstract class ComponentState<T extends StatefulComponent> {
/// Returns another Widget out of which this StatefulComponent is built. /// Returns another Widget out of which this StatefulComponent is built.
/// Typically that Widget will have been configured with further children, /// Typically that Widget will have been configured with further children,
/// such that really this function returns a tree of configuration. /// 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) { bool _canUpdate(Widget oldWidget, Widget newWidget) {
...@@ -180,11 +218,15 @@ typedef void ElementVisitor(Element element); ...@@ -180,11 +218,15 @@ typedef void ElementVisitor(Element element);
const Object _uniqueChild = const Object(); const Object _uniqueChild = const Object();
abstract class BuildContext {
InheritedWidget inheritedWidgetOfType(Type targetType);
}
/// Elements are the instantiations of Widget configurations. /// Elements are the instantiations of Widget configurations.
/// ///
/// Elements can, in principle, have children. Only subclasses of /// Elements can, in principle, have children. Only subclasses of
/// RenderObjectElement are allowed to have more than one child. /// 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 { Element(T widget) : _widget = widget {
assert(_widget != null); assert(_widget != null);
} }
...@@ -355,9 +397,24 @@ abstract class Element<T extends Widget> { ...@@ -355,9 +397,24 @@ abstract class Element<T extends Widget> {
assert(_depth != null); assert(_depth != null);
assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; }); 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); typedef void BuildScheduler(BuildableElement element);
/// Base class for the instantiation of StatelessComponent and StatefulComponent /// Base class for the instantiation of StatelessComponent and StatefulComponent
...@@ -393,7 +450,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -393,7 +450,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
_dirty = false; _dirty = false;
Widget built; Widget built;
try { try {
built = _builder(); built = _builder(this);
assert(built != null); assert(built != null);
} catch (e, stack) { } catch (e, stack) {
_debugReportException('building $this', e, stack); _debugReportException('building $this', e, stack);
...@@ -428,10 +485,14 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -428,10 +485,14 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
super.unmount(); super.unmount();
_dirty = false; // so that we don't get rebuilt even if we're already marked dirty _dirty = false; // so that we don't get rebuilt even if we're already marked dirty
} }
void dependenciesChanged() {
markNeedsBuild();
}
} }
/// Instantiation of StatelessComponent widgets. /// Instantiation of StatelessComponent widgets.
class StatelessComponentElement extends BuildableElement<StatelessComponent> { class StatelessComponentElement<T extends StatelessComponent> extends BuildableElement<T> {
StatelessComponentElement(StatelessComponent widget) : super(widget) { StatelessComponentElement(StatelessComponent widget) : super(widget) {
_builder = _widget.build; _builder = _widget.build;
} }
...@@ -474,6 +535,52 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> { ...@@ -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 /// Base class for instantiations of RenderObjectWidget subclasses
abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> { abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
RenderObjectElement(T widget) RenderObjectElement(T widget)
...@@ -490,6 +597,16 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element ...@@ -490,6 +597,16 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
return ancestor; 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 Map<RenderObject, RenderObjectElement> _registry = new Map<RenderObject, RenderObjectElement>();
static Iterable<RenderObjectElement> getElementsForRenderObject(RenderObject renderObject) sync* { static Iterable<RenderObjectElement> getElementsForRenderObject(RenderObject renderObject) sync* {
Element target = _registry[renderObject]; Element target = _registry[renderObject];
...@@ -507,10 +624,13 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element ...@@ -507,10 +624,13 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
assert(_ancestorRenderObjectElement == null); assert(_ancestorRenderObjectElement == null);
_ancestorRenderObjectElement = _findAncestorRenderObjectElement(); _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, slot); _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, slot);
ParentDataElement parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
updateParentData(parentDataElement._widget);
} }
void update(T newWidget) { void update(T newWidget) {
Widget oldWidget = _widget; RenderObjectWidget oldWidget = _widget;
super.update(newWidget); super.update(newWidget);
assert(_widget == newWidget); assert(_widget == newWidget);
_widget.updateRenderObject(renderObject, oldWidget); _widget.updateRenderObject(renderObject, oldWidget);
...@@ -522,6 +642,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element ...@@ -522,6 +642,14 @@ abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element
_registry.remove(renderObject); _registry.remove(renderObject);
} }
void updateParentData(ParentDataWidget parentData) {
assert(() {
parentData.debugValidateAncestor(_ancestorRenderObjectElement._widget);
return true;
});
parentData.applyParentData(renderObject);
}
void detachRenderObject() { void detachRenderObject() {
if (_ancestorRenderObjectElement != null) { if (_ancestorRenderObjectElement != null) {
_ancestorRenderObjectElement.removeChildRenderObject(renderObject); _ancestorRenderObjectElement.removeChildRenderObject(renderObject);
...@@ -590,7 +718,15 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends ...@@ -590,7 +718,15 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends
class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> extends RenderObjectElement<T> { class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> extends RenderObjectElement<T> {
MultiChildRenderObjectElement(T widget) : super(widget); 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); typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack);
......
...@@ -11,7 +11,7 @@ final BoxDecoration kBoxDecorationC = new BoxDecoration(); ...@@ -11,7 +11,7 @@ final BoxDecoration kBoxDecorationC = new BoxDecoration();
class TestComponent extends StatelessComponent { class TestComponent extends StatelessComponent {
const TestComponent({ this.child }); const TestComponent({ this.child });
final Widget child; final Widget child;
Widget build() => child; Widget build(BuildContext context) => child;
} }
void main() { void main() {
......
...@@ -23,7 +23,7 @@ class TestComponentState extends ComponentState<TestComponentConfig> { ...@@ -23,7 +23,7 @@ class TestComponentState extends ComponentState<TestComponentConfig> {
}); });
} }
Widget build() { Widget build(BuildContext context) {
return _showLeft ? config.left : config.right; return _showLeft ? config.left : config.right;
} }
} }
...@@ -34,7 +34,7 @@ final BoxDecoration kBoxDecorationB = new BoxDecoration(); ...@@ -34,7 +34,7 @@ final BoxDecoration kBoxDecorationB = new BoxDecoration();
class TestBuildCounter extends StatelessComponent { class TestBuildCounter extends StatelessComponent {
static int buildCount = 0; static int buildCount = 0;
Widget build() { Widget build(BuildContext context) {
++buildCount; ++buildCount;
return new DecoratedBox(decoration: kBoxDecorationA); return new DecoratedBox(decoration: kBoxDecorationA);
} }
......
...@@ -15,7 +15,7 @@ class RootComponentState extends ComponentState<RootComponent> { ...@@ -15,7 +15,7 @@ class RootComponentState extends ComponentState<RootComponent> {
}); });
} }
} }
Widget build() => child; Widget build(BuildContext context) => child;
} }
const Object _rootSlot = const Object(); 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