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

Commit 19799c44 authored by qqd's avatar qqd
Browse files

Disable double cursor for BiDi in Layout.

-- Disabled double cursor for BiDi in Layout.getCursorPath.
-- Add tests in LayoutTest.

Bug: 112875662
Test: Manual (no more double cursors, bidi editing feels OK)
Test: bit FrameworksCoreTests:android.text.LayoutTest
Change-Id: I3d201734d12f59724fa627b06ba9ca8c307c27fb
parent d8c16347
Loading
Loading
Loading
Loading
+13 −23
Original line number Diff line number Diff line
@@ -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);
        }

        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) {
+2.03 KiB

File added.

No diff preview for this file type.

+166 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}