Index: sky/specs/style.md |
diff --git a/sky/specs/style.md b/sky/specs/style.md |
index 61ae4b4076a3dea09b15de546fa63c16a018b56a..72daaaa4278d1e0c865517fc73939aa48b5fe45f 100644 |
--- a/sky/specs/style.md |
+++ b/sky/specs/style.md |
@@ -352,17 +352,27 @@ class StyleNode { |
// 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 Boolean needsLayout; |
+ // means that either needsLayout is true or a property with needsLayout:true has changed on this node |
// needsLayout is set to false by the ownerLayoutManager's default layout() method |
- readonly attribute LayoutManager layoutManager; |
+ readonly attribute Boolean descendantNeedsLayout; |
+ // means that some child of this node has needsLayout set to true |
+ // descendantNeedsLayout is set to false by the ownerLayoutManager's default layout() method |
+ |
+ 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 |
+ readonly attribute Boolean needsPaint; |
+ // means that either needsLayout is true or a property with needsPaint:true has changed on this node |
// needsPaint is set to false by the ownerLayoutManager's default paint() method |
+ readonly attribute Boolean descendantNeedsPaint; |
+ // means that some child of this node has needsPaint set to true |
+ // descendantNeedsPaint 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 |
@@ -448,31 +458,100 @@ class LayoutManager { |
// 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 |
+ /* |
+ function getIntrinsicWidth(defaultWidth) { |
+ if (defaultWidth == null) { |
+ defaultWidth = this.node.getProperty('width'); |
+ if (typeof defaultWidth != 'number') |
+ defaultWidth = 0; |
+ } |
+ let minWidth = this.node.getProperty('min-width'); |
+ if (typeof minWidth != 'number') |
+ minWidth = 0; |
+ let maxWidth = this.node.getProperty('max-width'); |
+ if (typeof maxWidth != 'number') |
+ maxWidth = Infinity; |
+ if (maxWidth < minWidth) |
+ maxWidth = minWidth; |
+ if (defaultWidth > maxWidth) |
+ defaultWidth = maxWidth; |
+ if (defaultWidth < minWidth) |
+ defaultWidth = minWidth; |
+ return { |
+ minimum: minWidth, |
+ value: defaultWidth, |
+ maximum: maxWidth, |
+ }; |
+ } |
+ */ |
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 |
- |
- void markAsLaidOut(); // sets this.node.needsLayout to false |
+ /* |
+ function getIntrinsicHeight(defaultHeight) { |
+ if (defaultHeight == null) { |
+ defaultHeight = this.node.getProperty('height'); |
+ if (typeof defaultHeight != 'number') |
+ defaultHeight = 0; |
+ } |
+ let minHeight = this.node.getProperty('min-height'); |
+ if (typeof minHeight != 'number') |
+ minHeight = 0; |
+ let maxHeight = this.node.getProperty('max-height'); |
+ if (typeof maxHeight != 'number') |
+ maxHeight = Infinity; |
+ if (maxHeight < minHeight) |
+ maxHeight = minHeight; |
+ if (defaultHeight > maxHeight) |
+ defaultHeight = maxHeight; |
+ if (defaultHeight < minHeight) |
+ defaultHeight = minHeight; |
+ return { |
+ minimum: minHeight, |
+ value: defaultHeight, |
+ maximum: maxHeight, |
+ }; |
+ } |
+ */ |
+ |
+ void markAsLaidOut(); // sets this.node.needsLayout and this.node.descendantNeedsLayout to false |
virtual Dimensions layout(Number? width, Number? height); |
- // default implementation calls markAsLaidOut() and returns arguments, with null values resolved to intrinsic dimensions |
- // this should always call this.markAsLaidOut() to reset needsLayout |
- // the return value should include the final value for whichever of the width and height arguments that is null |
- |
- void markAsPainted(); // sets this.node.needsPaint to false |
+ // call markAsLaidOut(); |
+ // if width is null, set width to getIntrinsicWidth().value |
+ // if height is null, set width height getIntrinsicHeight().value |
+ // call this.layoutChildren(width, height); |
+ // return { width: width, height: height } |
+ // - this should always call this.markAsLaidOut() to reset needsLayout |
+ // - the return value should include the final value for whichever of the width and height arguments |
+ // that is null |
+ // - subclasses that want to make 'auto' values dependent on the children should override this |
+ // entirely, rather than overriding layoutChildren |
+ |
+ virtual void layoutChildren(Number width, Number height); |
+ // default implementation does nothing |
+ // - override this if you want to lay out children but not have the children affect your dimensions |
+ |
+ void markAsPainted(); // sets this.node.needsPaint and this.node.descendantNeedsPaint 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() |
+ // if needsPaint is true: |
+ // call the painter of each property, in order they were registered, which on this element has a painter |
+ // call this.paintChildren(canvas) |
+ // (the default implementation doesn't paint anything on top of the children) |
// 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 |
+ // if descendantNeedsPaint is true: |
+ // for each child returned by walkChildren(): |
+ // if child.needsPaint or child.descendantNeedsPaint: |
+ // call this.paintChild(canvas, child) |
+ // - you should skip children that will be clipped out of yourself because they're outside your bounds |
+ |
+ virtual void paintChild(RenderingSurface canvas, LayoutManager child); |
+ // insert a "paint this child" instruction in our canvas instruction list (we should probably make sure we expose that API directly, too) |
+ // start a new canvas for the child: |
+ // transform the coordinate space by translate(child.x, child.y) |
+ // call child.paint(canvas) |
virtual Node hitTest(Float x, Float y); |
// default implementation uses the node's children nodes' x, y, |