void layout( float layoutX, float layoutY, float layoutWidth, float layoutHeight )
{
if( _sizeInvalid )
computeSize();
var cellCount = _cells.Count;
var padLeft = _padLeft.get( this );
var hpadding = padLeft + _padRight.get( this );
var padTop = _padTop.get( this );
var vpadding = padTop + _padBottom.get( this );
int columns = _columns, rows = _rows;
float[] expandWidth = _expandWidth, expandHeight = _expandHeight;
float[] columnWidth = _columnWidth, rowHeight = _rowHeight;
float totalExpandWidth = 0, totalExpandHeight = 0;
for( var i = 0; i < columns; i++ )
totalExpandWidth += expandWidth[i];
for( var i = 0; i < rows; 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 - _tableMinWidth;
if( totalGrowWidth == 0 )
{
columnWeightedWidth = _columnMinWidth;
}
else
{
var extraWidth = Math.Min( totalGrowWidth, Math.Max( 0, layoutWidth - _tableMinWidth ) );
columnWeightedWidth = Table._columnWeightedWidth = ensureSize( Table._columnWeightedWidth, columns );
float[] columnMinWidth = _columnMinWidth, columnPrefWidth = _columnPrefWidth;
for( var i = 0; i < columns; i++ )
{
var growWidth = columnPrefWidth[i] - columnMinWidth[i];
var growRatio = growWidth / totalGrowWidth;
columnWeightedWidth[i] = columnMinWidth[i] + extraWidth * growRatio;
}
}
float[] rowWeightedHeight;
var totalGrowHeight = _tablePrefHeight - _tableMinHeight;
if( totalGrowHeight == 0 )
{
rowWeightedHeight = _rowMinHeight;
}
else
{
rowWeightedHeight = Table._rowWeightedHeight = ensureSize( Table._rowWeightedHeight, rows );
var extraHeight = Math.Min( totalGrowHeight, Math.Max( 0, layoutHeight - _tableMinHeight ) );
float[] rowMinHeight = _rowMinHeight, rowPrefHeight = _rowPrefHeight;
for( int i = 0; i < rows; i++ )
{
float growHeight = rowPrefHeight[i] - rowMinHeight[i];
float growRatio = growHeight / totalGrowHeight;
rowWeightedHeight[i] = rowMinHeight[i] + extraHeight * growRatio;
}
}
// Determine element and cell sizes (before expand or fill).
for( var i = 0; i < cellCount; i++ )
{
var cell = _cells[i];
int column = cell.column, row = cell.row;
var spannedWeightedWidth = 0f;
var colspan = cell.colspan.Value;
for( int ii = column, nn = ii + colspan; ii < nn; ii++ )
spannedWeightedWidth += columnWeightedWidth[ii];
var weightedHeight = rowWeightedHeight[row];
var prefWidth = cell.prefWidth.get( cell.element );
var prefHeight = cell.prefHeight.get( cell.element );
var minWidth = cell.minWidth.get( cell.element );
var minHeight = cell.minHeight.get( cell.element );
var maxWidth = cell.maxWidth.get( cell.element );
var maxHeight = cell.maxHeight.get( cell.element );
if( prefWidth < minWidth )
prefWidth = minWidth;
if( prefHeight < minHeight )
prefHeight = minHeight;
if( maxWidth > 0 && prefWidth > maxWidth )
prefWidth = maxWidth;
if( maxHeight > 0 && prefHeight > maxHeight )
prefHeight = maxHeight;
cell.elementWidth = Math.Min( spannedWeightedWidth - cell.computedPadLeft - cell.computedPadRight, prefWidth );
cell.elementHeight = Math.Min( weightedHeight - cell.computedPadTop - cell.computedPadBottom, prefHeight );
if( colspan == 1 )
columnWidth[column] = Math.Max( columnWidth[column], spannedWeightedWidth );
rowHeight[row] = Math.Max( rowHeight[row], weightedHeight );
}
// distribute remaining space to any expanding columns/rows.
if( totalExpandWidth > 0 )
{
var extra = layoutWidth - hpadding;
for( var i = 0; i < columns; i++ )
extra -= columnWidth[i];
var used = 0f;
var lastIndex = 0;
for( var i = 0; i < columns; i++ )
{
if( expandWidth[i] == 0 )
continue;
var amount = extra * expandWidth[i] / totalExpandWidth;
columnWidth[i] += amount;
used += amount;
lastIndex = i;
}
columnWidth[lastIndex] += extra - used;
}
if( totalExpandHeight > 0 )
{
var extra = layoutHeight - vpadding;
for( var i = 0; i < rows; i++ )
extra -= rowHeight[i];
var used = 0f;
var lastIndex = 0;
for( var i = 0; i < rows; i++ )
{
if( expandHeight[i] == 0 )
continue;
var 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( var i = 0; i < cellCount; i++ )
{
var c = _cells[i];
var colspan = c.colspan.Value;
if( colspan == 1 )
continue;
var extraWidth = 0f;
for( int column = c.column, nn = column + colspan; column < nn; column++ )
extraWidth += columnWeightedWidth[column] - columnWidth[column];
extraWidth -= Math.Max( 0, c.computedPadLeft + c.computedPadRight );
extraWidth /= colspan;
if( extraWidth > 0 )
{
for( int column = c.column, nn = column + colspan; column < nn; column++ )
columnWidth[column] += extraWidth;
}
}
// Determine table size.
float tableWidth = hpadding, tableHeight = vpadding;
for( var i = 0; i < columns; i++ )
tableWidth += columnWidth[i];
for( var i = 0; i < rows; i++ )
tableHeight += rowHeight[i];
// Position table within the container.
var x = layoutX + padLeft;
if( ( _align & AlignInternal.right ) != 0 )
x += layoutWidth - tableWidth;
else if( ( _align & AlignInternal.left ) == 0 ) // Center
x += ( layoutWidth - tableWidth ) / 2;
var y = layoutY + padTop; // bottom
if( ( _align & AlignInternal.bottom ) != 0 )
y += layoutHeight - tableHeight;
else if( ( _align & AlignInternal.top ) == 0 ) // Center
y += ( layoutHeight - tableHeight ) / 2;
// position elements within cells.
float currentX = x, currentY = y;
for( var i = 0; i < cellCount; i++ )
{
var c = _cells[i];
var spannedCellWidth = 0f;
for( int column = c.column, nn = column + c.colspan.Value; column < nn; column++ )
spannedCellWidth += columnWidth[column];
spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
currentX += c.computedPadLeft;
float fillX = c.fillX.Value, fillY = c.fillY.Value;
if( fillX > 0 )
{
c.elementWidth = Math.Max( spannedCellWidth * fillX, c.minWidth.get( c.element ) );
var maxWidth = c.maxWidth.get( c.element );
if( maxWidth > 0 )
c.elementWidth = Math.Min( c.elementWidth, maxWidth );
}
if( fillY > 0 )
{
c.elementHeight = Math.Max( rowHeight[c.row] * fillY - c.computedPadTop - c.computedPadBottom, c.minHeight.get( c.element ) );
var maxHeight = c.maxHeight.get( c.element );
if( maxHeight > 0 )
c.elementHeight = Math.Min( c.elementHeight, maxHeight );
}
var cellAlign = c.align.Value;
if( ( cellAlign & AlignInternal.left ) != 0 )
c.elementX = currentX;
else if( ( cellAlign & AlignInternal.right ) != 0 )
c.elementX = currentX + spannedCellWidth - c.elementWidth;
else
c.elementX = currentX + ( spannedCellWidth - c.elementWidth ) / 2;
if( ( cellAlign & AlignInternal.top ) != 0 )
c.elementY = currentY + c.computedPadTop;
else if( ( cellAlign & AlignInternal.bottom ) != 0 )
c.elementY = currentY + rowHeight[c.row] - c.elementHeight - c.computedPadBottom;
else
c.elementY = currentY + ( rowHeight[c.row] - c.elementHeight + c.computedPadTop - c.computedPadBottom ) / 2;
if( c.endRow )
{
currentX = x;
currentY += rowHeight[c.row];
}
else
{
currentX += spannedCellWidth + c.computedPadRight;
}
}
if( _tableDebug != TableDebug.None )
computeDebugRects( x, y, layoutX, layoutY, layoutWidth, layoutHeight, tableWidth, tableHeight, hpadding, vpadding );
}