flutter/examples/style/toolbar-layout.sky

209 lines
6.9 KiB
Plaintext

SKY MODULE
<import src="sky:core" as="sky"/>
<script>
// display: toolbar;
// toolbar-spacing: <length>
// display: spring; // remaining space is split equally amongst the springs
// children are vertically centered, layout out left-to-right with toolbar-spacing space between them
// last child is hidden by default unless there's not enough room for the others, then it's shown last, right-aligned
module.exports.SpringLayoutManager = class SpringLayoutManager extends sky.LayoutManager { }
sky.registerLayoutManager('spring', module.exports.SpringLayoutManager);
sky.registerProperty({
name: 'toolbar-spacing',
type: sky.LengthStyleValueType,
inherits: true,
initialValue: { value: 8, unit: 'px' },
needsLayout: true,
});
module.exports.ToolbarLayoutManager = class ToolbarLayoutManager extends sky.LayoutManager {
constructor (styleNode) {
super(styleNode);
this.showingOverflow = false;
this.firstSkippedChild = null;
this.overflowChild = null;
}
function layout(width, height) {
let children = null;
let loop = null;
if (height == null)
height = this.getIntrinsicHeight().value;
if (width == null)
this.assumeDimensions(0, height);
else
this.assumeDimensions(width, height);
let spacing = this.node.getProperty('toolbar-spacing');
if (typeof spacing != 'number')
spacing = 0;
this.overflowChild = null;
this.firstSkippedChild = null;
// layout children and figure out whether we need to truncate the child list and show the overflow child
let springCount = 0;
let minX = 0;
let overflowChildWidth = 0;
let pendingSpacing = 0;
children = this.walkChildren();
loop = children.next();
while (!loop.done) {
let child = loop.value;
let dims = null;
if (child.layoutManager instanceof module.exports.SpringLayoutManager) {
springCount += 1;
pendingSpacing = spacing; // not +=, because we only have one extra spacing per batch of springs
} else {
if (child.needsLayout) {
childHeight = child.layoutManager.getIntrinsicHeight();
if (childHeight.value < height)
childHeight = childHeight.value;
else
childHeight = height;
dims = child.layoutManager.layout(width, height);
this.setChildSize(child, dims.width, dims.height);
} else {
dims = {
width: child.width,
height: child.height,
};
}
loop = children.next();
if (!loop.done) {
if (minX > 0)
minX += spacing + pendingSpacing;
minX += dims.width;
pendingSpacing = 0;
} else {
overflowChildWidth = spacing + dims.width;
this.overflowChild = child;
}
}
}
// figure out the spacing
this.showingOverflow = false;
let springSize = 0;
if (width != null) {
if (minX <= width) {
if (springCount > 0)
springSize = (width - minX) / sprintCount;
} else {
this.showingOverflow = true;
}
} else {
width = minX;
}
// position the children
// TODO(ianh): support rtl toolbars
let x = 0;
let lastWasNonSpring = false;
children = this.walkChildren();
loop = children.next();
while (!loop.done) {
let child = loop.value;
if (child.layoutManager instanceof module.exports.SpringLayoutManager) {
x += springSize;
if (lastWasNonSpring)
x += spacing;
lastWasNonSpring = false;
} else {
if (!loop.done) {
if (x + child.width + overflowChildWidth > width) {
this.firstSkippedChild = child;
break; // don't display any more children
}
this.setChildPosition(child, x, (height - child.height)/2);
x += child.width + spacing;
lastWasNonSpring = true;
} else {
// assert: this.showingOverflow == false
}
}
}
if (this.showingOverflow)
this.setChildPosition(this.overflowChild, width-this.overflowChild.width, (height - this.overflowChild.height)/2);
else
this.firstSkippedChild = this.overflowChild;
return {
width: width,
height: height,
}
}
function getIntrinsicWidth() {
let width = this.node.getProperty('width');
if (typeof height != 'number') {
let spacing = this.node.getProperty('toolbar-spacing');
if (typeof spacing != 'number')
spacing = 0;
width = 0;
let children = this.walkChildren();
let loop = children.next();
// we exclude the last child because at our ideal width we wouldn't need it
let last1 = null; // last one
let last2 = null; // one before the last one
while (!loop.done) {
if (last1)
width += last1.layoutManager.getIntrinsicWidth().value;
if (last2)
width += spacing;
last2 = last1;
last1 = loop.value;
loop = children.next();
}
}
return super(width); // applies and provides our own min-width/max-width rules
}
function getIntrinsicHeight() {
// we grow our minimum height to be no smaller than the children's
let result = super();
let determineHeight = false;
let heightProperty = this.node.getProperty('height');
if (typeof heightProperty != 'number')
determineHeight = true;
let children = this.walkChildren();
let loop = children.next();
// here we include the last child so that if it pops in our height doesn't change
while (!loop.done) {
let child = loop.value;
let childHeight = child.layoutManager.getIntrinsicHeight();
if (determineHeight) {
if (result.value < childHeight.value)
result.value = childHeight.value;
}
if (result.minimum < childHeight.minimum)
result.minimum = childHeight.minimum;
loop = children.next();
}
if (result.minimum > result.maximum)
result.maximum = result.minimum;
if (result.value > result.maximum)
result.value = result.maximum;
if (result.value < result.minimum)
result.value = result.minimum;
return result;
}
function paintChildren(canvas) {
let width = this.node.width;
let loop = children.next();
while ((!loop.done) && (loop.value != this.firstSkippedChild))
this.paintChild(loop.value, canvas);
if (this.showingOverflow)
this.paintChild(this.overflowChild, canvas);
}
function paintChild(child, canvas) {
if (child.needsPaint) {
canvas.save();
try {
canvas.beginPath();
canvas.rect(child.x, child.y, child.width, child.height);
canvas.clip();
child.paint(canvas);
} finally {
canvas.restore();
}
}
}
}
sky.registerLayoutManager('toolbar', module.exports.ToolbarLayoutManager);
</script>