
actually telling your child to paint, you just say where it would paint. The platform then takes care of making sure all the dirty nodes have their paint() methods called. Review URL: https://codereview.chromium.org/744843003
211 lines
7.2 KiB
Plaintext
211 lines
7.2 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.PositiveLengthStyleValueType,
|
|
inherits: true,
|
|
initialValue: 8,
|
|
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) {
|
|
this.markAsLaidOut();
|
|
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 || child.descendantNeedsLayout) {
|
|
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 width != '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 children = this.walkChildren();
|
|
let loop = children.next();
|
|
while ((!loop.done) && (loop.value != this.firstSkippedChild))
|
|
canvas.paintChild(loop.value);
|
|
if (this.showingOverflow)
|
|
canvas.paintChild(this.overflowChild);
|
|
}
|
|
function inChild(child, x, y) {
|
|
return (x >= child.x) && (y >= child.y) && (x < child.x+child.width) && (y < child.y+child.height);
|
|
}
|
|
function hitTest(x, y) {
|
|
let children = this.walkChildrenBackwards();
|
|
let loop = children.next();
|
|
while ((!loop.done) && (loop.value != this.firstSkippedChild))
|
|
if (this.inChild(loop.value, x, y))
|
|
return loop.value;
|
|
if (this.showingOverflow)
|
|
if (this.inChild(this.overflowChild, x, y))
|
|
return this.overflowChild;
|
|
}
|
|
}
|
|
sky.registerLayoutManager('toolbar', module.exports.ToolbarLayoutManager);
|
|
</script>
|