| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:vector_math/vector_math.dart'; |
| |
| import '../rendering/block.dart'; |
| import '../rendering/box.dart'; |
| import '../rendering/flex.dart'; |
| import '../rendering/object.dart'; |
| import '../rendering/paragraph.dart'; |
| import '../rendering/stack.dart'; |
| import 'widget.dart'; |
| |
| export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims; |
| export '../rendering/flex.dart' show FlexDirection, FlexJustifyContent, FlexAlignItems; |
| export '../rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
| export 'widget.dart' show Widget, Component, App, Listener, ParentDataNode; |
| |
| |
| // PAINTING NODES |
| |
| class Opacity extends OneChildRenderObjectWrapper { |
| Opacity({ String key, this.opacity, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderOpacity get root => super.root; |
| final double opacity; |
| |
| RenderOpacity createNode() => new RenderOpacity(opacity: opacity); |
| |
| void syncRenderObject(Opacity old) { |
| super.syncRenderObject(old); |
| root.opacity = opacity; |
| } |
| } |
| |
| class DecoratedBox extends OneChildRenderObjectWrapper { |
| |
| DecoratedBox({ String key, this.decoration, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderDecoratedBox get root => super.root; |
| final BoxDecoration decoration; |
| |
| RenderDecoratedBox createNode() => new RenderDecoratedBox(decoration: decoration); |
| |
| void syncRenderObject(DecoratedBox old) { |
| super.syncRenderObject(old); |
| root.decoration = decoration; |
| } |
| |
| } |
| |
| class CustomPaint extends OneChildRenderObjectWrapper { |
| |
| CustomPaint({ String key, this.callback, this.token, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderCustomPaint get root => super.root; |
| final CustomPaintCallback callback; |
| final dynamic token; // set this to be repainted automatically when the token changes |
| |
| RenderCustomPaint createNode() => new RenderCustomPaint(callback: callback); |
| |
| void syncRenderObject(CustomPaint old) { |
| super.syncRenderObject(old); |
| if (old != null && old.token != token) |
| root.markNeedsPaint(); |
| root.callback = callback; |
| } |
| |
| void remove() { |
| root.callback = null; |
| super.remove(); |
| } |
| |
| } |
| |
| class ClipRect extends OneChildRenderObjectWrapper { |
| ClipRect({ String key, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderClipRect get root => super.root; |
| RenderClipRect createNode() => new RenderClipRect(); |
| } |
| |
| class ClipOval extends OneChildRenderObjectWrapper { |
| ClipOval({ String key, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderClipOval get root => super.root; |
| RenderClipOval createNode() => new RenderClipOval(); |
| } |
| |
| |
| // POSITIONING AND SIZING NODES |
| |
| class Transform extends OneChildRenderObjectWrapper { |
| |
| Transform({ String key, this.transform, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderTransform get root => super.root; |
| final Matrix4 transform; |
| |
| RenderTransform createNode() => new RenderTransform(transform: transform); |
| |
| void syncRenderObject(Transform old) { |
| super.syncRenderObject(old); |
| root.transform = transform; |
| } |
| |
| } |
| |
| class Padding extends OneChildRenderObjectWrapper { |
| |
| Padding({ String key, this.padding, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderPadding get root => super.root; |
| final EdgeDims padding; |
| |
| RenderPadding createNode() => new RenderPadding(padding: padding); |
| |
| void syncRenderObject(Padding old) { |
| super.syncRenderObject(old); |
| root.padding = padding; |
| } |
| |
| } |
| |
| class Center extends OneChildRenderObjectWrapper { |
| Center({ String key, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderPositionedBox get root => super.root; |
| RenderPositionedBox createNode() => new RenderPositionedBox(); |
| } |
| |
| class SizedBox extends OneChildRenderObjectWrapper { |
| |
| SizedBox({ |
| String key, |
| this.width, |
| this.height, |
| Widget child |
| }) : super(key: key, child: child); |
| |
| RenderConstrainedBox get root => super.root; |
| |
| final double width; |
| final double height; |
| |
| RenderConstrainedBox createNode() => new RenderConstrainedBox(additionalConstraints: _additionalConstraints()); |
| |
| BoxConstraints _additionalConstraints() { |
| var result = const BoxConstraints(); |
| if (width != null) |
| result = result.applyWidth(width); |
| if (height != null) |
| result = result.applyHeight(height); |
| return result; |
| } |
| |
| void syncRenderObject(SizedBox old) { |
| super.syncRenderObject(old); |
| root.additionalConstraints = _additionalConstraints(); |
| } |
| |
| } |
| |
| class ConstrainedBox extends OneChildRenderObjectWrapper { |
| |
| ConstrainedBox({ String key, this.constraints, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderConstrainedBox get root => super.root; |
| |
| final BoxConstraints constraints; |
| |
| RenderConstrainedBox createNode() => new RenderConstrainedBox(additionalConstraints: constraints); |
| |
| void syncRenderObject(ConstrainedBox old) { |
| super.syncRenderObject(old); |
| root.additionalConstraints = constraints; |
| } |
| |
| } |
| |
| class ShrinkWrapWidth extends OneChildRenderObjectWrapper { |
| ShrinkWrapWidth({ String key, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderShrinkWrapWidth get root => super.root; |
| RenderShrinkWrapWidth createNode() => new RenderShrinkWrapWidth(); |
| } |
| |
| class SizeObserver extends OneChildRenderObjectWrapper { |
| |
| SizeObserver({ String key, this.callback, Widget child }) |
| : super(key: key, child: child); |
| |
| RenderSizeObserver get root => super.root; |
| final SizeChangedCallback callback; |
| |
| RenderSizeObserver createNode() => new RenderSizeObserver(callback: callback); |
| |
| void syncRenderObject(SizeObserver old) { |
| super.syncRenderObject(old); |
| root.callback = callback; |
| } |
| |
| void remove() { |
| root.callback = null; |
| super.remove(); |
| } |
| |
| } |
| |
| |
| // CONVENIENCE CLASS TO COMBINE COMMON PAINTING, POSITIONING, AND SIZING NODES |
| |
| class Container extends Component { |
| |
| Container({ |
| String key, |
| this.child, |
| this.constraints, |
| this.decoration, |
| this.width, |
| this.height, |
| this.margin, |
| this.padding, |
| this.transform |
| }) : super(key: key); |
| |
| final Widget child; |
| final BoxConstraints constraints; |
| final BoxDecoration decoration; |
| final EdgeDims margin; |
| final EdgeDims padding; |
| final Matrix4 transform; |
| final double width; |
| final double height; |
| |
| Widget build() { |
| Widget current = child; |
| |
| if (child == null && width == null && height == null) |
| current = new SizedBox( |
| width: double.INFINITY, |
| height: double.INFINITY |
| ); |
| |
| if (padding != null) |
| current = new Padding(padding: padding, child: current); |
| |
| if (decoration != null) |
| current = new DecoratedBox(decoration: decoration, child: current); |
| |
| if (width != null || height != null) |
| current = new SizedBox( |
| width: width, |
| height: height, |
| child: current |
| ); |
| |
| if (constraints != null) |
| current = new ConstrainedBox(constraints: constraints, child: current); |
| |
| if (margin != null) |
| current = new Padding(padding: margin, child: current); |
| |
| if (transform != null) |
| current = new Transform(transform: transform, child: current); |
| |
| return current; |
| } |
| |
| } |
| |
| |
| // LAYOUT NODES |
| |
| class Block extends MultiChildRenderObjectWrapper { |
| Block(List<Widget> children, { String key }) |
| : super(key: key, children: children); |
| |
| RenderBlock get root => super.root; |
| RenderBlock createNode() => new RenderBlock(); |
| } |
| |
| class Stack extends MultiChildRenderObjectWrapper { |
| Stack(List<Widget> children, { String key }) |
| : super(key: key, children: children); |
| |
| RenderStack get root => super.root; |
| RenderStack createNode() => new RenderStack(); |
| } |
| |
| class Positioned extends ParentDataNode { |
| Positioned({ |
| String key, |
| Widget child, |
| double top, |
| double right, |
| double bottom, |
| double left |
| }) : super(child, |
| new StackParentData()..top = top |
| ..right = right |
| ..bottom = bottom |
| ..left = left, |
| key: key); |
| } |
| |
| class Flex extends MultiChildRenderObjectWrapper { |
| |
| Flex(List<Widget> children, { |
| String key, |
| this.direction: FlexDirection.horizontal, |
| this.justifyContent: FlexJustifyContent.flexStart, |
| this.alignItems: FlexAlignItems.center |
| }) : super(key: key, children: children); |
| |
| RenderFlex get root => super.root; |
| RenderFlex createNode() => new RenderFlex(direction: this.direction); |
| |
| final FlexDirection direction; |
| final FlexJustifyContent justifyContent; |
| final FlexAlignItems alignItems; |
| |
| void syncRenderObject(Widget old) { |
| super.syncRenderObject(old); |
| root.direction = direction; |
| root.justifyContent = justifyContent; |
| root.alignItems = alignItems; |
| } |
| |
| } |
| |
| class Flexible extends ParentDataNode { |
| Flexible({ String key, Widget child, int flex: 1 }) |
| : super(child, new FlexBoxParentData()..flex = flex, key: key); |
| } |
| |
| class Inline extends RenderObjectWrapper { |
| Inline({ Object key, this.text }) : super(key: key); |
| |
| RenderParagraph get root => super.root; |
| RenderParagraph createNode() => new RenderParagraph(text); |
| |
| final InlineBase text; |
| |
| void syncRenderObject(Widget old) { |
| super.syncRenderObject(old); |
| root.inline = text; |
| } |
| |
| void insert(RenderObjectWrapper child, dynamic slot) { |
| assert(false); |
| // Inline does not support having children currently |
| } |
| |
| } |
| |
| class Text extends Component { |
| Text(this.data, { TextStyle this.style }) : super(key: '*text*'); |
| final String data; |
| final TextStyle style; |
| bool get interchangeable => true; |
| Widget build() { |
| InlineBase text = new InlineText(data); |
| if (style != null) text = new InlineStyle(style, [text]); |
| return new Inline(text: text); |
| } |
| } |
| |
| class Image extends RenderObjectWrapper { |
| |
| Image({ |
| String key, |
| this.src, |
| this.size |
| }) : super(key: key); |
| |
| RenderImage get root => super.root; |
| RenderImage createNode() => new RenderImage(this.src, this.size); |
| |
| final String src; |
| final Size size; |
| |
| void syncRenderObject(Widget old) { |
| super.syncRenderObject(old); |
| root.src = src; |
| root.requestedSize = size; |
| } |
| |
| void insert(RenderObjectWrapper child, dynamic slot) { |
| assert(false); |
| // Image does not support having children currently |
| } |
| |
| } |
| |
| class WidgetToRenderBoxAdapter extends RenderObjectWrapper { |
| |
| WidgetToRenderBoxAdapter(RenderBox renderBox) |
| : this.renderBox = renderBox, |
| super(key: renderBox.hashCode.toString()); |
| |
| RenderBox get root => super.root; |
| RenderBox createNode() => this.renderBox; |
| |
| final RenderBox renderBox; |
| |
| void syncRenderObject(Widget old) { |
| super.syncRenderObject(old); |
| if (old != null) { |
| assert(old is WidgetToRenderBoxAdapter); |
| assert(root == old.root); |
| } |
| } |
| |
| void insert(RenderObjectWrapper child, dynamic slot) { |
| assert(false); |
| // WidgetToRenderBoxAdapter cannot have Widget children; by |
| // definition, it is the transition out of the Widget world. |
| } |
| |
| } |