Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d2c6014 authored by Vladislav Kaznacheev's avatar Vladislav Kaznacheev
Browse files

Introduce a minimum scrollbar touch target size

Currently a default scrollbar is fairly hard to grab with
a mouse as it is sometimes as narrow as 8dp.

Introduce a larger touch target configured by
config_minScrollbarTouchTarget

Bug: 35114313
Test: manual
Change-Id: Ib15b24e5a82ea912ec5837d965319b758b4d9b6b
parent b23cd8ec
Loading
Loading
Loading
Loading
+80 −22
Original line number Diff line number Diff line
@@ -5373,16 +5373,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        x += getScrollX();
        y += getScrollY();
        if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) {
            final Rect bounds = mScrollCache.mScrollBarBounds;
            getVerticalScrollBarBounds(bounds);
            if (bounds.contains((int)x, (int)y)) {
            final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
            getVerticalScrollBarBounds(null, touchBounds);
            if (touchBounds.contains((int) x, (int) y)) {
                return true;
            }
        }
        if (isHorizontalScrollBarEnabled()) {
            final Rect bounds = mScrollCache.mScrollBarBounds;
            getHorizontalScrollBarBounds(bounds);
            if (bounds.contains((int)x, (int)y)) {
            final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
            getHorizontalScrollBarBounds(null, touchBounds);
            if (touchBounds.contains((int) x, (int) y)) {
                return true;
            }
        }
@@ -5401,7 +5401,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            x += getScrollX();
            y += getScrollY();
            final Rect bounds = mScrollCache.mScrollBarBounds;
            getVerticalScrollBarBounds(bounds);
            final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
            getVerticalScrollBarBounds(bounds, touchBounds);
            final int range = computeVerticalScrollRange();
            final int offset = computeVerticalScrollOffset();
            final int extent = computeVerticalScrollExtent();
@@ -5410,8 +5411,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            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) {
            final int adjust = Math.max(mScrollCache.scrollBarMinTouchTarget - thumbLength, 0) / 2;
            if (x >= touchBounds.left && x <= touchBounds.right
                    && y >= thumbTop - adjust && y <= thumbTop + thumbLength + adjust) {
                return true;
            }
        }
@@ -5426,7 +5428,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            x += getScrollX();
            y += getScrollY();
            final Rect bounds = mScrollCache.mScrollBarBounds;
            getHorizontalScrollBarBounds(bounds);
            final Rect touchBounds = mScrollCache.mScrollBarTouchBounds;
            getHorizontalScrollBarBounds(bounds, touchBounds);
            final int range = computeHorizontalScrollRange();
            final int offset = computeHorizontalScrollOffset();
            final int extent = computeHorizontalScrollExtent();
@@ -5435,8 +5438,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            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) {
            final int adjust = Math.max(mScrollCache.scrollBarMinTouchTarget - thumbLength, 0) / 2;
            if (x >= thumbLeft - adjust && x <= thumbLeft + thumbLength + adjust
                    && y >= touchBounds.top && y <= touchBounds.bottom) {
                return true;
            }
        }
