| Index: sky/specs/style.md
|
| diff --git a/sky/specs/style.md b/sky/specs/style.md
|
| index fd3008c553f76e0137bbe8f8546c42dfdeda945d..0db40badedb3a56a2b3127307ca0787ac554c3c7 100644
|
| --- a/sky/specs/style.md
|
| +++ b/sky/specs/style.md
|
| @@ -137,18 +137,22 @@ Sky Style uses whatever SelectorQuery. Maybe one day we'll make
|
| SelectorQuery support being extended to support arbitrary selectors,
|
| but for now, it supports:
|
|
|
| - tagname
|
| - #id
|
| - .class
|
| - [attrname]
|
| - [attrname=value]
|
| - :host ("host" string is fixed)
|
| - ::pseudo-element
|
| +```css
|
| +tagname
|
| +#id
|
| +.class
|
| +[attrname]
|
| +[attrname=value]
|
| +:host ("host" string is fixed)
|
| +::pseudo-element
|
| +```
|
|
|
| These can be combined (without whitespace), with at most one tagname
|
| (must be first) and at most one pseudo-element (must be last) as in:
|
|
|
| - tagname[attrname]#id:host.class.class[attrname=value]::foo
|
| +```css
|
| +tagname[attrname]#id:host.class.class[attrname=value]::foo
|
| +```
|
|
|
| In debug mode, giving two IDs, or the same selector twice (e.g. the
|
| same classname), or specifying other redundant or conflicting
|
| @@ -158,96 +162,100 @@ flagged.
|
| Alternatively, a selector can be the special value "@document",
|
| optionally followed by a pseudo-element, as in:
|
|
|
| - @document::bar
|
| +```css
|
| +@document::bar
|
| +```
|
|
|
|
|
| Value Parser
|
| ------------
|
|
|
| - class StyleToken {
|
| - constructor (String king, String value);
|
| - readonly attribute String kind;
|
| - // string
|
| - // identifier
|
| - // function (identifier + '(')
|
| - // number
|
| - // symbol (one of @#$%& if not immediately following numeric or preceding alphanumeric, or one of *^!?,/<[)>]+ or, if not followed by a digit, -)
|
| - // dimension (number + identifier or number + one of @#$%&)
|
| - // literal (one of @#$%& + alphanumeric)
|
| - readonly attribute String value;
|
| - readonly attribute String unit; // for 'dimension' type, this is the punctuation or identifier that follows the number, for 'literal' type, this is the punctuation that precedes it
|
| - }
|
| +```javascript
|
| +class StyleToken {
|
| + constructor (String king, String value);
|
| + readonly attribute String kind;
|
| + // string
|
| + // identifier
|
| + // function (identifier + '(')
|
| + // number
|
| + // symbol (one of @#$%& if not immediately following numeric or preceding alphanumeric, or one of *^!?,/<[)>]+ or, if not followed by a digit, -)
|
| + // dimension (number + identifier or number + one of @#$%&)
|
| + // literal (one of @#$%& + alphanumeric)
|
| + readonly attribute String value;
|
| + readonly attribute String unit; // for 'dimension' type, this is the punctuation or identifier that follows the number, for 'literal' type, this is the punctuation that precedes it
|
| +}
|
|
|
| - class TokenSource {
|
| - constructor (Array<StyleToken> tokens);
|
| - IteratorResult next();
|
| - TokenSourceBookmark getBookmark();
|
| - void rewind(TokenSourceBookmark bookmark);
|
| - }
|
| - class TokenSourceBookmark {
|
| - constructor ();
|
| - // TokenSource stores unforgeable state on this object using symbols or a weakmap or some such
|
| - }
|
| +class TokenSource {
|
| + constructor (Array<StyleToken> tokens);
|
| + IteratorResult next();
|
| + TokenSourceBookmark getBookmark();
|
| + void rewind(TokenSourceBookmark bookmark);
|
| +}
|
| +class TokenSourceBookmark {
|
| + constructor ();
|
| + // TokenSource stores unforgeable state on this object using symbols or a weakmap or some such
|
| +}
|
|
|
| - dictionary ParsedValue {
|
| - any value = null;
|
| - ValueResolver? resolver = null;
|
| - Boolean relativeDimension = false; // if true, e.g. for % lengths, the callback will be called again if an ancestor's dimensions change
|
| - Painter? painter = null;
|
| - }
|
| +dictionary ParsedValue {
|
| + any value = null;
|
| + ValueResolver? resolver = null;
|
| + Boolean relativeDimension = false; // if true, e.g. for % lengths, the callback will be called again if an ancestor's dimensions change
|
| + Painter? painter = null;
|
| +}
|
|
|
| - // best practice convention: if you're creating a property with needsPaint, you should
|
| - // create a new style value type for it so that it can set the paint callback right;
|
| - // you should never use such a style type when parsing another property
|
| -
|
| - callback any ParserCallback (TokenSource tokens);
|
| -
|
| - class StyleValueType {
|
| - constructor ();
|
| - void addParser(ParserCallback parser);
|
| - any parse(TokenSource tokens, Boolean root = false);
|
| - // for each parser callback that was registered, in reverse
|
| - // order (most recently registered first), run these steps:
|
| - // let bookmark = tokens.getBookmark();
|
| - // try {
|
| - // let result = parser(tokens);
|
| - // if (root) {
|
| - // if (!tokens.next().done)
|
| - // throw new Error();
|
| - // }
|
| - // } except {
|
| - // tokens.rewind(bookmark);
|
| - // }
|
| - // (root is set when you need to parse the entire token stream to be valid)
|
| - }
|
| +// best practice convention: if you're creating a property with needsPaint, you should
|
| +// create a new style value type for it so that it can set the paint callback right;
|
| +// you should never use such a style type when parsing another property
|
|
|
| - // note: if you define a style value type that uses other style value types, e.g. a "length pair" that accepts two lengths, then
|
| - // if any of the subtypes have a resolver, you need to make sure you have a resolver that calls them to compute the final value
|
| +callback any ParserCallback (TokenSource tokens);
|
|
|
| - dictionary PropertySettings {
|
| - String name;
|
| - StyleValueType type; // the output from the parser is coerced to a ParsedValue
|
| - Boolean inherits = false;
|
| - any initialValue = null;
|
| - Boolean needsLayout = false;
|
| - Boolean needsPaint = false;
|
| - }
|
| +class StyleValueType {
|
| + constructor ();
|
| + void addParser(ParserCallback parser);
|
| + any parse(TokenSource tokens, Boolean root = false);
|
| + // for each parser callback that was registered, in reverse
|
| + // order (most recently registered first), run these steps:
|
| + // let bookmark = tokens.getBookmark();
|
| + // try {
|
| + // let result = parser(tokens);
|
| + // if (root) {
|
| + // if (!tokens.next().done)
|
| + // throw new Error();
|
| + // }
|
| + // } except {
|
| + // tokens.rewind(bookmark);
|
| + // }
|
| + // (root is set when you need to parse the entire token stream to be valid)
|
| +}
|
|
|
| - void registerProperty(PropertySettings propertySettings);
|
| - // when you register a new property, document the format that is expected to be cascaded
|
| - // (the output from the propertySettings.type parser's ParsedValue.value field after the resolver, if any, has been called)
|
| +// note: if you define a style value type that uses other style value types, e.g. a "length pair" that accepts two lengths, then
|
| +// if any of the subtypes have a resolver, you need to make sure you have a resolver that calls them to compute the final value
|
|
|
| - // sky:core exports a bunch of style value types so that people can
|
| - // extend them
|
| - attribute StyleValueType PositiveLengthOrInfinityStyleValueType;
|
| - attribute StyleValueType PositiveLengthOrAutoStyleValueType;
|
| - attribute StyleValueType PositiveLengthStyleValueType;
|
| - attribute StyleValueType DisplayStyleValueType;
|
| -
|
| +dictionary PropertySettings {
|
| + String name;
|
| + StyleValueType type; // the output from the parser is coerced to a ParsedValue
|
| + Boolean inherits = false;
|
| + any initialValue = null;
|
| + Boolean needsLayout = false;
|
| + Boolean needsPaint = false;
|
| +}
|
| +
|
| +void registerProperty(PropertySettings propertySettings);
|
| + // when you register a new property, document the format that is expected to be cascaded
|
| + // (the output from the propertySettings.type parser's ParsedValue.value field after the resolver, if any, has been called)
|
| +
|
| +// sky:core exports a bunch of style value types so that people can
|
| +// extend them
|
| +attribute StyleValueType PositiveLengthOrInfinityStyleValueType;
|
| +attribute StyleValueType PositiveLengthOrAutoStyleValueType;
|
| +attribute StyleValueType PositiveLengthStyleValueType;
|
| +attribute StyleValueType DisplayStyleValueType;
|
| +```
|
|
|
| Inline Styles
|
| -------------
|
|
|
| +```javascript
|
| partial class Element {
|
| readonly attribute StyleDeclarationList style;
|
| }
|
| @@ -261,11 +269,12 @@ class StyleDeclarationList {
|
| }
|
|
|
| typedef StyleDeclaration Dictionary<ParsedValue>;
|
| -
|
| +```
|
|
|
| Rule Matching
|
| -------------
|
|
|
| +```javascript
|
| partial class StyleElement {
|
| Array<Rule> getRules(); // O(N) in rules
|
| }
|
| @@ -276,6 +285,7 @@ class Rule {
|
| attribute String? pseudoElement; // O(1)
|
| attribute StyleDeclaration styles; // O(1)
|
| }
|
| +```
|
|
|
| Each frame, at some defined point relative to requestAnimationFrame():
|
| - If a rule starts applying to an element, sky:core calls thatElement.style.add(rule.styles, rule.pseudoElement);
|
| @@ -294,63 +304,69 @@ Create the flattened render tree as a tree of StyleNode objects
|
| (described below). For each one, run the equivalent of the following
|
| code:
|
|
|
| - var display = node.getProperty('display');
|
| - if (display) {
|
| - node.layoutManager = new display(node, ownerManager);
|
| - return true;
|
| - }
|
| - return false;
|
| +```javascript
|
| +var display = node.getProperty('display');
|
| +if (display) {
|
| + node.layoutManager = new display(node, ownerManager);
|
| + return true;
|
| +}
|
| +return false;
|
| +```
|
|
|
| If that code returns false, then that node an all its descendants must
|
| be dropped from the render tree.
|
|
|
| If any node is removed in this pass relative to the previous pass, and
|
| it has an ownerLayoutManager, then call
|
| - ``node.ownerLayoutManager.release(node)``
|
| +
|
| +```node.ownerLayoutManager.release(node)```
|
| +
|
| ...to notify the layout manager that the node went away, then set the
|
| node's layoutManager and ownerLayoutManager attributes to null.
|
|
|
| - callback any ValueResolver (any value, String propertyName, StyleNode node, Float containerWidth, Float containerHeight);
|
| -
|
| - class StyleNode {
|
| - // this is generated before layout
|
| - readonly attribute String text;
|
| - readonly attribute Node? parentNode;
|
| - readonly attribute Node? firstChild;
|
| - readonly attribute Node? nextSibling;
|
| -
|
| - // access to the results of the cascade
|
| - any getProperty(String name, String? pseudoElement = null);
|
| - // looking at the declarations for the given pseudoElement:
|
| - // if there's a cached value, return it
|
| - // otherwise, if there's an applicable ParsedValue, then
|
| - // if it has a resolver:
|
| - // call it
|
| - // cache the value
|
| - // if relativeDimension is true, then mark the value as provisional
|
| - // return the value
|
| - // otherwise use the ParsedValue's value; cache it; return it
|
| - // otherwise, if a pseudo-element was specified, try again without one
|
| - // otherwise, if the property is inherited and there's a parent:
|
| - // get it from the parent (without pseudo); cache it; return it
|
| - // otherwise, get the default value; cache it; return it
|
| -
|
| - readonly attribute Boolean needsLayout; // means that a property with needsLayout:true has changed on this node or one of its descendants
|
| - readonly attribute LayoutManager layoutManager;
|
| -
|
| - readonly attribute LayoutManager ownerLayoutManager; // defaults to the parentNode.layoutManager
|
| - // if you are not the ownerLayoutManager, then ignore this StyleNode in layout() and paintChildren()
|
| - // using walkChildren() does this for you
|
| -
|
| - readonly attribute Boolean needsPaint; // means that either needsLayout is true or a property with needsPaint:true has changed on this node or one of its descendants
|
| - // needsPaint is set to false by the ownerLayoutManager's default paint() method
|
| -
|
| - // only the ownerLayoutManager can change these
|
| - readonly attribute Float x; // relative to left edge of ownerLayoutManager
|
| - readonly attribute Float y; // relative to top edge of ownerLayoutManager
|
| - readonly attribute Float width;
|
| - readonly attribute Float height;
|
| - }
|
| +```javascript
|
| +callback any ValueResolver (any value, String propertyName, StyleNode node, Float containerWidth, Float containerHeight);
|
| +
|
| +class StyleNode {
|
| + // this is generated before layout
|
| + readonly attribute String text;
|
| + readonly attribute Node? parentNode;
|
| + readonly attribute Node? firstChild;
|
| + readonly attribute Node? nextSibling;
|
| +
|
| + // access to the results of the cascade
|
| + any getProperty(String name, String? pseudoElement = null);
|
| + // looking at the declarations for the given pseudoElement:
|
| + // if there's a cached value, return it
|
| + // otherwise, if there's an applicable ParsedValue, then
|
| + // if it has a resolver:
|
| + // call it
|
| + // cache the value
|
| + // if relativeDimension is true, then mark the value as provisional
|
| + // return the value
|
| + // otherwise use the ParsedValue's value; cache it; return it
|
| + // otherwise, if a pseudo-element was specified, try again without one
|
| + // otherwise, if the property is inherited and there's a parent:
|
| + // get it from the parent (without pseudo); cache it; return it
|
| + // otherwise, get the default value; cache it; return it
|
| +
|
| + readonly attribute Boolean needsLayout; // means that a property with needsLayout:true has changed on this node or one of its descendants
|
| + readonly attribute LayoutManager layoutManager;
|
| +
|
| + readonly attribute LayoutManager ownerLayoutManager; // defaults to the parentNode.layoutManager
|
| + // if you are not the ownerLayoutManager, then ignore this StyleNode in layout() and paintChildren()
|
| + // using walkChildren() does this for you
|
| +
|
| + readonly attribute Boolean needsPaint; // means that either needsLayout is true or a property with needsPaint:true has changed on this node or one of its descendants
|
| + // needsPaint is set to false by the ownerLayoutManager's default paint() method
|
| +
|
| + // only the ownerLayoutManager can change these
|
| + readonly attribute Float x; // relative to left edge of ownerLayoutManager
|
| + readonly attribute Float y; // relative to top edge of ownerLayoutManager
|
| + readonly attribute Float width;
|
| + readonly attribute Float height;
|
| +}
|
| +```
|
|
|
| The flattened tree is represented as a hierarchy of Node objects. For
|
| any element that only contains text node children, the "text" property
|
| @@ -365,6 +381,7 @@ Layout
|
|
|
| sky:core registers 'display' as follows:
|
|
|
| +```javascript
|
| {
|
| name: 'display',
|
| type: sky.DisplayStyleValueType,
|
| @@ -372,125 +389,132 @@ sky:core registers 'display' as follows:
|
| initialValue: sky.BlockLayoutManager,
|
| needsLayout: true,
|
| }
|
| +```
|
|
|
| The following API is then used to add new layout manager types to 'display':
|
|
|
| - void registerLayoutManager(String displayValue, LayoutManagerConstructor? layoutManager);
|
| +```javascript
|
| +void registerLayoutManager(String displayValue, LayoutManagerConstructor? layoutManager);
|
| +```
|
|
|
| sky:core by default registers:
|
|
|
| - 'block': sky.BlockLayoutManager
|
| - 'paragraph': sky.ParagraphLayoutManager
|
| - 'inline': sky.InlineLayoutManager
|
| - 'none': null
|
| +- 'block': sky.BlockLayoutManager
|
| +- 'paragraph': sky.ParagraphLayoutManager
|
| +- 'inline': sky.InlineLayoutManager
|
| +- 'none': null
|
|
|
|
|
| Layout managers inherit from the following API:
|
|
|
| - class LayoutManager {
|
| - readonly attribute StyleNode node;
|
| - constructor LayoutManager(StyleNode node);
|
| -
|
| - void take(StyleNode victim); // sets victim.ownerLayoutManager = this;
|
| - // assert: victim hasn't been take()n yet during this layout
|
| - // assert: victim.needsLayout == true
|
| - // assert: an ancestor of victim has needsLayout == this (aka, victim is a descendant of this.node)
|
| -
|
| - virtual void release(StyleNode victim);
|
| - // called when the StyleNode was removed from the tree
|
| -
|
| - void setChildPosition(child, x, y); // sets child.x, child.y
|
| - void setChildX(child, y); // sets child.x
|
| - void setChildY(child, y); // sets child.y
|
| - void setChildSize(child, width, height); // sets child.width, child.height
|
| - void setChildWidth(child, width); // sets child.width
|
| - void setChildHeight(child, height); // sets child.height
|
| - // these set needsPaint on the node and on any node impacted by this (?)
|
| - // for setChildSize/Width/Height: if the new dimension is different than the last assumed dimensions, and
|
| - // any StyleNodes with an ownerLayoutManager==this have cached values for getProperty() that are marked
|
| - // as provisional, clear them
|
| -
|
| - Generator<StyleNode> walkChildren();
|
| - // returns a generator that iterates over the children, skipping any whose ownerLayoutManager is not |this|
|
| -
|
| - Generator<StyleNode> walkChildrenBackwards();
|
| - // returns a generator that iterates over the children backwards, skipping any whose ownerLayoutManager is not |this|
|
| -
|
| - void assumeDimensions(Float width, Float height);
|
| - // sets the assumed dimensions for calls to getProperty() on StyleNodes that have this as an ownerLayoutManager
|
| - // if the new dimension is different than the last assumed dimensions, and any StyleNodes with an
|
| - // ownerLayoutManager==this have cached values for getProperty() that are marked as provisional, clear them
|
| - // TODO(ianh): should we force this to match the input to layout(), when called from inside layout() and when
|
| - // layout() has a forced width and/or height?
|
| -
|
| - virtual LayoutValueRange getIntrinsicWidth(Float? defaultWidth = null);
|
| - // returns min-width, width, and max-width, normalised, defaulting to values given in LayoutValueRange
|
| - // if argument is provided, it overrides width
|
| -
|
| - virtual LayoutValueRange getIntrinsicHeight(Float? defaultHeight = null);
|
| - // returns min-height, height, and max-height, normalised, defaulting to values given in LayoutValueRange
|
| - // if argument is provided, it overrides height
|
| -
|
| - virtual Dimensions layout(Number? width, Number? height);
|
| - // returns { }
|
| - // the return value should include the final value for whichever of the width and height arguments that is null
|
| - // TODO(ianh): should we just grab the width and height from assumeDimensions()?
|
| -
|
| - void markAsPainted(); // sets this.node.needsPaint to false
|
| - virtual void paint(RenderingSurface canvas);
|
| - // set a clip rect on the canvas for rect(0,0,this.width,this.height)
|
| - // call the painter of each property, in order they were registered, which on this element has a painter
|
| - // call this.paintChildren()
|
| - // unset the clip
|
| - // call markAsPainted()
|
| -
|
| - virtual void paintChildren(RenderingSurface canvas);
|
| - // just calls paint() for each child returned by walkChildren() whose needsPaint is true,
|
| - // after transforming the coordinate space by translate(child.x,child.y)
|
| - // you should skip children that will be clipped out of yourself because they're outside your bounds
|
| -
|
| - virtual Node hitTest(Float x, Float y);
|
| - // default implementation uses the node's children nodes' x, y,
|
| - // width, and height, skipping any that have width=0 or height=0, or
|
| - // whose ownerLayoutManager is not |this|
|
| - // default implementation walks the tree backwards from its built-in order
|
| - // if no child is hit, then return this.node
|
| - // override this if you changed your children's z-order, or if you used take() to
|
| - // hoist some descendants up to be your responsibility, or if your children aren't
|
| - // rectangular (e.g. you lay them out in a hex grid)
|
| - // make sure to offset the value you pass your children: child.layoutManager.hitTest(x-child.x, y-child.y)
|
| -
|
| - }
|
| +```
|
| +class LayoutManager {
|
| + readonly attribute StyleNode node;
|
| + constructor LayoutManager(StyleNode node);
|
| +
|
| + void take(StyleNode victim); // sets victim.ownerLayoutManager = this;
|
| + // assert: victim hasn't been take()n yet during this layout
|
| + // assert: victim.needsLayout == true
|
| + // assert: an ancestor of victim has needsLayout == this (aka, victim is a descendant of this.node)
|
| +
|
| + virtual void release(StyleNode victim);
|
| + // called when the StyleNode was removed from the tree
|
| +
|
| + void setChildPosition(child, x, y); // sets child.x, child.y
|
| + void setChildX(child, y); // sets child.x
|
| + void setChildY(child, y); // sets child.y
|
| + void setChildSize(child, width, height); // sets child.width, child.height
|
| + void setChildWidth(child, width); // sets child.width
|
| + void setChildHeight(child, height); // sets child.height
|
| + // these set needsPaint on the node and on any node impacted by this (?)
|
| + // for setChildSize/Width/Height: if the new dimension is different than the last assumed dimensions, and
|
| + // any StyleNodes with an ownerLayoutManager==this have cached values for getProperty() that are marked
|
| + // as provisional, clear them
|
| +
|
| + Generator<StyleNode> walkChildren();
|
| + // returns a generator that iterates over the children, skipping any whose ownerLayoutManager is not |this|
|
| +
|
| + Generator<StyleNode> walkChildrenBackwards();
|
| + // returns a generator that iterates over the children backwards, skipping any whose ownerLayoutManager is not |this|
|
| +
|
| + void assumeDimensions(Float width, Float height);
|
| + // sets the assumed dimensions for calls to getProperty() on StyleNodes that have this as an ownerLayoutManager
|
| + // if the new dimension is different than the last assumed dimensions, and any StyleNodes with an
|
| + // ownerLayoutManager==this have cached values for getProperty() that are marked as provisional, clear them
|
| + // TODO(ianh): should we force this to match the input to layout(), when called from inside layout() and when
|
| + // layout() has a forced width and/or height?
|
| +
|
| + virtual LayoutValueRange getIntrinsicWidth(Float? defaultWidth = null);
|
| + // returns min-width, width, and max-width, normalised, defaulting to values given in LayoutValueRange
|
| + // if argument is provided, it overrides width
|
| +
|
| + virtual LayoutValueRange getIntrinsicHeight(Float? defaultHeight = null);
|
| + // returns min-height, height, and max-height, normalised, defaulting to values given in LayoutValueRange
|
| + // if argument is provided, it overrides height
|
| +
|
| + virtual Dimensions layout(Number? width, Number? height);
|
| + // returns { }
|
| + // the return value should include the final value for whichever of the width and height arguments that is null
|
| + // TODO(ianh): should we just grab the width and height from assumeDimensions()?
|
| +
|
| + void markAsPainted(); // sets this.node.needsPaint to false
|
| + virtual void paint(RenderingSurface canvas);
|
| + // set a clip rect on the canvas for rect(0,0,this.width,this.height)
|
| + // call the painter of each property, in order they were registered, which on this element has a painter
|
| + // call this.paintChildren()
|
| + // unset the clip
|
| + // call markAsPainted()
|
| +
|
| + virtual void paintChildren(RenderingSurface canvas);
|
| + // just calls paint() for each child returned by walkChildren() whose needsPaint is true,
|
| + // after transforming the coordinate space by translate(child.x,child.y)
|
| + // you should skip children that will be clipped out of yourself because they're outside your bounds
|
| +
|
| + virtual Node hitTest(Float x, Float y);
|
| + // default implementation uses the node's children nodes' x, y,
|
| + // width, and height, skipping any that have width=0 or height=0, or
|
| + // whose ownerLayoutManager is not |this|
|
| + // default implementation walks the tree backwards from its built-in order
|
| + // if no child is hit, then return this.node
|
| + // override this if you changed your children's z-order, or if you used take() to
|
| + // hoist some descendants up to be your responsibility, or if your children aren't
|
| + // rectangular (e.g. you lay them out in a hex grid)
|
| + // make sure to offset the value you pass your children: child.layoutManager.hitTest(x-child.x, y-child.y)
|
|
|
| - dictionary LayoutValueRange {
|
| - // negative values here should be treated as zero
|
| - Float minimum = 0;
|
| - Float value = 0; // ideal desired width; if it's not in the range minimum .. maximum then it overrides minimum and maximum
|
| - (Float or Infinity) maximum = Infinity;
|
| - }
|
| +}
|
|
|
| - dictionary Dimensions {
|
| - Float width = 0;
|
| - Float height = 0;
|
| - }
|
| +dictionary LayoutValueRange {
|
| + // negative values here should be treated as zero
|
| + Float minimum = 0;
|
| + Float value = 0; // ideal desired width; if it's not in the range minimum .. maximum then it overrides minimum and maximum
|
| + (Float or Infinity) maximum = Infinity;
|
| +}
|
|
|
| +dictionary Dimensions {
|
| + Float width = 0;
|
| + Float height = 0;
|
| +}
|
| +```
|
|
|
| Given a tree of StyleNode objects rooted at /node/, the application is
|
| rendered as follows:
|
|
|
| - node.layoutManager.layout(screen.width, screen.height);
|
| - node.layoutManager.paint();
|
| -
|
| +```javascript
|
| +node.layoutManager.layout(screen.width, screen.height);
|
| +node.layoutManager.paint();
|
| +```
|
|
|
|
|
| Paint
|
| -----
|
|
|
| - callback void Painter (StyleNode node, RenderingSurface canvas);
|
| +```javascript
|
| +callback void Painter (StyleNode node, RenderingSurface canvas);
|
|
|
| - class RenderingSurface {
|
| - // ...
|
| - }
|
| +class RenderingSurface {
|
| + // ...
|
| +}
|
| +```
|
|
|
| The convention is that the layout manager who calls your paint will
|
| have transformed the coordinate space so that you should assume that
|
|
|