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

Commit 7f8dcaaa authored by Roozbeh Pournader's avatar Roozbeh Pournader
Browse files

Revert "Make ellipsize retry if text doesn't fit"

This reverts commit 287c8d6f.

Bug: 64156587
Bug: 31537595
Change-Id: Ifbab89df78e79fe01881e9d203f46e656985518c
parent 287c8d6f
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -2047,11 +2047,9 @@ public abstract class Layout {

        final String ellipsisString = TextUtils.getEllipsisString(method);
        final int ellipsisStringLen = ellipsisString.length();
        // Use the ellipsis string only if there are that at least as many characters to replace.
        final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
        for (int i = 0; i < ellipsisCount; i++) {
            final char c;
            if (useEllipsisString && i < ellipsisStringLen) {
            if (i < ellipsisStringLen && ellipsisCount <= ellipsisStringLen) {
                c = ellipsisString.charAt(i);
            } else {
                c = TextUtils.ELLIPSIS_FILLER;
+48 −110
Original line number Diff line number Diff line
@@ -707,7 +707,7 @@ public class StaticLayout extends Layout {
                    // TODO: Support more justification mode, e.g. letter spacing, stretching.
                    b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE);
            if (mLeftIndents != null || mRightIndents != null) {
                // TODO(performance): it would be better to do this once per layout rather
                // TODO(raph) performance: it would be better to do this once per layout rather
                // than once per paragraph, but that would require a change to the native
                // interface.
                int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
@@ -794,7 +794,7 @@ public class StaticLayout extends Layout {
                            width += widths[j];
                        }
                    }
                    flag |= flags[i] & TAB_MASK; // XXX May need to also have starting hyphen edit
                    flag |= flags[i] & TAB_MASK;
                }
                // Treat the last line and overflowed lines as a single line.
                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
@@ -947,25 +947,6 @@ public class StaticLayout extends Layout {
            bottom = fm.bottom;
        }

        // Information about hyphenation, tabs, and directions are needed for determining
        // ellipsization, so the values should be assigned before ellipsization.

        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
        // one bit for start field
        lines[off + TAB] = flags & TAB_MASK;
        lines[off + HYPHEN] = flags;

        lines[off + DIR] |= dir << DIR_SHIFT;
        // easy means all chars < the first RTL, so no emoji, no nothing
        // XXX a run with no text or all spaces is easy but might be an empty
        // RTL paragraph.  Make sure easy is false if this is the case.
        if (easy) {
            mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT;
        } else {
            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
                    start - widthStart, end - start);
        }

        boolean firstLine = (j == 0);
        boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);

@@ -981,8 +962,8 @@ public class StaticLayout extends Layout {
                            ellipsize == TextUtils.TruncateAt.END);
            if (doEllipsis) {
                calculateEllipsis(start, end, widths, widthStart,
                        ellipsisWidth - getTotalInsets(j), ellipsize, j,
                        textWidth, paint, forceEllipsis, dir);
                        ellipsisWidth, ellipsize, j,
                        textWidth, paint, forceEllipsis);
            }
        }

@@ -1021,7 +1002,7 @@ public class StaticLayout extends Layout {
            extra = 0;
        }

        lines[off + START] |= start;
        lines[off + START] = start;
        lines[off + TOP] = v;
        lines[off + DESCENT] = below + extra;

