| #!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); |
| } else if (child.descendantNeedsLayout) { |
| child.layoutManager.layoutDescendants(); |
| 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; |
| this.markAsLaidOut(); |
| return { |
| width: width, |
| height: height, |
| } |
| } |
| 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(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; |
| 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(); |
| canvas.paintChild(child); |
| } finally { |
| canvas.restore(); |
| } |
| loop = children.next(); |
| } |
| } |
| function inHex(topLeftX, topLeftY, width, height, hitX, hitY) { |
| let centerX = topLeftX - width/2; |
| let absCenteredHitX = Math.abs(hitX - centerX); |
| if (absCenteredHitX > width/2) |
| return false; |
| let centerY = topLeftY - height/2; |
| let absCenteredHitY = Math.abs(hitY - centerY); |
| if (absCenteredHitY > height/2) |
| return false; |
| if (absCenteredHitY < height * absCenteredHitX / (2 * width) + height / 2) |
| return true; |
| return false; |
| } |
| function hitTest(x, y) { |
| 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 (this.inHex(child.x, child.y, child.width, child.height, x, y)) |
| return child.layoutManager.hitTest(x-child.x, y-child.y); |
| loop = children.next(); |
| } |
| return this.node; |
| } |
| } |
| sky.registerLayoutManager('beehive', BeehiveLayoutManager); |
| let BeehiveCountStyleGrammar = new StyleGrammar(); |
| BeehiveCountStyleGrammar.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) // is integer |
| throw new Error(); |
| return new NumericStyleValue(token.value.value); |
| }); |
| sky.registerProperty({ |
| name: 'beehive-count', |
| type: BeehiveCountStyleGrammar, |
| 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> |