diff options
| -rw-r--r-- | core/java/android/text/Layout.java | 36 | ||||
| -rw-r--r-- | core/tests/coretests/assets/fonts/1em_bidi_font.ttf | bin | 0 -> 2076 bytes | |||
| -rw-r--r-- | core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java | 166 |
3 files changed, 179 insertions, 23 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 0808cdd6aedb..3ab8a0a8885f 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1806,6 +1806,7 @@ public abstract class Layout { } } + /** * Fills in the specified Path with a representation of a cursor * at the specified offset. This will often be a vertical line @@ -1821,7 +1822,6 @@ public abstract class Layout { 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); @@ -1839,34 +1839,24 @@ public abstract class Layout { if (h1 < 0.5f) h1 = 0.5f; - if (h2 < 0.5f) - h2 = 0.5f; - if (Float.compare(h1, h2) == 0) { - dest.moveTo(h1, top); - dest.lineTo(h1, bottom); - } else { - dest.moveTo(h1, top); - dest.lineTo(h1, (top + bottom) >> 1); - - dest.moveTo(h2, (top + bottom) >> 1); - dest.lineTo(h2, bottom); - } + dest.moveTo(h1, top); + dest.lineTo(h1, bottom); if (caps == 2) { - dest.moveTo(h2, bottom); - dest.lineTo(h2 - dist, bottom + dist); - dest.lineTo(h2, bottom); - dest.lineTo(h2 + dist, bottom + dist); + dest.moveTo(h1, bottom); + dest.lineTo(h1 - dist, bottom + dist); + dest.lineTo(h1, bottom); + dest.lineTo(h1 + dist, bottom + dist); } else if (caps == 1) { - dest.moveTo(h2, bottom); - dest.lineTo(h2 - dist, bottom + dist); + dest.moveTo(h1, bottom); + dest.lineTo(h1 - dist, bottom + dist); - dest.moveTo(h2 - dist, bottom + dist - 0.5f); - dest.lineTo(h2 + dist, bottom + dist - 0.5f); + dest.moveTo(h1 - dist, bottom + dist - 0.5f); + dest.lineTo(h1 + dist, bottom + dist - 0.5f); - dest.moveTo(h2 + dist, bottom + dist); - dest.lineTo(h2, bottom); + dest.moveTo(h1 + dist, bottom + dist); + dest.lineTo(h1, bottom); } if (fn == 2) { diff --git a/core/tests/coretests/assets/fonts/1em_bidi_font.ttf b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf Binary files differnew file mode 100644 index 000000000000..459925433349 --- /dev/null +++ b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java new file mode 100644 index 000000000000..1208d7ca194a --- /dev/null +++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 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.text; + +import static org.junit.Assert.assertArrayEquals; + +import android.content.Context; +import android.graphics.Path; +import android.graphics.Typeface; +import android.platform.test.annotations.Presubmit; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.text.method.MetaKeyKeyListener; +import android.view.KeyEvent; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LayoutBidiCursorPathTest { + + private static final float BIDI_TEXT_SIZE = 12f; + private static final String LTR_TEXT = "hello"; + private static final String RTL_TEXT = "مرحبا"; + + private SpannableStringBuilder mBidiText; + private TextPaint mTextPaint; + + @Before + public void setup() { + mBidiText = new SpannableStringBuilder(LTR_TEXT + RTL_TEXT); + + final Context context = InstrumentationRegistry.getTargetContext(); + mTextPaint = new TextPaint(); + mTextPaint.setTypeface( + Typeface.createFromAsset(context.getAssets(), "fonts/1em_bidi_font.ttf")); + mTextPaint.setTextSize(BIDI_TEXT_SIZE); + } + + @Test + public void testGetCursorPathSegments() { + // Setup layout and Act. + final Path actualPath = new Path(); + setupLayoutAndGetCursorPath(actualPath); + + // Expected path. + final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f; + final int top = 0; + // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here. + final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f); + + final Path expectedPath = new Path(); + + expectedPath.moveTo(h1, top); + expectedPath.lineTo(h1, bottom); + + // Assert. + assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f); + } + + @Test + public void testGetCursorPath_whenShiftIsPressed() { + // When shift is pressed a triangle is drawn at the bottom quarter of the cursor. + // Set up key. + final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {}; + metaKeyKeyListener + .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_SHIFT_RIGHT, null /*keyEvent*/); + + // Setup layout and Act. + final Path actualPath = new Path(); + setupLayoutAndGetCursorPath(actualPath); + + // Expected path. + final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f; + final int top = 0; + // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here. + int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f); + // Draw a triangle at the bottom quarter of the cursor, thus cut the cursor to its 3/4 + // length. + final int dist = (bottom - top) / 4; + bottom -= dist; + + final Path expectedPath = new Path(); + + expectedPath.moveTo(h1, top); + expectedPath.lineTo(h1, bottom); + + expectedPath.moveTo(h1, bottom); + expectedPath.lineTo(h1 - dist, bottom + dist); + + expectedPath.moveTo(h1 - dist, bottom + dist - 0.5f); + expectedPath.lineTo(h1 + dist, bottom + dist - 0.5f); + + expectedPath.moveTo(h1 + dist, bottom + dist); + expectedPath.lineTo(h1, bottom); + + // Assert. + assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f); + } + + @Test + public void testGetCursorPath_whenAltIsPressed() { + // When alt is pressed a triangle is drawn at the top quarter of the cursor. + // Set up key. + final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {}; + metaKeyKeyListener + .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_ALT_RIGHT, null /*keyEvent*/); + + // Setup layout and Act. + final Path actualPath = new Path(); + setupLayoutAndGetCursorPath(actualPath); + + // Expected path. + final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f; + int top = 0; + // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here. + final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f); + // Draw a triangle at the top quarter of the cursor, thus cut the cursor to its 3/4 length. + final int dist = (bottom - top) / 4; + top += dist; + + final Path expectedPath = new Path(); + + expectedPath.moveTo(h1, top); + expectedPath.lineTo(h1, bottom); + + expectedPath.moveTo(h1, top); + expectedPath.lineTo(h1 - dist, top - dist); + + expectedPath.moveTo(h1 - dist, top - dist + 0.5f); + expectedPath.lineTo(h1 + dist, top - dist + 0.5f); + + expectedPath.moveTo(h1 + dist, top - dist); + expectedPath.lineTo(h1, top); + + // Assert. + assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f); + } + + private void setupLayoutAndGetCursorPath(Path path) { + final Layout layout = StaticLayout.Builder.obtain( + mBidiText, 0, mBidiText.length(), mTextPaint, Integer.MAX_VALUE) + .setIncludePad(false) + .build(); + + layout.getCursorPath(LTR_TEXT.length(), path, mBidiText); + } +} |