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

Commit c1a79aeb authored by Siyamed Sinir's avatar Siyamed Sinir Committed by android-build-merger
Browse files

Merge "Clamp EditText cursor in the drawable boundaries." into nyc-dev

am: f4aa7d2e

* commit 'f4aa7d2e':
  Clamp EditText cursor in the drawable boundaries.
parents 80ede362 f4aa7d2e
Loading
Loading
Loading
Loading
+71 −12
Original line number Diff line number Diff line
@@ -1851,8 +1851,7 @@ public class Editor {
        updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped));

        if (mCursorCount == 2) {
            updateCursorPosition(1, middle, bottom,
                    layout.getSecondaryHorizontal(offset, clamped));
            updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped));
        }
    }

@@ -2151,18 +2150,57 @@ public class Editor {
        return mSelectionModifierCursorController;
    }

    /**
     * @hide
     */
    @VisibleForTesting
    public Drawable[] getCursorDrawable() {
        return mCursorDrawable;
    }

    private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
        if (mCursorDrawable[cursorIndex] == null)
            mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable(
                    mTextView.mCursorDrawableRes);
        final Drawable drawable = mCursorDrawable[cursorIndex];
        final int left = clampCursorHorizontalPosition(drawable, horizontal);
        final int width = drawable.getIntrinsicWidth();
        drawable.setBounds(left, top - mTempRect.top, left + width,
                bottom + mTempRect.bottom);
    }

        if (mTempRect == null) mTempRect = new Rect();
        mCursorDrawable[cursorIndex].getPadding(mTempRect);
        final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
    /**
     * Return clamped position for the cursor. If the cursor is within the boundaries of the view,
     * then it is offset with the left padding of the cursor drawable. If the cursor is at
     * the beginning or the end of the text then its drawable edge is aligned with left or right of
     * the view boundary.
     *
     * @param drawable   Cursor drawable.
     * @param horizontal Horizontal position for the cursor.
     * @return The clamped horizontal position for the cursor.
     */
    private final int clampCursorHorizontalPosition(final Drawable drawable, float
            horizontal) {
        horizontal = Math.max(0.5f, horizontal - 0.5f);
        final int left = (int) (horizontal) - mTempRect.left;
        mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
                bottom + mTempRect.bottom);
        if (mTempRect == null) mTempRect = new Rect();
        drawable.getPadding(mTempRect);
        int scrollX = mTextView.getScrollX();
        float horizontalDiff = horizontal - scrollX;
        int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft()
                - mTextView.getCompoundPaddingRight();

        final int left;
        if (horizontalDiff >= (viewClippedWidth - 1f)) {
            // at the rightmost position
            final int cursorWidth = drawable.getIntrinsicWidth();
            left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right);
        } else if (Math.abs(horizontalDiff) <= 1f) {
            // at the leftmost position
            left = scrollX - mTempRect.left;
        } else {
            left = (int) horizontal - mTempRect.left;
        }
        return left;
    }

    /**
@@ -3919,8 +3957,8 @@ public class Editor {
            final Layout layout = mTextView.getLayout();
            if (layout != null && oldDrawable != mDrawable && isShowing()) {
                // Update popup window position.
                mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
                        getHorizontalOffset() + getCursorOffset());
                mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
                        getHorizontalOffset() + getCursorOffset();
                mPositionX += mTextView.viewportToContentHorizontalOffset();
                mPositionHasChanged = true;
                updatePosition(mLastParentX, mLastParentY, false, false);
@@ -4049,8 +4087,8 @@ public class Editor {
                final int line = layout.getLineForOffset(offset);
                mPrevLine = line;

                mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
                        getHorizontalOffset() + getCursorOffset());
                mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
                        getHorizontalOffset() + getCursorOffset();
                mPositionY = layout.getLineBottom(line);

                // Take TextView's padding and scroll into account.
@@ -4062,6 +4100,17 @@ public class Editor {
            }
        }

        /**
         * Return the clamped horizontal position for the first cursor.
         *
         * @param layout Text layout.
         * @param offset Character offset for the cursor.
         * @return The clamped horizontal position for the cursor.
         */
        int getCursorHorizontalPosition(Layout layout, int offset) {
            return (int) (layout.getPrimaryHorizontal(offset) - 0.5f);
        }

        public void updatePosition(int parentPositionX, int parentPositionY,
                boolean parentPositionChanged, boolean parentScrolled) {
            positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled);
