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

Commit 0fa89d6f authored by Siyamed Sinir's avatar Siyamed Sinir
Browse files

Store line extra in layouts

StaticLayout and Dynamic layout changes the values of line bottom and
descent according to linespacing modification attributes. This loses
information of the real line descent and bottom, which causes some
utilities not to be able to draw themselves in correct positions.
Examples are cursor drawing to the empty space, BulletSpan not being
able to vertically center itself in the line, and DynamicLayout not
being able to calculate the last line height properly.

This CL stores the extra added to bottom and descent values so that it
can be used later.

Test: bit CtsTextTestCases:android.text.cts.LayoutTest
Test: bit CtsTextTestCases:android.text.cts.StaticLayoutTest
Test: bit CtsTextTestCases:android.text.cts.DynamicLayoutTest
Test: bit FrameworksCoreTests:android.text.LayoutTest
Test: bit FrameworksCoreTests:android.text.StaticLayoutTest
Test: bit FrameworksCoreTests:android.text.DynamicLayoutTest
Test: bit FrameworksCoreTests:android.text.DynamicLayoutBlocksTest

Bug: 30870806
Bug: 33138492
Bug: 25194907
Change-Id: I7a1039e71e4999c75b5f26122fe6239e3ee24868
parent 62944851
Loading
Loading
Loading
Loading
+15 −5
Original line number Diff line number Diff line
@@ -365,6 +365,7 @@ public class DynamicLayout extends Layout
                desc += botpad;

            ints[DESCENT] = desc;
            ints[EXTRA] = reflowed.getLineExtra(i);
            objects[0] = reflowed.getLineDirections(i);

            final int end = (i == n - 1) ? where + after : reflowed.getLineStart(i + 1);
