diff options
author | Henrik Paul <henrik@vaadin.com> | 2013-11-21 16:52:32 +0200 |
---|---|---|
committer | Henrik Paul <henrik@vaadin.com> | 2013-11-21 16:54:53 +0200 |
commit | c2d38fa6c2d67457065fd3dce7e0d939ae0a1278 (patch) | |
tree | 88bca7fb1008a3ce5c36d7f8ba9e0f1da894e48f | |
parent | c2b988781045d1e6723b8159ca34d6c0afcdcde8 (diff) | |
download | vaadin-framework-c2d38fa6c2d67457065fd3dce7e0d939ae0a1278.tar.gz vaadin-framework-c2d38fa6c2d67457065fd3dce7e0d939ae0a1278.zip |
Support OSX's hiding scrollbars (#12645)
Change-Id: If5df6a7651482a33558088398330fd73a4d43645
3 files changed, 455 insertions, 89 deletions
diff --git a/WebContent/VAADIN/themes/base/escalator/escalator.scss b/WebContent/VAADIN/themes/base/escalator/escalator.scss index 436f849456..8136f23df6 100644 --- a/WebContent/VAADIN/themes/base/escalator/escalator.scss +++ b/WebContent/VAADIN/themes/base/escalator/escalator.scss @@ -11,9 +11,23 @@ $border-color: #aaa; .#{$primaryStyleName}-scroller { position: absolute; overflow: auto; - height: inherit; + z-index: 20; +} + +.#{$primaryStyleName}-scroller-horizontal { left: 0; /* Left position adjusted to align with frozen columns */ right: 0; + bottom: 0; + overflow-y: hidden; + -ms-overflow-y: hidden; +} + +.#{$primaryStyleName}-scroller-vertical { + right: 0; + top: 0; /* this will be overridden by code, but it's a good default behavior */ + bottom: 0; /* this will be overridden by code, but it's a good default behavior */ + overflow-x: hidden; + -ms-overflow-x: hidden; } .#{$primaryStyleName}-tablewrapper { diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java index bd9d4ba245..769a109569 100644 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ b/client/src/com/vaadin/client/ui/grid/Escalator.java @@ -46,6 +46,8 @@ import com.vaadin.client.ui.grid.PositionFunction.AbsolutePosition; import com.vaadin.client.ui.grid.PositionFunction.Translate3DPosition; import com.vaadin.client.ui.grid.PositionFunction.TranslatePosition; import com.vaadin.client.ui.grid.PositionFunction.WebkitTranslate3DPosition; +import com.vaadin.client.ui.grid.ScrollbarBundle.HorizontalScrollbarBundle; +import com.vaadin.client.ui.grid.ScrollbarBundle.VerticalScrollbarBundle; /*- @@ -227,10 +229,6 @@ public class Escalator extends Widget { * re-measure) */ /* - * [[escalator]]: This code will require alterations that are relevant for - * the escalator functionality. - */ - /* * [[rowwidth]] [[colwidth]]: This code will require alterations that are * relevant for being able to support variable row heights or column widths. * NOTE: these bits can most often also be identified by searching for code @@ -391,7 +389,7 @@ public class Escalator extends Widget { } } - moveScrollFromEvent(escalator.scrollerElem, -deltaX, -deltaY, + moveScrollFromEvent(escalator, -deltaX, -deltaY, event.getNativeEvent()); } @@ -406,21 +404,16 @@ public class Escalator extends Widget { } } - public static void moveScrollFromEvent(final Element scrollerElem, + public static void moveScrollFromEvent(final Escalator escalator, final double deltaX, final double deltaY, final NativeEvent event) { - /* - * TODO [[optimize]]: instead of calling getScollLeft/Top that - * potentially causes a reflow, update a new scroll absolute - * position. - */ - if (!Double.isNaN(deltaX) && deltaX != 0) { - scrollerElem - .setScrollLeft((int) (scrollerElem.getScrollLeft() + deltaX)); + + if (!Double.isNaN(deltaX)) { + escalator.horizontalScrollbar.setScrollPosByDelta((int) deltaX); } - if (!Double.isNaN(deltaY) && deltaY != 0) { - scrollerElem - .setScrollTop((int) (scrollerElem.getScrollTop() + deltaY)); + + if (!Double.isNaN(deltaY)) { + escalator.verticalScrollbar.setScrollPosByDelta((int) deltaY); } /* @@ -514,8 +507,8 @@ public class Escalator extends Widget { private void cancelBecauseOfEdgeOrCornerMaybe(final int lastLeft, final int lastTop) { - if (lastLeft == scrollerElem.getScrollLeft() - && lastTop == scrollerElem.getScrollTop()) { + if (lastLeft == horizontalScrollbar.getScrollPos() + && lastTop == verticalScrollbar.getScrollPos()) { cancel(); } } @@ -564,8 +557,7 @@ public class Escalator extends Widget { deltaY = -0.5*e.wheelDelta; } - var scroller = esc.@com.vaadin.client.ui.grid.Escalator::scrollerElem; - @com.vaadin.client.ui.grid.Escalator.JsniUtil::moveScrollFromEvent(*)(scroller, deltaX, deltaY, e); + @com.vaadin.client.ui.grid.Escalator.JsniUtil::moveScrollFromEvent(*)(esc, deltaX, deltaY, e); }); }-*/; @@ -574,41 +566,35 @@ public class Escalator extends Widget { * that the sizes of the scroll handles appear correct in the browser */ public void recalculateScrollbarsForVirtualViewport() { - double scrollerHeight = height - header.height; - - /* - * It looks better this way: if we only have a vertical scrollbar, - * keep it between the header and footer. But if we have a - * horizontal scrollbar, envelop the footer also. - */ - if (!needsHorizontalScrollbars()) { - scrollerHeight -= footer.height; - } - - scrollerElem.getStyle().setHeight(scrollerHeight, Unit.PX); - - final double scrollerTop = header.height; - scrollerElem.getStyle().setTop(scrollerTop, Unit.PX); - - /* - * TODO [[freezecol]]: cut scrollerElem from the left to take freeze - * columns into account. innerScroller probably needs some - * adjustments too. - */ - // TODO [[rowheight]]: adjust for variable row heights. - double innerScrollerHeight = ROW_HEIGHT_PX * body.getRowCount(); + int innerScrollerHeight = ROW_HEIGHT_PX * body.getRowCount(); + + double vScrollBottom = footer.height; if (needsHorizontalScrollbars()) { - innerScrollerHeight += footer.height; + vScrollBottom += horizontalScrollbar.getScrollbarThickness(); } - innerScrollerElem.getStyle() - .setHeight(innerScrollerHeight, Unit.PX); + + verticalScrollbar.getElement().getStyle() + .setTop(header.height, Unit.PX); + verticalScrollbar.getElement().getStyle() + .setBottom(vScrollBottom, Unit.PX); + verticalScrollbar.setScrollSize(innerScrollerHeight); // TODO [[colwidth]]: adjust for variable column widths. int columnsToScroll = columnConfiguration.getColumnCount() - columnConfiguration.getFrozenColumnCount(); - innerScrollerElem.getStyle().setWidth( - COLUMN_WIDTH_PX * columnsToScroll, Unit.PX); + horizontalScrollbar + .setScrollSize(COLUMN_WIDTH_PX * columnsToScroll); + + final Style hScrollbarStyle = horizontalScrollbar.getElement() + .getStyle(); + if (needsVerticalScrollbars()) { + final int hScrollbarRight = verticalScrollbar + .getScrollbarThickness(); + hScrollbarStyle.setRight(hScrollbarRight, Unit.PX); + } else { + hScrollbarStyle.clearRight(); + } // we might've got new or got rid of old scrollbars. recalculateTableWrapperSize(); @@ -620,15 +606,15 @@ public class Escalator extends Widget { */ public void recalculateTableWrapperSize() { double wrapperHeight = height; - if (scrollerElem.getOffsetWidth() < innerScrollerElem - .getOffsetWidth()) { - wrapperHeight -= Util.getNativeScrollbarSize(); + if (horizontalScrollbar.getOffsetSize() < horizontalScrollbar + .getScrollSize()) { + wrapperHeight -= horizontalScrollbar.getScrollbarThickness(); } double wrapperWidth = width; - if (scrollerElem.getOffsetHeight() - footer.height < innerScrollerElem - .getOffsetHeight()) { - wrapperWidth -= Util.getNativeScrollbarSize(); + if (verticalScrollbar.getOffsetSize() - footer.height < verticalScrollbar + .getScrollSize()) { + wrapperWidth -= verticalScrollbar.getScrollbarThickness(); } tableWrapper.getStyle().setHeight(wrapperHeight, Unit.PX); @@ -651,8 +637,8 @@ public class Escalator extends Widget { return; } - final int scrollLeft = scrollerElem.getScrollLeft(); - final int scrollTop = scrollerElem.getScrollTop(); + final int scrollLeft = horizontalScrollbar.getScrollPos(); + final int scrollTop = verticalScrollbar.getScrollPos(); if (lastScrollLeft != scrollLeft) { for (int i = 0; i < columnConfiguration.frozenColumns; i++) { @@ -895,7 +881,7 @@ public class Escalator extends Widget { } } - private static final String CLASS_NAME = "v-escalator"; + static final String CLASS_NAME = "v-escalator"; private abstract class AbstractRowContainer implements RowContainer { private EscalatorUpdater updater = EscalatorUpdater.NULL; @@ -1165,11 +1151,11 @@ public class Escalator extends Widget { } /* - * TODO [[escalator]][[rowheight]]: even if no rows are evaluated in - * the current viewport, the heights of some unrendered rows might - * change in a refresh. This would cause the scrollbar to be - * adjusted (in scrollHeight and/or scrollTop). Do we want to take - * this into account? + * TODO [[rowheight]]: even if no rows are evaluated in the current + * viewport, the heights of some unrendered rows might change in a + * refresh. This would cause the scrollbar to be adjusted (in + * scrollHeight and/or scrollTop). Do we want to take this into + * account? */ if (hasColumnAndRowData()) { /* @@ -1251,7 +1237,7 @@ public class Escalator extends Widget { final int leftByDiff = (int) (scroller.lastScrollLeft - removedColumnsPxAmount); final int newScrollLeft = Math.max(firstRemovedColumnLeft, leftByDiff); - scrollerElem.setScrollLeft(newScrollLeft); + horizontalScrollbar.setScrollPos(newScrollLeft); } // this needs to be after the scroll position adjustment above. @@ -1304,8 +1290,8 @@ public class Escalator extends Widget { * COLUMN_WIDTH_PX; if (columnsWereAddedToTheLeftOfViewport) { - scrollerElem - .setScrollLeft((int) (scroller.lastScrollLeft + numberOfColumns + horizontalScrollbar + .setScrollPos((int) (scroller.lastScrollLeft + numberOfColumns * COLUMN_WIDTH_PX)); } } @@ -1562,10 +1548,9 @@ public class Escalator extends Widget { */ scroller.recalculateScrollbarsForVirtualViewport(); - final boolean addedRowsAboveCurrentViewport = index * ROW_HEIGHT_PX < scrollerElem - .getScrollTop(); - final boolean addedRowsBelowCurrentViewport = index * ROW_HEIGHT_PX > scrollerElem - .getScrollTop() + calculateHeight(); + final boolean addedRowsAboveCurrentViewport = index * ROW_HEIGHT_PX < getScrollTop(); + final boolean addedRowsBelowCurrentViewport = index * ROW_HEIGHT_PX > getScrollTop() + + calculateHeight(); if (addedRowsAboveCurrentViewport) { /* @@ -1751,7 +1736,7 @@ public class Escalator extends Widget { } internalScrollEventCalls++; - scrollerElem.setScrollTop(scrollerElem.getScrollTop() + yDelta); + verticalScrollbar.setScrollPosByDelta(yDelta); final int snappedYDelta = yDelta - yDelta % ROW_HEIGHT_PX; for (final Element tr : visualRowOrder) { @@ -1876,8 +1861,8 @@ public class Escalator extends Widget { // TODO [[rowheight]] final int yDelta = removedAbove.length() * ROW_HEIGHT_PX; final int firstLogicalRowHeight = ROW_HEIGHT_PX; - final boolean removalScrollsToShowFirstLogicalRow = scrollerElem - .getScrollTop() - yDelta < firstLogicalRowHeight; + final boolean removalScrollsToShowFirstLogicalRow = verticalScrollbar + .getScrollPos() - yDelta < firstLogicalRowHeight; if (removedVisualInside.isEmpty() && (!removalScrollsToShowFirstLogicalRow || !firstVisualRowIsRemoved)) { @@ -1895,7 +1880,8 @@ public class Escalator extends Widget { * current negative scrolltop, presto!), so that it isn't * aligned funnily */ - adjustScrollPosIgnoreEvents(-scrollerElem.getScrollTop()); + adjustScrollPosIgnoreEvents(-verticalScrollbar + .getScrollPos()); } } @@ -2584,9 +2570,17 @@ public class Escalator extends Widget { } } - scrollerElem.getStyle().setLeft(frozenColumns * COLUMN_WIDTH_PX, - Unit.PX); + /* + * If decreasing the amount of frozen columns, and scrolled to the + * right, the scroll position will reset. So we need to remember the + * scroll position, and re-apply it once the scrollbar size has been + * adjusted. + */ + int scrollPos = horizontalScrollbar.getScrollPos(); + horizontalScrollbar.getElement().getStyle() + .setLeft(frozenColumns * COLUMN_WIDTH_PX, Unit.PX); scroller.recalculateScrollbarsForVirtualViewport(); + horizontalScrollbar.setScrollPos(scrollPos); } @Override @@ -2632,8 +2626,8 @@ public class Escalator extends Widget { private int tBodyScrollTop = 0; private int tBodyScrollLeft = 0; - private final Element scrollerElem = DOM.createDiv(); - private final Element innerScrollerElem = DOM.createDiv(); + private final VerticalScrollbarBundle verticalScrollbar = new VerticalScrollbarBundle(); + private final HorizontalScrollbarBundle horizontalScrollbar = new HorizontalScrollbarBundle(); private final HeaderRowContainer header = new HeaderRowContainer(headElem); private final BodyRowContainer body = new BodyRowContainer(bodyElem); @@ -2690,10 +2684,11 @@ public class Escalator extends Widget { setElement(root); setStyleName(CLASS_NAME); - scrollerElem.setClassName(CLASS_NAME + "-scroller"); - root.appendChild(scrollerElem); - - scrollerElem.appendChild(innerScrollerElem); + root.appendChild(verticalScrollbar.getElement()); + root.appendChild(horizontalScrollbar.getElement()); + verticalScrollbar.setScrollbarThickness(Util.getNativeScrollbarSize()); + horizontalScrollbar + .setScrollbarThickness(Util.getNativeScrollbarSize()); tableWrapper = DOM.createDiv(); tableWrapper.setClassName(CLASS_NAME + "-tablewrapper"); @@ -2732,11 +2727,17 @@ public class Escalator extends Widget { recalculateElementSizes(); body.paintInsertRows(0, body.getRowCount()); - scroller.attachScrollListener(scrollerElem); + scroller.attachScrollListener(verticalScrollbar + .getElement()); + scroller.attachScrollListener(horizontalScrollbar + .getElement()); scroller.attachMousewheelListener(getElement()); scroller.attachTouchListeners(getElement()); } else { - scroller.detachScrollListener(scrollerElem); + scroller.detachScrollListener(verticalScrollbar + .getElement()); + scroller.detachScrollListener(horizontalScrollbar + .getElement()); scroller.detachMousewheelListener(getElement()); scroller.detachTouchListeners(getElement()); } @@ -2870,7 +2871,7 @@ public class Escalator extends Widget { * @return the logical vertical scroll offset */ public double getScrollTop() { - return scrollerElem.getScrollTop(); + return verticalScrollbar.getScrollPos(); } /** @@ -2881,7 +2882,7 @@ public class Escalator extends Widget { * the number of pixels to scroll vertically */ public void setScrollTop(final double scrollTop) { - scrollerElem.setScrollTop((int) scrollTop); + verticalScrollbar.setScrollPos((int) scrollTop); } /** @@ -2891,7 +2892,7 @@ public class Escalator extends Widget { * @return the logical horizontal scroll offset */ public int getScrollLeft() { - return scrollerElem.getScrollLeft(); + return horizontalScrollbar.getScrollPos(); } /** @@ -2902,7 +2903,7 @@ public class Escalator extends Widget { * the number of pixels to scroll horizontally */ public void setScrollLeft(final int scrollLeft) { - scrollerElem.setScrollLeft(scrollLeft); + horizontalScrollbar.setScrollPos(scrollLeft); } /** diff --git a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java new file mode 100644 index 0000000000..82c2c56117 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java @@ -0,0 +1,351 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.grid; + +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; + +/** + * An element-like bundle representing a configurable and visual scrollbar in + * one axis. + * + * @since 7.2 + * @author Vaadin Ltd + * @see VerticalScrollbarBundle + * @see HorizontalScrollbarBundle + */ +abstract class ScrollbarBundle { + private static final String CLASS_NAME = Escalator.CLASS_NAME + "-scroller"; + + /** + * The pixel size for OSX's invisible scrollbars. + * <p> + * Touch devices don't show a scrollbar at all, so the scrollbar size is + * irrelevant in their case. There doesn't seem to be any other popular + * platforms that has scrollbars similar to OSX. Thus, this behavior is + * tailored for OSX only, until additional platforms start behaving this + * way. + */ + private static final int OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX = 13; + + /** + * A representation of a single vertical scrollbar. + * + * @see VerticalScrollbarBundle#getElement() + */ + final static class VerticalScrollbarBundle extends ScrollbarBundle { + public VerticalScrollbarBundle() { + root.addClassName(CLASS_NAME + "-vertical"); + } + + @Override + public void setScrollPos(int px) { + root.setScrollTop(px); + } + + @Override + public int getScrollPos() { + return root.getScrollTop(); + } + + @Override + protected void internalSetScrollSize(int px) { + scrollSizeElement.getStyle().setHeight(px, Unit.PX); + } + + @Override + public int getScrollSize() { + return scrollSizeElement.getOffsetHeight(); + } + + @Override + protected void internalSetOffsetSize(int px) { + root.getStyle().setHeight(px, Unit.PX); + } + + @Override + public int getOffsetSize() { + return root.getOffsetHeight(); + } + + @Override + protected void internalSetScrollbarThickness(int px) { + root.getStyle().setWidth(px, Unit.PX); + scrollSizeElement.getStyle().setWidth(px, Unit.PX); + } + + @Override + protected int internalGetScrollbarThickness() { + return root.getOffsetWidth(); + } + + @Override + protected void forceScrollbar(boolean enable) { + if (enable) { + root.getStyle().setOverflowY(Overflow.SCROLL); + } else { + root.getStyle().clearOverflowY(); + } + } + } + + /** + * A representation of a single horizontal scrollbar. + * + * @see HorizontalScrollbarBundle#getElement() + */ + final static class HorizontalScrollbarBundle extends ScrollbarBundle { + protected HorizontalScrollbarBundle() { + root.addClassName(CLASS_NAME + "-horizontal"); + } + + @Override + public void setScrollPos(int px) { + root.setScrollLeft(px); + } + + @Override + public int getScrollPos() { + return root.getScrollLeft(); + } + + @Override + protected void internalSetScrollSize(int px) { + scrollSizeElement.getStyle().setWidth(px, Unit.PX); + } + + @Override + public int getScrollSize() { + return scrollSizeElement.getOffsetWidth(); + } + + @Override + protected void internalSetOffsetSize(int px) { + root.getStyle().setWidth(px, Unit.PX); + } + + @Override + public int getOffsetSize() { + return root.getOffsetWidth(); + } + + @Override + protected void internalSetScrollbarThickness(int px) { + root.getStyle().setHeight(px, Unit.PX); + scrollSizeElement.getStyle().setHeight(px, Unit.PX); + } + + @Override + protected int internalGetScrollbarThickness() { + return root.getOffsetHeight(); + } + + @Override + protected void forceScrollbar(boolean enable) { + if (enable) { + root.getStyle().setOverflowX(Overflow.SCROLL); + } else { + root.getStyle().clearOverflowX(); + } + } + } + + protected final Element root = DOM.createDiv(); + protected final Element scrollSizeElement = DOM.createDiv(); + protected boolean isInvisibleScrollbar = false; + + private ScrollbarBundle() { + root.appendChild(scrollSizeElement); + root.setClassName(CLASS_NAME); + } + + /** + * Gets the root element of this scrollbar-composition. + * + * @return the root element + */ + public Element getElement() { + return root; + } + + /** + * Modifies the scroll position of this scrollbar by a number of pixels + * + * @param delta + * the delta in pixels to change the scroll position by + */ + public void setScrollPosByDelta(int delta) { + if (delta != 0) { + /* + * TODO [[optimize]]: rewrite this so that we can evoid a potential + * reflow from getScrollPos() + */ + setScrollPos(getScrollPos() + delta); + } + } + + /** + * Modifies {@link #root root's} dimensions in the axis the scrollbar is + * representing. + * + * @param px + * the new size of {@link #root} in the dimension this scrollbar + * is representing + */ + protected abstract void internalSetOffsetSize(int px); + + /** + * Sets the length of the scrollbar. + * + * @param px + * the length of the scrollbar in pixels + */ + public void setOffsetSize(int px) { + internalSetOffsetSize(px); + forceScrollbar(needsScrollbars()); + } + + /** + * Force the scrollbar to be visible with CSS. In practice, this means to + * set either <code>overflow-x</code> or <code>overflow-y</code> to " + * <code>scroll</code>" in the scrollbar's direction. + * <p> + * This is an IE8 workaround, since it doesn't always show scrollbars with + * <code>overflow: auto</code> enabled. + */ + protected abstract void forceScrollbar(boolean enable); + + /** + * Gets the length of the scrollbar + * + * @return the length of the scrollbar in pixels + */ + public abstract int getOffsetSize(); + + /** + * Sets the scroll position of the scrollbar in the axis the scrollbar is + * representing. + * + * @param px + * the new scroll position in pixels + */ + public abstract void setScrollPos(int px); + + /** + * Gets the scroll position of the scrollbar in the axis the scrollbar is + * representing. + * + * @return the new scroll position in pixels + */ + public abstract int getScrollPos(); + + /** + * Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in + * such a way that the scrollbar is able to scroll a certain number of + * pixels in the axis it is representing. + * + * @param px + * the new size of {@link #scrollSizeElement} in the dimension + * this scrollbar is representing + */ + protected abstract void internalSetScrollSize(int px); + + /** + * Sets the amount of pixels the scrollbar needs to be able to scroll + * through. + * + * @param px + * the number of pixels the scrollbar should be able to scroll + * through + */ + public void setScrollSize(int px) { + internalSetScrollSize(px); + forceScrollbar(needsScrollbars()); + } + + /** + * Gets the amount of pixels the scrollbar needs to be able to scroll + * through. + * + * @return the number of pixels the scrollbar should be able to scroll + * through + */ + public abstract int getScrollSize(); + + /** + * Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in the + * opposite axis to what the scrollbar is representing. + * + * @param px + * the dimension that {@link #scrollSizeElement} should take in + * the opposite axis to what the scrollbar is representing + */ + protected abstract void internalSetScrollbarThickness(int px); + + /** + * Sets the scrollbar's thickness. + * <p> + * If the thickness is set to 0, the scrollbar will be treated as an + * "invisible" scrollbar. This means, the DOM structure will be given a + * non-zero size, but {@link #getScrollbarThickness()} will still return the + * value 0. + * + * @param px + * the scrollbar's thickness in pixels + */ + public void setScrollbarThickness(int px) { + isInvisibleScrollbar = (px == 0); + internalSetScrollbarThickness(px != 0 ? px + : OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX); + } + + /** + * Gets the scrollbar's thickness as defined in the DOM. + * + * @return the scrollbar's thickness as defined in the DOM, in pixels + */ + protected abstract int internalGetScrollbarThickness(); + + /** + * Gets the scrollbar's thickness. + * <p> + * This value will differ from the value in the DOM, if the thickness was + * set to 0 with {@link #setScrollbarThickness(int)}, as the scrollbar is + * then treated as "invisible." + * + * @return the scrollbar's thickness in pixels + */ + public int getScrollbarThickness() { + if (!isInvisibleScrollbar) { + return internalGetScrollbarThickness(); + } else { + return 0; + } + } + + /** + * Checks whether the scrollbar should be active and needed, i.e. if the + * "content" is larger than the "frame". + * + * @return <code>true</code> iff the scrollbar should be active and needed + */ + protected boolean needsScrollbars() { + return getOffsetSize() < getScrollSize(); + } +} |