Chromium Code Reviews| Index: sky/sdk/lib/rendering/README.md |
| diff --git a/sky/sdk/lib/rendering/README.md b/sky/sdk/lib/rendering/README.md |
| index 77ea6d3de4a2c564fc854329e52c37c390aab9a1..3570d0ac9b6246450191cf44d935d2015e671682 100644 |
| --- a/sky/sdk/lib/rendering/README.md |
| +++ b/sky/sdk/lib/rendering/README.md |
| @@ -77,8 +77,31 @@ models and are free to invent novel child models for their specific use cases. |
| ### Parent Data |
| +TODO(ianh): Describe the parent data concept. |
| + |
| +The `setParentData()` method is automatically called for each child |
| +when the child's parent is changed. However, if you need to |
| +preinitialise the `parentData` member to set its values before you add |
| +a node to its parent, you can preemptively call that future parent's |
| +`setParentData()` method with the future child as the argument. |
| + |
| ### Box Model |
| +#### Dimensions |
| + |
| +All dimensions are expressed as logical pixel units. Font sizes are |
| +also in logical pixel units. Logical pixel units are approximately |
| +96dpi, but the precise value varies based on the hardware, in such a |
| +way as to optimise for performance and rendering quality while keeping |
| +interfaces roughly the same size across devices regardless of the |
| +hardware pixel density. |
| + |
| +Logical pixel units are automatically converted to device (hardware) |
| +pixels when painting by applying an appropriate scale factor. |
| + |
| +TODO(ianh): Define how you actually get the device pixel ratio if you |
| +need it, and document best practices around that. |
| + |
| #### EdgeDims |
| #### BoxConstraints |
| @@ -113,14 +136,208 @@ Writing new subclasses |
| ### The RenderObject contract |
| +If you want to define a `RenderObject` that uses a new coordinate |
| +system, then you have to inherit straight from `RenderObject`. |
|
abarth-chromium
2015/06/25 19:41:06
s/have to inherit straight/should inherit directly
|
| +Examples of doing this can be found in [`RenderBox`](box.dart), which |
| +deals in rectangles in cartesian space, and in the [sector_layout.dart |
| +example](../../../examples/rendering/sector_layout.dart), which |
|
abarth-chromium
2015/06/25 19:41:06
We should move the examples into the sdk directory
|
| +implements a toy model based on polar coordinates. The `RenderView` |
| +class, which is used internally to adapt from the host system to this |
| +rendering framework, is another example. |
| + |
| +A subclass of `RenderObject` must fulfill the following contract: |
| + |
| +* It must fulfill the [AbstractNode contract](../base/README.md) when |
| + dealing with children. Using `RenderObjectWithChildMixin` or |
| + `ContainerRenderObjectMixin` can make this easier. |
| + |
| +* Information about the child managed by the parent, e.g. typically |
| + position information and configuration for the parent's layout, |
| + should be stored on the `parentData` member; to this effect, a |
| + ParentData subclass should be defined and the `setParentData()` |
| + method should be overriden to initialise the child's parent data |
| + appropriately. |
| + |
| +* Layout constraints must be expressed in a Constraints subclass. This |
| + subclass must implement operator== (and hashCode). |
|
abarth-chromium
2015/06/25 19:41:06
`operator==` and `hashCode` because these are code
|
| + |
| +* Whenever the layout needs updating, the `markNeedsLayout()` method |
| + should be called. |
| + |
| +* Whenever the rendering needs updating without changing the layout, |
| + the `markNeedsPaint()` method should be called. (Calling |
| + `markNeedsLayout()` implies a call to `markNeedsPaint()`, so you |
| + don't need to call both.) |
| + |
| +* The subclass must override `performLayout()` to perform layout based |
| + on the constraints given in the `constraints` member. Each object is |
| + responsible for sizing itself; positioning must be done by the |
| + object calling `performLayout()`. Whether positioning is done before |
| + or after the child's layout is a decision to be made by the class. |
| + TODO(ianh): Document sizedByParent, performResize(), rotate |
| + |
| +* TODO(ianh): Document painting, hit testing, debug* |
| + |
| #### The ParentData contract |
| #### Using RenderObjectWithChildMixin |
| -#### Using ContainerParentDataMixin and ContainerRenderObjectMixin |
| +#### Using ContainerRenderObjectMixin (and ContainerParentDataMixin) |
| + |
| +This mixin can be used for classes that have a child list, to manage |
| +the list. It implements the list using linked list pointers in the |
| +`parentData` structure. |
| + |
| +TODO(ianh): Document this mixin. |
| + |
| +Subclasses must follow the following contract, in addition to the |
| +contracts of any other classes they subclass: |
| + |
| +* If the constructor takes a list of children, it must call addAll() |
| + with that list. |
| + |
| +TODO(ianh): Document how to walk the children. |
| ### The RenderBox contract |
| +A `RenderBox` subclass is required to implement the following contract: |
| + |
| +* It must fulfill the [AbstractNode contract](../base/README.md) when |
| + dealing with children. Note that using `RenderObjectWithChildMixin` |
| + or `ContainerRenderObjectMixin` takes care of this for you, assuming |
| + you fulfill their contract instead. |
| + |
| +* If it has any data to store on its children, it must define a |
| + BoxParentData subclass and override setParentData() to initialise |
| + the child's parent data appropriately, as in the following example. |
| + (If the subclass has an opinion about what type its children must |
| + be, e.g. the way that `RenderBlock` wants its children to be |
| + `RenderBox` nodes, then change the `setParentData()` signature |
| + accordingly, to catch misuse of the method.) |
| + |
| +```dart |
| + class FooParentData extends BoxParentData { ... } |
| + |
| + // In RenderFoo |
| + void setParentData(RenderObject child) { |
| + if (child.parentData is! FooParentData) |
| + child.parentData = new FooParentData(); |
| + } |
| +``` |
| + |
| +* The class must encapsulate a layout algorithm that has the following |
| + features: |
| + |
| +** It uses as input a set of constraints, described by a |
| + BoxConstraints object, and a set of zero or more children, as |
| + determined by the class itself, and has as output a Size (which is |
| + set on the object's own `size` field), and positions for each child |
| + (which are set on the children's `parentData.position` field). |
| + |
| +** The algorithm can decide the Size in one of two ways: either |
| + exclusively based on the given constraints (i.e. it is effectively |
| + sized entirely by its parent), or based on those constraints and |
| + the dimensions of the children. |
| + |
| + In the former case, the class must have a sizedByParent getter that |
| + returns true, and it must have a `performResize()` method that uses |
| + the object's `constraints` member to size itself by setting the |
| + `size` member. The size must be consistent, a given set of |
| + constraints must always result in the same size. |
| + |
| + In the latter case, it will inherit the default `sizedByParent` |
| + getter that returns false, and it will size itself in the |
| + `performLayout()` function described below. |
| + |
| + The `sizedByParent` distinction is purely a performance |
| + optimisation. It allows nodes that only set their size based on the |
| + incoming constraints to skip that logic when they need to be |
| + re-laid-out. |
|
abarth-chromium
2015/06/25 19:41:06
I wonder if we should remove this concept until we
|
| + |
| +* The following methods must report numbers consistent with the output |
| + of the layout algorithm used: |
| + |
| +** `double getMinIntrinsicWidth(BoxConstraints constraints)` must |
| + return the width that fits within the given constraints below which |
| + making the width constraint smaller would not increase the |
| + resulting height, or, to put it another way, the narrowest width at |
| + which the box can be rendered without failing to lay the children |
| + out within itself. |
| + |
| + For example, the minimum intrinsic width of a piece of text like "a |
| + b cd e", where the text is allowed to wrap at spaces, would be the |
| + width of "cd". |
| + |
| +** `double getMaxIntrinsicWidth(BoxConstraints constraints)` must |
| + return the width that fits within the given constraints above which |
| + making the width constraint larger would not decrease the resulting |
| + height. |
| + |
| + For example, the maximum intrinsic width of a piece of text like "a |
| + b cd e", where the text is allowed to wrap at spaces, would be the |
| + width of the whole "a b cd e" string, with no wrapping. |
| + |
| +** `double getMinIntrinsicHeight(BoxConstraints constraints)` must |
| + return the height that fits within the given constraints below |
| + which making the height constraint smaller would not increase the |
| + resulting width, or, to put it another way, the shortest height at |
| + which the box can be rendered without failing to lay the children |
| + out within itself. |
| + |
| + The minimum intrinsic height of a width-in-height-out algorithm, |
| + like English text layout, would be the height of the text at the |
| + width that would be used given the constraints. So for instance, |
| + given the text "hello world", if the constraints were such that it |
| + had to wrap at the space, then the minimum intrinsic height would |
| + be the height of two lines (and the appropriate line spacing). If |
| + the constraints were such that it all fit on one line, then it |
| + would be the height of one line. |
| + |
| +** `double getMaxIntrinsicHeight(BoxConstraints constraints)` must |
| + return the height that fits within the given constraints above |
| + which making the height constraint larger would not decrease the |
| + resulting width. If the height depends exclusively on the width, |
| + and the width does not depend on the height, then |
| + `getMinIntrinsicHeight()` and `getMaxIntrinsicHeight()` will return the |
| + same number given the same constraints. |
| + |
| + In the case of English text, the maximum intrinsic height is the |
| + same as the minimum instrinsic height. |
| + |
| +* The box must have a `performLayout()` method that encapsulates the |
| + layout algorithm that this class represents. It is responsible for |
| + telling the children to lay out, positioning the children, and, if |
| + sizedByParent is false, sizing the object. |
| + |
| + Specifically, the method must walk over the object's children, if |
| + any, and for each one call `child.layout()` with a BoxConstraints |
| + object as the first argument, and a second argument named |
| + `parentUsesSize` which is set to true if the child's resulting size |
| + will in any way influence the layout, and omitted (or set to false) |
| + if the child's resulting size is ignored. The children's positions |
| + (`child.parentData.position`) must then be set. |
| + |
| + (Calling `layout()` can result in the child's own `performLayout()` |
| + method being called recursively, if the child also needs to be laid |
| + out. If the child's constraints haven't changed and the child is not |
| + marked as needing layout, however, this will be skipped.) |
| + |
| + The parent must not set a child's `size` directly. If the parent |
| + wants to influence the child's size, it must do so via the |
| + constraints that it passes to the child's `layout()` method. |
| + |
| + If an object's `sizedByParent` is false, then its `performLayout()` |
| + must also size the object (by setting `size`), otherwise, the size |
| + must be left untouched. |
| + |
| +* The `size` member must never be set to an infinite value. |
| + |
| +* The box must also implement `hitTestChildren()`. |
| + TODO(ianh): Define this better |
| + |
| +* The box must also implement `paint()`. |
| + TODO(ianh): Define this better |
| + |
| #### Using RenderProxyBox |
| ### The Hit Testing contract |