/* * Copyright 2011 Google Inc. * * 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.google.gwt.user.client.ui; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; /** * Implementation of scrolling behavior. */ class ScrollImpl { /** * IE does not fire a scroll event when the scrollable element or the * container is resized, so we synthesize one as needed. IE scrolls in the * positive direction, even in RTL mode. */ static class ScrollImplTrident extends ScrollImpl { private static JavaScriptObject scrollHandler; private static JavaScriptObject resizeHandler; /** * Creates static, leak-safe scroll/resize handlers. */ private static native void initStaticHandlers() /*-{ // caches last scroll position @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::scrollHandler = function() { var scrollableElem = $wnd.event.srcElement; scrollableElem.__lastScrollTop = scrollableElem.scrollTop; scrollableElem.__lastScrollLeft = scrollableElem.scrollLeft; }; // watches for resizes that should fire a fake scroll event @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler = function() { var scrollableElem = $wnd.event.srcElement; if (scrollableElem.__isScrollContainer) { scrollableElem = scrollableElem.parentNode; } // Give the browser a chance to fire a native scroll event before synthesizing one. setTimeout($entry(function() { // Trigger a synthetic scroll event if the scroll position changes. if (scrollableElem.scrollTop != scrollableElem.__lastScrollTop || scrollableElem.scrollLeft != scrollableElem.__lastScrollLeft) { // Update scroll positions. scrollableElem.__lastScrollTop = scrollableElem.scrollTop; scrollableElem.__lastScrollLeft = scrollableElem.scrollLeft; @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::triggerScrollEvent(Lcom/google/gwt/dom/client/Element;) (scrollableElem); } }), 1); }; }-*/; private static void triggerScrollEvent(Element elem) { elem.dispatchEvent(Document.get().createScrollEvent()); } ScrollImplTrident() { initStaticHandlers(); } @Override public native void initialize(Element scrollable, Element container) /*-{ // Remember the last scroll position. scrollable.__lastScrollTop = scrollable.__lastScrollLeft = 0; scrollable.attachEvent('onscroll', @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::scrollHandler); // Detect if the scrollable element or the container within it changes // size, either of which could affect the scroll position. scrollable.attachEvent('onresize', @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler); container.attachEvent('onresize', @com.google.gwt.user.client.ui.ScrollImpl.ScrollImplTrident::resizeHandler); // use boolean (primitive, won't leak) hint to resizeHandler that its the container container.__isScrollContainer = true; }-*/; @Override public native boolean isRtl(Element scrollable) /*-{ return scrollable.currentStyle.direction == 'rtl'; }-*/; } private static ScrollImpl impl; /** * Get the singleton instance of {@link ScrollImpl}. */ static ScrollImpl get() { if (impl == null) { impl = GWT.create(ScrollImpl.class); } return impl; } /** * Get the maximum horizontal scroll position. * * @param scrollable the scrollable element * @return the maximum scroll position */ public int getMaximumHorizontalScrollPosition(Element scrollable) { return isRtl(scrollable) ? 0 : scrollable.getScrollWidth() - scrollable.getClientWidth(); } /** * Get the minimum horizontal scroll position. * * @param scrollable the scrollable element * @return the minimum scroll position */ public int getMinimumHorizontalScrollPosition(Element scrollable) { return isRtl(scrollable) ? scrollable.getClientWidth() - scrollable.getScrollWidth() : 0; } /** * Initialize a scrollable element. * * @param scrollable the scrollable element * @param container the container */ public void initialize(Element scrollable, Element container) { // Overridden by ScrollImplTrident. } /** * Check if the specified element has an RTL direction. We can't base this on * the current locale because the user can modify the direction at the DOM * level. * * @param scrollable the scrollable element * @return true if the direction is RTL, false if LTR */ public native boolean isRtl(Element scrollable) /*-{ var computedStyle = $doc.defaultView.getComputedStyle(scrollable, null); return computedStyle.getPropertyValue('direction') == 'rtl'; }-*/; }