@@ -1038,13 +1019,33 @@ public class StaticLayout extends Layout {
        lines[off + mColumns + START] = end;
        lines[off + mColumns + TOP] = v;

        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
        // one bit for start field
        lines[off + TAB] |= flags & TAB_MASK;
        lines[off + HYPHEN] = flags;

        lines[off + DIR] |= dir << DIR_SHIFT;
        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
        // easy means all chars < the first RTL, so no emoji, no nothing
        // XXX a run with no text or all spaces is easy but might be an empty
        // RTL paragraph.  Make sure easy is false if this is the case.
        if (easy) {
            mLineDirections[j] = linedirs;
        } else {
            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
                    start - widthStart, end - start);
        }

        mLineCount++;
        return v;
    }

    private void calculateEllipsis(int lineStart, int lineEnd, float[] widths, int widthStart,
            float avail, TextUtils.TruncateAt where, int line, float textWidth, TextPaint paint,
            boolean forceEllipsis, int dir) {
    private void calculateEllipsis(int lineStart, int lineEnd,
                                   float[] widths, int widthStart,
                                   float avail, TextUtils.TruncateAt where,
                                   int line, float textWidth, TextPaint paint,
                                   boolean forceEllipsis) {
        avail -= getTotalInsets(line);
        if (textWidth <= avail && !forceEllipsis) {
            // Everything fits!
            mLines[mColumns * line + ELLIPSIS_START] = 0;
@@ -1052,35 +1053,11 @@ public class StaticLayout extends Layout {
            return;
        }

        float tempAvail = avail;
        while (true) {
            float ellipsizedWidth = guessEllipsis(lineStart, lineEnd, widths, widthStart, tempAvail,
                    where, line, textWidth, paint, forceEllipsis, dir);
            if (ellipsizedWidth <= avail) {
                mEllipsized = true;
                return; // The ellipsized line fits.
            } else {
                // Some side effect of ellipsization has caused the text to go over the available
                // width. Let's make the available width shorter by exactly that amount and retry.
                tempAvail -= ellipsizedWidth - avail;
            }
        }
    }

    // Returns the width of the ellipsized line which in some rare cases can actually be larger
    // than 'avail' (due to kerning or other context-based effect of replacement of text by
    // ellipsis). If all the line needs to ellipsized away, or it's an invalud hyphenation mode,
    // returns 0 so the caller can stop iterating.
    private float guessEllipsis(int lineStart, int lineEnd, float[] widths, int widthStart,
            float avail, TextUtils.TruncateAt where, int line, float textWidth, TextPaint paint,
            boolean forceEllipsis, int dir) {
        final float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
        final int ellipsisStart;
        final int ellipsisCount;
        final int len = lineEnd - lineStart;
        final int offset = lineStart - widthStart;
        float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
        int ellipsisStart = 0;
        int ellipsisCount = 0;
        int len = lineEnd - lineStart;

        int hyphen = getHyphen(line);
        // We only support start ellipsis on a single line
        if (where == TextUtils.TruncateAt.START) {
            if (mMaximumVisibleLineCount == 1) {
@@ -1088,9 +1065,9 @@ public class StaticLayout extends Layout {
                int i;

                for (i = len; i > 0; i--) {
                    final float w = widths[i - 1 + offset];
                    float w = widths[i - 1 + lineStart - widthStart];
                    if (w + sum + ellipsisWidth > avail) {
                        while (i < len && widths[i + offset] == 0.0f) {
                        while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
                            i++;
                        }
                        break;
@@ -1101,13 +1078,9 @@ public class StaticLayout extends Layout {

                ellipsisStart = 0;
                ellipsisCount = i;
                // Strip the potential hyphenation at beginning of line.
                hyphen &= ~Paint.HYPHENEDIT_MASK_START_OF_LINE;
            } else {
                ellipsisStart = 0;
                ellipsisCount = 0;
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Start ellipsis only supported with one line");
                    Log.w(TAG, "Start Ellipsis only supported with one line");
                }
            }
        } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
@@ -1116,7 +1089,7 @@ public class StaticLayout extends Layout {
            int i;

            for (i = 0; i < len; i++) {
                final float w = widths[i + offset];
                float w = widths[i + lineStart - widthStart];

                if (w + sum + ellipsisWidth > avail) {
                    break;
@@ -1125,27 +1098,24 @@ public class StaticLayout extends Layout {
                sum += w;
            }

            if (forceEllipsis && i == len && len > 0) {
                ellipsisStart = len - 1;
                ellipsisCount = 1;
            } else {
            ellipsisStart = i;
            ellipsisCount = len - i;
            if (forceEllipsis && ellipsisCount == 0 && len > 0) {
                ellipsisStart = len - 1;
                ellipsisCount = 1;
            }
            // Strip the potential hyphenation at end of line.
            hyphen &= ~Paint.HYPHENEDIT_MASK_END_OF_LINE;
        } else { // where = TextUtils.TruncateAt.MIDDLE
            // We only support middle ellipsis on a single line.
        } else {
            // where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
            if (mMaximumVisibleLineCount == 1) {
                float lsum = 0, rsum = 0;
                int left = 0, right = len;

                final float ravail = (avail - ellipsisWidth) / 2;
                float ravail = (avail - ellipsisWidth) / 2;
                for (right = len; right > 0; right--) {
                    final float w = widths[right - 1 + offset];
                    float w = widths[right - 1 + lineStart - widthStart];

                    if (w + rsum > ravail) {
                        while (right < len && widths[right + offset] == 0.0f) {
                        while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
                            right++;
                        }
                        break;
@@ -1153,9 +1123,9 @@ public class StaticLayout extends Layout {
                    rsum += w;
                }

                final float lavail = avail - ellipsisWidth - rsum;
                float lavail = avail - ellipsisWidth - rsum;
                for (left = 0; left < right; left++) {
                    final float w = widths[left + offset];
                    float w = widths[left + lineStart - widthStart];

                    if (w + lsum > lavail) {
                        break;
@@ -1167,46 +1137,14 @@ public class StaticLayout extends Layout {
                ellipsisStart = left;
                ellipsisCount = right - left;
            } else {
                ellipsisStart = 0;
                ellipsisCount = 0;
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Middle ellipsis only supported with one line");
                    Log.w(TAG, "Middle Ellipsis only supported with one line");
                }
            }
        }
        mEllipsized = true;
        mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
        mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;

        if (ellipsisStart == 0 && (ellipsisCount == 0 || ellipsisCount == len)) {
            // Unsupported ellipsization mode or all text is ellipsized away. Return 0.
            return 0.0f;
        }

        final CharSequence text = getText();
        final boolean hasTabs = getLineContainsTab(line);
        final TabStops tabStops;
        if (hasTabs && text instanceof Spanned) {
            final TabStopSpan[] tabs = getParagraphSpans((Spanned) text, lineStart, lineEnd,
                    TabStopSpan.class);
            if (tabs.length == 0) {
                tabStops = null;
            } else {
                tabStops = new TabStops(TAB_INCREMENT, tabs);
            }
        } else {
            tabStops = null;
        }
        final TextLine textline = TextLine.obtain();
        paint.setHyphenEdit(hyphen);
        textline.set(paint,
                text, /* this is an instance of Ellipsizer or SpannedEllipsizer */
                lineStart, lineEnd,
                dir, getLineDirections(line),
                hasTabs, tabStops);
        final float hyphenatedWidth = textline.metrics(null);
        paint.setHyphenEdit(0);
        TextLine.recycle(textline);
        return hyphenatedWidth;
    }

    private float getTotalInsets(int line) {
+75 −95
Original line number Diff line number Diff line
@@ -88,8 +88,8 @@ public class TextUtils {

    /** {@hide} */
    @NonNull
    public static String getEllipsisString(@NonNull TruncateAt method) {
        return (method == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
    public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
        return (method == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
    }


@@ -1177,11 +1177,9 @@ public class TextUtils {
     * or, if it does not fit, a truncated
     * copy with ellipsis character added at the specified edge or center.
     */
    @NonNull
    public static CharSequence ellipsize(@NonNull CharSequence text,
                                         @NonNull TextPaint p,
                                         @FloatRange(from = 0.0) float avail,
                                         @NonNull TruncateAt where) {
    public static CharSequence ellipsize(CharSequence text,
                                         TextPaint p,
                                         float avail, TruncateAt where) {
        return ellipsize(text, p, avail, where, false, null);
    }

@@ -1197,11 +1195,9 @@ public class TextUtils {
     * report the start and end of the ellipsized range.  TextDirection
     * is determined by the first strong directional character.
     */
    @NonNull
    public static CharSequence ellipsize(@NonNull CharSequence text,
                                         @NonNull TextPaint paint,
                                         @FloatRange(from = 0.0) float avail,
                                         @NonNull TruncateAt where,
    public static CharSequence ellipsize(CharSequence text,
                                         TextPaint paint,
                                         float avail, TruncateAt where,
                                         boolean preserveLength,
                                         @Nullable EllipsizeCallback callback) {
        return ellipsize(text, paint, avail, where, preserveLength, callback,
@@ -1222,19 +1218,16 @@ public class TextUtils {
     *
     * @hide
     */
    @NonNull
    public static CharSequence ellipsize(@NonNull CharSequence text,
            @NonNull TextPaint paint,
            @FloatRange(from = 0.0) float avail,
            @NonNull TruncateAt where,
    public static CharSequence ellipsize(CharSequence text,
            TextPaint paint,
            float avail, TruncateAt where,
            boolean preserveLength,
            @Nullable EllipsizeCallback callback,
            @NonNull TextDirectionHeuristic textDir,
            @NonNull String ellipsis) {
            TextDirectionHeuristic textDir, String ellipsis) {

        int len = text.length();

        final int len = text.length();
        final MeasuredText mt = MeasuredText.obtain();
        MeasuredText resultMt = null;
        MeasuredText mt = MeasuredText.obtain();
        try {
            float width = setPara(mt, paint, text, 0, text.length(), textDir);

@@ -1242,88 +1235,74 @@ public class TextUtils {
                if (callback != null) {
                    callback.ellipsized(0, 0);
                }

                return text;
            }

            resultMt = MeasuredText.obtain();
            // First estimate of effective width of ellipsis.
            float ellipsisWidth = paint.measureText(ellipsis);
            while (true) { // Repeat until the ellipsized text fits.
                final float remainingWidth = avail - ellipsisWidth;
                final int start, end;
                if (remainingWidth < 0) {
                    // Even the ellipsis can't fit. So it all goes.
                    start = 0;
                    end = len;
            // XXX assumes ellipsis string does not require shaping and
            // is unaffected by style
            float ellipsiswid = paint.measureText(ellipsis);
            avail -= ellipsiswid;

            int left = 0;
            int right = len;
            if (avail < 0) {
                // it all goes
            } else if (where == TruncateAt.START) {
                    start = 0;
                    end = len - mt.breakText(len, false /* backwards */, remainingWidth);
                right = len - mt.breakText(len, false, avail);
            } else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
                    start = mt.breakText(len, true /* forwards */, remainingWidth);
                    end = len;
                left = mt.breakText(len, true, avail);
            } else {
                    end = len - mt.breakText(len, false /* backwards */, remainingWidth / 2);
                    start = mt.breakText(end, true /* forwards */,
                            remainingWidth - mt.measure(end, len));
                right = len - mt.breakText(len, false, avail / 2);
                avail -= mt.measure(right, len);
                left = mt.breakText(right, true, avail);
            }

            if (callback != null) {
                callback.ellipsized(left, right);
            }

                final char[] buf = mt.mChars;
                final Spanned sp = text instanceof Spanned ? (Spanned) text : null;
            char[] buf = mt.mChars;
            Spanned sp = text instanceof Spanned ? (Spanned) text : null;

                final CharSequence result;
                final int removed = end - start;
            final int removed = right - left;
            final int remaining = len - removed;
            if (preserveLength) {
                    int pos = start;
                if (remaining > 0 && removed >= ellipsis.length()) {
                        ellipsis.getChars(0, ellipsis.length(), buf, start);
                        pos += ellipsis.length();
                    } // else eliminate the ellipsis
                    while (pos < end) {
                        buf[pos++] = ELLIPSIS_FILLER;
                    ellipsis.getChars(0, ellipsis.length(), buf, left);
                    left += ellipsis.length();
                } // else skip the ellipsis
                for (int i = left; i < right; i++) {
                    buf[i] = ELLIPSIS_FILLER;
                }
                    final String s = new String(buf, 0, len);
                String s = new String(buf, 0, len);
                if (sp == null) {
                        result = s;
                    } else {
                        final SpannableString ss = new SpannableString(s);
                    return s;
                }
                SpannableString ss = new SpannableString(s);
                copySpansFrom(sp, 0, len, Object.class, ss, 0);
                        result = ss;
                return ss;
            }
                } else {

            if (remaining == 0) {
                        result = "";
                    } else if (sp == null) {
                        final StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
                        sb.append(buf, 0, start);
                        sb.append(ellipsis);
                        sb.append(buf, end, len - end);
                        result = sb.toString();
                    } else {
                        final SpannableStringBuilder ssb = new SpannableStringBuilder();
                        ssb.append(text, 0, start);
                        ssb.append(ellipsis);
                        ssb.append(text, end, len);
                        result = ssb;
                    }
                return "";
            }

                width = setPara(resultMt, paint, result, 0, result.length(), textDir);
                if (width <= avail || remaining == 0) { // Text fits or all text is gone.
                    if (callback != null) {
                        callback.ellipsized(start, end);
                    }
                    return result;
                } else {
                    // Adjust the width of the ellipsis by adding the amount 'width' is still over.
                    ellipsisWidth += width - avail;
                }
            if (sp == null) {
                StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
                sb.append(buf, 0, left);
                sb.append(ellipsis);
                sb.append(buf, right, len - right);
                return sb.toString();
            }

            SpannableStringBuilder ssb = new SpannableStringBuilder();
            ssb.append(text, 0, left);
            ssb.append(ellipsis);
            ssb.append(text, right, len);
            return ssb;
        } finally {
            MeasuredText.recycle(mt);
            if (resultMt != null) {
                MeasuredText.recycle(resultMt);
            }
        }
    }

@@ -1354,6 +1333,7 @@ public class TextUtils {
     * @return the formatted CharSequence. If even the shortest sequence (e.g. {@code "A, 11 more"})
     *     doesn't fit, it will return an empty string.
     */

    public static CharSequence listEllipsize(@Nullable Context context,
            @Nullable List<CharSequence> elements, @NonNull String separator,
            @NonNull TextPaint paint, @FloatRange(from=0.0,fromInclusive=false) float avail,