diff options
| author | 2016-01-21 14:50:38 +0900 | |
|---|---|---|
| committer | 2016-01-21 14:50:38 +0900 | |
| commit | d85bc5074dc1211fdb1c6799d9321af54ac5a32f (patch) | |
| tree | 3966af7121295840c2be68b327d3b35483d9fa4b | |
| parent | 1ee3e370f9f92721c3ea06344a4e2e80a1bf1bed (diff) | |
Make scroll bar mouse draggable.
This is for Views that have special mouse dragging handling. e.g.
TextView invokes text selection on mouse dragging, so it cannot be
scrolled by mouse dragging.
This provides such Views or ViewGroups a last resort to scroll as we
don't assume that all devices have touch panel, touch pad, or mouse
wheel.
Bug: 20016455
Change-Id: I68a13258a50b5e4ea681b2576da6000a0bb3fa65
| -rw-r--r-- | core/java/android/view/View.java | 318 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 21 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 7 | ||||
| -rw-r--r-- | core/java/android/widget/HorizontalScrollView.java | 4 | ||||
| -rw-r--r-- | core/java/android/widget/ScrollBarDrawable.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/ScrollView.java | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/ScrollBarUtils.java | 39 |
7 files changed, 342 insertions, 75 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 537f887305ee..eddcd25c4184 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -98,13 +98,13 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Checkable; import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; - import static android.os.Build.VERSION_CODES.*; import static java.lang.Math.max; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.widget.ScrollBarUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -5110,6 +5110,88 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mVerticalScrollbarPosition; } + boolean isOnScrollbar(float x, float y) { + if (mScrollCache == null) { + return false; + } + x += getScrollX(); + y += getScrollY(); + if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + if (bounds.contains((int)x, (int)y)) { + return true; + } + } + if (isHorizontalScrollBarEnabled()) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + if (bounds.contains((int)x, (int)y)) { + return true; + } + } + return false; + } + + boolean isOnScrollbarThumb(float x, float y) { + return isOnVerticalScrollbarThumb(x, y) || isOnHorizontalScrollbarThumb(x, y); + } + + private boolean isOnVerticalScrollbarThumb(float x, float y) { + if (mScrollCache == null) { + return false; + } + if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) { + x += getScrollX(); + y += getScrollY(); + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + final int range = computeVerticalScrollRange(); + final int offset = computeVerticalScrollOffset(); + final int extent = computeVerticalScrollExtent(); + final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(), + extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength, + extent, range, offset); + final int thumbTop = bounds.top + thumbOffset; + if (x >= bounds.left && x <= bounds.right && y >= thumbTop + && y <= thumbTop + thumbLength) { + return true; + } + } + return false; + } + + private boolean isOnHorizontalScrollbarThumb(float x, float y) { + if (mScrollCache == null) { + return false; + } + if (isHorizontalScrollBarEnabled()) { + x += getScrollX(); + y += getScrollY(); + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + final int range = computeHorizontalScrollRange(); + final int offset = computeHorizontalScrollOffset(); + final int extent = computeHorizontalScrollExtent(); + final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(), + extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength, + extent, range, offset); + final int thumbLeft = bounds.left + thumbOffset; + if (x >= thumbLeft && x <= thumbLeft + thumbLength && y >= bounds.top + && y <= bounds.bottom) { + return true; + } + } + return false; + } + + boolean isDraggingScrollBar() { + return mScrollCache != null + && mScrollCache.mScrollBarDraggingState != ScrollabilityCache.NOT_DRAGGING; + } + /** * Sets the state of all scroll indicators. * <p> @@ -9586,6 +9668,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (onFilterTouchEventForSecurity(event)) { + if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { + result = true; + } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null @@ -10450,6 +10535,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + if ((action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE) + && event.isFromSource(InputDevice.SOURCE_MOUSE) + && isOnScrollbar(event.getX(), event.getY())) { + awakenScrollBars(); + } if (isHoverable()) { switch (action) { case MotionEvent.ACTION_HOVER_ENTER: @@ -10553,6 +10643,110 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Handles scroll bar dragging by mouse input. + * + * @hide + * @param event The motion event. + * + * @return true if the event was handled as a scroll bar dragging, false otherwise. + */ + protected boolean handleScrollBarDragging(MotionEvent event) { + if (mScrollCache == null) { + return false; + } + final float x = event.getX(); + final float y = event.getY(); + final int action = event.getAction(); + if ((mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING + && action != MotionEvent.ACTION_DOWN) + || !event.isFromSource(InputDevice.SOURCE_MOUSE) + || !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { + mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING; + return false; + } + + switch (action) { + case MotionEvent.ACTION_MOVE: + if (mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING) { + return false; + } + if (mScrollCache.mScrollBarDraggingState + == ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + final int range = computeVerticalScrollRange(); + final int offset = computeVerticalScrollOffset(); + final int extent = computeVerticalScrollExtent(); + + final int thumbLength = ScrollBarUtils.getThumbLength( + bounds.height(), bounds.width(), extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset( + bounds.height(), thumbLength, extent, range, offset); + + final float diff = y - mScrollCache.mScrollBarDraggingPos; + final float maxThumbOffset = bounds.height() - thumbLength; + final float newThumbOffset = + Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset); + final int height = getHeight(); + if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0 + && height > 0 && extent > 0) { + final int newY = Math.round((range - extent) + / ((float)extent / height) * (newThumbOffset / maxThumbOffset)); + if (newY != getScrollY()) { + mScrollCache.mScrollBarDraggingPos = y; + setScrollY(newY); + } + } + return true; + } + if (mScrollCache.mScrollBarDraggingState + == ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + final int range = computeHorizontalScrollRange(); + final int offset = computeHorizontalScrollOffset(); + final int extent = computeHorizontalScrollExtent(); + + final int thumbLength = ScrollBarUtils.getThumbLength( + bounds.width(), bounds.height(), extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset( + bounds.width(), thumbLength, extent, range, offset); + + final float diff = x - mScrollCache.mScrollBarDraggingPos; + final float maxThumbOffset = bounds.width() - thumbLength; + final float newThumbOffset = + Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset); + final int width = getWidth(); + if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0 + && width > 0 && extent > 0) { + final int newX = Math.round((range - extent) + / ((float)extent / width) * (newThumbOffset / maxThumbOffset)); + if (newX != getScrollX()) { + mScrollCache.mScrollBarDraggingPos = x; + setScrollX(newX); + } + } + return true; + } + case MotionEvent.ACTION_DOWN: + if (isOnVerticalScrollbarThumb(x, y)) { + mScrollCache.mScrollBarDraggingState = + ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR; + mScrollCache.mScrollBarDraggingPos = y; + return true; + } + if (isOnHorizontalScrollbarThumb(x, y)) { + mScrollCache.mScrollBarDraggingState = + ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR; + mScrollCache.mScrollBarDraggingPos = x; + return true; + } + } + mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING; + return false; + } + + /** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that @@ -10585,7 +10779,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE); } - if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; @@ -14146,6 +14339,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private void getHorizontalScrollBarBounds(Rect bounds) { + final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; + final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() + && !isVerticalScrollBarHidden(); + final int size = getHorizontalScrollbarHeight(); + final int verticalScrollBarGap = drawVerticalScrollBar ? + getVerticalScrollbarWidth() : 0; + final int width = mRight - mLeft; + final int height = mBottom - mTop; + bounds.top = mScrollY + height - size - (mUserPaddingBottom & inside); + bounds.left = mScrollX + (mPaddingLeft & inside); + bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; + bounds.bottom = bounds.top + size; + } + + private void getVerticalScrollBarBounds(Rect bounds) { + final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; + final int size = getVerticalScrollbarWidth(); + int verticalScrollbarPosition = mVerticalScrollbarPosition; + if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) { + verticalScrollbarPosition = isLayoutRtl() ? + SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT; + } + final int width = mRight - mLeft; + final int height = mBottom - mTop; + switch (verticalScrollbarPosition) { + default: + case SCROLLBAR_POSITION_RIGHT: + bounds.left = mScrollX + width - size - (mUserPaddingRight & inside); + break; + case SCROLLBAR_POSITION_LEFT: + bounds.left = mScrollX + (mUserPaddingLeft & inside); + break; + } + bounds.top = mScrollY + (mPaddingTop & inside); + bounds.right = bounds.left + size; + bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside); + } + /** * <p>Request the drawing of the horizontal and the vertical scrollbar. The * scrollbars are painted only if they have been awakened first.</p> @@ -14193,80 +14425,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, cache.scrollBar.mutate().setAlpha(255); } - - final int viewFlags = mViewFlags; - - final boolean drawHorizontalScrollBar = - (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL; - final boolean drawVerticalScrollBar = - (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL - && !isVerticalScrollBarHidden(); + final boolean drawHorizontalScrollBar = isHorizontalScrollBarEnabled(); + final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() + && !isVerticalScrollBarHidden(); if (drawVerticalScrollBar || drawHorizontalScrollBar) { - final int width = mRight - mLeft; - final int height = mBottom - mTop; - final ScrollBarDrawable scrollBar = cache.scrollBar; - final int scrollX = mScrollX; - final int scrollY = mScrollY; - final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; - - int left; - int top; - int right; - int bottom; - if (drawHorizontalScrollBar) { - int size = scrollBar.getSize(false); - if (size <= 0) { - size = cache.scrollBarSize; - } - scrollBar.setParameters(computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); - final int verticalScrollBarGap = drawVerticalScrollBar ? - getVerticalScrollbarWidth() : 0; - top = scrollY + height - size - (mUserPaddingBottom & inside); - left = scrollX + (mPaddingLeft & inside); - right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; - bottom = top + size; - onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom); + final Rect bounds = cache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top, + bounds.right, bounds.bottom); if (invalidate) { - invalidate(left, top, right, bottom); + invalidate(bounds); } } if (drawVerticalScrollBar) { - int size = scrollBar.getSize(true); - if (size <= 0) { - size = cache.scrollBarSize; - } - scrollBar.setParameters(computeVerticalScrollRange(), computeVerticalScrollOffset(), computeVerticalScrollExtent(), true); - int verticalScrollbarPosition = mVerticalScrollbarPosition; - if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) { - verticalScrollbarPosition = isLayoutRtl() ? - SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT; - } - switch (verticalScrollbarPosition) { - default: - case SCROLLBAR_POSITION_RIGHT: - left = scrollX + width - size - (mUserPaddingRight & inside); - break; - case SCROLLBAR_POSITION_LEFT: - left = scrollX + (mUserPaddingLeft & inside); - break; - } - top = scrollY + (mPaddingTop & inside); - right = left + size; - bottom = scrollY + height - (mUserPaddingBottom & inside); - onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom); + final Rect bounds = cache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top, + bounds.right, bounds.bottom); if (invalidate) { - invalidate(left, top, right, bottom); + invalidate(bounds); } } } @@ -21201,6 +21389,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see PointerIcon */ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) { + if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW); + } return mPointerIcon; } @@ -22457,6 +22648,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int mLastColor; + public final Rect mScrollBarBounds = new Rect(); + + public static final int NOT_DRAGGING = 0; + public static final int DRAGGING_VERTICAL_SCROLL_BAR = 1; + public static final int DRAGGING_HORIZONTAL_SCROLL_BAR = 2; + public int mScrollBarDraggingState = NOT_DRAGGING; + + public float mScrollBarDraggingPos = 0; + public ScrollabilityCache(ViewConfiguration configuration, View host) { fadingEdgeLength = configuration.getScaledFadingEdgeLength(); scrollBarSize = configuration.getScaledScrollBarSize(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1c243929fa49..5d02c175794b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -59,6 +59,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; + import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; /** @@ -1717,6 +1718,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public PointerIcon getPointerIcon(MotionEvent event, float x, float y) { + if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW); + } // Check what the child under the pointer says about the pointer. final int childrenCount = mChildrenCount; if (childrenCount != 0) { @@ -2027,7 +2031,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * hover exit event in {@link #onHoverEvent} and then the hovered child will * receive a hover enter event. * </p><p> - * The default implementation always returns false. + * The default implementation handles mouse hover on the scroll bars. * </p> * * @param event The motion event that describes the hover. @@ -2035,6 +2039,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * and prevent its children from receiving it. */ public boolean onInterceptHoverEvent(MotionEvent event) { + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + final int action = event.getAction(); + final float x = event.getX(); + final float y = event.getY(); + if ((action == MotionEvent.ACTION_HOVER_MOVE + || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) { + return true; + } + } return false; } @@ -2781,6 +2794,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * messages will be delivered here. */ public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.isFromSource(InputDevice.SOURCE_MOUSE) + && ev.getAction() == MotionEvent.ACTION_DOWN + && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY) + && isOnScrollbarThumb(ev.getX(), ev.getY())) { + return true; + } return false; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e1ce9fe7c039..bf3dce037e91 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3669,6 +3669,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** @hide */ + @Override + protected boolean handleScrollBarDragging(MotionEvent event) { + // Doesn't support normal scroll bar dragging. Use FastScroller. + return false; + } + @Override public boolean onTouchEvent(MotionEvent ev) { if (!isEnabled()) { diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index ebc7eb3e3646..f16fdd657f84 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -454,6 +454,10 @@ public class HorizontalScrollView extends FrameLayout { return true; } + if (super.onInterceptTouchEvent(ev)) { + return true; + } + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index 91d623216137..8880217d9461 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -16,6 +16,8 @@ package android.widget; +import com.android.internal.widget.ScrollBarUtils; + import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; @@ -135,23 +137,15 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback { } if (drawThumb) { - final int size = vertical ? r.height() : r.width(); + final int scrollBarLength = vertical ? r.height() : r.width(); final int thickness = vertical ? r.width() : r.height(); - final int minLength = thickness * 2; - - // Avoid the tiny thumb. - int length = Math.round((float) size * extent / range); - if (length < minLength) { - length = minLength; - } - - // Avoid the too-big thumb. - int offset = Math.round((float) (size - length) * mOffset / (range - extent)); - if (offset > size - length) { - offset = size - length; - } + final int thumbLength = + ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range); + final int thumbOffset = + ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range, + mOffset); - drawThumb(canvas, r, offset, length, vertical); + drawThumb(canvas, r, thumbOffset, thumbLength, vertical); } } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 78b931d94fd2..3f7a07bbd31a 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -489,6 +489,10 @@ public class ScrollView extends FrameLayout { return true; } + if (super.onInterceptTouchEvent(ev)) { + return true; + } + /* * Don't try to intercept touch if we can't scroll anyway. */ diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java new file mode 100644 index 000000000000..0ae9f74167d5 --- /dev/null +++ b/core/java/com/android/internal/widget/ScrollBarUtils.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.android.internal.widget; + +public class ScrollBarUtils { + + public static int getThumbLength(int size, int thickness, int extent, int range) { + // Avoid the tiny thumb. + final int minLength = thickness * 2; + int length = Math.round((float) size * extent / range); + if (length < minLength) { + length = minLength; + } + return length; + } + + public static int getThumbOffset(int size, int thumbLength, int extent, int range, int offset) { + // Avoid the too-big thumb. + int thumbOffset = Math.round((float) (size - thumbLength) * offset / (range - extent)); + if (thumbOffset > size - thumbLength) { + thumbOffset = size - thumbLength; + } + return thumbOffset; + } +} |