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

Commit afe8e9b6 authored by Raph Levien's avatar Raph Levien
Browse files

Suppress horizontal scrolling with trailing blanks

The existing behavior of EditText is that trailing blanks can cause a
line to exceed the layout width, causing the cursor to extend past the
line, which in turn causes horizontal scrolling. This patch clamps the
cursor to the layout width in the non-scrolling case, which makes the
spaces effectively invisible when they're at the end of the line, but at
least suppresses the scrolling.

The clamping only works reliably in left-to-right alignments, so this
patch checks for than and only enables the clamping in those cases.

Fix for bug 7699295.

Change-Id: I22bc4e6c9ded3d7716edfcf10dd2b5c31a5da9de
parent 412fbe7f
Loading
Loading
Loading
Loading
+48 −9
Original line number Diff line number Diff line
@@ -792,8 +792,17 @@ public abstract class Layout {
     * the paragraph's primary direction.
     */
    public float getPrimaryHorizontal(int offset) {
        return getPrimaryHorizontal(offset, false /* not clamped */);
    }

    /**
     * Get the primary horizontal position for the specified text offset, but
     * optionally clamp it so that it doesn't exceed the width of the layout.
     * @hide
     */
    public float getPrimaryHorizontal(int offset, boolean clamped) {
        boolean trailing = primaryIsTrailingPrevious(offset);
        return getHorizontal(offset, trailing);
        return getHorizontal(offset, trailing, clamped);
    }

    /**
@@ -802,17 +811,26 @@ public abstract class Layout {
     * the direction other than the paragraph's primary direction.
     */
    public float getSecondaryHorizontal(int offset) {
        return getSecondaryHorizontal(offset, false /* not clamped */);
    }

    /**
     * Get the secondary horizontal position for the specified text offset, but
     * optionally clamp it so that it doesn't exceed the width of the layout.
     * @hide
     */
    public float getSecondaryHorizontal(int offset, boolean clamped) {
        boolean trailing = primaryIsTrailingPrevious(offset);
        return getHorizontal(offset, !trailing);
        return getHorizontal(offset, !trailing, clamped);
    }

    private float getHorizontal(int offset, boolean trailing) {
    private float getHorizontal(int offset, boolean trailing, boolean clamped) {
        int line = getLineForOffset(offset);

        return getHorizontal(offset, trailing, line);
        return getHorizontal(offset, trailing, line, clamped);
    }

    private float getHorizontal(int offset, boolean trailing, int line) {
    private float getHorizontal(int offset, boolean trailing, int line, boolean clamped) {
        int start = getLineStart(line);
        int end = getLineEnd(line);
        int dir = getParagraphDirection(line);
@@ -834,6 +852,9 @@ public abstract class Layout {
        float wid = tl.measure(offset - start, trailing, null);
        TextLine.recycle(tl);

        if (clamped && wid > mWidth) {
            wid = mWidth;
        }
        int left = getParagraphLeft(line);
        int right = getParagraphRight(line);

@@ -1256,6 +1277,23 @@ public abstract class Layout {
        return offset;
    }

    /**
     * Determine whether we should clamp cursor position. Currently it's
     * only robust for left-aligned displays.
     * @hide
     */
    public boolean shouldClampCursor(int line) {
        // Only clamp cursor position in left-aligned displays.
        switch (getParagraphAlignment(line)) {
            case ALIGN_LEFT:
                return true;
            case ALIGN_NORMAL:
                return getParagraphDirection(line) > 0;
            default:
                return false;
        }

    }
    /**
     * Fills in the specified Path with a representation of a cursor
     * at the specified offset.  This will often be a vertical line
@@ -1270,8 +1308,9 @@ public abstract class Layout {
        int top = getLineTop(line);
        int bottom = getLineTop(line+1);

        float h1 = getPrimaryHorizontal(point) - 0.5f;
        float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1;
        boolean clamped = shouldClampCursor(line);
        float h1 = getPrimaryHorizontal(point, clamped) - 0.5f;
        float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1;

        int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
                   TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
@@ -1357,8 +1396,8 @@ public abstract class Layout {
                int en = Math.min(end, there);

                if (st != en) {
                    float h1 = getHorizontal(st, false, line);
                    float h2 = getHorizontal(en, true, line);
                    float h1 = getHorizontal(st, false, line, false /* not clamped */);
                    float h2 = getHorizontal(en, true, line, false /* not clamped */);

                    float left = Math.min(h1, h2);
                    float right = Math.max(h1, h2);
+9 −5
Original line number Diff line number Diff line
@@ -1468,20 +1468,24 @@ public class Editor {
            middle = (top + bottom) >> 1;
        }

        updateCursorPosition(0, top, middle, getPrimaryHorizontal(layout, hintLayout, offset));
        boolean clamped = layout.shouldClampCursor(line);
        updateCursorPosition(0, top, middle,
                getPrimaryHorizontal(layout, hintLayout, offset, clamped));

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

    private float getPrimaryHorizontal(Layout layout, Layout hintLayout, int offset) {
    private float getPrimaryHorizontal(Layout layout, Layout hintLayout, int offset,
            boolean clamped) {
        if (TextUtils.isEmpty(layout.getText()) &&
                hintLayout != null &&
                !TextUtils.isEmpty(hintLayout.getText())) {
            return hintLayout.getPrimaryHorizontal(offset);
            return hintLayout.getPrimaryHorizontal(offset, clamped);
        } else {
            return layout.getPrimaryHorizontal(offset);
            return layout.getPrimaryHorizontal(offset, clamped);
        }
    }

+24 −9
Original line number Diff line number Diff line
@@ -6553,15 +6553,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener

        int line = layout.getLineForOffset(offset);

        // FIXME: Is it okay to truncate this, or should we round?
        final int x = (int)layout.getPrimaryHorizontal(offset);
        final int top = layout.getLineTop(line);
        final int bottom = layout.getLineTop(line + 1);

        int left = (int) FloatMath.floor(layout.getLineLeft(line));
        int right = (int) FloatMath.ceil(layout.getLineRight(line));
        int ht = layout.getHeight();

        int grav;

        switch (layout.getParagraphAlignment(line)) {
@@ -6583,8 +6574,32 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                break;
        }

        // We only want to clamp the cursor to fit within the layout width
        // in left-to-right modes, because in a right to left alignment,
        // we want to scroll to keep the line-right on the screen, as other
        // lines are likely to have text flush with the right margin, which
        // we want to keep visible.
        // A better long-term solution would probably be to measure both
        // the full line and a blank-trimmed version, and, for example, use
        // the latter measurement for centering and right alignment, but for
        // the time being we only implement the cursor clamping in left to
        // right where it is most likely to be annoying.
        final boolean clamped = grav > 0;
        // FIXME: Is it okay to truncate this, or should we round?
        final int x = (int)layout.getPrimaryHorizontal(offset, clamped);
        final int top = layout.getLineTop(line);
        final int bottom = layout.getLineTop(line + 1);

        int left = (int) FloatMath.floor(layout.getLineLeft(line));
        int right = (int) FloatMath.ceil(layout.getLineRight(line));
        int ht = layout.getHeight();

        int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
        int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
        if (!mHorizontallyScrolling && right - left > hspace && right > x) {
            // If cursor has been clamped, make sure we don't scroll.
            right = Math.max(x, left + hspace);
        }

        int hslack = (bottom - top) / 2;
        int vslack = hslack;