* 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.
+ */
private static final int ROW_HEIGHT_PX = 20;
private static final int COLUMN_WIDTH_PX = 100;
element.detachEvent("onmousewheel", this.@com.vaadin.client.ui.grid.JsniWorkaround::mousewheelListenerFunction);
}
}-*/;
+
+ 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;
+
+ /*
+ * TODO [[frozencol]]: offset startPx by the pixels occupied by
+ * frozen columns.
+ */
+ final int viewportStartPx = getScrollLeft();
+ int viewportEndPx = viewportStartPx + getElement().getOffsetWidth();
+ if (needsVerticalScrollbars()) {
+ viewportEndPx -= Util.getNativeScrollbarSize();
+ }
+
+ final double scrollLeft = destination.getScrollPos(targetStartPx,
+ targetEndPx, viewportStartPx, viewportEndPx, padding);
+
+ /*
+ * note that it doesn't matter if the scroll would go beyond the
+ * content, since the browser will adjust for that, and everything
+ * fall into line accordingly.
+ */
+ setScrollLeft((int) scrollLeft);
+ }
+
+ public void scrollToRow(final int rowIndex,
+ final ScrollDestination destination, final int padding) {
+ // TODO [[rowheight]]
+ final int targetStartPx = ROW_HEIGHT_PX * rowIndex;
+ final int targetEndPx = targetStartPx + ROW_HEIGHT_PX;
+
+ final double viewportStartPx = getScrollTop();
+ final double viewportEndPx = viewportStartPx
+ + body.calculateHeight();
+
+ final double scrollTop = destination.getScrollPos(targetStartPx,
+ targetEndPx, viewportStartPx, viewportEndPx, padding);
+
+ /*
+ * note that it doesn't matter if the scroll would go beyond the
+ * content, since the browser will adjust for that, and everything
+ * falls into line accordingly.
+ */
+ setScrollTop(scrollTop);
+ }
}
private static class CellImpl implements Cell {
* @throws IndexOutOfBoundsException
* if {@code columnIndex} is not a valid index for an existing
* column
+ * @throws IllegalArgumentException
+ * if {@code columnIndex} indicates a column that is set to be
+ * frozen
*/
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination)
throws IndexOutOfBoundsException {
- // FIXME [[escalator]]
- throw new UnsupportedOperationException("Not implemented yet");
+ // TODO [[frozencol]] throw IAE if frozen
+ verifyValidColumnIndex(columnIndex);
+
+ 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
+ * @throws IllegalArgumentException
+ * if {@code columnIndex} indicates a column that is set to be
+ * frozen
*/
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination, final int padding)
throws IndexOutOfBoundsException, IllegalArgumentException {
- // FIXME [[escalator]]
- throw new UnsupportedOperationException("Not implemented yet");
+ // TODO [[frozencol]] throw IAE if frozen
+ if (destination == ScrollDestination.MIDDLE) {
+ throw new IllegalArgumentException(
+ "You cannot have a padding with a MIDDLE destination");
+ }
+ verifyValidColumnIndex(columnIndex);
+
+ scroller.scrollToColumn(columnIndex, destination, padding);
+ }
+
+ private void verifyValidColumnIndex(final int columnIndex)
+ throws IndexOutOfBoundsException {
+ if (columnIndex < 0
+ || columnIndex >= columnConfiguration.getColumnCount()) {
+ throw new IndexOutOfBoundsException("The given column index "
+ + columnIndex + " does not exist.");
+ }
}
/**
public void scrollToRow(final int rowIndex,
final ScrollDestination destination)
throws IndexOutOfBoundsException {
- // FIXME [[escalator]]
- throw new UnsupportedOperationException("Not implemented yet");
+ verifyValidRowIndex(rowIndex);
+
+ scroller.scrollToRow(rowIndex, destination, 0);
}
/**
*
* @param rowIndex
* the index of the logical row to scroll to
- *
* @param destination
* where the row should be aligned visually after scrolling
* @param padding
public void scrollToRow(final int rowIndex,
final ScrollDestination destination, final int padding)
throws IndexOutOfBoundsException, IllegalArgumentException {
- // FIXME [[escalator]]
- throw new UnsupportedOperationException("Not implemented yet");
+ if (destination == ScrollDestination.MIDDLE) {
+ throw new IllegalArgumentException(
+ "You cannot have a padding with a MIDDLE destination");
+ }
+ verifyValidRowIndex(rowIndex);
+
+ scroller.scrollToRow(rowIndex, destination, padding);
+ }
+
+ private void verifyValidRowIndex(final int rowIndex) {
+ if (rowIndex < 0 || rowIndex >= body.getRowCount()) {
+ throw new IndexOutOfBoundsException("The given row index "
+ + rowIndex + " does not exist.");
+ }
}
private void recalculateElementSizes() {
* @author Vaadin Ltd
*/
public enum ScrollDestination {
+
/**
- * "scrollIntoView" i.e. scroll as little as possible to show the target
- * element. If the element fits into view, this works as START or END
- * depending on the current scroll position. If the element does not fit
- * into view, this works as START.
+ * Scroll as little as possible to show the target element. If the element
+ * fits into view, this works as START or END depending on the current
+ * scroll position. If the element does not fit into view, this works as
+ * START.
*/
- ANY,
+ ANY {
+ @Override
+ double getScrollPos(final double targetStartPx,
+ final double targetEndPx, final double viewportStartPx,
+ final double viewportEndPx, final int padding) {
+
+ final double startScrollPos = targetStartPx - padding;
+ final double viewportLength = viewportEndPx - viewportStartPx;
+ final double endScrollPos = targetEndPx + padding - viewportLength;
+
+ if (startScrollPos < viewportStartPx) {
+ return startScrollPos;
+ } else if (targetEndPx + padding > viewportEndPx) {
+ return endScrollPos;
+ } else {
+ // NOOP, it's already visible
+ return viewportStartPx;
+ }
+ }
+ },
+
/**
- * Scrolls so that the element is shown at the start of the view port.
+ * Scrolls so that the element is shown at the start of the viewport. The
+ * viewport will, however, not scroll beyond its contents.
*/
- START,
+ START {
+ @Override
+ double getScrollPos(final double targetStartPx,
+ final double targetEndPx, final double viewportStartPx,
+ final double viewportEndPx, final int padding) {
+ return targetStartPx - padding;
+ }
+ },
+
/**
- * Scrolls so that the element is shown in the middle of the view port.
+ * Scrolls so that the element is shown in the middle of the viewport. The
+ * viewport will, however, not scroll beyond its contents, given more
+ * elements than what the viewport is able to show at once. Under no
+ * circumstances will the viewport scroll before its first element.
*/
- MIDDLE,
+ MIDDLE {
+ @Override
+ double getScrollPos(final double targetStartPx,
+ final double targetEndPx, final double viewportStartPx,
+ final double viewportEndPx, final int padding) {
+ final double targetMiddle = targetStartPx
+ + (targetEndPx - targetStartPx) / 2;
+ final double viewportLength = viewportEndPx - viewportStartPx;
+ return targetMiddle - viewportLength / 2;
+ }
+ },
+
/**
- * Scrolls so that the element is shown at the end of the view port.
+ * Scrolls so that the element is shown at the end of the viewport. The
+ * viewport will, however, not scroll before its first element.
*/
- END
-}
\ No newline at end of file
+ END {
+ @Override
+ double getScrollPos(final double targetStartPx,
+ final double targetEndPx, final double viewportStartPx,
+ final double viewportEndPx, final int padding) {
+ final double viewportLength = viewportEndPx - viewportStartPx;
+ return targetEndPx + padding - viewportLength;
+ }
+ };
+
+ abstract double getScrollPos(final double targetStartPx,
+ final double targetEndPx, final double viewportStartPx,
+ final double viewportEndPx, final int padding);
+}
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Layout;
+import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.TextField;
/**
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- int offset = Integer.valueOf(insertRowsOffset
+ final int offset = Integer.valueOf(insertRowsOffset
.getValue());
- int amount = Integer.valueOf(insertRowsAmount
+ final int amount = Integer.valueOf(insertRowsAmount
.getValue());
grid.insertRows(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- int offset = Integer.valueOf(removeRowsOffset
+ final int offset = Integer.valueOf(removeRowsOffset
.getValue());
- int amount = Integer.valueOf(removeRowsAmount
+ final int amount = Integer.valueOf(removeRowsAmount
.getValue());
grid.removeRows(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- int offset = Integer.valueOf(insertColumnsOffset
+ final int offset = Integer.valueOf(insertColumnsOffset
.getValue());
- int amount = Integer.valueOf(insertColumnsAmount
+ final int amount = Integer.valueOf(insertColumnsAmount
.getValue());
grid.insertColumns(offset, amount);
}
@Override
@SuppressWarnings("boxing")
public void buttonClick(final ClickEvent event) {
- int offset = Integer.valueOf(removeColumnsOffset
+ final int offset = Integer.valueOf(removeColumnsOffset
.getValue());
- int amount = Integer.valueOf(removeColumnsAmount
+ final int amount = Integer.valueOf(removeColumnsAmount
.getValue());
grid.removeColumns(offset, amount);
}
}));
addComponent(removeColumnsLayout);
+
+ final HorizontalLayout rowScroll = new HorizontalLayout();
+ final NativeSelect destination = new NativeSelect();
+ destination.setNullSelectionAllowed(false);
+ destination.addItem("any");
+ destination.setValue("any");
+ destination.addItem("start");
+ destination.addItem("end");
+ destination.addItem("middle");
+ rowScroll.addComponent(destination);
+ final TextField rowIndex = new TextField();
+ rowScroll.addComponent(rowIndex);
+ final TextField rowPadding = new TextField();
+ rowScroll.addComponent(rowPadding);
+ rowScroll.addComponent(new Button("scroll to row",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(final ClickEvent event) {
+ int index;
+ try {
+ index = Integer.valueOf(rowIndex.getValue());
+ } catch (NumberFormatException e) {
+ index = 0;
+ }
+
+ int padding;
+ try {
+ padding = Integer.valueOf(rowPadding.getValue());
+ } catch (NumberFormatException e) {
+ padding = 0;
+ }
+
+ grid.scrollToRow(index,
+ (String) destination.getValue(), padding);
+ }
+ }));
+ addComponent(rowScroll);
+
+ final HorizontalLayout colScroll = new HorizontalLayout();
+ final NativeSelect colDestination = new NativeSelect();
+ colDestination.setNullSelectionAllowed(false);
+ colDestination.addItem("any");
+ colDestination.setValue("any");
+ colDestination.addItem("start");
+ colDestination.addItem("end");
+ colDestination.addItem("middle");
+ colScroll.addComponent(colDestination);
+ final TextField colIndex = new TextField();
+ colScroll.addComponent(colIndex);
+ final TextField colPadding = new TextField();
+ colScroll.addComponent(colPadding);
+ colScroll.addComponent(new Button("scroll to column",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(final ClickEvent event) {
+ int index;
+ try {
+ index = Integer.valueOf(colIndex.getValue());
+ } catch (NumberFormatException e) {
+ index = 0;
+ }
+
+ int padding;
+ try {
+ padding = Integer.valueOf(colPadding.getValue());
+ } catch (NumberFormatException e) {
+ padding = 0;
+ }
+
+ grid.scrollToColumn(index,
+ (String) colDestination.getValue(), padding);
+ }
+ }));
+ addComponent(colScroll);
}
@Override
void insertColumns(int offset, int amount);
void removeColumns(int offset, int amount);
+
+ void scrollToRow(int index, String destination, int padding);
+
+ void scrollToColumn(int index, String destination, int padding);
}
package com.vaadin.tests.widgetset.client.grid;
import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.client.ui.grid.ScrollDestination;
import com.vaadin.shared.ui.Connect;
import com.vaadin.tests.widgetset.server.grid.TestGrid;
getWidget().getColumnConfiguration().insertColumns(offset,
amount);
}
+
+ @Override
+ public void scrollToRow(int index, String destination, int padding) {
+ getWidget().scrollToRow(index, getDestination(destination),
+ padding);
+ }
+
+ @Override
+ public void scrollToColumn(int index, String destination,
+ int padding) {
+ getWidget().scrollToColumn(index, getDestination(destination),
+ padding);
+ }
+
+ private ScrollDestination getDestination(String destination) {
+ final ScrollDestination d;
+ if (destination.equals("start")) {
+ d = ScrollDestination.START;
+ } else if (destination.equals("middle")) {
+ d = ScrollDestination.MIDDLE;
+ } else if (destination.equals("end")) {
+ d = ScrollDestination.END;
+ } else {
+ d = ScrollDestination.ANY;
+ }
+ return d;
+ }
});
}
import com.vaadin.client.ui.grid.ColumnConfiguration;
import com.vaadin.client.ui.grid.Escalator;
import com.vaadin.client.ui.grid.RowContainer;
+import com.vaadin.client.ui.grid.ScrollDestination;
public class VTestGrid extends Composite {
public static class HeaderRenderer implements CellRenderer {
public VTestGrid() {
initWidget(escalator);
final ColumnConfiguration cConf = escalator.getColumnConfiguration();
- cConf.insertColumns(cConf.getColumnCount(), 5);
+ cConf.insertColumns(cConf.getColumnCount(), 10);
final RowContainer h = escalator.getHeader();
h.setCellRenderer(new HeaderRenderer());
public ColumnConfiguration getColumnConfiguration() {
return escalator.getColumnConfiguration();
}
+
+ public void scrollToRow(int index, ScrollDestination destination,
+ int padding) {
+ if (padding != 0) {
+ escalator.scrollToRow(index, destination, padding);
+ } else {
+ escalator.scrollToRow(index, destination);
+ }
+ }
+
+ public void scrollToColumn(int index, ScrollDestination destination,
+ int padding) {
+ if (padding != 0) {
+ escalator.scrollToColumn(index, destination, padding);
+ } else {
+ escalator.scrollToColumn(index, destination);
+ }
+ }
}
private TestGridClientRpc rpc() {
return getRpcProxy(TestGridClientRpc.class);
}
+
+ public void scrollToRow(int index, String destination, int padding) {
+ rpc().scrollToRow(index, destination, padding);
+ }
+
+ public void scrollToColumn(int index, String destination, int padding) {
+ rpc().scrollToColumn(index, destination, padding);
+ }
}