Commit 8601e237 authored by Adam Barth's avatar Adam Barth

Add some more docs to the rendering library

parent 63101e49
......@@ -88,6 +88,6 @@ void main() {
transformBox = new RenderTransform(child: flexRoot, transform: new Matrix4.identity());
RenderPadding root = new RenderPadding(padding: new EdgeDims.all(80.0), child: transformBox);
SkyBinding.instance.root = root;
SkyBinding.instance.renderView.child = root;
scheduler.addPersistentFrameCallback(rotate);
}
......@@ -7,24 +7,36 @@ import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path;
import 'package:vector_math/vector_math.dart';
/// A composited layer
///
/// During painting, the render tree generates a tree of composited layers that
/// are uploaded into the engine and displayed by the compositor. This class is
/// the base class for all composited layers.
abstract class Layer {
Layer({ this.offset: Offset.zero });
Offset offset; // From parent, in parent's coordinate system.
/// Offset from parent in the parent's coordinate system.
Offset offset;
ContainerLayer _parent;
/// This layer's parent in the layer tree
ContainerLayer get parent => _parent;
ContainerLayer _parent;
Layer _nextSibling;
/// This layer's next sibling in the parent layer's child list
Layer get nextSibling => _nextSibling;
Layer _nextSibling;
Layer _previousSibling;
/// This layer's previous sibling in the parent layer's child list
Layer get previousSibling => _previousSibling;
Layer _previousSibling;
/// Removes this layer from its parent layer's child list
void detach() {
if (_parent != null)
_parent.remove(this);
_parent._remove(this);
}
/// Replaces this layer with the given layer in the parent layer's child list
void replaceWith(Layer newLayer) {
assert(_parent != null);
assert(newLayer._parent == null);
......@@ -46,14 +58,27 @@ abstract class Layer {
_parent = null;
}
/// Override this function to upload this layer to the engine
///
/// The layerOffset is the accumulated offset of this layer's parent from the
/// origin of the builder's coordinate system.
void addToScene(sky.SceneBuilder builder, Offset layerOffset);
}
/// A composited layer containing a [Picture]
class PictureLayer extends Layer {
PictureLayer({ Offset offset: Offset.zero, this.paintBounds })
: super(offset: offset);
/// The rectangle in this layer's coodinate system that bounds the recording
///
/// The paint bounds are used to decide how much graphics memory to allocate
/// when rasterizing this layer.
Rect paintBounds;
/// The picture recorded for this layer
///
/// The picture's coodinate system matches this layer's coodinate system
sky.Picture picture;
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
......@@ -62,16 +87,17 @@ class PictureLayer extends Layer {
}
/// A composited layer that has a list of children
class ContainerLayer extends Layer {
ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset);
// TODO(ianh): hide firstChild since nobody uses it
Layer _firstChild;
/// The first composited layer in this layer's child list
Layer get firstChild => _firstChild;
Layer _firstChild;
// TODO(ianh): remove _lastChild since nobody uses it
Layer _lastChild;
/// The last composited layer in this layer's child list
Layer get lastChild => _lastChild;
Layer _lastChild;
bool _debugUltimatePreviousSiblingOf(Layer child, { Layer equals }) {
while (child._previousSibling != null) {
......@@ -89,46 +115,24 @@ class ContainerLayer extends Layer {
return child == equals;
}
// TODO(ianh): Remove 'before' and rename the function to 'append' since nobody uses 'before'
void add(Layer child, { Layer before }) {
/// Adds the given layer to the end of this layer's child list
void append(Layer child) {
assert(child != this);
assert(before != this);
assert(child != before);
assert(child != _firstChild);
assert(child != _lastChild);
assert(child._parent == null);
assert(child._nextSibling == null);
assert(child._previousSibling == null);
child._parent = this;
if (before == null) {
child._previousSibling = _lastChild;
if (_lastChild != null)
_lastChild._nextSibling = child;
_lastChild = child;
if (_firstChild == null)
_firstChild = child;
} else {
assert(_firstChild != null);
assert(_lastChild != null);
assert(_debugUltimatePreviousSiblingOf(before, equals: _firstChild));
assert(_debugUltimateNextSiblingOf(before, equals: _lastChild));
if (before._previousSibling == null) {
assert(before == _firstChild);
child._nextSibling = before;
before._previousSibling = child;
_firstChild = child;
} else {
child._previousSibling = before._previousSibling;
child._nextSibling = before;
child._previousSibling._nextSibling = child;
child._nextSibling._previousSibling = child;
assert(before._previousSibling == child);
}
}
child._previousSibling = _lastChild;
if (_lastChild != null)
_lastChild._nextSibling = child;
_lastChild = child;
if (_firstChild == null)
_firstChild = child;
}
// TODO(ianh): Hide this function since only detach() uses it
void remove(Layer child) {
void _remove(Layer child) {
assert(child._parent == this);
assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild));
assert(_debugUltimateNextSiblingOf(child, equals: _lastChild));
......@@ -149,6 +153,7 @@ class ContainerLayer extends Layer {
child._parent = null;
}
/// Removes all of this layer's children from its child list
void removeAllChildren() {
Layer child = _firstChild;
while (child != null) {
......@@ -166,8 +171,9 @@ class ContainerLayer extends Layer {
addChildrenToScene(builder, offset + layerOffset);
}
/// Uploads all of this layer's children to the engine
void addChildrenToScene(sky.SceneBuilder builder, Offset layerOffset) {
Layer child = firstChild;
Layer child = _firstChild;
while (child != null) {
child.addToScene(builder, layerOffset);
child = child.nextSibling;
......@@ -176,11 +182,14 @@ class ContainerLayer extends Layer {
}
/// A composite layer that clips its children using a rectangle
class ClipRectLayer extends ContainerLayer {
ClipRectLayer({ Offset offset: Offset.zero, this.clipRect }) : super(offset: offset);
// clipRect is _not_ affected by given offset
/// The rectangle to clip in the parent's coordinate system
Rect clipRect;
// TODO(abarth): Why is the rectangle in the parent's coordinate system
// instead of in the coordinate system of this layer?
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
builder.pushClipRect(clipRect.shift(layerOffset));
......@@ -190,12 +199,18 @@ class ClipRectLayer extends ContainerLayer {
}
/// A composite layer that clips its children using a rounded rectangle
class ClipRRectLayer extends ContainerLayer {
ClipRRectLayer({ Offset offset: Offset.zero, this.bounds, this.clipRRect }) : super(offset: offset);
// bounds and clipRRect are _not_ affected by given offset
/// Unused
Rect bounds;
// TODO(abarth): Remove.
/// The rounded-rect to clip in the parent's coordinate system
sky.RRect clipRRect;
// TODO(abarth): Why is the rounded-rect in the parent's coordinate system
// instead of in the coordinate system of this layer?
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
builder.pushClipRRect(clipRRect.shift(layerOffset), bounds.shift(layerOffset));
......@@ -205,12 +220,18 @@ class ClipRRectLayer extends ContainerLayer {
}
/// A composite layer that clips its children using a path
class ClipPathLayer extends ContainerLayer {
ClipPathLayer({ Offset offset: Offset.zero, this.bounds, this.clipPath }) : super(offset: offset);
// bounds and clipPath are _not_ affected by given offset
/// Unused
Rect bounds;
// TODO(abarth): Remove.
/// The path to clip in the parent's coordinate system
Path clipPath;
// TODO(abarth): Why is the path in the parent's coordinate system instead of
// in the coordinate system of this layer?
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
builder.pushClipPath(clipPath.shift(layerOffset), bounds.shift(layerOffset));
......@@ -220,9 +241,11 @@ class ClipPathLayer extends ContainerLayer {
}
/// A composited layer that applies a transformation matrix to its children
class TransformLayer extends ContainerLayer {
TransformLayer({ Offset offset: Offset.zero, this.transform }) : super(offset: offset);
/// The matrix to apply
Matrix4 transform;
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
......@@ -234,11 +257,18 @@ class TransformLayer extends ContainerLayer {
}
}
/// A composited layer that makes its children partially transparent
class OpacityLayer extends ContainerLayer {
OpacityLayer({ Offset offset: Offset.zero, this.bounds, this.alpha }) : super(offset: offset);
// bounds is _not_ affected by given offset
/// Unused
Rect bounds;
// TODO(abarth): Remove.
/// The amount to multiply into the alpha channel
///
/// The opacity is expressed as an integer from 0 to 255, where 0 is fully
/// transparent and 255 is fully opaque.
int alpha;
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
......@@ -248,6 +278,7 @@ class OpacityLayer extends ContainerLayer {
}
}
/// A composited layer that applies a color filter to its children
class ColorFilterLayer extends ContainerLayer {
ColorFilterLayer({
Offset offset: Offset.zero,
......@@ -256,9 +287,14 @@ class ColorFilterLayer extends ContainerLayer {
this.transferMode
}) : super(offset: offset);
// bounds is _not_ affected by given offset
/// Unused
Rect bounds;
// TODO(abarth): Remove.
/// The color to use as input to the color filter
Color color;
/// The transfer mode to use to combine [color] with the children's painting
sky.TransferMode transferMode;
void addToScene(sky.SceneBuilder builder, Offset layerOffset) {
......
......@@ -89,7 +89,7 @@ class PaintingContext {
_currentLayer = new PictureLayer(paintBounds: paintBounds);
_recorder = new sky.PictureRecorder();
_canvas = new PaintingCanvas(_recorder, paintBounds);
_containerLayer.add(_currentLayer);
_containerLayer.append(_currentLayer);
}
/// Stop recording draw operations into the current compositing layer
......@@ -162,7 +162,7 @@ class PaintingContext {
canvas.restore();
} else {
ClipRectLayer clipLayer = new ClipRectLayer(offset: childOffset, clipRect: clipRect);
_containerLayer.add(clipLayer);
_containerLayer.append(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
......@@ -183,7 +183,7 @@ class PaintingContext {
canvas.restore();
} else {
ClipRRectLayer clipLayer = new ClipRRectLayer(offset: childOffset, bounds: bounds, clipRRect: clipRRect);
_containerLayer.add(clipLayer);
_containerLayer.append(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
......@@ -205,7 +205,7 @@ class PaintingContext {
canvas.restore();
} else {
ClipPathLayer clipLayer = new ClipPathLayer(offset: childOffset, bounds: bounds, clipPath: clipPath);
_containerLayer.add(clipLayer);
_containerLayer.append(clipLayer);
compositeChild(child, parentLayer: clipLayer);
}
}
......@@ -225,7 +225,7 @@ class PaintingContext {
canvas.restore();
} else {
TransformLayer transformLayer = new TransformLayer(offset: childOffset, transform: transform);
_containerLayer.add(transformLayer);
_containerLayer.append(transformLayer);
compositeChild(child, parentLayer: transformLayer);
}
}
......@@ -258,7 +258,7 @@ class PaintingContext {
offset: childOffset,
bounds: bounds,
alpha: alpha);
_containerLayer.add(paintLayer);
_containerLayer.append(paintLayer);
compositeChild(child, parentLayer: paintLayer);
}
}
......@@ -295,7 +295,7 @@ class PaintingContext {
bounds: bounds,
color: color,
transferMode: transferMode);
_containerLayer.add(paintLayer);
_containerLayer.append(paintLayer);
compositeChild(child, parentLayer: paintLayer);
}
}
......@@ -345,7 +345,7 @@ class PaintingContext {
child._layer.detach();
child._layer.offset = childOffset;
}
parentLayer.add(child._layer);
parentLayer.append(child._layer);
// Start a new layer for anything that remains of our own paint.
_startRecording(originalLayer.paintBounds);
......
......@@ -19,6 +19,7 @@ double _applyFloatingPointHack(double layoutValue) {
return layoutValue.ceilToDouble();
}
/// A render object that displays a paragraph of text
class RenderParagraph extends RenderBox {
RenderParagraph(TextSpan text) : _textPainter = new TextPainter(text) {
......@@ -29,6 +30,7 @@ class RenderParagraph extends RenderBox {
BoxConstraints _constraintsForCurrentLayout; // when null, we don't have a current layout
/// The text to display
TextSpan get text => _textPainter.text;
void set text(TextSpan value) {
if (_textPainter.text == value)
......
......@@ -23,19 +23,23 @@ int _hammingWeight(int value) {
return weight;
}
class PointerState {
PointerState({ this.result, this.lastPosition });
class _PointerState {
_PointerState({ this.result, this.lastPosition });
HitTestResult result;
Point lastPosition;
}
typedef void EventListener(sky.Event event);
/// A hit test entry used by [SkyBinding]
class BindingHitTestEntry extends HitTestEntry {
const BindingHitTestEntry(HitTestTarget target, this.result) : super(target);
/// The result of the hit test
final HitTestResult result;
}
/// The glue between the render tree and the sky engine
class SkyBinding extends HitTestTarget {
SkyBinding({ RenderBox root: null, RenderView renderViewOverride }) {
......@@ -59,11 +63,13 @@ class SkyBinding extends HitTestTarget {
assert(_instance == this);
}
static SkyBinding _instance; // used to enforce that we're a singleton
/// The singleton instance of the binding
static SkyBinding get instance => _instance;
static SkyBinding _instance;
RenderView _renderView;
/// The render tree that's attached to the output surface
RenderView get renderView => _renderView;
RenderView _renderView;
ViewConstraints _createConstraints() {
return new ViewConstraints(size: new Size(sky.view.width, sky.view.height));
......@@ -72,10 +78,7 @@ class SkyBinding extends HitTestTarget {
_renderView.rootConstraints = _createConstraints();
}
RenderBox get root => _renderView.child;
void set root(RenderBox value) {
_renderView.child = value;
}
/// Pump the rendering pipeline to generate a frame for the given time stamp
void beginFrame(double timeStamp) {
RenderObject.flushLayout();
_renderView.updateCompositingBits();
......@@ -84,8 +87,12 @@ class SkyBinding extends HitTestTarget {
}
final List<EventListener> _eventListeners = new List<EventListener>();
void addEventListener(EventListener e) => _eventListeners.add(e);
bool removeEventListener(EventListener e) => _eventListeners.remove(e);
/// Calls listener for every event that isn't localized to a given view coordinate
void addEventListener(EventListener listener) => _eventListeners.add(listener);
/// Stops calling listener for every event that isn't localized to a given view coordinate
bool removeEventListener(EventListener listener) => _eventListeners.remove(listener);
void _handleEvent(sky.Event event) {
if (event is sky.PointerEvent) {
......@@ -98,19 +105,20 @@ class SkyBinding extends HitTestTarget {
}
}
/// A router that routes all pointer events received from the engine
final PointerRouter pointerRouter = new PointerRouter();
Map<int, PointerState> _stateForPointer = new Map<int, PointerState>();
Map<int, _PointerState> _stateForPointer = new Map<int, _PointerState>();
PointerState _createStateForPointer(sky.PointerEvent event, Point position) {
_PointerState _createStateForPointer(sky.PointerEvent event, Point position) {
HitTestResult result = hitTest(position);
PointerState state = new PointerState(result: result, lastPosition: position);
_PointerState state = new _PointerState(result: result, lastPosition: position);
_stateForPointer[event.pointer] = state;
return state;
}
PointerState _getOrCreateStateForPointer(event, position) {
PointerState state = _stateForPointer[event.pointer];
_PointerState _getOrCreateStateForPointer(event, position) {
_PointerState state = _stateForPointer[event.pointer];
if (state == null)
state = _createStateForPointer(event, position);
return state;
......@@ -119,7 +127,7 @@ class SkyBinding extends HitTestTarget {
EventDisposition _handlePointerEvent(sky.PointerEvent event) {
Point position = new Point(event.x, event.y);
PointerState state = _getOrCreateStateForPointer(event, position);
_PointerState state = _getOrCreateStateForPointer(event, position);
if (event.type == 'pointerup' || event.type == 'pointercancel') {
if (_hammingWeight(event.buttons) <= 1)
......@@ -133,6 +141,7 @@ class SkyBinding extends HitTestTarget {
return dispatchEvent(event, state.result);
}
/// Determine which [HitTestTarget] objects are located at a given position
HitTestResult hitTest(Point position) {
HitTestResult result = new HitTestResult();
_renderView.hitTest(result, position: position);
......@@ -140,6 +149,7 @@ class SkyBinding extends HitTestTarget {
return result;
}
/// Dispatch the given event to the path of the given hit test result
EventDisposition dispatchEvent(sky.Event event, HitTestResult result) {
assert(result != null);
EventDisposition disposition = EventDisposition.ignored;
......@@ -165,6 +175,7 @@ class SkyBinding extends HitTestTarget {
String toString() => 'Render Tree:\n${_renderView}';
/// Prints a textual representation of the entire render tree
void debugDumpRenderTree() {
toString().split('\n').forEach(print);
}
......
......@@ -7,10 +7,18 @@ import 'dart:math' as math;
import 'package:sky/src/rendering/box.dart';
import 'package:sky/src/rendering/object.dart';
/// Parent data for use with [RenderStack]
class StackParentData extends BoxParentData with ContainerParentDataMixin<RenderBox> {
/// The offset of the child's top edge from the top of the stack
double top;
/// The offset of the child's right edge from the right of the stack
double right;
/// The offset of the child's bottom edge from the bottom of the stack
double bottom;
/// The offset of the child's left edge from the left of the stack
double left;
void merge(StackParentData other) {
......@@ -25,11 +33,38 @@ class StackParentData extends BoxParentData with ContainerParentDataMixin<Render
super.merge(other);
}
/// Whether this child is considered positioned
///
/// A child is positioned if any of the top, right, bottom, or left offsets
/// are non-null. Positioned children do not factor into determining the size
/// of the stack but are instead placed relative to the non-positioned
/// children in the stack.
bool get isPositioned => top != null || right != null || bottom != null || left != null;
String toString() => '${super.toString()}; top=$top; right=$right; bottom=$bottom, left=$left';
}
/// Implements the stack layout algorithm
///
/// In a stack layout, the children are positioned on top of each other in the
/// order in which they appear in the child list. First, the non-positioned
/// children (those with null values for top, right, bottom, and left) are
/// layed out and placed in the upper-left corner of the stack. The stack is
/// then sized to enclose all of the non-positioned children. If there are no
/// non-positioned children, the stack becomes as large as possible.
///
/// Next, the positioned children are laid out. If a child has top and bottom
/// values that are both non-null, the child is given a fixed height determined
/// by deflating the width of the stack by the sum of the top and bottom values.
/// Similarly, if the child has rigth and left values that are both non-null,
/// the child is given a fixed width. Otherwise, the child is given unbounded
/// space in the non-fixed dimensions.
///
/// Once the child is laid out, the stack positions the child according to the
/// top, right, bottom, and left offsets. For example, if the top value is 10.0,
/// the top edge of the child will be placed 10.0 pixels from the top edge of
/// the stack. If the child extends beyond the bounds of the stack, the stack
/// will clip the child's painting to the bounds of the stack.
class RenderStack extends RenderBox with ContainerRenderObjectMixin<RenderBox, StackParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, StackParentData> {
RenderStack({
......
......@@ -10,15 +10,25 @@ import 'package:sky/src/rendering/object.dart';
import 'package:sky/src/rendering/box.dart';
import 'package:vector_math/vector_math.dart';
/// The layout constraints for the root render object
class ViewConstraints {
const ViewConstraints({
this.size: Size.zero,
this.orientation
});
/// The size of the output surface
final Size size;
/// The orientation of the output surface (aspirational)
final int orientation;
}
/// The root of the render tree
///
/// The view represents the total output surface of the render tree and handles
/// bootstraping the rendering pipeline. The view has a unique child
/// [RenderBox], which is required to fill the entire output surface.
class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
RenderView({
RenderBox child,
......@@ -27,16 +37,20 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
this.child = child;
}
/// The amount of time the screen rotation animation should last (aspirational)
Duration timeForRotation;
Size _size = Size.zero;
/// The current layout size of the view
Size get size => _size;
Size _size = Size.zero;
int _orientation; // 0..3
/// The current orientation of the view (aspirational)
int get orientation => _orientation;
int _orientation; // 0..3
ViewConstraints _rootConstraints;
/// The constraints used for the root layout
ViewConstraints get rootConstraints => _rootConstraints;
ViewConstraints _rootConstraints;
void set rootConstraints(ViewConstraints value) {
if (_rootConstraints == value)
return;
......@@ -49,6 +63,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
return new Matrix4.diagonal3Values(devicePixelRatio, devicePixelRatio, 1.0);
}
/// Bootstrap the rendering pipeline by scheduling the first frame
void scheduleInitialFrame() {
scheduleInitialLayout();
scheduleInitialPaint(new TransformLayer(transform: _logicalToDeviceTransform));
......@@ -94,6 +109,9 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
context.paintChild(child, offset.toPoint());
}
/// Uploads the composited layer tree to the engine
///
/// Actually causes the output of the rendering pipeline to appear on screen.
void compositeFrame() {
sky.tracing.begin('RenderView.compositeFrame');
try {
......
......@@ -8,8 +8,25 @@ import 'package:sky/src/rendering/object.dart';
import 'package:sky/src/rendering/box.dart';
import 'package:vector_math/vector_math.dart';
enum ScrollDirection { horizontal, vertical, both }
/// The direction in which to scroll
enum ScrollDirection {
/// Scroll left and right
horizontal,
/// Scroll up and down
vertical,
/// Scroll in all four cardinal directions
both
}
/// A render object that's bigger on the inside
///
/// A viewport is the core scrolling primitive in the render tree. The child of
/// a viewport can layout to a larger size than the viewport itself. If that
/// happens, only a portion of the child will be visible through the viewport.
/// The portiion of the child that is visible is controlled by the scroll
/// offset.
class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox> {
RenderViewport({
......@@ -33,8 +50,11 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
}
}
Offset _scrollOffset;
/// The offset at which to paint the child
///
/// The offset can be non-zero only in the [scrollDirection].
Offset get scrollOffset => _scrollOffset;
Offset _scrollOffset;
void set scrollOffset(Offset value) {
if (value == _scrollOffset)
return;
......@@ -43,8 +63,13 @@ class RenderViewport extends RenderBox with RenderObjectWithChildMixin<RenderBox
markNeedsPaint();
}
ScrollDirection _scrollDirection;
/// In which direction the child is permitted to be larger than the viewport
///
/// If the viewport is scrollable in a particular direction (e.g., vertically),
/// the child is given layout constraints that are fully unconstrainted in
/// that direction (e.g., the child can be as tall as it wants).
ScrollDirection get scrollDirection => _scrollDirection;
ScrollDirection _scrollDirection;
void set scrollDirection(ScrollDirection value) {
if (value == _scrollDirection)
return;
......
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