aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/com/vaadin/client/ui/grid/Escalator.java98
-rw-r--r--client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java162
2 files changed, 181 insertions, 79 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java
index 787631fb24..5d310d4d1a 100644
--- a/client/src/com/vaadin/client/ui/grid/Escalator.java
+++ b/client/src/com/vaadin/client/ui/grid/Escalator.java
@@ -436,11 +436,11 @@ public class Escalator extends Widget {
final NativeEvent event) {
if (!Double.isNaN(deltaX)) {
- escalator.horizontalScrollbar.setScrollPosByDelta((int) deltaX);
+ escalator.horizontalScrollbar.setScrollPosByDelta(deltaX);
}
if (!Double.isNaN(deltaY)) {
- escalator.verticalScrollbar.setScrollPosByDelta((int) deltaY);
+ escalator.verticalScrollbar.setScrollPosByDelta(deltaY);
}
/*
@@ -473,8 +473,8 @@ public class Escalator extends Widget {
private double yFric;
private boolean cancelled = false;
- private int lastLeft;
- private int lastTop;
+ private double lastLeft;
+ private double lastTop;
/**
* Creates a new animation callback to handle touch-scrolling flick with
@@ -531,12 +531,12 @@ public class Escalator extends Widget {
return;
}
- int currentLeft = horizontalScrollbar.getScrollPos();
- int currentTop = verticalScrollbar.getScrollPos();
+ double currentLeft = horizontalScrollbar.getScrollPos();
+ double currentTop = verticalScrollbar.getScrollPos();
final double timeDiff = timestamp - prevTime;
double left = currentLeft - velX * timeDiff;
- setScrollLeft((int) left);
+ setScrollLeft(left);
velX -= xFric * timeDiff;
double top = currentTop - velY * timeDiff;
@@ -736,8 +736,8 @@ public class Escalator extends Widget {
tableWrapper.getStyle().setHeight(tableWrapperHeight, Unit.PX);
tableWrapper.getStyle().setWidth(tableWrapperWidth, Unit.PX);
- verticalScrollbar.setOffsetSize((int) (tableWrapperHeight
- - footer.heightOfSection - header.heightOfSection));
+ verticalScrollbar.setOffsetSize(tableWrapperHeight
+ - footer.heightOfSection - header.heightOfSection);
verticalScrollbar.setScrollSize(scrollContentHeight);
/*
@@ -746,7 +746,7 @@ public class Escalator extends Widget {
* the scroll position, and re-apply it once the scrollbar size has
* been adjusted.
*/
- int prevScrollPos = horizontalScrollbar.getScrollPos();
+ double prevScrollPos = horizontalScrollbar.getScrollPos();
int unfrozenPixels = columnConfiguration
.getCalculatedColumnsWidth(Range.between(
@@ -754,7 +754,7 @@ public class Escalator extends Widget {
columnConfiguration.getColumnCount()));
int frozenPixels = scrollContentWidth - unfrozenPixels;
double hScrollOffsetWidth = tableWrapperWidth - frozenPixels;
- horizontalScrollbar.setOffsetSize((int) hScrollOffsetWidth);
+ horizontalScrollbar.setOffsetSize(hScrollOffsetWidth);
horizontalScrollbar.setScrollSize(unfrozenPixels);
horizontalScrollbar.getElement().getStyle()
.setLeft(frozenPixels, Unit.PX);
@@ -770,9 +770,8 @@ public class Escalator extends Widget {
return;
}
- final int scrollLeft = horizontalScrollbar.getScrollPos();
- final int scrollTop = verticalScrollbar.getScrollPos();
-
+ final double scrollTop = verticalScrollbar.getScrollPos();
+ final double scrollLeft = horizontalScrollbar.getScrollPos();
if (lastScrollLeft != scrollLeft) {
for (int i = 0; i < columnConfiguration.frozenColumns; i++) {
header.updateFreezePosition(i, scrollLeft);
@@ -976,9 +975,9 @@ public class Escalator extends Widget {
final int targetEndPx = targetStartPx
+ columnConfiguration.getColumnWidthActual(columnIndex);
- final int viewportStartPx = getScrollLeft();
- int viewportEndPx = viewportStartPx + getElement().getOffsetWidth()
- - frozenPixels;
+ final double viewportStartPx = getScrollLeft();
+ double viewportEndPx = viewportStartPx
+ + getElement().getOffsetWidth() - frozenPixels;
if (verticalScrollbar.showsScrollHandle()) {
viewportEndPx -= Util.getNativeScrollbarSize();
}
@@ -991,7 +990,7 @@ public class Escalator extends Widget {
* content, since the browser will adjust for that, and everything
* fall into line accordingly.
*/
- setScrollLeft((int) scrollLeft);
+ setScrollLeft(scrollLeft);
}
public void scrollToRow(final int rowIndex,
@@ -1460,8 +1459,8 @@ public class Escalator extends Widget {
int insertedColumnsWidth = columnConfiguration
.getCalculatedColumnsWidth(Range.withLength(offset,
numberOfColumns));
- horizontalScrollbar
- .setScrollPos((int) (scroller.lastScrollLeft + insertedColumnsWidth));
+ horizontalScrollbar.setScrollPos(scroller.lastScrollLeft
+ + insertedColumnsWidth);
}
/*
@@ -1936,10 +1935,10 @@ public class Escalator extends Widget {
boolean rowsWereMoved = false;
- final int topRowPos = getRowTop(visualRowOrder.getFirst());
+ final double topRowPos = getRowTop(visualRowOrder.getFirst());
// TODO [[mpixscroll]]
- final int scrollTop = tBodyScrollTop;
- final int viewportOffset = topRowPos - scrollTop;
+ final double scrollTop = tBodyScrollTop;
+ final double viewportOffset = topRowPos - scrollTop;
/*
* TODO [[optimize]] this if-else can most probably be refactored
@@ -1954,7 +1953,7 @@ public class Escalator extends Widget {
* heights - will not work with variable row heights
*/
int originalRowsToMove = (int) Math.ceil(viewportOffset
- / (double) getDefaultRowHeight());
+ / getDefaultRowHeight());
int rowsToMove = Math.min(originalRowsToMove,
root.getChildCount());
@@ -1964,7 +1963,7 @@ public class Escalator extends Widget {
* FIXME [[rowheight]]: coded to work only with default row
* heights - will not work with variable row heights
*/
- final int logicalRowIndex = scrollTop / getDefaultRowHeight();
+ final int logicalRowIndex = (int) (scrollTop / getDefaultRowHeight());
moveAndUpdateEscalatorRows(Range.between(start, end), 0,
logicalRowIndex);
@@ -1984,11 +1983,7 @@ public class Escalator extends Widget {
* row.
*/
- /*
- * Using the fact that integer division has implicit
- * floor-function to our advantage here.
- */
- int originalRowsToMove = Math.abs(viewportOffset
+ int originalRowsToMove = (int) Math.abs(viewportOffset
/ getDefaultRowHeight());
int rowsToMove = Math.min(originalRowsToMove,
root.getChildCount());
@@ -2011,7 +2006,7 @@ public class Escalator extends Widget {
* calculate the first logical row index from the scroll
* position.
*/
- logicalRowIndex = scrollTop / getDefaultRowHeight();
+ logicalRowIndex = (int) (scrollTop / getDefaultRowHeight());
}
/*
@@ -2300,15 +2295,15 @@ public class Escalator extends Widget {
* side-effects.
* <p>
* <em>Note:</em> {@link Scroller#onScroll()} <em>will</em> be
- * triggered, but it will not do anything, with the help of {@link
- * Escalator#internalScrollEventCalls}.
- *
+ * triggered, but it will not do anything, with the help of
+ * {@link Escalator#internalScrollEventCalls}.
+ *
* @param yDelta
* the delta of pixels to scrolls. A positive value moves the
* viewport downwards, while a negative value moves the
* viewport upwards
*/
- public void adjustScrollPosIgnoreEvents(final int yDelta) {
+ public void adjustScrollPosIgnoreEvents(final double yDelta) {
if (yDelta == 0) {
return;
}
@@ -2320,7 +2315,8 @@ public class Escalator extends Widget {
* FIXME [[rowheight]]: coded to work only with default row heights
* - will not work with variable row heights
*/
- final int rowTopPos = yDelta - yDelta % getDefaultRowHeight();
+ final int rowTopPos = (int) yDelta
+ - ((int) yDelta % getDefaultRowHeight());
for (final Element tr : visualRowOrder) {
setRowPosition(tr, 0, getRowTop(tr) + rowTopPos);
}
@@ -2632,7 +2628,7 @@ public class Escalator extends Widget {
* |4| |4| |*|
* 5 5 5
*/
- int newTop = getRowTop(visualRowOrder
+ double newTop = getRowTop(visualRowOrder
.get(removedVisualInside.getStart()));
for (int i = 0; i < removedVisualInside.length(); i++) {
final Element tr = visualRowOrder
@@ -2642,7 +2638,7 @@ public class Escalator extends Widget {
for (int i = removedVisualInside.getStart(); i < escalatorRowCount; i++) {
final Element tr = visualRowOrder.get(i);
- setRowPosition(tr, 0, newTop);
+ setRowPosition(tr, 0, (int) newTop);
/*
* FIXME [[rowheight]]: coded to work only with
@@ -2907,8 +2903,8 @@ public class Escalator extends Widget {
}
}
- private void setBodyScrollPosition(final int scrollLeft,
- final int scrollTop) {
+ private void setBodyScrollPosition(final double scrollLeft,
+ final double scrollTop) {
tBodyScrollLeft = scrollLeft;
tBodyScrollTop = scrollTop;
position.set(bodyElem, -tBodyScrollLeft, -tBodyScrollTop);
@@ -3121,8 +3117,8 @@ public class Escalator extends Widget {
* scroll position) in order to align the top row with the new
* scroll position.
*/
- double scrollRatio = (double) verticalScrollbar.getScrollPos()
- / (double) verticalScrollbar.getScrollSize();
+ double scrollRatio = verticalScrollbar.getScrollPos()
+ / verticalScrollbar.getScrollSize();
scroller.recalculateScrollbarsForVirtualViewport();
internalScrollEventCalls++;
verticalScrollbar.setScrollPos((int) (getDefaultRowHeight()
@@ -3463,7 +3459,7 @@ public class Escalator extends Widget {
* @deprecated maybe...
*/
@Deprecated
- private int tBodyScrollTop = 0;
+ private double tBodyScrollTop = 0;
/**
* TODO: investigate whether this field is now unnecessary, as
@@ -3472,7 +3468,7 @@ public class Escalator extends Widget {
* @deprecated maybe...
*/
@Deprecated
- private int tBodyScrollLeft = 0;
+ private double tBodyScrollLeft = 0;
private final VerticalScrollbarBundle verticalScrollbar = new VerticalScrollbarBundle();
private final HorizontalScrollbarBundle horizontalScrollbar = new HorizontalScrollbarBundle();
@@ -3772,7 +3768,7 @@ public class Escalator extends Widget {
* the number of pixels to scroll vertically
*/
public void setScrollTop(final double scrollTop) {
- verticalScrollbar.setScrollPos((int) scrollTop);
+ verticalScrollbar.setScrollPos(scrollTop);
}
/**
@@ -3781,7 +3777,7 @@ public class Escalator extends Widget {
*
* @return the logical horizontal scroll offset
*/
- public int getScrollLeft() {
+ public double getScrollLeft() {
return horizontalScrollbar.getScrollPos();
}
@@ -3792,7 +3788,7 @@ public class Escalator extends Widget {
* @param scrollLeft
* the number of pixels to scroll horizontally
*/
- public void setScrollLeft(final int scrollLeft) {
+ public void setScrollLeft(final double scrollLeft) {
horizontalScrollbar.setScrollPos(scrollLeft);
}
@@ -3915,8 +3911,8 @@ public class Escalator extends Widget {
* classes, so instead we call the outer class' method, which calls the
* inner class' respective method.
* <p>
- * Ideally, this method would not exist, and
- * {@link Scroller#onScroll()} would be called directly.
+ * Ideally, this method would not exist, and {@link Scroller#onScroll()}
+ * would be called directly.
*/
private void onScroll() {
scroller.onScroll();
@@ -4108,8 +4104,8 @@ public class Escalator extends Widget {
}
/**
- * Gets the amount of rows in Escalator's body that are shown, while {@link
- * #getHeightMode()} is {@link HeightMode#ROW}.
+ * Gets the amount of rows in Escalator's body that are shown, while
+ * {@link #getHeightMode()} is {@link HeightMode#ROW}.
* <p>
* By default, it is {@value GridState#DEFAULT_HEIGHT_BY_ROWS}.
*
diff --git a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java
index 21935df4e6..1f637a9ccc 100644
--- a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java
+++ b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java
@@ -99,6 +99,15 @@ abstract class ScrollbarBundle {
private static final int OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX = 13;
/**
+ * The allowed value inaccuracy when comparing two double-typed pixel
+ * values.
+ * <p>
+ * Since we're comparing pixels on a screen, epsilon must be less than 1.
+ * 0.49 was deemed a perfectly fine and beautifully round number.
+ */
+ private static final double PIXEL_EPSILON = 0.49d;
+
+ /**
* A representation of a single vertical scrollbar.
*
* @see VerticalScrollbarBundle#getElement()
@@ -127,17 +136,17 @@ abstract class ScrollbarBundle {
}
@Override
- public int getScrollSize() {
+ protected int internalGetScrollSize() {
return scrollSizeElement.getOffsetHeight();
}
@Override
- protected void internalSetOffsetSize(int px) {
+ protected void internalSetOffsetSize(double px) {
root.getStyle().setHeight(px, Unit.PX);
}
@Override
- public int getOffsetSize() {
+ public double getOffsetSize() {
return root.getOffsetHeight();
}
@@ -191,17 +200,17 @@ abstract class ScrollbarBundle {
}
@Override
- public int getScrollSize() {
+ protected int internalGetScrollSize() {
return scrollSizeElement.getOffsetWidth();
}
@Override
- protected void internalSetOffsetSize(int px) {
+ protected void internalSetOffsetSize(double px) {
root.getStyle().setWidth(px, Unit.PX);
}
@Override
- public int getOffsetSize() {
+ public double getOffsetSize() {
return root.getOffsetWidth();
}
@@ -230,8 +239,8 @@ abstract class ScrollbarBundle {
protected final Element scrollSizeElement = DOM.createDiv();
protected boolean isInvisibleScrollbar = false;
- private int scrollPos = 0;
- private int maxScrollPos = 0;
+ private double scrollPos = 0;
+ private double maxScrollPos = 0;
private boolean scrollHandleIsVisible = false;
@@ -243,6 +252,8 @@ abstract class ScrollbarBundle {
root.appendChild(scrollSizeElement);
}
+ protected abstract int internalGetScrollSize();
+
/**
* Sets the primary style name
*
@@ -263,12 +274,17 @@ abstract class ScrollbarBundle {
}
/**
- * Modifies the scroll position of this scrollbar by a number of pixels
+ * Modifies the scroll position of this scrollbar by a number of pixels.
+ * <p>
+ * <em>Note:</em> Even though {@code double} values are used, they are
+ * currently only used as integers as large {@code int} (or small but fast
+ * {@code long}). This means, all values are truncated to zero decimal
+ * places.
*
* @param delta
* the delta in pixels to change the scroll position by
*/
- public final void setScrollPosByDelta(int delta) {
+ public final void setScrollPosByDelta(double delta) {
if (delta != 0) {
setScrollPos(getScrollPos() + delta);
}
@@ -282,16 +298,21 @@ abstract class ScrollbarBundle {
* the new size of {@link #root} in the dimension this scrollbar
* is representing
*/
- protected abstract void internalSetOffsetSize(int px);
+ protected abstract void internalSetOffsetSize(double px);
/**
* Sets the length of the scrollbar.
+ * <p>
+ * <em>Note:</em> Even though {@code double} values are used, they are
+ * currently only used as integers as large {@code int} (or small but fast
+ * {@code long}). This means, all values are truncated to zero decimal
+ * places.
*
* @param px
* the length of the scrollbar in pixels
*/
- public final void setOffsetSize(int px) {
- internalSetOffsetSize(px);
+ public final void setOffsetSize(double px) {
+ internalSetOffsetSize(truncate(px));
forceScrollbar(showsScrollHandle());
recalculateMaxScrollPos();
fireVisibilityChangeIfNeeded();
@@ -312,24 +333,65 @@ abstract class ScrollbarBundle {
*
* @return the length of the scrollbar in pixels
*/
- public abstract int getOffsetSize();
+ public abstract double getOffsetSize();
/**
* Sets the scroll position of the scrollbar in the axis the scrollbar is
* representing.
+ * <p>
+ * <em>Note:</em> Even though {@code double} values are used, they are
+ * currently only used as integers as large {@code int} (or small but fast
+ * {@code long}). This means, all values are truncated to zero decimal
+ * places.
*
* @param px
* the new scroll position in pixels
*/
- public final void setScrollPos(int px) {
- int oldScrollPos = scrollPos;
- scrollPos = Math.max(0, Math.min(maxScrollPos, px));
+ public final void setScrollPos(double px) {
+ double oldScrollPos = scrollPos;
+ scrollPos = Math.max(0, Math.min(maxScrollPos, truncate(px)));
+
+ if (!pixelValuesEqual(oldScrollPos, scrollPos)) {
+ /*
+ * This is where the value needs to be converted into an integer no
+ * matter how we flip it, since GWT expects an integer value.
+ * There's no point making a JSNI method that accepts doubles as the
+ * scroll position, since the browsers themselves don't support such
+ * large numbers (as of today, 25.3.2014). This double-ranged is
+ * only facilitating future virtual scrollbars.
+ */
+ internalSetScrollPos(toInt32(px));
+ }
+ }
- if (oldScrollPos != scrollPos) {
- internalSetScrollPos(px);
+ /**
+ * Truncates a double such that no decimal places are retained.
+ * <p>
+ * E.g. {@code trunc(2.3d) == 2.0d} and {@code trunc(-2.3d) == -2.0d}.
+ *
+ * @param num
+ * the double value to be truncated
+ * @return the {@code num} value without any decimal digits
+ */
+ private static double truncate(double num) {
+ if (num > 0) {
+ return Math.floor(num);
+ } else {
+ return Math.ceil(num);
}
}
+ /**
+ * Modifies the element's scroll position (scrollTop or scrollLeft).
+ * <p>
+ * <em>Note:</em> The parameter here is a type of integer (instead of a
+ * double) by design. The browsers internally convert all double values into
+ * an integer value. To make this fact explicit, this API has chosen to
+ * force integers already at this level.
+ *
+ * @param px
+ * integer pixel value to scroll to
+ */
protected abstract void internalSetScrollPos(int px);
/**
@@ -338,14 +400,24 @@ abstract class ScrollbarBundle {
*
* @return the new scroll position in pixels
*/
- public final int getScrollPos() {
- assert internalGetScrollPos() == scrollPos : "calculated scroll position ("
- + scrollPos
+ public final double getScrollPos() {
+ assert internalGetScrollPos() == toInt32(scrollPos) : "calculated scroll position ("
+ + toInt32(scrollPos)
+ ") did not match the DOM element scroll position ("
+ internalGetScrollPos() + ")";
return scrollPos;
}
+ /**
+ * Retrieves the element's scroll position (scrollTop or scrollLeft).
+ * <p>
+ * <em>Note:</em> The parameter here is a type of integer (instead of a
+ * double) by design. The browsers internally convert all double values into
+ * an integer value. To make this fact explicit, this API has chosen to
+ * force integers already at this level.
+ *
+ * @return integer pixel value of the scroll position
+ */
protected abstract int internalGetScrollPos();
/**
@@ -362,13 +434,18 @@ abstract class ScrollbarBundle {
/**
* Sets the amount of pixels the scrollbar needs to be able to scroll
* through.
+ * <p>
+ * <em>Note:</em> Even though {@code double} values are used, they are
+ * currently only used as integers as large {@code int} (or small but fast
+ * {@code long}). This means, all values are truncated to zero decimal
+ * places.
*
* @param px
* the number of pixels the scrollbar should be able to scroll
* through
*/
- public final void setScrollSize(int px) {
- internalSetScrollSize(px);
+ public final void setScrollSize(double px) {
+ internalSetScrollSize(toInt32(truncate(px)));
forceScrollbar(showsScrollHandle());
recalculateMaxScrollPos();
fireVisibilityChangeIfNeeded();
@@ -381,7 +458,9 @@ abstract class ScrollbarBundle {
* @return the number of pixels the scrollbar should be able to scroll
* through
*/
- public abstract int getScrollSize();
+ public double getScrollSize() {
+ return internalGetScrollSize();
+ }
/**
* Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in the
@@ -447,8 +526,8 @@ abstract class ScrollbarBundle {
}
public void recalculateMaxScrollPos() {
- int scrollSize = getScrollSize();
- int offsetSize = getOffsetSize();
+ double scrollSize = getScrollSize();
+ double offsetSize = getOffsetSize();
maxScrollPos = Math.max(0, scrollSize - offsetSize);
// make sure that the correct max scroll position is maintained.
@@ -459,7 +538,6 @@ abstract class ScrollbarBundle {
* This is a method that JSNI can call to synchronize the object state from
* the DOM.
*/
- @SuppressWarnings("unused")
private final void updateScrollPosFromDom() {
scrollPos = internalGetScrollPos();
}
@@ -493,4 +571,32 @@ abstract class ScrollbarBundle {
getHandlerManager().fireEvent(event);
}
}
+
+
+ /**
+ * Converts a double into an integer by JavaScript's terms.
+ * <p>
+ * Implementation copied from {@link Element#toInt32(double)}.
+ *
+ * @param val
+ * the double value to convert into an integer
+ * @return the double value converted to an integer
+ */
+ private static native int toInt32(double val)
+ /*-{
+ return val | 0;
+ }-*/;
+
+ /**
+ * Compares two double values with the error margin of
+ * {@link #PIXEL_EPSILON} (i.e. {@value #PIXEL_EPSILON})
+ *
+ * @param num1
+ * the first value for which to compare equality
+ * @param num2
+ * the second value for which to compare equality
+ */
+ private static boolean pixelValuesEqual(final double num1, final double num2) {
+ return Math.abs(num1 - num2) <= PIXEL_EPSILON;
+ }
}