boolean verticalScrollNeeded = scrollContentHeight > tableWrapperHeight
+ WidgetUtil.PIXEL_EPSILON
- - header.heightOfSection
- - footer.heightOfSection;
+ - header.getHeightOfSection()
+ - footer.getHeightOfSection();
boolean horizontalScrollNeeded = scrollContentWidth > tableWrapperWidth
+ WidgetUtil.PIXEL_EPSILON;
if (!verticalScrollNeeded && horizontalScrollNeeded) {
verticalScrollNeeded = scrollContentHeight > tableWrapperHeight
+ WidgetUtil.PIXEL_EPSILON
- - header.heightOfSection
- - footer.heightOfSection
+ - header.getHeightOfSection()
+ - footer.getHeightOfSection()
- horizontalScrollbar.getScrollbarThickness();
} else {
horizontalScrollNeeded = scrollContentWidth > tableWrapperWidth
tableWrapper.getStyle().setHeight(tableWrapperHeight, Unit.PX);
tableWrapper.getStyle().setWidth(tableWrapperWidth, Unit.PX);
+ double footerHeight = footer.getHeightOfSection();
+ double headerHeight = header.getHeightOfSection();
double vScrollbarHeight = Math.max(0, tableWrapperHeight
- - footer.heightOfSection - header.heightOfSection);
+ - footerHeight - headerHeight);
verticalScrollbar.setOffsetSize(vScrollbarHeight);
verticalScrollbar.setScrollSize(scrollContentHeight);
final double viewportStartPx = getScrollTop();
final double viewportEndPx = viewportStartPx
- + body.calculateHeight();
+ + body.getHeightOfSection();
final double scrollTop = getScrollPos(destination, targetStartPx,
targetEndPx, viewportStartPx, viewportEndPx, padding);
*/
protected final TableSectionElement root;
- /** The height of the combined rows in the DOM. Never negative. */
- protected double heightOfSection = 0;
-
/**
* The primary style name of the escalator. Most commonly provided by
* Escalator as "v-escalator".
refreshCells(rowRange, colRange);
}
}
+
+ /**
+ * The height of this table section.
+ * <p>
+ * Note that {@link Escalator#getBody() the body} will calculate its
+ * height, while the others will return a precomputed value.
+ *
+ * @return the height of this table section
+ */
+ protected abstract double getHeightOfSection();
}
private abstract class AbstractStaticRowContainer extends
AbstractRowContainer {
+
+ /** The height of the combined rows in the DOM. Never negative. */
+ private double heightOfSection = 0;
+
public AbstractStaticRowContainer(final TableSectionElement headElement) {
super(headElement);
}
* indices are calculated from the scrollbar position.
*/
verticalScrollbar.setOffsetSize(heightOfEscalator
- - header.heightOfSection - footer.heightOfSection);
+ - header.getHeightOfSection()
+ - footer.getHeightOfSection());
body.verifyEscalatorCount();
}
assert root.isOrHasChild(tr) : "Row does not belong to this table section";
return true;
}
+
+ @Override
+ protected double getHeightOfSection() {
+ return Math.max(0, heightOfSection);
+ }
}
private class HeaderRowContainer extends AbstractStaticRowContainer {
@Override
protected void sectionHeightCalculated() {
- bodyElem.getStyle().setMarginTop(heightOfSection, Unit.PX);
+ bodyElem.getStyle().setMarginTop(getHeightOfSection(), Unit.PX);
verticalScrollbar.getElement().getStyle()
- .setTop(heightOfSection, Unit.PX);
- headerDeco.getStyle().setHeight(heightOfSection, Unit.PX);
+ .setTop(getHeightOfSection(), Unit.PX);
+ headerDeco.getStyle().setHeight(getHeightOfSection(), Unit.PX);
}
@Override
@Override
protected void sectionHeightCalculated() {
+ double headerHeight = header.getHeightOfSection();
+ double footerHeight = footer.getHeightOfSection();
int vscrollHeight = (int) Math.floor(heightOfEscalator
- - header.heightOfSection - footer.heightOfSection);
+ - headerHeight - footerHeight);
final boolean horizontalScrollbarNeeded = columnConfiguration
.calculateRowWidth() > widthOfEscalator;
vscrollHeight -= horizontalScrollbar.getScrollbarThickness();
}
- footerDeco.getStyle().setHeight(footer.heightOfSection, Unit.PX);
+ footerDeco.getStyle().setHeight(footer.getHeightOfSection(),
+ Unit.PX);
verticalScrollbar.setOffsetSize(vscrollHeight);
}
* getDefaultRowHeight() < getScrollTop();
final boolean addedRowsBelowCurrentViewport = index
* getDefaultRowHeight() > getScrollTop()
- + calculateHeight();
+ + getHeightOfSection();
if (addedRowsAboveCurrentViewport) {
/*
private int getMaxEscalatorRowCapacity() {
final int maxEscalatorRowCapacity = (int) Math
- .ceil(calculateHeight() / getDefaultRowHeight()) + 1;
+ .ceil(getHeightOfSection() / getDefaultRowHeight()) + 1;
/*
* maxEscalatorRowCapacity can become negative if the headers and
final double contentBottom = getRowCount()
* getDefaultRowHeight();
final double viewportBottom = tBodyScrollTop
- + calculateHeight();
+ + getHeightOfSection();
if (viewportBottom <= contentBottom) {
/*
* We're in the middle of the row container, everything
* 5 5
*/
final double newScrollTop = contentBottom
- - calculateHeight();
+ - getHeightOfSection();
setScrollTop(newScrollTop);
/*
* Manually call the scroll handler, so we get immediate
return "td";
}
- /**
- * Calculates the height of the {@code <tbody>} as it should be rendered
- * in the DOM.
- */
- private double calculateHeight() {
+ @Override
+ protected double getHeightOfSection() {
final int tableHeight = tableWrapper.getOffsetHeight();
- final double footerHeight = footer.heightOfSection;
- final double headerHeight = header.heightOfSection;
- return tableHeight - footerHeight - headerHeight;
+ final double footerHeight = footer.getHeightOfSection();
+ final double headerHeight = header.getHeightOfSection();
+
+ double heightOfSection = tableHeight - footerHeight - headerHeight;
+ return Math.max(0, heightOfSection);
}
@Override
return visualRowOrder.contains(tr);
}
- public void reapplySpacerWidths() {
+ void reapplySpacerWidths() {
spacerContainer.reapplySpacerWidths();
}
+
+ void scrollToSpacer(int spacerIndex, ScrollDestination destination,
+ int padding) {
+ spacerContainer.scrollToSpacer(spacerIndex, destination, padding);
+ }
}
private class ColumnConfigurationImpl implements ColumnConfiguration {
}
}
+ @SuppressWarnings("boxing")
+ void scrollToSpacer(int spacerIndex, ScrollDestination destination,
+ int padding) {
+
+ assert !destination.equals(ScrollDestination.MIDDLE)
+ || padding != 0 : "destination/padding check should be done before this method";
+
+ if (!rowIndexToSpacer.containsKey(spacerIndex)) {
+ throw new IllegalArgumentException("No spacer open at index "
+ + spacerIndex);
+ }
+
+ SpacerImpl spacer = rowIndexToSpacer.get(spacerIndex);
+ double targetStartPx = spacer.getTop();
+ double targetEndPx = targetStartPx + spacer.getHeight();
+
+ Range viewportPixels = getViewportPixels();
+ double viewportStartPx = viewportPixels.getStart();
+ double viewportEndPx = viewportPixels.getEnd();
+
+ double scrollTop = getScrollPos(destination, targetStartPx,
+ targetEndPx, viewportStartPx, viewportEndPx, padding);
+
+ setScrollTop(scrollTop);
+ }
+
public void reapplySpacerWidths() {
for (SpacerImpl spacer : rowIndexToSpacer.values()) {
spacer.getRootElement().getStyle()
public void scrollToColumn(final int columnIndex,
final ScrollDestination destination, final int padding)
throws IndexOutOfBoundsException, IllegalArgumentException {
- if (destination == ScrollDestination.MIDDLE && padding != 0) {
- throw new IllegalArgumentException(
- "You cannot have a padding with a MIDDLE destination");
- }
+ validateScrollDestination(destination, padding);
verifyValidColumnIndex(columnIndex);
if (columnIndex < columnConfiguration.frozenColumns) {
public void scrollToRow(final int rowIndex,
final ScrollDestination destination, final int padding)
throws IndexOutOfBoundsException, IllegalArgumentException {
- if (destination == ScrollDestination.MIDDLE && padding != 0) {
- throw new IllegalArgumentException(
- "You cannot have a padding with a MIDDLE destination");
- }
+ validateScrollDestination(destination, padding);
verifyValidRowIndex(rowIndex);
scroller.scrollToRow(rowIndex, destination, padding);
}
}
+ /**
+ * Scrolls the body vertically so that the spacer at the given row index is
+ * visible and there is at least {@literal padding} pixesl to the given
+ * scroll destination
+ *
+ * @since
+ * @param spacerIndex
+ * the row index of the spacer to scroll to
+ * @param destination
+ * where the spacer should be aligned visually after scrolling
+ * @param padding
+ * the number of pixels to place between the scrolled-to spacer
+ * and the viewport edge
+ * @throws IllegalArgumentException
+ * if {@code spacerIndex} is not an opened spacer if
+ * {@code destination} is {@link ScrollDestination#MIDDLE} and
+ * padding is nonzero
+ */
+ public void scrollToSpacer(final int spacerIndex,
+ ScrollDestination destination, final int padding)
+ throws IllegalArgumentException {
+ validateScrollDestination(destination, padding);
+ body.scrollToSpacer(spacerIndex, destination, padding);
+ }
+
+ private static void validateScrollDestination(
+ final ScrollDestination destination, final int padding) {
+ if (destination == ScrollDestination.MIDDLE && padding != 0) {
+ throw new IllegalArgumentException(
+ "You cannot have a padding with a MIDDLE destination");
+ }
+ }
+
/**
* Recalculates the dimensions for all elements that require manual
* calculations. Also updates the dimension caches.
return;
}
- double headerHeight = header.heightOfSection;
- double footerHeight = footer.heightOfSection;
+ double headerHeight = header.getHeightOfSection();
+ double footerHeight = footer.getHeightOfSection();
double bodyHeight = body.getDefaultRowHeight() * heightByRows;
double scrollbar = horizontalScrollbar.showsScrollHandle() ? horizontalScrollbar
.getScrollbarThickness() : 0;
private Range getViewportPixels() {
int from = (int) Math.floor(verticalScrollbar.getScrollPos());
- int to = (int) Math.ceil(body.heightOfSection);
- return Range.between(from, to);
+ int to = (int) body.getHeightOfSection();
+ return Range.withLength(from, to);
}
@Override
import org.openqa.selenium.WebElement;
import com.vaadin.client.WidgetUtil;
+import com.vaadin.shared.ui.grid.Range;
import com.vaadin.testbench.elements.NotificationElement;
import com.vaadin.tests.components.grid.basicfeatures.EscalatorBasicClientFeaturesTest;
@Before
public void before() {
openTestURL();
+ selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, "Set 20px default height");
populate();
}
}
}
+ @Test
+ public void scrollToSpacerFromAbove() throws Exception {
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
+
+ // Browsers might vary with a few pixels.
+ Range allowableScrollRange = Range.between(765, 780);
+ int scrollTop = (int) getScrollTop();
+ assertTrue("Scroll position was not " + allowableScrollRange + ", but "
+ + scrollTop, allowableScrollRange.contains(scrollTop));
+ }
+
+ @Test
+ public void scrollToSpacerFromBelow() throws Exception {
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
+ scrollVerticallyTo(999999);
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
+
+ // Browsers might vary with a few pixels.
+ Range allowableScrollRange = Range.between(1015, 1025);
+ int scrollTop = (int) getScrollTop();
+ assertTrue("Scroll position was not " + allowableScrollRange + ", but "
+ + scrollTop, allowableScrollRange.contains(scrollTop));
+ }
+
+ @Test
+ public void scrollToSpacerAlreadyInViewport() throws Exception {
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
+ scrollVerticallyTo(1000);
+ selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
+
+ // Browsers might vary with a few pixels.
+ assertEquals(getScrollTop(), 1000);
+ }
+
private static double[] getElementDimensions(WebElement element) {
/*
* we need to parse the style attribute, since using getCssValue gets a