@@ -11739,7 +11743,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                if (mScrollCache.mScrollBarDraggingState
                        == ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR) {
                    final Rect bounds = mScrollCache.mScrollBarBounds;
                    getVerticalScrollBarBounds(bounds);
                    getVerticalScrollBarBounds(bounds, null);
                    final int range = computeVerticalScrollRange();
                    final int offset = computeVerticalScrollOffset();
                    final int extent = computeVerticalScrollExtent();
@@ -11768,7 +11772,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                if (mScrollCache.mScrollBarDraggingState
                        == ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR) {
                    final Rect bounds = mScrollCache.mScrollBarBounds;
                    getHorizontalScrollBarBounds(bounds);
                    getHorizontalScrollBarBounds(bounds, null);
                    final int range = computeHorizontalScrollRange();
                    final int offset = computeHorizontalScrollOffset();
                    final int extent = computeHorizontalScrollExtent();
@@ -15466,7 +15470,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
    }
    private void getHorizontalScrollBarBounds(Rect bounds) {
    private void getHorizontalScrollBarBounds(@Nullable Rect drawBounds,
            @Nullable Rect touchBounds) {
        final Rect bounds = drawBounds != null ? drawBounds : touchBounds;
        if (bounds == null) {
            return;
        }
        final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
        final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled()
                && !isVerticalScrollBarHidden();
@@ -15479,13 +15488,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        bounds.left = mScrollX + (mPaddingLeft & inside);
        bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap;
        bounds.bottom = bounds.top + size;
        if (touchBounds == null) {
            return;
        }
        if (touchBounds != bounds) {
            touchBounds.set(bounds);
        }
        final int minTouchTarget = mScrollCache.scrollBarMinTouchTarget;
        if (touchBounds.height() < minTouchTarget) {
            final int adjust = (minTouchTarget - touchBounds.height()) / 2;
            touchBounds.bottom = Math.min(touchBounds.bottom + adjust, mScrollY + height);
            touchBounds.top = touchBounds.bottom - minTouchTarget;
        }
        if (touchBounds.width() < minTouchTarget) {
            final int adjust = (minTouchTarget - touchBounds.width()) / 2;
            touchBounds.left -= adjust;
            touchBounds.right = touchBounds.left + minTouchTarget;
        }
    }
    private void getVerticalScrollBarBounds(Rect bounds) {
    private void getVerticalScrollBarBounds(@Nullable Rect bounds, @Nullable Rect touchBounds) {
        if (mRoundScrollbarRenderer == null) {
            getStraightVerticalScrollBarBounds(bounds);
            getStraightVerticalScrollBarBounds(bounds, touchBounds);
        } else {
            getRoundVerticalScrollBarBounds(bounds);
            getRoundVerticalScrollBarBounds(bounds != null ? bounds : touchBounds);
        }
    }
@@ -15500,7 +15527,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        bounds.bottom = mScrollY + height;
    }
    private void getStraightVerticalScrollBarBounds(Rect bounds) {
    private void getStraightVerticalScrollBarBounds(@Nullable Rect drawBounds,
            @Nullable Rect touchBounds) {
        final Rect bounds = drawBounds != null ? drawBounds : touchBounds;
        if (bounds == null) {
            return;
        }
        final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
        final int size = getVerticalScrollbarWidth();
        int verticalScrollbarPosition = mVerticalScrollbarPosition;
@@ -15522,6 +15554,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        bounds.top = mScrollY + (mPaddingTop & inside);
        bounds.right = bounds.left + size;
        bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside);
        if (touchBounds == null) {
            return;
        }
        if (touchBounds != bounds) {
            touchBounds.set(bounds);
        }
        final int minTouchTarget = mScrollCache.scrollBarMinTouchTarget;
        if (touchBounds.width() < minTouchTarget) {
            final int adjust = (minTouchTarget - touchBounds.width()) / 2;
            if (verticalScrollbarPosition == SCROLLBAR_POSITION_RIGHT) {
                touchBounds.right = Math.min(touchBounds.right + adjust, mScrollX + width);
                touchBounds.left = touchBounds.right - minTouchTarget;
            } else {
                touchBounds.left = Math.max(touchBounds.left + adjust, mScrollX);
                touchBounds.right = touchBounds.left + minTouchTarget;
            }
        }
        if (touchBounds.height() < minTouchTarget) {
            final int adjust = (minTouchTarget - touchBounds.height()) / 2;
            touchBounds.top -= adjust;
            touchBounds.bottom = touchBounds.top + minTouchTarget;
        }
    }
    /**
@@ -15580,7 +15635,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            if (mRoundScrollbarRenderer != null) {
                if (drawVerticalScrollBar) {
                    final Rect bounds = cache.mScrollBarBounds;
                    getVerticalScrollBarBounds(bounds);
                    getVerticalScrollBarBounds(bounds, null);
                    mRoundScrollbarRenderer.drawRoundScrollbars(
                            canvas, (float) cache.scrollBar.getAlpha() / 255f, bounds);
                    if (invalidate) {
@@ -15596,7 +15651,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                            computeHorizontalScrollOffset(),
                            computeHorizontalScrollExtent(), false);
                    final Rect bounds = cache.mScrollBarBounds;
                    getHorizontalScrollBarBounds(bounds);
                    getHorizontalScrollBarBounds(bounds, null);
                    onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
                            bounds.right, bounds.bottom);
                    if (invalidate) {
@@ -15609,7 +15664,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                            computeVerticalScrollOffset(),
                            computeVerticalScrollExtent(), true);
                    final Rect bounds = cache.mScrollBarBounds;
                    getVerticalScrollBarBounds(bounds);
                    getVerticalScrollBarBounds(bounds, null);
                    onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top,
                            bounds.right, bounds.bottom);
                    if (invalidate) {
@@ -24101,6 +24156,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        public int scrollBarFadeDuration;
        public int scrollBarSize;
        public int scrollBarMinTouchTarget;
        public ScrollBarDrawable scrollBar;
        public float[] interpolatorValues;
        public View host;
@@ -24129,6 +24185,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        private int mLastColor;
        public final Rect mScrollBarBounds = new Rect();
        public final Rect mScrollBarTouchBounds = new Rect();
        public static final int NOT_DRAGGING = 0;
        public static final int DRAGGING_VERTICAL_SCROLL_BAR = 1;
@@ -24140,6 +24197,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        public ScrollabilityCache(ViewConfiguration configuration, View host) {
            fadingEdgeLength = configuration.getScaledFadingEdgeLength();
            scrollBarSize = configuration.getScaledScrollBarSize();
            scrollBarMinTouchTarget = configuration.getScaledMinScrollbarTouchTarget();
            scrollBarDefaultDelayBeforeFade = ViewConfiguration.getScrollDefaultDelay();
            scrollBarFadeDuration = ViewConfiguration.getScrollBarFadeDuration();
+17 −0
Original line number Diff line number Diff line
@@ -155,6 +155,11 @@ public class ViewConfiguration {
     */
    private static final int TOUCH_SLOP = 8;

    /**
     * Defines the minimum size of the touch target for a scrollbar in dips
     */
    private static final int MIN_SCROLLBAR_TOUCH_TARGET = 48;

    /**
     * Distance the first touch can wander before we stop considering this event a double tap
     * (in dips)
@@ -274,6 +279,7 @@ public class ViewConfiguration {
    private final int mMaximumFlingVelocity;
    private final int mScrollbarSize;
    private final int mTouchSlop;
    private final int mMinScrollbarTouchTarget;
    private final int mDoubleTapTouchSlop;
    private final int mPagingTouchSlop;
    private final int mDoubleTapSlop;
@@ -302,6 +308,7 @@ public class ViewConfiguration {
        mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY;
        mScrollbarSize = SCROLL_BAR_SIZE;
        mTouchSlop = TOUCH_SLOP;
        mMinScrollbarTouchTarget = MIN_SCROLLBAR_TOUCH_TARGET;
        mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
        mPagingTouchSlop = PAGING_TOUCH_SLOP;
        mDoubleTapSlop = DOUBLE_TAP_SLOP;
@@ -386,6 +393,8 @@ public class ViewConfiguration {
                com.android.internal.R.bool.config_ui_enableFadingMarquee);
        mTouchSlop = res.getDimensionPixelSize(
                com.android.internal.R.dimen.config_viewConfigurationTouchSlop);
        mMinScrollbarTouchTarget = res.getDimensionPixelSize(
                com.android.internal.R.dimen.config_minScrollbarTouchTarget);
        mPagingTouchSlop = mTouchSlop * 2;

        mDoubleTapTouchSlop = mTouchSlop;
@@ -439,6 +448,14 @@ public class ViewConfiguration {
        return mScrollbarSize;
    }

    /**
     * @return the minimum size of the scrollbar thumb's touch target in pixels
     * @hide
     */
    public int getScaledMinScrollbarTouchTarget() {
        return mMinScrollbarTouchTarget;
    }

    /**
     * @return Duration of the fade when scrollbars fade away in milliseconds
     */
+3 −0
Original line number Diff line number Diff line
@@ -1847,6 +1847,9 @@
    -->
    <fraction name="config_maximumScreenDimRatio">20%</fraction>

    <!-- Minimum size of the scrollbar thumb's touch target. -->
    <dimen name="config_minScrollbarTouchTarget">48dp</dimen>

    <!-- Base "touch slop" value used by ViewConfiguration as a
         movement threshold where scrolling should begin. -->
    <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
+1 −0
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@

  <java-symbol type="dimen" name="accessibility_touch_slop" />
  <java-symbol type="dimen" name="alert_dialog_round_padding"/>
  <java-symbol type="dimen" name="config_minScrollbarTouchTarget" />
  <java-symbol type="dimen" name="config_prefDialogWidth" />
  <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
  <java-symbol type="dimen" name="config_viewMinFlingVelocity" />