/*******************************************************************************
* Copyright (c) 2011, Nathan Sweet <nathan.sweet@gmail.com> All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
* distribution. * Neither the name of the <organization> nor the names of its contributors may be used to endorse or
* promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package com.esotericsoftware.tablelayout;
import com.esotericsoftware.tablelayout.Value.FixedValue;
import java.util.ArrayList;
import java.util.List;
// BOZO - Support inserting cells/rows.
/**
* Base layout functionality.
*
* @author Nathan Sweet
*/
abstract public class BaseTableLayout<C, T extends C, L extends BaseTableLayout, K extends Toolkit<C, T, L>> {
static public final int CENTER = 1 << 0;
static public final int TOP = 1 << 1;
static public final int BOTTOM = 1 << 2;
static public final int LEFT = 1 << 3;
static public final int RIGHT = 1 << 4;
static public enum Debug {
none, all, table, cell, widget
}
K toolkit;
T table;
private int columns, rows;
private final ArrayList<Cell> cells = new ArrayList(4);
private final Cell cellDefaults = Cell.defaults(this);
private final ArrayList<Cell> columnDefaults = new ArrayList(2);
private Cell rowDefaults;
private boolean sizeInvalid = true;
private float[] columnMinWidth, rowMinHeight;
private float[] columnPrefWidth, rowPrefHeight;
private float tableMinWidth, tableMinHeight;
private float tablePrefWidth, tablePrefHeight;
private float[] columnWidth, rowHeight;
private float[] expandWidth, expandHeight;
private float[] columnWeightedWidth, rowWeightedHeight;
Value padTop, padLeft, padBottom, padRight;
int align = CENTER;
Debug debug = Debug.none;
public BaseTableLayout(K toolkit) {
this.toolkit = toolkit;
}
/**
* Invalidates the layout. The cached min and pref sizes are recalculated the next time layout is done or the min or
* pref sizes are accessed.
*/
public void invalidate() {
sizeInvalid = true;
}
/** Invalidates the layout of this table and every parent widget. */
abstract public void invalidateHierarchy();
/** Adds a new cell to the table with the specified widget. */
public Cell<C> add(C widget) {
Cell cell = new Cell(this);
cell.widget = widget;
if (cells.size() > 0) {
// Set cell x and y.
Cell lastCell = cells.get(cells.size() - 1);
if (!lastCell.endRow) {
cell.column = lastCell.column + lastCell.colspan;
cell.row = lastCell.row;
} else
cell.row = lastCell.row + 1;
// Set the index of the cell above.
if (cell.row > 0) {
outer: for (int i = cells.size() - 1; i >= 0; i--) {
Cell other = cells.get(i);
for (int column = other.column, nn = column + other.colspan; column < nn; column++) {
if (column == cell.column) {
cell.cellAboveIndex = i;
break outer;
}
}
}
}
}
cells.add(cell);
cell.set(cellDefaults);
if (cell.column < columnDefaults.size()) {
Cell columnDefaults = this.columnDefaults.get(cell.column);
if (columnDefaults != null)
cell.merge(columnDefaults);
}
cell.merge(rowDefaults);
if (widget != null)
toolkit.addChild(table, widget);
return cell;
}
/**
* Indicates that subsequent cells should be added to a new row and returns the cell values that will be used as the
* defaults for all cells in the new row.
*/
public Cell row() {
if (cells.size() > 0)
endRow();
rowDefaults = new Cell(this);
return rowDefaults;
}
private void endRow() {
int rowColumns = 0;
for (int i = cells.size() - 1; i >= 0; i--) {
Cell cell = cells.get(i);
if (cell.endRow)
break;
rowColumns += cell.colspan;
}
columns = Math.max(columns, rowColumns);
rows++;
cells.get(cells.size() - 1).endRow = true;
invalidate();
}
/**
* Gets the cell values that will be used as the defaults for all cells in the specified column. Columns are indexed
* starting at 0.
*/
public Cell columnDefaults(int column) {
Cell cell = columnDefaults.size() > column ? columnDefaults.get(column) : null;
if (cell == null) {
cell = new Cell(this);
if (column >= columnDefaults.size()) {
for (int i = columnDefaults.size(); i < column; i++)
columnDefaults.add(null);
columnDefaults.add(cell);
} else
columnDefaults.set(column, cell);
}
return cell;
}
/**
* Removes all widgets and cells from the table (same as {@link #clear()}) and additionally resets all table
* properties and cell, column, and row defaults.
*/
public void reset() {
clear();
padTop = null;
padLeft = null;
padBottom = null;
padRight = null;
align = CENTER;
if (debug != Debug.none)
toolkit.clearDebugRectangles((L) this);
debug = Debug.none;
cellDefaults.set(Cell.defaults(this));
columnDefaults.clear();
}
/** Removes all widgets and cells from the table. */
public void clear() {
for (int i = cells.size() - 1; i >= 0; i--) {
Object widget = cells.get(i).widget;
if (widget != null)
toolkit.removeChild(table, (C) widget);
}
cells.clear();
rows = 0;
columns = 0;
rowDefaults = null;
invalidate();
}
/** Returns the cell for the specified widget in this table, or null. */
public Cell getCell(C widget) {
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.widget == widget)
return c;
}
return null;
}
/** Returns the cells for this table. */
public List<Cell> getCells() {
return cells;
}
public void setToolkit(K toolkit) {
this.toolkit = toolkit;
}
/** Returns the table widget that will be laid out. */
public T getTable() {
return table;
}
/** Sets the table widget that will be laid out. */
public void setTable(T table) {
this.table = table;
}
/** The minimum width of the table. */
public float getMinWidth() {
if (sizeInvalid)
computeSize();
return tableMinWidth;
}
/** The minimum size of the table. */
public float getMinHeight() {
if (sizeInvalid)
computeSize();
return tableMinHeight;
}
/** The preferred width of the table. */
public float getPrefWidth() {
if (sizeInvalid)
computeSize();
return tablePrefWidth;
}
/** The preferred height of the table. */
public float getPrefHeight() {
if (sizeInvalid)
computeSize();
return tablePrefHeight;
}
/** The cell values that will be used as the defaults for all cells. */
public Cell defaults() {
return cellDefaults;
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public L pad(Value pad) {
padTop = pad;
padLeft = pad;
padBottom = pad;
padRight = pad;
sizeInvalid = true;
return (L) this;
}
public L pad(Value top, Value left, Value bottom, Value right) {
padTop = top;
padLeft = left;
padBottom = bottom;
padRight = right;
sizeInvalid = true;
return (L) this;
}
/** Padding at the top edge of the table. */
public L padTop(Value padTop) {
this.padTop = padTop;
sizeInvalid = true;
return (L) this;
}
/** Padding at the left edge of the table. */
public L padLeft(Value padLeft) {
this.padLeft = padLeft;
sizeInvalid = true;
return (L) this;
}
/** Padding at the bottom edge of the table. */
public L padBottom(Value padBottom) {
this.padBottom = padBottom;
sizeInvalid = true;
return (L) this;
}
/** Padding at the right edge of the table. */
public L padRight(Value padRight) {
this.padRight = padRight;
sizeInvalid = true;
return (L) this;
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public L pad(float pad) {
padTop = new FixedValue(pad);
padLeft = new FixedValue(pad);
padBottom = new FixedValue(pad);
padRight = new FixedValue(pad);
sizeInvalid = true;
return (L) this;
}
public L pad(float top, float left, float bottom, float right) {
padTop = new FixedValue(top);
padLeft = new FixedValue(left);
padBottom = new FixedValue(bottom);
padRight = new FixedValue(right);
sizeInvalid = true;
return (L) this;
}
/** Padding at the top edge of the table. */
public L padTop(float padTop) {
this.padTop = new FixedValue(padTop);
sizeInvalid = true;
return (L) this;
}
/** Padding at the left edge of the table. */
public L padLeft(float padLeft) {
this.padLeft = new FixedValue(padLeft);
sizeInvalid = true;
return (L) this;
}
/** Padding at the bottom edge of the table. */
public L padBottom(float padBottom) {
this.padBottom = new FixedValue(padBottom);
sizeInvalid = true;
return (L) this;
}
/** Padding at the right edge of the table. */
public L padRight(float padRight) {
this.padRight = new FixedValue(padRight);
sizeInvalid = true;
return (L) this;
}
/**
* Alignment of the logical table within the table widget. Set to {@link #CENTER}, {@link #TOP}, {@link #BOTTOM} ,
* {@link #LEFT}, {@link #RIGHT}, or any combination of those.
*/
public L align(int align) {
this.align = align;
return (L) this;
}
/**
* Sets the alignment of the logical table within the table widget to {@link #CENTER}. This clears any other
* alignment.
*/
public L center() {
align = CENTER;
return (L) this;
}
/** Adds {@link #TOP} and clears {@link #BOTTOM} for the alignment of the logical table within the table widget. */
public L top() {
align |= TOP;
align &= ~BOTTOM;
return (L) this;
}
/** Adds {@link #LEFT} and clears {@link #RIGHT} for the alignment of the logical table within the table widget. */
public L left() {
align |= LEFT;
align &= ~RIGHT;
return (L) this;
}
/** Adds {@link #BOTTOM} and clears {@link #TOP} for the alignment of the logical table within the table widget. */
public L bottom() {
align |= BOTTOM;
align &= ~TOP;
return (L) this;
}
/** Adds {@link #RIGHT} and clears {@link #LEFT} for the alignment of the logical table within the table widget. */
public L right() {
align |= RIGHT;
align &= ~LEFT;
return (L) this;
}
/** Turns on all debug lines. */
public L debug() {
this.debug = Debug.all;
invalidate();
return (L) this;
}
/** Turns on table debug lines. */
public L debugTable() {
this.debug = Debug.table;
invalidate();
return (L) this;
}
/** Turns on cell debug lines. */
public L debugCell() {
this.debug = Debug.cell;
invalidate();
return (L) this;
}
/** Turns on widget debug lines. */
public L debugWidget() {
this.debug = Debug.widget;
invalidate();
return (L) this;
}
/** Turns on debug lines. */
public L debug(Debug debug) {
this.debug = debug;
if (debug == Debug.none)
toolkit.clearDebugRectangles((L) this);
else
invalidate();
return (L) this;
}
public Debug getDebug() {
return debug;
}
public Value getPadTopValue() {
return padTop;
}
public float getPadTop() {
return padTop == null ? 0 : padTop.height(this);
}
public Value getPadLeftValue() {
return padLeft;
}
public float getPadLeft() {
return padLeft == null ? 0 : padLeft.width(this);
}
public Value getPadBottomValue() {
return padBottom;
}
public float getPadBottom() {
return padBottom == null ? 0 : padBottom.height(this);
}
public Value getPadRightValue() {
return padRight;
}
public float getPadRight() {
return padRight == null ? 0 : padRight.width(this);
}
public int getAlign() {
return align;
}
/** Returns the row index for the y coordinate, or -1 if there are no cells. */
public int getRow(float y) {
int row = 0;
y += h(padTop);
int i = 0, n = cells.size();
if (n == 0)
return -1;
// Skip first row.
while (i < n && !cells.get(i).isEndRow())
i++;
while (i < n) {
Cell c = cells.get(i++);
if (c.getIgnore())
continue;
if (c.widgetY + c.computedPadTop > y)
break;
if (c.endRow)
row++;
}
return rows - row;
}
private float[] ensureSize(float[] array, int size) {
if (array == null || array.length < size)
return new float[size];
for (int i = 0, n = array.length; i < n; i++)
array[i] = 0;
return array;
}
private float w(Value value) {
return value == null ? 0 : value.width(table);
}
private float h(Value value) {
return value == null ? 0 : value.height(table);
}
private float w(Value value, Cell cell) {
return value == null ? 0 : value.width(cell);
}
private float h(Value value, Cell cell) {
return value == null ? 0 : value.height(cell);
}
private void computeSize() {
sizeInvalid = false;
Toolkit toolkit = this.toolkit;
ArrayList<Cell> cells = this.cells;
if (cells.size() > 0 && !cells.get(cells.size() - 1).endRow)
endRow();
columnMinWidth = ensureSize(columnMinWidth, columns);
rowMinHeight = ensureSize(rowMinHeight, rows);
columnPrefWidth = ensureSize(columnPrefWidth, columns);
rowPrefHeight = ensureSize(rowPrefHeight, rows);
columnWidth = ensureSize(columnWidth, columns);
rowHeight = ensureSize(rowHeight, rows);
expandWidth = ensureSize(expandWidth, columns);
expandHeight = ensureSize(expandHeight, rows);
float spaceRightLast = 0;
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
// Collect columns/rows that expand.
if (c.expandY != 0 && expandHeight[c.row] == 0)
expandHeight[c.row] = c.expandY;
if (c.colspan == 1 && c.expandX != 0 && expandWidth[c.column] == 0)
expandWidth[c.column] = c.expandX;
// Compute combined padding/spacing for cells.
// Spacing between widgets isn't additive, the larger is used. Also, no spacing around edges.
c.computedPadLeft = w(c.padLeft, c) + (c.column == 0 ? 0 : Math.max(0, w(c.spaceLeft, c) - spaceRightLast));
c.computedPadTop = h(c.padTop, c);
if (c.cellAboveIndex != -1) {
Cell above = cells.get(c.cellAboveIndex);
c.computedPadTop += Math.max(0, h(c.spaceTop, c) - h(above.spaceBottom, above));
}
float spaceRight = w(c.spaceRight, c);
c.computedPadRight = w(c.padRight, c) + ((c.column + c.colspan) == columns ? 0 : spaceRight);
c.computedPadBottom = h(c.padBottom, c) + (c.row == rows - 1 ? 0 : h(c.spaceBottom, c));
spaceRightLast = spaceRight;
// Determine minimum and preferred cell sizes.
float prefWidth = w(c.prefWidth, c);
float prefHeight = h(c.prefHeight, c);
float minWidth = w(c.minWidth, c);
float minHeight = h(c.minHeight, c);
float maxWidth = w(c.maxWidth, c);
float maxHeight = h(c.maxHeight, c);
if (prefWidth < minWidth)
prefWidth = minWidth;
if (prefHeight < minHeight)
prefHeight = minHeight;
if (maxWidth > 0 && prefWidth > maxWidth)
prefWidth = maxWidth;
if (maxHeight > 0 && prefHeight > maxHeight)
prefHeight = maxHeight;
if (c.colspan == 1) { // Spanned column min and pref width is added later.
float hpadding = c.computedPadLeft + c.computedPadRight;
columnPrefWidth[c.column] = Math.max(columnPrefWidth[c.column], prefWidth + hpadding);
columnMinWidth[c.column] = Math.max(columnMinWidth[c.column], minWidth + hpadding);
}
float vpadding = c.computedPadTop + c.computedPadBottom;
rowPrefHeight[c.row] = Math.max(rowPrefHeight[c.row], prefHeight + vpadding);
rowMinHeight[c.row] = Math.max(rowMinHeight[c.row], minHeight + vpadding);
}
// Colspan with expand will expand all spanned columns if none of the spanned columns have expand.
outer: for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore || c.expandX == 0)
continue;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
if (expandWidth[column] != 0)
continue outer;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
expandWidth[column] = c.expandX;
}
// Distribute any additional min and pref width added by colspanned cells to the columns spanned.
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore || c.colspan == 1)
continue;
float minWidth = w(c.minWidth, c);
float prefWidth = w(c.prefWidth, c);
float maxWidth = w(c.maxWidth, c);
if (prefWidth < minWidth)
prefWidth = minWidth;
if (maxWidth > 0 && prefWidth > maxWidth)
prefWidth = maxWidth;
float spannedMinWidth = 0, spannedPrefWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++) {
spannedMinWidth += columnMinWidth[column];
spannedPrefWidth += columnPrefWidth[column];
}
// Distribute extra space using expand, if any columns have expand.
float totalExpandWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
totalExpandWidth += expandWidth[column];
float extraMinWidth = Math.max(0, minWidth - spannedMinWidth);
float extraPrefWidth = Math.max(0, prefWidth - spannedPrefWidth);
for (int column = c.column, nn = column + c.colspan; column < nn; column++) {
float ratio = totalExpandWidth == 0 ? 1f / c.colspan : expandWidth[column] / totalExpandWidth;
columnMinWidth[column] += extraMinWidth * ratio;
columnPrefWidth[column] += extraPrefWidth * ratio;
}
}
// Collect uniform size.
float uniformMinWidth = 0, uniformMinHeight = 0;
float uniformPrefWidth = 0, uniformPrefHeight = 0;
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
// Collect uniform sizes.
if (c.uniformX == Boolean.TRUE && c.colspan == 1) {
float hpadding = c.computedPadLeft + c.computedPadRight;
uniformMinWidth = Math.max(uniformMinWidth, columnMinWidth[c.column] - hpadding);
uniformPrefWidth = Math.max(uniformPrefWidth, columnPrefWidth[c.column] - hpadding);
}
if (c.uniformY == Boolean.TRUE) {
float vpadding = c.computedPadTop + c.computedPadBottom;
uniformMinHeight = Math.max(uniformMinHeight, rowMinHeight[c.row] - vpadding);
uniformPrefHeight = Math.max(uniformPrefHeight, rowPrefHeight[c.row] - vpadding);
}
}
// Size uniform cells to the same width/height.
if (uniformPrefWidth > 0 || uniformPrefHeight > 0) {
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
if (uniformPrefWidth > 0 && c.uniformX == Boolean.TRUE && c.colspan == 1) {
float hpadding = c.computedPadLeft + c.computedPadRight;
columnMinWidth[c.column] = uniformMinWidth + hpadding;
columnPrefWidth[c.column] = uniformPrefWidth + hpadding;
}
if (uniformPrefHeight > 0 && c.uniformY == Boolean.TRUE) {
float vpadding = c.computedPadTop + c.computedPadBottom;
rowMinHeight[c.row] = uniformMinHeight + vpadding;
rowPrefHeight[c.row] = uniformPrefHeight + vpadding;
}
}
}
// Determine table min and pref size.
tableMinWidth = 0;
tableMinHeight = 0;
tablePrefWidth = 0;
tablePrefHeight = 0;
for (int i = 0; i < columns; i++) {
tableMinWidth += columnMinWidth[i];
tablePrefWidth += columnPrefWidth[i];
}
for (int i = 0; i < rows; i++) {
tableMinHeight += rowMinHeight[i];
tablePrefHeight += Math.max(rowMinHeight[i], rowPrefHeight[i]);
}
float hpadding = w(padLeft) + w(padRight);
float vpadding = h(padTop) + h(padBottom);
tableMinWidth = tableMinWidth + hpadding;
tableMinHeight = tableMinHeight + vpadding;
tablePrefWidth = Math.max(tablePrefWidth + hpadding, tableMinWidth);
tablePrefHeight = Math.max(tablePrefHeight + vpadding, tableMinHeight);
}
/**
* Positions and sizes children of the table using the cell associated with each child. The values given are the
* position within the parent and size of the table.
*/
public void layout(float layoutX, float layoutY, float layoutWidth, float layoutHeight) {
Toolkit toolkit = this.toolkit;
ArrayList<Cell> cells = this.cells;
if (sizeInvalid)
computeSize();
float hpadding = w(padLeft) + w(padRight);
float vpadding = h(padTop) + h(padBottom);
// totalMinWidth/totalMinHeight are needed because tableMinWidth/tableMinHeight could be based on this.width or this.height.
float totalMinWidth = 0, totalMinHeight = 0;
float totalExpandWidth = 0, totalExpandHeight = 0;
for (int i = 0; i < columns; i++) {
totalMinWidth += columnMinWidth[i];
totalExpandWidth += expandWidth[i];
}
for (int i = 0; i < rows; i++) {
totalMinHeight += rowMinHeight[i];
totalExpandHeight += expandHeight[i];
}
// Size columns and rows between min and pref size using (preferred - min) size to weight distribution of extra space.
float[] columnWeightedWidth;
float totalGrowWidth = tablePrefWidth - totalMinWidth;
if (totalGrowWidth == 0)
columnWeightedWidth = columnMinWidth;
else {
float extraWidth = Math.min(totalGrowWidth, Math.max(0, layoutWidth - totalMinWidth));
columnWeightedWidth = this.columnWeightedWidth = ensureSize(this.columnWeightedWidth, columns);
for (int i = 0; i < columns; i++) {
float growWidth = columnPrefWidth[i] - columnMinWidth[i];
float growRatio = growWidth / (float) totalGrowWidth;
columnWeightedWidth[i] = columnMinWidth[i] + extraWidth * growRatio;
}
}
float[] rowWeightedHeight;
float totalGrowHeight = tablePrefHeight - totalMinHeight;
if (totalGrowHeight == 0)
rowWeightedHeight = rowMinHeight;
else {
rowWeightedHeight = this.rowWeightedHeight = ensureSize(this.rowWeightedHeight, rows);
float extraHeight = Math.min(totalGrowHeight, Math.max(0, layoutHeight - totalMinHeight));
for (int i = 0; i < rows; i++) {
float growHeight = rowPrefHeight[i] - rowMinHeight[i];
float growRatio = growHeight / (float) totalGrowHeight;
rowWeightedHeight[i] = rowMinHeight[i] + extraHeight * growRatio;
}
}
// Determine widget and cell sizes (before expand or fill).
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
float spannedWeightedWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
spannedWeightedWidth += columnWeightedWidth[column];
float weightedHeight = rowWeightedHeight[c.row];
float prefWidth = w(c.prefWidth, c);
float prefHeight = h(c.prefHeight, c);
float minWidth = w(c.minWidth, c);
float minHeight = h(c.minHeight, c);
float maxWidth = w(c.maxWidth, c);
float maxHeight = h(c.maxHeight, c);
if (prefWidth < minWidth)
prefWidth = minWidth;
if (prefHeight < minHeight)
prefHeight = minHeight;
if (maxWidth > 0 && prefWidth > maxWidth)
prefWidth = maxWidth;
if (maxHeight > 0 && prefHeight > maxHeight)
prefHeight = maxHeight;
c.widgetWidth = Math.min(spannedWeightedWidth - c.computedPadLeft - c.computedPadRight, prefWidth);
c.widgetHeight = Math.min(weightedHeight - c.computedPadTop - c.computedPadBottom, prefHeight);
if (c.colspan == 1)
columnWidth[c.column] = Math.max(columnWidth[c.column], spannedWeightedWidth);
rowHeight[c.row] = Math.max(rowHeight[c.row], weightedHeight);
}
// Distribute remaining space to any expanding columns/rows.
if (totalExpandWidth > 0) {
float extra = layoutWidth - hpadding;
for (int i = 0; i < columns; i++)
extra -= columnWidth[i];
float used = 0;
int lastIndex = 0;
for (int i = 0; i < columns; i++) {
if (expandWidth[i] == 0)
continue;
float amount = extra * expandWidth[i] / totalExpandWidth;
columnWidth[i] += amount;
used += amount;
lastIndex = i;
}
columnWidth[lastIndex] += extra - used;
}
if (totalExpandHeight > 0) {
float extra = layoutHeight - vpadding;
for (int i = 0; i < rows; i++)
extra -= rowHeight[i];
float used = 0;
int lastIndex = 0;
for (int i = 0; i < rows; i++) {
if (expandHeight[i] == 0)
continue;
float amount = extra * expandHeight[i] / totalExpandHeight;
rowHeight[i] += amount;
used += amount;
lastIndex = i;
}
rowHeight[lastIndex] += extra - used;
}
// Distribute any additional width added by colspanned cells to the columns spanned.
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
if (c.colspan == 1)
continue;
float extraWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
extraWidth += columnWeightedWidth[column] - columnWidth[column];
extraWidth -= Math.max(0, c.computedPadLeft + c.computedPadRight);
extraWidth /= c.colspan;
if (extraWidth > 0) {
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
columnWidth[column] += extraWidth;
}
}
// Determine table size.
float tableWidth = hpadding, tableHeight = vpadding;
for (int i = 0; i < columns; i++)
tableWidth += columnWidth[i];
for (int i = 0; i < rows; i++)
tableHeight += rowHeight[i];
// Position table within the container.
float x = layoutX + w(padLeft);
if ((align & RIGHT) != 0)
x += layoutWidth - tableWidth;
else if ((align & LEFT) == 0) // Center
x += (layoutWidth - tableWidth) / 2;
float y = layoutY + w(padTop);
if ((align & BOTTOM) != 0)
y += layoutHeight - tableHeight;
else if ((align & TOP) == 0) // Center
y += (layoutHeight - tableHeight) / 2;
// Position widgets within cells.
float currentX = x, currentY = y;
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
float spannedCellWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
spannedCellWidth += columnWidth[column];
spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
currentX += c.computedPadLeft;
if (c.fillX > 0) {
c.widgetWidth = spannedCellWidth * c.fillX;
float maxWidth = w(c.maxWidth, c);
if (maxWidth > 0)
c.widgetWidth = Math.min(c.widgetWidth, maxWidth);
}
if (c.fillY > 0) {
c.widgetHeight = rowHeight[c.row] * c.fillY - c.computedPadTop - c.computedPadBottom;
float maxHeight = h(c.maxHeight, c);
if (maxHeight > 0)
c.widgetHeight = Math.min(c.widgetHeight, maxHeight);
}
if ((c.align & LEFT) != 0)
c.widgetX = currentX;
else if ((c.align & RIGHT) != 0)
c.widgetX = currentX + spannedCellWidth - c.widgetWidth;
else
c.widgetX = currentX + (spannedCellWidth - c.widgetWidth) / 2;
if ((c.align & TOP) != 0)
c.widgetY = currentY + c.computedPadTop;
else if ((c.align & BOTTOM) != 0)
c.widgetY = currentY + rowHeight[c.row] - c.widgetHeight - c.computedPadBottom;
else
c.widgetY = currentY + (rowHeight[c.row] - c.widgetHeight + c.computedPadTop - c.computedPadBottom) / 2;
if (c.endRow) {
currentX = x;
currentY += rowHeight[c.row];
} else
currentX += spannedCellWidth + c.computedPadRight;
}
// Draw debug widgets and bounds.
if (debug == Debug.none)
return;
toolkit.clearDebugRectangles(this);
currentX = x;
currentY = y;
if (debug == Debug.table || debug == Debug.all) {
toolkit.addDebugRectangle(this, Debug.table, layoutX, layoutY, layoutWidth, layoutHeight);
toolkit.addDebugRectangle(this, Debug.table, x, y, tableWidth - hpadding, tableHeight - vpadding);
}
for (int i = 0, n = cells.size(); i < n; i++) {
Cell c = cells.get(i);
if (c.ignore)
continue;
// Widget bounds.
if (debug == Debug.widget || debug == Debug.all)
toolkit.addDebugRectangle(this, Debug.widget, c.widgetX, c.widgetY, c.widgetWidth, c.widgetHeight);
// Cell bounds.
float spannedCellWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
spannedCellWidth += columnWidth[column];
spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
currentX += c.computedPadLeft;
if (debug == Debug.cell || debug == Debug.all) {
toolkit.addDebugRectangle(this, Debug.cell, currentX, currentY + c.computedPadTop, spannedCellWidth,
rowHeight[c.row] - c.computedPadTop - c.computedPadBottom);
}
if (c.endRow) {
currentX = x;
currentY += rowHeight[c.row];
} else
currentX += spannedCellWidth + c.computedPadRight;
}
}
}