| Index: sky/examples/style/hex-layout.sky | 
| diff --git a/sky/examples/style/hex-layout.sky b/sky/examples/style/hex-layout.sky | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..ca286f0401a2c604ea34221c8a48b6502e7f7784 | 
| --- /dev/null | 
| +++ b/sky/examples/style/hex-layout.sky | 
| @@ -0,0 +1,146 @@ | 
| +#!mojo mojo:sky | 
| +<import src="sky:core" as="sky"/> | 
| +<script> | 
| + class BeehiveLayoutManager extends sky.LayoutManager { | 
| +   function layout(width, height) { | 
| +     if (width == null) | 
| +       width = this.getIntrinsicWidth().value; | 
| +     let autoHeight = false; | 
| +     if (height == null) { | 
| +       height = 0; | 
| +       autoHeight = true; | 
| +     } | 
| +     this.assumeDimensions(width, height); | 
| +     let cellCount = this.node.getProperty('beehive-count'); | 
| +     let cellDim = width / cellCount; | 
| +     let children = this.walkChildren(); | 
| +     let loop = children.next(); | 
| +     let x = 0; | 
| +     let y = 0; | 
| +     while (!loop.done) { | 
| +       let child = loop.value; | 
| +       if (child.needsLayout) { | 
| +         child.layoutManager.layout(cellDim, cellDim); | 
| +         // we ignore the size the child reported from layout(), and force it to the cell dimensions | 
| +         this.setChildSize(child, cellDim, cellDim); | 
| +       } | 
| +       this.setChildPosition(child, x * cellDim + (y % 2) * cellDim/2, y * 3/4 * cellDim); | 
| +       x += 1; | 
| +       if (x > cellCount) { | 
| +         y += 1; | 
| +         x = 0; | 
| +       } | 
| +       loop = children.next(); | 
| +     } | 
| +     if (height == 0) | 
| +       height = (1 + y * 3/4) * cellDim; | 
| +     return { | 
| +       width: width, | 
| +       height: height, | 
| +     } | 
| +   } | 
| +   function getIntrinsicWidth() { | 
| +     // this is the logic that LayoutManager.getIntrinsicWidth() has by default | 
| +     // shown here because I wrote it before realising it should be the default | 
| +     let width = this.node.getProperty('width'); | 
| +     if (typeof width != 'number') | 
| +       width = 0; | 
| +     let minWidth = this.node.getProperty('min-width'); | 
| +     if (typeof width != 'number') | 
| +       minWidth = 0; | 
| +     let maxWidth = this.node.getProperty('max-width'); | 
| +     if (typeof width != 'number') | 
| +       maxWidth = Infinity; | 
| +     if (maxWidth < minWidth) | 
| +       maxWidth = minWidth; | 
| +     if (width > maxWidth) | 
| +       width = maxWidth; | 
| +     if (width < minWidth) | 
| +       width = minWidth; | 
| +     return { | 
| +       minimum: minWidth, | 
| +       value: width, | 
| +       maximum: maxWidth, | 
| +     }; | 
| +   } | 
| +   function getIntrinsicHeight() { | 
| +     let height = this.node.getProperty('height'); | 
| +     if (typeof height != 'number') { | 
| +       // e.g. height: auto | 
| +       width = this.getIntrinsicWidth().value; | 
| +       let cellCount = this.node.getProperty('beehive-count'); | 
| +       let cellDim = width / cellCount; | 
| +       let children = this.walkChildren(); | 
| +       let loop = children.next(); | 
| +       let childCount = 0; | 
| +       while (!loop.done) { | 
| +         childCount += 1; | 
| +         loop.next(); | 
| +       } | 
| +       if (childCount > 0) | 
| +         height = cellDim * (1/4 + Math.ceil(childCount / cellCount) * 3/4); | 
| +       else | 
| +         height = 0; | 
| +     } | 
| +     return super(height); // does the equivalent of getIntrinsicWidth() above, applying min-height etc | 
| +   } | 
| +   function paintChildren(RenderingSurface canvas) { | 
| +     let width = this.node.width; | 
| +     let cellCount = this.node.getProperty('beehive-count'); | 
| +     let cellDim = width / cellCount; | 
| +     let children = this.walkChildren(); | 
| +     let loop = children.next(); | 
| +     while (!loop.done) { | 
| +       let child = loop.value; | 
| +       if (child.needsPaint) { | 
| +         canvas.save(); | 
| +         try { | 
| +           canvas.beginPath(); | 
| +           canvas.moveTo(child.x, child.y + cellDim/4); | 
| +           canvas.lineTo(child.x + cellDim/2, child.y); | 
| +           canvas.lineTo(child.x + cellDim, child.y + cellDim/4); | 
| +           canvas.lineTo(child.x + cellDim, child.y + 3*cellDim/4); | 
| +           canvas.lineTo(child.x + cellDim/2, child.y + cellDim); | 
| +           canvas.moveTo(child.x, child.y + 3*cellDim/4); | 
| +           canvas.closePath(); | 
| +           canvas.clip(); | 
| +           child.paint(canvas); | 
| +         } finally { | 
| +           canvas.restore(); | 
| +         } | 
| +       } | 
| +       loop = children.next(); | 
| +     } | 
| +   } | 
| + } | 
| + sky.registerLayoutManager('beehive', BeehiveLayoutManager); | 
| + let BeehiveCountStyleValueType = new StyleValueType(); | 
| + BeehiveCountStyleValueType.addParser((tokens) => { | 
| +   let token = tokens.next(); | 
| +   if (token.done) throw new Error(); | 
| +   if (token.value.kind != 'number') throw new Error(); | 
| +   if (token.value.value <= 0) throw new Error(); | 
| +   if (Math.trunc(token.value.value) != token.value.value) throw new Error(); | 
| +   let result = token.value.value; | 
| +   if (!token.next().done) throw new Error(); | 
| +   return result; | 
| + }); | 
| + sky.registerProperty({ | 
| +   name: 'beehive-count', | 
| +   type: BeehiveCountStyleValueType, | 
| +   inherits: true, | 
| +   initialValue: 5, | 
| +   needsLayout: true, | 
| + }); | 
| +</script> | 
| +<style> | 
| + div { display: beehive; beehive-count: 3; } | 
| +</style> | 
| +<div> | 
| + <t>Hello</t> | 
| + <t>World</t> | 
| + <t>How</t> | 
| + <t>Are</t> | 
| + <t>You</t> | 
| + <t>Today?</t> | 
| +</div> | 
|  |