314 lines
13 KiB
C++
314 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2002 Lars Knoll (knoll@kde.org)
|
|
* (C) 2002 Dirk Mueller (mueller@kde.org)
|
|
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "FixedTableLayout.h"
|
|
|
|
#include "RenderTable.h"
|
|
#include "RenderTableCell.h"
|
|
#include "RenderTableCol.h"
|
|
#include "RenderTableSection.h"
|
|
|
|
/*
|
|
The text below is from the CSS 2.1 specs.
|
|
|
|
Fixed table layout
|
|
|
|
With this (fast) algorithm, the horizontal layout of the table does
|
|
not depend on the contents of the cells; it only depends on the
|
|
table's width, the width of the columns, and borders or cell
|
|
spacing.
|
|
|
|
The table's width may be specified explicitly with the 'width'
|
|
property. A value of 'auto' (for both 'display: table' and 'display:
|
|
inline-table') means use the automatic table layout algorithm.
|
|
|
|
In the fixed table layout algorithm, the width of each column is
|
|
determined as follows:
|
|
|
|
1. A column element with a value other than 'auto' for the 'width'
|
|
property sets the width for that column.
|
|
|
|
2. Otherwise, a cell in the first row with a value other than
|
|
'auto' for the 'width' property sets the width for that column. If
|
|
the cell spans more than one column, the width is divided over the
|
|
columns.
|
|
|
|
3. Any remaining columns equally divide the remaining horizontal
|
|
table space (minus borders or cell spacing).
|
|
|
|
The width of the table is then the greater of the value of the
|
|
'width' property for the table element and the sum of the column
|
|
widths (plus cell spacing or borders). If the table is wider than
|
|
the columns, the extra space should be distributed over the columns.
|
|
|
|
|
|
In this manner, the user agent can begin to lay out the table once
|
|
the entire first row has been received. Cells in subsequent rows do
|
|
not affect column widths. Any cell that has content that overflows
|
|
uses the 'overflow' property to determine whether to clip the
|
|
overflow content.
|
|
*/
|
|
|
|
namespace WebCore {
|
|
|
|
FixedTableLayout::FixedTableLayout(RenderTable* table)
|
|
: TableLayout(table)
|
|
{
|
|
}
|
|
|
|
float FixedTableLayout::calcWidthArray()
|
|
{
|
|
// FIXME: We might want to wait until we have all of the first row before computing for the first time.
|
|
float usedWidth = 0;
|
|
|
|
// iterate over all <col> elements
|
|
unsigned nEffCols = m_table->numEffCols();
|
|
m_width.resize(nEffCols);
|
|
m_width.fill(Length(LengthType::Auto));
|
|
|
|
unsigned currentEffectiveColumn = 0;
|
|
for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) {
|
|
// RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits
|
|
// so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's
|
|
// ancestors as dirty.
|
|
col->clearPreferredLogicalWidthsDirtyBits();
|
|
|
|
// Width specified by column-groups that have column child does not affect column width in fixed layout tables
|
|
if (col->isTableColumnGroupWithColumnChildren())
|
|
continue;
|
|
|
|
Length colStyleLogicalWidth = col->style().logicalWidth();
|
|
float effectiveColWidth = 0;
|
|
if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0)
|
|
effectiveColWidth = colStyleLogicalWidth.value();
|
|
|
|
unsigned span = col->span();
|
|
while (span) {
|
|
unsigned spanInCurrentEffectiveColumn;
|
|
if (currentEffectiveColumn >= nEffCols) {
|
|
m_table->appendColumn(span);
|
|
nEffCols++;
|
|
m_width.append(Length());
|
|
spanInCurrentEffectiveColumn = span;
|
|
} else {
|
|
if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
|
|
m_table->splitColumn(currentEffectiveColumn, span);
|
|
nEffCols++;
|
|
m_width.append(Length());
|
|
}
|
|
spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
|
|
}
|
|
if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.isPercentOrCalculated()) && colStyleLogicalWidth.isPositive()) {
|
|
m_width[currentEffectiveColumn] = colStyleLogicalWidth;
|
|
m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn;
|
|
usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn;
|
|
}
|
|
span -= spanInCurrentEffectiveColumn;
|
|
currentEffectiveColumn++;
|
|
}
|
|
}
|
|
|
|
// Iterate over the first row in case some are unspecified.
|
|
RenderTableSection* section = m_table->topNonEmptySection();
|
|
if (!section)
|
|
return usedWidth;
|
|
|
|
unsigned currentColumn = 0;
|
|
|
|
RenderTableRow* firstRow = section->firstRow();
|
|
for (RenderTableCell* cell = firstRow->firstCell(); cell; cell = cell->nextCell()) {
|
|
Length logicalWidth = cell->styleOrColLogicalWidth();
|
|
unsigned span = cell->colSpan();
|
|
float fixedBorderBoxLogicalWidth = 0;
|
|
// FIXME: Support other length types. If the width is non-auto, it should probably just use
|
|
// RenderBox::computeLogicalWidthInFragmentUsing to compute the width.
|
|
if (logicalWidth.isFixed() && logicalWidth.isPositive()) {
|
|
fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth);
|
|
logicalWidth.setValue(LengthType::Fixed, fixedBorderBoxLogicalWidth);
|
|
}
|
|
|
|
unsigned usedSpan = 0;
|
|
while (usedSpan < span && currentColumn < nEffCols) {
|
|
float eSpan = m_table->spanOfEffCol(currentColumn);
|
|
// Only set if no col element has already set it.
|
|
if (m_width[currentColumn].isAuto() && logicalWidth.type() != LengthType::Auto) {
|
|
m_width[currentColumn] = logicalWidth;
|
|
m_width[currentColumn] *= eSpan / span;
|
|
usedWidth += fixedBorderBoxLogicalWidth * eSpan / span;
|
|
}
|
|
usedSpan += eSpan;
|
|
++currentColumn;
|
|
}
|
|
|
|
// FixedTableLayout doesn't use min/maxPreferredLogicalWidths, but we need to clear the
|
|
// dirty bit on the cell so that we'll correctly mark its ancestors dirty
|
|
// in case we later call setPreferredLogicalWidthsDirty(true) on it later.
|
|
if (cell->preferredLogicalWidthsDirty())
|
|
cell->setPreferredLogicalWidthsDirty(false);
|
|
}
|
|
|
|
return usedWidth;
|
|
}
|
|
|
|
void FixedTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth, TableIntrinsics)
|
|
{
|
|
minWidth = maxWidth = calcWidthArray();
|
|
}
|
|
|
|
void FixedTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
|
|
{
|
|
Length tableLogicalWidth = m_table->style().logicalWidth();
|
|
if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive())
|
|
minWidth = maxWidth = std::max(minWidth, LayoutUnit(tableLogicalWidth.value()) - m_table->bordersPaddingAndSpacingInRowDirection());
|
|
|
|
/*
|
|
<table style="width:100%; background-color:red"><tr><td>
|
|
<table style="background-color:blue"><tr><td>
|
|
<table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
|
|
Content
|
|
</td></tr></table>
|
|
</td></tr></table>
|
|
</td></tr></table>
|
|
*/
|
|
// In this example, the two inner tables should be as large as the outer table.
|
|
// We can achieve this effect by making the maxwidth of fixed tables with percentage
|
|
// widths be infinite.
|
|
if (m_table->style().logicalWidth().isPercentOrCalculated() && maxWidth < tableMaxWidth)
|
|
maxWidth = tableMaxWidth;
|
|
}
|
|
|
|
void FixedTableLayout::layout()
|
|
{
|
|
float tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
|
|
unsigned nEffCols = m_table->numEffCols();
|
|
|
|
// FIXME: It is possible to be called without having properly updated our internal representation.
|
|
// This means that our preferred logical widths were not recomputed as expected.
|
|
if (nEffCols != m_width.size()) {
|
|
calcWidthArray();
|
|
// FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
|
|
nEffCols = m_table->numEffCols();
|
|
}
|
|
|
|
Vector<float> calcWidth(nEffCols, 0);
|
|
|
|
unsigned numAuto = 0;
|
|
unsigned autoSpan = 0;
|
|
float totalFixedWidth = 0;
|
|
float totalPercentWidth = 0;
|
|
float totalPercent = 0;
|
|
|
|
// Compute requirements and try to satisfy fixed and percent widths.
|
|
// Percentages are of the table's width, so for example
|
|
// for a table width of 100px with columns (40px, 10%), the 10% compute
|
|
// to 10px here, and will scale up to 20px in the final (80px, 20px).
|
|
for (unsigned i = 0; i < nEffCols; i++) {
|
|
if (m_width[i].isFixed()) {
|
|
calcWidth[i] = m_width[i].value();
|
|
totalFixedWidth += calcWidth[i];
|
|
} else if (m_width[i].isPercent()) {
|
|
calcWidth[i] = valueForLength(m_width[i], tableLogicalWidth);
|
|
totalPercentWidth += calcWidth[i];
|
|
totalPercent += m_width[i].percent();
|
|
} else if (m_width[i].isAuto()) {
|
|
numAuto++;
|
|
autoSpan += m_table->spanOfEffCol(i);
|
|
}
|
|
}
|
|
|
|
float hspacing = m_table->hBorderSpacing();
|
|
float totalWidth = totalFixedWidth + totalPercentWidth;
|
|
if (!numAuto || totalWidth > tableLogicalWidth) {
|
|
// If there are no auto columns, or if the total is too wide, take
|
|
// what we have and scale it to fit as necessary.
|
|
if (totalWidth != tableLogicalWidth) {
|
|
// Fixed widths only scale up
|
|
if (totalFixedWidth && totalWidth < tableLogicalWidth) {
|
|
totalFixedWidth = 0;
|
|
for (unsigned i = 0; i < nEffCols; i++) {
|
|
if (m_width[i].isFixed()) {
|
|
calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth;
|
|
totalFixedWidth += calcWidth[i];
|
|
}
|
|
}
|
|
}
|
|
if (totalPercent) {
|
|
totalPercentWidth = 0;
|
|
for (unsigned i = 0; i < nEffCols; i++) {
|
|
if (m_width[i].isPercent()) {
|
|
calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent;
|
|
totalPercentWidth += calcWidth[i];
|
|
}
|
|
}
|
|
}
|
|
totalWidth = totalFixedWidth + totalPercentWidth;
|
|
}
|
|
} else {
|
|
// Divide the remaining width among the auto columns.
|
|
ASSERT(autoSpan >= numAuto);
|
|
float remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
|
|
int lastAuto = 0;
|
|
for (unsigned i = 0; i < nEffCols; i++) {
|
|
if (m_width[i].isAuto()) {
|
|
unsigned span = m_table->spanOfEffCol(i);
|
|
float w = remainingWidth * span / autoSpan;
|
|
calcWidth[i] = w + hspacing * (span - 1);
|
|
remainingWidth -= w;
|
|
if (!remainingWidth)
|
|
break;
|
|
lastAuto = i;
|
|
numAuto--;
|
|
ASSERT(autoSpan >= span);
|
|
autoSpan -= span;
|
|
}
|
|
}
|
|
// Last one gets the remainder.
|
|
if (remainingWidth)
|
|
calcWidth[lastAuto] += remainingWidth;
|
|
totalWidth = tableLogicalWidth;
|
|
}
|
|
|
|
if (totalWidth < tableLogicalWidth) {
|
|
// Spread extra space over columns.
|
|
float remainingWidth = tableLogicalWidth - totalWidth;
|
|
int total = nEffCols;
|
|
while (total) {
|
|
float w = remainingWidth / total;
|
|
remainingWidth -= w;
|
|
calcWidth[--total] += w;
|
|
}
|
|
if (nEffCols > 0)
|
|
calcWidth[nEffCols - 1] += remainingWidth;
|
|
}
|
|
|
|
float pos = 0;
|
|
for (unsigned i = 0; i < nEffCols; i++) {
|
|
m_table->setColumnPosition(i, LayoutUnit(pos));
|
|
pos += calcWidth[i] + hspacing;
|
|
}
|
|
float colPositionsSize = m_table->columnPositions().size();
|
|
if (colPositionsSize > 0)
|
|
m_table->setColumnPosition(colPositionsSize - 1, LayoutUnit(pos));
|
|
}
|
|
|
|
} // namespace WebCore
|