* escalator DOM). NOTE: these bits can most often also be identified by
* searching for code that call scrollElem.getScrollTop();.
*/
- /*
- * [[frozencol]]: This needs to be re-inspected once frozen columns are
- * being implemented.
- */
/*
* [[widgets]]: This needs to be re-inspected once GWT/Vaadin widgets are
* being supported.
/** An inner class that handles all logic related to scrolling. */
private class Scroller extends JsniWorkaround {
- private double lastScrollTop = -1;
- private double lastScrollLeft = -1;
+ private double lastScrollTop = 0;
+ private double lastScrollLeft = 0;
public Scroller() {
super(Escalator.this);
.setHeight(innerScrollerHeight, Unit.PX);
// TODO [[colwidth]]: adjust for variable column widths.
+ int columnsToScroll = columnConfiguration.getColumnCount()
+ - columnConfiguration.getFrozenColumnCount();
innerScrollerElem.getStyle().setWidth(
- COLUMN_WIDTH_PX * columnConfiguration.getColumnCount(),
- Unit.PX);
+ COLUMN_WIDTH_PX * columnsToScroll, Unit.PX);
// we might've got new or got rid of old scrollbars.
recalculateTableWrapperSize();
final int scrollTop = scrollerElem.getScrollTop();
if (lastScrollLeft != scrollLeft) {
- // TODO [[frozen]]: frozen columns should be offset here.
+ for (int i = 0; i < columnConfiguration.frozenColumns; i++) {
+ header.updateFreezePosition(i, scrollLeft);
+ body.updateFreezePosition(i, scrollLeft);
+ footer.updateFreezePosition(i, scrollLeft);
+ }
position.set(headElem, -scrollLeft, 0);
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination, final int padding) {
- // TODO [[colwidth]]
- final int targetStartPx = COLUMN_WIDTH_PX * columnIndex;
- final int targetEndPx = targetStartPx + COLUMN_WIDTH_PX;
+ assert columnIndex >= columnConfiguration.frozenColumns : "Can't scroll to a frozen column";
+ // TODO [[colwidth]]
/*
- * TODO [[frozencol]]: offset startPx by the pixels occupied by
+ * To cope with frozen columns, we just pretend those columns are
+ * not there at all when calculating the position of the target
+ * column and the boundaries of the viewport. The resulting
+ * scrollLeft will be correct without compensation since the DOM
+ * structure effectively means that scrollLeft also ignores the
* frozen columns.
*/
+ final int frozenPixels = columnConfiguration.frozenColumns
+ * COLUMN_WIDTH_PX;
+
+ final int targetStartPx = COLUMN_WIDTH_PX * columnIndex
+ - frozenPixels;
+ final int targetEndPx = targetStartPx + COLUMN_WIDTH_PX;
+
final int viewportStartPx = getScrollLeft();
- int viewportEndPx = viewportStartPx + getElement().getOffsetWidth();
+ int viewportEndPx = viewportStartPx + getElement().getOffsetWidth()
+ - frozenPixels;
if (needsVerticalScrollbars()) {
viewportEndPx -= Util.getNativeScrollbarSize();
}
for (int col = 0; col < columnConfiguration.getColumnCount(); col++) {
final Element cellElem = createCellElement();
tr.appendChild(cellElem);
+
+ // Set stylename and position if new cell is frozen
+ if (col < columnConfiguration.frozenColumns) {
+ cellElem.addClassName("frozen");
+ position.set(cellElem, scroller.lastScrollLeft, 0);
+ }
}
refreshRow(tr, row);
}
protected void paintInsertColumns(final int offset,
- final int numberOfColumns) {
+ final int numberOfColumns, boolean frozen) {
final NodeList<Node> childNodes = root.getChildNodes();
final int topVisualRowLogicalIndex = getTopVisualRowLogicalIndex();
recalculateRowWidth(tr);
}
+ if (frozen) {
+ for (int col = offset; col < offset + numberOfColumns; col++) {
+ setColumnFrozen(col, true);
+ }
+ }
+
// this needs to be before the scrollbar adjustment.
scroller.recalculateScrollbarsForVirtualViewport();
* COLUMN_WIDTH_PX));
}
}
+
+ public void setColumnFrozen(int column, boolean frozen) {
+ final NodeList<Node> childNodes = root.getChildNodes();
+
+ for (int row = 0; row < childNodes.getLength(); row++) {
+ final Element tr = childNodes.getItem(row).cast();
+
+ Element cell = (Element) tr.getChild(column);
+ if (frozen) {
+ cell.addClassName("frozen");
+ } else {
+ cell.removeClassName("frozen");
+ position.reset(cell);
+ }
+ }
+
+ if (frozen) {
+ updateFreezePosition(column, scroller.lastScrollLeft);
+ }
+ }
+
+ public void updateFreezePosition(int column, double scrollLeft) {
+ final NodeList<Node> childNodes = root.getChildNodes();
+
+ for (int row = 0; row < childNodes.getLength(); row++) {
+ final Element tr = childNodes.getItem(row).cast();
+
+ Element cell = (Element) tr.getChild(column);
+ position.set(cell, scrollLeft, 0);
+ }
+ }
}
private abstract class AbstractStaticRowContainer extends
private class ColumnConfigurationImpl implements ColumnConfiguration {
private int columns = 0;
+ private int frozenColumns = 0;
/**
* {@inheritDoc}
assertArgumentsAreValidAndWithinRange(index, numberOfColumns);
flyweightRow.removeCells(index, numberOfColumns);
+
+ // Cope with removing frozen columns
+ if (index < frozenColumns) {
+ if (index + numberOfColumns < frozenColumns) {
+ /*
+ * Last removed column was frozen, meaning that all removed
+ * columns were frozen. Just decrement the number of frozen
+ * columns accordingly.
+ */
+ frozenColumns -= numberOfColumns;
+ } else {
+ /*
+ * If last removed column was not frozen, we have removed
+ * columns beyond the frozen range, so all remaining frozen
+ * columns are to the left of the removed columns.
+ */
+ frozenColumns = index;
+ }
+ }
+
columns -= numberOfColumns;
if (hasSomethingInDom()) {
flyweightRow.addCells(index, numberOfColumns);
columns += numberOfColumns;
+
+ // Either all or none of the new columns are frozen
+ boolean frozen = index < frozenColumns;
+ if (frozen) {
+ frozenColumns += numberOfColumns;
+ }
+
if (hasColumnAndRowData()) {
for (final AbstractRowContainer rowContainer : rowContainers) {
- rowContainer.paintInsertColumns(index, numberOfColumns);
+ rowContainer.paintInsertColumns(index, numberOfColumns,
+ frozen);
}
}
}
public int getColumnCount() {
return columns;
}
+
+ @Override
+ public void setFrozenColumnCount(int count)
+ throws IllegalArgumentException {
+ if (count < 0 || count > columns) {
+ throw new IllegalArgumentException(
+ "count must be between 0 and the current number of columns ("
+ + columns + ")");
+ }
+ int oldCount = frozenColumns;
+ if (count == oldCount) {
+ return;
+ }
+
+ frozenColumns = count;
+
+ if (hasSomethingInDom()) {
+ // Are we freezing or unfreezing?
+ boolean frozen = count > oldCount;
+
+ int firstAffectedCol;
+ int firstUnaffectedCol;
+
+ if (frozen) {
+ firstAffectedCol = oldCount;
+ firstUnaffectedCol = count;
+ } else {
+ firstAffectedCol = count;
+ firstUnaffectedCol = oldCount;
+ }
+
+ for (int col = firstAffectedCol; col < firstUnaffectedCol; col++) {
+ header.setColumnFrozen(col, frozen);
+ body.setColumnFrozen(col, frozen);
+ footer.setColumnFrozen(col, frozen);
+ }
+ }
+
+ scrollerElem.getStyle().setLeft(frozenColumns * COLUMN_WIDTH_PX,
+ Unit.PX);
+ scroller.recalculateScrollbarsForVirtualViewport();
+ }
+
+ @Override
+ public int getFrozenColumnCount() {
+ return frozenColumns;
+ }
}
private FlyweightRow flyweightRow = new FlyweightRow(this);
* @throws IndexOutOfBoundsException
* if {@code columnIndex} is not a valid index for an existing
* column
+ * @throws IllegalArgumentException
+ * if the column is frozen
*/
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination)
- throws IndexOutOfBoundsException {
- // TODO [[frozencol]] throw IAE if frozen
+ throws IndexOutOfBoundsException, IllegalArgumentException {
verifyValidColumnIndex(columnIndex);
+ if (columnIndex < columnConfiguration.frozenColumns) {
+ throw new IllegalArgumentException("The given column index "
+ + columnIndex + " is frozen.");
+ }
+
scroller.scrollToColumn(columnIndex, destination, 0);
}
* @throws IllegalArgumentException
* if {@code destination} is {@link ScrollDestination#MIDDLE},
* because having a padding on a centered column is undefined
- * behavior
+ * behavior or if the column is frozen
*/
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination, final int padding)
throws IndexOutOfBoundsException, IllegalArgumentException {
- // TODO [[frozencol]] throw IAE if frozen
if (destination == ScrollDestination.MIDDLE) {
throw new IllegalArgumentException(
"You cannot have a padding with a MIDDLE destination");
}
verifyValidColumnIndex(columnIndex);
+ if (columnIndex < columnConfiguration.frozenColumns) {
+ throw new IllegalArgumentException("The given column index "
+ + columnIndex + " is frozen.");
+ }
+
scroller.scrollToColumn(columnIndex, destination, padding);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- final int offset = Integer.valueOf(insertRowsOffset
+ final int offset = Integer.parseInt(insertRowsOffset
.getValue());
- final int amount = Integer.valueOf(insertRowsAmount
+ final int amount = Integer.parseInt(insertRowsAmount
.getValue());
grid.insertRows(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- final int offset = Integer.valueOf(removeRowsOffset
+ final int offset = Integer.parseInt(removeRowsOffset
.getValue());
- final int amount = Integer.valueOf(removeRowsAmount
+ final int amount = Integer.parseInt(removeRowsAmount
.getValue());
grid.removeRows(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- final int offset = Integer.valueOf(insertColumnsOffset
+ final int offset = Integer.parseInt(insertColumnsOffset
.getValue());
- final int amount = Integer.valueOf(insertColumnsAmount
+ final int amount = Integer.parseInt(insertColumnsAmount
.getValue());
grid.insertColumns(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- final int offset = Integer.valueOf(removeColumnsOffset
+ final int offset = Integer.parseInt(removeColumnsOffset
.getValue());
- final int amount = Integer.valueOf(removeColumnsAmount
+ final int amount = Integer.parseInt(removeColumnsAmount
.getValue());
grid.removeColumns(offset, amount);
}
public void buttonClick(final ClickEvent event) {
int index;
try {
- index = Integer.valueOf(rowIndex.getValue());
+ index = Integer.parseInt(rowIndex.getValue());
} catch (NumberFormatException e) {
index = 0;
}
int padding;
try {
- padding = Integer.valueOf(rowPadding.getValue());
+ padding = Integer.parseInt(rowPadding.getValue());
} catch (NumberFormatException e) {
padding = 0;
}
public void buttonClick(final ClickEvent event) {
int index;
try {
- index = Integer.valueOf(colIndex.getValue());
+ index = Integer.parseInt(colIndex.getValue());
} catch (NumberFormatException e) {
index = 0;
}
int padding;
try {
- padding = Integer.valueOf(colPadding.getValue());
+ padding = Integer.parseInt(colPadding.getValue());
} catch (NumberFormatException e) {
padding = 0;
}
}
}));
addComponent(colScroll);
+
+ final TextField freezeCount = new TextField();
+ freezeCount.setConverter(Integer.class);
+ freezeCount.setNullRepresentation("");
+ addComponent(new HorizontalLayout(freezeCount, new Button(
+ "set frozen columns", new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ grid.setFrozenColumns(((Integer) freezeCount
+ .getConvertedValue()).intValue());
+ freezeCount.setValue(null);
+ }
+ })));
+
}
@Override