@@ -4299,6 +4348,16 @@ public class Editor {
            return offset;
        }

        @Override
        int getCursorHorizontalPosition(Layout layout, int offset) {
            final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null;
            if (drawable != null) {
                final float horizontal = layout.getPrimaryHorizontal(offset);
                return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left;
            }
            return super.getCursorHorizontalPosition(layout, offset);
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            final boolean result = super.onTouchEvent(ev);
+2 −8
Original line number Diff line number Diff line
@@ -5460,15 +5460,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return (int) Math.max(0, mShadowDy + mShadowRadius);
    }

    private int getFudgedPaddingRight() {
        // Add sufficient space for cursor and tone marks
        int cursorWidth = 2 + (int)mTextPaint.density; // adequate for Material cursors
        return Math.max(0, getCompoundPaddingRight() - (cursorWidth - 1));
    }

    @Override
    protected int getRightPaddingOffset() {
        return -(getFudgedPaddingRight() - mPaddingRight) +
        return -(getCompoundPaddingRight() - mPaddingRight) +
                (int) Math.max(0, mShadowDx + mShadowRadius);
    }

@@ -5813,7 +5807,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        float clipLeft = compoundPaddingLeft + scrollX;
        float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
        float clipRight = right - left - getFudgedPaddingRight() + scrollX;
        float clipRight = right - left - getCompoundPaddingRight() + scrollX;
        float clipBottom = bottom - top + scrollY -
                ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);

+164 −0
Original line number Diff line number Diff line
/*
 * 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 android.widget;

import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.ViewGroup;

public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> {

    private EditText mEditText;
    private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";

    public EditorCursorTest() {
        super(TextViewActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mEditText = new EditText(getActivity());
        mEditText.setTextSize(30);
        mEditText.setSingleLine(true);
        mEditText.setLines(1);
        mEditText.setPadding(15, 15, 15, 15);
        ViewGroup.LayoutParams editTextLayoutParams = new ViewGroup.LayoutParams(200,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        mEditText.setLayoutParams(editTextLayoutParams);

        final FrameLayout layout = new FrameLayout(getActivity());
        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        layout.setLayoutParams(layoutParams);
        layout.addView(mEditText);

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                getActivity().setContentView(layout);
                mEditText.requestFocus();
            }
        });
        getInstrumentation().waitForIdleSync();
    }

    @SmallTest
    public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception {
        // Asserts that when an EditText has LTR text, and cursor is at the end (right),
        // cursor is drawn to the right edge of the view
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
                int length = mEditText.getText().length();
                mEditText.setSelection(length, length);
            }
        });
        getInstrumentation().waitForIdleSync();

        Editor editor = mEditText.getEditorForTesting();
        Drawable drawable = editor.getCursorDrawable()[0];
        Rect drawableBounds = drawable.getBounds();
        Rect drawablePadding = new Rect();
        drawable.getPadding(drawablePadding);

        // right edge of the view including the scroll
        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
                - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX();
        int diff = drawableBounds.right - drawablePadding.right - maxRight;
        assertTrue(diff >= 0 && diff <= 1);
    }

    @SmallTest
    public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception {
        // Asserts that when an EditText has LTR text, and cursor is at the beginning,
        // cursor is drawn to the left edge of the view

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
                mEditText.setSelection(0, 0);
            }
        });
        getInstrumentation().waitForIdleSync();

        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
        Rect drawableBounds = drawable.getBounds();
        Rect drawablePadding = new Rect();
        drawable.getPadding(drawablePadding);

        int diff = drawableBounds.left + drawablePadding.left;
        assertTrue(diff >= 0 && diff <= 1);
    }

    @SmallTest
    public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception {
        // Asserts that when an EditText has RTL text, and cursor is at the end,
        // cursor is drawn to the left edge of the view

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mEditText.setText(RTL_STRING);
                mEditText.setSelection(0, 0);
            }
        });
        getInstrumentation().waitForIdleSync();

        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
        Rect drawableBounds = drawable.getBounds();
        Rect drawablePadding = new Rect();
        drawable.getPadding(drawablePadding);

        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
                - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX();

        int diff = drawableBounds.right - drawablePadding.right - maxRight;
        assertTrue(diff >= 0 && diff <= 1);
    }

    @SmallTest
    public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception {
        // Asserts that when an EditText has RTL text, and cursor is at the beginning,
        // cursor is drawn to the right edge of the view

        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mEditText.setText(RTL_STRING);
                int length = mEditText.getText().length();
                mEditText.setSelection(length, length);
            }
        });
        getInstrumentation().waitForIdleSync();

        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
        Rect drawableBounds = drawable.getBounds();
        Rect drawablePadding = new Rect();
        drawable.getPadding(drawablePadding);

        int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left;
        assertTrue(diff >= 0 && diff <= 1);
    }

}