@@ -692,6 +693,14 @@ public class DynamicLayout extends Layout
        return mInts.getValue(line, DESCENT);
    }

    /**
     * @hide
     */
    @Override
    public int getLineExtra(int line) {
        return mInts.getValue(line, EXTRA);
    }

    @Override
    public int getLineStart(int line) {
        return mInts.getValue(line, START) & START_MASK;
@@ -851,14 +860,15 @@ public class DynamicLayout extends Layout
    private static final int TAB = START;
    private static final int TOP = 1;
    private static final int DESCENT = 2;
    private static final int EXTRA = 3;
    // HYPHEN and MAY_PROTRUDE_FROM_TOP_OR_BOTTOM share the same entry.
    private static final int HYPHEN = 3;
    private static final int HYPHEN = 4;
    private static final int MAY_PROTRUDE_FROM_TOP_OR_BOTTOM = HYPHEN;
    private static final int COLUMNS_NORMAL = 4;
    private static final int COLUMNS_NORMAL = 5;

    private static final int ELLIPSIS_START = 4;
    private static final int ELLIPSIS_COUNT = 5;
    private static final int COLUMNS_ELLIPSIZE = 6;
    private static final int ELLIPSIS_START = 5;
    private static final int ELLIPSIS_COUNT = 6;
    private static final int COLUMNS_ELLIPSIZE = 7;

    private static final int START_MASK = 0x1FFFFFFF;
    private static final int DIR_SHIFT  = 30;
+12 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.text;

import android.annotation.IntDef;
import android.annotation.IntRange;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
@@ -1466,6 +1467,17 @@ public abstract class Layout {
        return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
    }

    /**
     * Return the extra space added as a result of line spacing attributes
     * {@link #getSpacingAdd()} and {@link #getSpacingMultiplier()}. Default value is {@code zero}.
     *
     * @param line the index of the line, the value should be equal or greater than {@code zero}
     * @hide
     */
    public int getLineExtra(@IntRange(from = 0) int line) {
        return 0;
    }

    public int getOffsetToLeftOf(int offset) {
        return getOffsetToLeftRightOf(offset, true);
    }
+18 −5
Original line number Diff line number Diff line
@@ -1005,6 +1005,7 @@ public class StaticLayout extends Layout {
        lines[off + START] = start;
        lines[off + TOP] = v;
        lines[off + DESCENT] = below + extra;
        lines[off + EXTRA] = extra;

        // special case for non-ellipsized last visible line when maxLines is set
        // store the height as if it was ellipsized
@@ -1195,6 +1196,14 @@ public class StaticLayout extends Layout {
        return mLines[mColumns * line + TOP];
    }

    /**
     * @hide
     */
    @Override
    public int getLineExtra(int line) {
        return mLines[mColumns * line + EXTRA];
    }

    @Override
    public int getLineDescent(int line) {
        return mLines[mColumns * line + DESCENT];
@@ -1217,6 +1226,9 @@ public class StaticLayout extends Layout {

    @Override
    public final Directions getLineDirections(int line) {
        if (line > getLineCount()) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return mLineDirections[line];
    }

@@ -1368,16 +1380,17 @@ public class StaticLayout extends Layout {
     */
    private int mMaxLineHeight = -1;

    private static final int COLUMNS_NORMAL = 4;
    private static final int COLUMNS_ELLIPSIZE = 6;
    private static final int COLUMNS_NORMAL = 5;
    private static final int COLUMNS_ELLIPSIZE = 7;
    private static final int START = 0;
    private static final int DIR = START;
    private static final int TAB = START;
    private static final int TOP = 1;
    private static final int DESCENT = 2;
    private static final int HYPHEN = 3;
    private static final int ELLIPSIS_START = 4;
    private static final int ELLIPSIS_COUNT = 5;
    private static final int EXTRA = 3;
    private static final int HYPHEN = 4;
    private static final int ELLIPSIS_START = 5;
    private static final int ELLIPSIS_COUNT = 6;

    private int[] mLines;
    private Directions[] mLineDirections;
+74 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.text;

import static android.text.Layout.Alignment.ALIGN_NORMAL;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -112,4 +113,77 @@ public class DynamicLayoutTest {
        assertFalse(layout.getBlocksAlwaysNeedToBeRedrawn().contains(0));
        assertTrue(layout.getBlocksAlwaysNeedToBeRedrawn().isEmpty());
    }

    @Test
    public void testGetLineExtra_withoutLinespacing() {
        final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
        final TextPaint textPaint = new TextPaint();

        // create a StaticLayout to check against
        final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0,
                text.length(), textPaint, WIDTH)
                .setAlignment(ALIGN_NORMAL)
                .setIncludePad(false)
                .build();

        // create the DynamicLayout
        final DynamicLayout dynamicLayout = new DynamicLayout(text,
                textPaint,
                WIDTH,
                ALIGN_NORMAL,
                1f /*spacingMultiplier*/,
                0 /*spacingAdd*/,
                false /*includepad*/);

        final int lineCount = staticLayout.getLineCount();
        assertEquals(lineCount, dynamicLayout.getLineCount());
        for (int i = 0; i < lineCount; i++) {
            assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i));
        }
    }

    @Test
    public void testGetLineExtra_withLinespacing() {
        final SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
        final TextPaint textPaint = new TextPaint();
        final float spacingMultiplier = 2f;
        final float spacingAdd = 4;

        // create a StaticLayout to check against
        final StaticLayout staticLayout = StaticLayout.Builder.obtain(text, 0,
                text.length(), textPaint, WIDTH)
                .setAlignment(ALIGN_NORMAL)
                .setIncludePad(false)
                .setLineSpacing(spacingAdd, spacingMultiplier)
                .build();

        // create the DynamicLayout
        final DynamicLayout dynamicLayout = new DynamicLayout(text,
                textPaint,
                WIDTH,
                ALIGN_NORMAL,
                spacingMultiplier,
                spacingAdd,
                false /*includepad*/);

        final int lineCount = staticLayout.getLineCount();
        assertEquals(lineCount, dynamicLayout.getLineCount());
        for (int i = 0; i < lineCount - 1; i++) {
            assertEquals(staticLayout.getLineExtra(i), dynamicLayout.getLineExtra(i));
        }
    }

    @Test(expected = IndexOutOfBoundsException.class)
    public void testGetLineExtra_withNegativeValue() {
        final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/,
                ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
        layout.getLineExtra(-1);
    }

    @Test(expected = IndexOutOfBoundsException.class)
    public void testGetLineExtra_withParamGreaterThanLineCount() {
        final DynamicLayout layout = new DynamicLayout("", new TextPaint(), 10 /*width*/,
                ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
        layout.getLineExtra(100);
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -214,6 +214,17 @@ public class LayoutTest {
        assertEquals(2, layout.getLineEnd(1));
    }

    @Test
    public void testGetLineExtra_returnsZeroByDefault() {
        final String text = "a\nb\nc\n";
        final Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
                mAlign, 100 /* spacingMult*/, 100 /*spacingAdd*/);
        final int lineCount = text.split("\n").length;
        for (int i = 0; i < lineCount; i++) {
            assertEquals(0, layout.getLineExtra(i));
        }
    }

    @Test
    public void testGetLineVisibleEnd() {
        Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
Loading