containingWidth = outerMaxWidth;
BlockWidget previousBlock = null;
removeAllChildren();
Style style = element.getComputedStyle();
int display = style.getEnum(Style.DISPLAY);
// Left and right margins are stored in members to avoid the auto margin
// calculation in other places.
marginLeft = style.getPx(Style.MARGIN_LEFT, containingWidth);
marginRight = style.getPx(Style.MARGIN_RIGHT, containingWidth);
int marginTop = style.getPx(Style.MARGIN_TOP, containingWidth);
int marginBottom = style.getPx(Style.MARGIN_BOTTOM, containingWidth);
int borderLeft = style.getPx(Style.BORDER_LEFT_WIDTH, containingWidth);
int borderTop = style.getPx(Style.BORDER_TOP_WIDTH, containingWidth);
int borderBottom = style.getPx(Style.BORDER_BOTTOM_WIDTH, containingWidth);
int borderRight = style.getPx(Style.BORDER_RIGHT_WIDTH, containingWidth);
int paddingLeft = style.getPx(Style.PADDING_LEFT, containingWidth);
int paddingTop = style.getPx(Style.PADDING_TOP, containingWidth);
int paddingBottom = style.getPx(Style.PADDING_BOTTOM, containingWidth);
int paddingRight = style.getPx(Style.PADDING_RIGHT, containingWidth);
int left = marginLeft + borderLeft + paddingLeft;
int right = marginRight + borderRight + paddingRight;
int top = marginTop + borderTop + paddingTop;
int bottom = marginBottom + borderBottom + paddingBottom;
// ShrinkWrap means we need to calculate the width based on the contents
// for floats, table entries etc. without a fixed width
if (shrinkWrap) {
outerMaxWidth = style.lengthIsFixed(Style.WIDTH, true)
? style.getPx(Style.WIDTH, outerMaxWidth) + left + right
: Math.min(outerMaxWidth, getMaximumWidth(containingWidth));
// Otherwise, if this is not a table cell and the width is fixed, we need
// to calculate the value for auto margins here (This is typically used
// to center the contents).
} else if (display != Style.TABLE_CELL &&
style.lengthIsFixed(Style.WIDTH, true)) {
int remaining = (containingWidth -
style.getPx(Style.WIDTH, containingWidth) - left - right);
if (style.getEnum(Style.MARGIN_LEFT) == Style.AUTO) {
if (style.getEnum(Style.MARGIN_RIGHT) == Style.AUTO) {
marginLeft = marginRight = remaining / 2;
left += marginLeft;
right += marginRight;
} else {
marginLeft = remaining;
left += marginLeft;
}
} else {
right += remaining;
marginRight += remaining;
}
}
boxWidth = outerMaxWidth;
boolean fixedHeight = isHeightFixed();
if (fixedHeight) {
boxHeight = top + style.getPx(Style.HEIGHT, getParent().getHeight()) + bottom;
}
// If this is an image element, handle image here and return
if (image != null) {
boolean fixedWidth = style.lengthIsFixed(Style.WIDTH, true);
if (fixedHeight && fixedWidth) {
int w = style.getPx(Style.WIDTH, containingWidth);
int h = style.getPx(Style.HEIGHT, containingWidth);
if ((w != image.getWidth() || h != image.getHeight()) && w > 0 && h > 0) {
image = GraphicsUtils.createScaledImage(image, 0, 0, image.getWidth(),
image.getHeight(), w, h, GraphicsUtils.SCALE_SIMPLE | GraphicsUtils.SCALE_PROCESS_ALPHA);
}
boxWidth = w + left + right;
} else {
boxWidth = image.getWidth() + left + right;
boxHeight = image.getHeight() + top + bottom;
}
setWidth(boxWidth);
setHeight(boxHeight);
if (parentLayoutContext != null) {
parentLayoutContext.advance(boxHeight);
}
return;
}
// calculate the maximum inner width (outerMaxWidth minus borders, padding,
// margin) -- the maximum width available for child layout
int innerMaxWidth = outerMaxWidth - left - right;
// Keeps track of y-position and borders for the regular layout set by
// floating elements. The viewport width is taken into account here.
LayoutContext layoutContext = new LayoutContext(
Math.min(innerMaxWidth, viewportWidth), style, parentLayoutContext,
left, top);
// line break positions determined when laying out TextFragmentWidget
// are carried over from one TextFragmentWidget to another.
int breakPosition = -1;
// Index of first widget on a single line. Used when adjusting position
// for top/bottom/left/right/center align
int lineStartIndex = 0;
// This is the position where regular in-flow widgets are inserted.
// Positioned widget are inserted at the end so they are always on top of
// regular widgets.
int childInsertionIndex = 0;
// iterate all child widgets
for (int childIndex = 0; childIndex < children.size(); childIndex++) {
Widget child = (Widget) children.elementAt(childIndex);
Style childStyle;
int childPosition;
int childWidth;
if (child instanceof TextFragmentWidget) {
TextFragmentWidget fragment = (TextFragmentWidget) child;
addChild(childInsertionIndex, fragment);
childStyle = fragment.element.getComputedStyle();
childPosition = childStyle.getEnum(Style.POSITION);
fragment.setX(left);
fragment.setY(top + layoutContext.getCurrentY());
fragment.setWidth(innerMaxWidth);
// line-break and size the fragment
breakPosition = fragment.doLayout(childIndex, layoutContext,
breakPosition, lineStartIndex, childInsertionIndex);
// update position and status accordingly
if (fragment.getLineCount() > 1) {
lineStartIndex = childInsertionIndex;
}
childInsertionIndex++;
childWidth = fragment.getWidth();
} else {
// break positions are valid only for sequences of TextFragmentWidget
breakPosition = -1;
BlockWidget block = (BlockWidget) child;
childStyle = block.element.getComputedStyle();
int childDisplay = childStyle.getEnum(Style.DISPLAY);
childPosition = childStyle.getEnum(Style.POSITION);
int floating = childStyle.getEnum(Style.FLOAT);
if (childPosition == Style.ABSOLUTE || childPosition == Style.FIXED){
// absolute or fixed position: move block to its position; leave
// anything else unaffected (in particular the layout context).
addChild(block);
block.doLayout(innerMaxWidth, viewportWidth, null, true);
int left1 = marginLeft + borderLeft;
int right1 = marginRight + borderRight;
int top1 = marginTop + borderTop;
int bottom1 = marginBottom + borderBottom;
int iw = boxWidth - left1 - right1;
if (childStyle.getEnum(Style.RIGHT) != Style.AUTO) {
block.setX(boxWidth - block.boxX - right1 - block.boxWidth -
childStyle.getPx(Style.RIGHT, iw));
} else if (childStyle.getEnum(Style.LEFT) != Style.AUTO) {
block.setX(left1 + childStyle.getPx(Style.LEFT, iw) - block.boxX);
} else {
block.setX(left1 - block.boxX);
}
if (childStyle.getEnum(Style.TOP) != Style.AUTO) {
block.setY(top1 - block.boxY +
childStyle.getPx(Style.TOP, getHeight() - top1 - bottom1));
} else if (childStyle.getEnum(Style.BOTTOM) != Style.AUTO) {
block.setY(top1 - block.boxY + boxHeight -
childStyle.getPx(Style.TOP, getHeight() - top1 - bottom1));
} else {
block.setY(top + layoutContext.getCurrentY() - block.boxY);
}
} else if (floating == Style.LEFT || floating == Style.RIGHT){
// float: easy. just call layout for the block and place it.
// the block is added to the layout context, but the current
// y-position remains unchanged (advance() is not called)
addChild(block);
block.doLayout(innerMaxWidth, viewportWidth, null, true);
layoutContext.placeBox(block.boxWidth, block.boxHeight,
floating, childStyle.getEnum(Style.CLEAR));
block.setX(left + layoutContext.getBoxX() - block.boxX);
block.setY(top + layoutContext.getBoxY() - block.boxY);
} else if (childDisplay == Style.BLOCK ||
childDisplay == Style.LIST_ITEM) {
// Blocks and list items always start a new paragraph (implying a new
// line.
// if there is a pending line, adjust the alignement for it
if (layoutContext.getLineHeight() > 0) {
if (lineStartIndex != childInsertionIndex) {
adjustLine(lineStartIndex, childInsertionIndex, layoutContext);
}
layoutContext.advance(layoutContext.getLineHeight());
previousBlock = null;
}
// if the position is relative, the widget is inserted on top of
// others. Other adjustments for relative layout are made at the end
if (childPosition == Style.RELATIVE) {
addChild(block);
} else {
addChild(childInsertionIndex++, block);
}
if (layoutContext.clear(childStyle.getEnum(Style.CLEAR))) {
previousBlock = null;
}
// check whether we can collapse margins with the previous block
if (previousBlock != null) {
int m1 = previousBlock.getElement().getComputedStyle().getPx(
Style.MARGIN_BOTTOM, outerMaxWidth);
int m2 = childStyle.getPx(Style.MARGIN_TOP, outerMaxWidth);
// m1 has been applied already, the difference between m1 and m2
// still needs to be applied
int delta;
if (m1 < 0) {
if (m2 < 0) {
delta = -(m1 + m2);
} else {
delta = -m1;
}
} else if (m2 < 0) {
delta = -m2;
} else {
delta = -Math.min(m1, m2);
}
layoutContext.advance(delta);
}
int saveY = layoutContext.getCurrentY();
block.doLayout(innerMaxWidth, viewportWidth, layoutContext, false);
block.setX(left - block.boxX);
block.setY(top + saveY - block.boxY);
lineStartIndex = childInsertionIndex;
previousBlock = block;
} else {
// inline-block, needs to be inserted in the regular text flow
// similar to text fragments.
addChild(childInsertionIndex, block);
previousBlock = null;
block.doLayout(innerMaxWidth, viewportWidth, null, childDisplay != Style.TABLE);
int avail = layoutContext.getHorizontalSpace(block.boxHeight);
if (avail >= block.boxWidth) {
layoutContext.placeBox(
block.boxWidth, block.boxHeight, Style.NONE, 0);
} else {
// line break necessary
adjustLine(lineStartIndex, childInsertionIndex, layoutContext);
lineStartIndex = childInsertionIndex;
layoutContext.advance(layoutContext.getLineHeight());
layoutContext.placeBox(
block.boxWidth, block.boxHeight, Style.NONE, 0);
layoutContext.advance(layoutContext.getBoxY() -
layoutContext.getCurrentY());
layoutContext.setLineHeight(block.boxHeight);
}
block.setX(left + layoutContext.getBoxX() - block.boxX);
block.setY(top + layoutContext.getCurrentY() - block.boxY);
childInsertionIndex++;
}
childWidth = block.boxWidth;
}
// Make adjustments for relative positioning
if (childPosition == Style.RELATIVE) {
if (childStyle.isSet(Style.RIGHT)) {
child.setX(child.getX() + boxWidth - childWidth -
childStyle.getPx(Style.RIGHT, getWidth()));
} else {
child.setX(child.getX() + childStyle.getPx(Style.LEFT, getWidth()));
}
child.setY(child.getY() + childStyle.getPx(Style.TOP, getHeight()));
}
}
// Still need to adjust alignment if there is a pending line.
if (lineStartIndex != childInsertionIndex &&