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

Commit aab21af2 authored by Steve Kondik's avatar Steve Kondik Committed by Gerrit Code Review
Browse files

Merge "Cursor movement makeover" into gingerbread

parents 13614910 7573c883
Loading
Loading
Loading
Loading
+234 −166
Original line number Diff line number Diff line
@@ -886,103 +886,118 @@ public abstract class Layout {
        return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
    }

    /**
     * Return the text offset that would be reached by moving left
     * (possibly onto another line) from the specified offset.
    /* This class is a helper for moving the cursor,
     * therefore it actually finds the directional run for
     * the character before the given cursor offset.
     */
    public int getOffsetToLeftOf(int offset) {
        int line = getLineForOffset(offset);
        int start = getLineStart(line);
        int end = getLineEnd(line);
        Directions dirs = getLineDirections(line);

        if (line != getLineCount() - 1)
            end = TextUtils.getOffsetBefore(mText, end);

        float horiz = getPrimaryHorizontal(offset);

        int best = offset;
        float besth = Integer.MIN_VALUE;
        int candidate;

        candidate = TextUtils.getOffsetBefore(mText, offset);
        if (candidate >= start && candidate <= end) {
            float h = getPrimaryHorizontal(candidate);

            if (h < horiz && h > besth) {
                best = candidate;
                besth = h;
    private class DirectionalRunFinder {
        public int line;
        public int lineDir;
        public int runDir;
        public int runStart;
        public int runEnd;
        public int runLength;
        public boolean runDegenerate;
        public int lineStart;
        public int lineEnd;
        public Directions lineDirs;

        public DirectionalRunFinder(int offset) {
            find(offset);
        }

        private void initLine(int newLine) {
            line = newLine;
            lineStart = getLineStart(line);
            lineEnd = getLineEnd(line);
            lineDirs = getLineDirections(line);
            lineDir = getParagraphDirection(line);
        }

        public void find(int offset) {
            initLine(getLineForOffset(offset));
            runDir = lineDir;
            int runIndex = 0;
            runStart = runEnd = lineStart;
            if (offset == runStart) {
                return;
            }
            for (; runIndex < lineDirs.mDirections.length; ++runIndex) {
                int length = lineDirs.mDirections[runIndex];
                if (length <= 0)
                    continue;
                runStart = runEnd;
                runEnd = runStart + length;
                runLength = length;
                if (offset <= runEnd) {
                    runDir = (runIndex & 1) != 0 ? DIR_RIGHT_TO_LEFT : DIR_LEFT_TO_RIGHT;
                    break;
                }

        candidate = TextUtils.getOffsetAfter(mText, offset);
        if (candidate >= start && candidate <= end) {
            float h = getPrimaryHorizontal(candidate);

            if (h < horiz && h > besth) {
                best = candidate;
                besth = h;
            }
            if (runEnd > lineEnd) {
                runEnd = lineEnd;
                runLength = runEnd - runStart;
            }

        int here = start;
        for (int i = 0; i < dirs.mDirections.length; i++) {
            int there = here + dirs.mDirections[i];
            if (there > end)
                there = end;

            float h = getPrimaryHorizontal(here);

            if (h < horiz && h > besth) {
                best = here;
                besth = h;
            runDegenerate = TextUtils.getOffsetAfter(mText,runStart) >= runEnd;
        }

            candidate = TextUtils.getOffsetAfter(mText, here);
            if (candidate >= start && candidate <= end) {
                h = getPrimaryHorizontal(candidate);

                if (h < horiz && h > besth) {
                    best = candidate;
                    besth = h;
        public void findFirstRun(int newLine) {
            initLine(newLine);
            runDir = lineDir;
            int runIndex = 0;
            runStart = runEnd = lineStart;
            for(; runIndex < lineDirs.mDirections.length && lineDirs.mDirections[runIndex]==0; ++runIndex);
            if (runIndex < lineDirs.mDirections.length) {
                runDir = (runIndex & 1) != 0 ? DIR_RIGHT_TO_LEFT : DIR_LEFT_TO_RIGHT;
                runEnd = Math.min(runStart + lineDirs.mDirections[runIndex],lineEnd);
            }
            runLength = runEnd - runStart;
            runDegenerate = TextUtils.getOffsetAfter(mText,runStart) >= runEnd;
        }

            candidate = TextUtils.getOffsetBefore(mText, there);
            if (candidate >= start && candidate <= end) {
                h = getPrimaryHorizontal(candidate);

                if (h < horiz && h > besth) {
                    best = candidate;
                    besth = h;
        public void findLastRun(int newLine) {
            initLine(newLine);
            runDir = lineDir;
            int runIndex = 0, lastRunIndex = -1;
            runStart = runEnd = lineStart;
            for(; runIndex < lineDirs.mDirections.length; ++runIndex) {
                int length = lineDirs.mDirections[runIndex];
                if (length <= 0)
                    continue;
                runStart = runEnd;
                runEnd = runStart + length;
                runLength = length;
                lastRunIndex = runIndex;
            }
            if (runEnd > lineEnd) {
                runEnd = lineEnd;
                runLength = runEnd - runStart;
            }

            here = there;
            runDegenerate = TextUtils.getOffsetAfter(mText,runStart) >= runEnd;
            if (lastRunIndex >= 0)
                runDir = (lastRunIndex & 1) != 0 ? DIR_RIGHT_TO_LEFT : DIR_LEFT_TO_RIGHT;
        }

        float h = getPrimaryHorizontal(end);

        if (h < horiz && h > besth) {
            best = end;
            besth = h;
        public boolean containedInRun(int offset, boolean includeEdges) {
            if (includeEdges)
                return runStart <= offset && offset <= runEnd;
            else
                return runStart < offset && offset < runEnd;
        }

        if (best != offset)
            return best;

        int dir = getParagraphDirection(line);
        public int nextNonEmptyLine(int line) {
            int lastLine = getLineCount()-1;
            if (line < lastLine)
                do { ++line; }
                while (line < lastLine && getLineStart(line)==getLineEnd(line));
            return line;
        }

        if (dir > 0) {
            if (line == 0)
                return best;
            else
                return getOffsetForHorizontal(line - 1, 10000);
        } else {
            if (line == getLineCount() - 1)
                return best;
            else
                return getOffsetForHorizontal(line + 1, 10000);
        public int prevNonEmptyLine(int line) {
            if (line > 0)
                do { --line; }
                while (line > 0 && getLineStart(line)==getLineEnd(line));
            return line;
        }
    }

@@ -991,101 +1006,154 @@ public abstract class Layout {
     * (possibly onto another line) from the specified offset.
     */
    public int getOffsetToRightOf(int offset) {
        int line = getLineForOffset(offset);
        int start = getLineStart(line);
        int end = getLineEnd(line);
        Directions dirs = getLineDirections(line);

        if (line != getLineCount() - 1)
            end = TextUtils.getOffsetBefore(mText, end);

        float horiz = getPrimaryHorizontal(offset);

        int best = offset;
        float besth = Integer.MAX_VALUE;
        int candidate;

        candidate = TextUtils.getOffsetBefore(mText, offset);
        if (candidate >= start && candidate <= end) {
            float h = getPrimaryHorizontal(candidate);

            if (h > horiz && h < besth) {
                best = candidate;
                besth = h;
            }
        }

        candidate = TextUtils.getOffsetAfter(mText, offset);
        if (candidate >= start && candidate <= end) {
            float h = getPrimaryHorizontal(candidate);

            if (h > horiz && h < besth) {
                best = candidate;
                besth = h;
            }
        }

        int here = start;
        for (int i = 0; i < dirs.mDirections.length; i++) {
            int there = here + dirs.mDirections[i];
            if (there > end)
                there = end;

            float h = getPrimaryHorizontal(here);

            if (h > horiz && h < besth) {
                best = here;
                besth = h;
            }

            candidate = TextUtils.getOffsetAfter(mText, here);
            if (candidate >= start && candidate <= end) {
                h = getPrimaryHorizontal(candidate);

                if (h > horiz && h < besth) {
                    best = candidate;
                    besth = h;
                }
            }

            candidate = TextUtils.getOffsetBefore(mText, there);
            if (candidate >= start && candidate <= end) {
                h = getPrimaryHorizontal(candidate);

                if (h > horiz && h < besth) {
                    best = candidate;
                    besth = h;
        DirectionalRunFinder run = new DirectionalRunFinder(offset);
        int runDir = run.runDir;
        boolean mainDir = runDir==run.lineDir;
        int line = run.line;
        // If run is only one character or if this is the last character
        // of a RTL run in a LTR paragraph, the use the paragraph direction:
        boolean useLineDir = run.runDegenerate ||
            offset==run.runEnd && runDir<0 && !mainDir;

        int dir = useLineDir ? run.lineDir : runDir;
        int res = dir > 0 ?
            TextUtils.getOffsetAfter(mText, offset) :
            TextUtils.getOffsetBefore(mText, offset) ;

        // if moved to a new run (or if stuck - i.e. end of text and going wrong way):
        if (!run.containedInRun(res,mainDir)) {
            if (mainDir) { // exiting directional run in same direction as paragraph:
                // find the new directional run (notice it may be in a different line)
                run.find(res);
                if (run.runDir != runDir && run.line==line && !run.runDegenerate) {
                    // if switched run direction:
                    // go to the leftmost char of the new run:
                    if (run.runDir > 0)
                        // entering LTR run in RTL paragraph, so move right should
                        // behave like backspace and move to the last character typed
                        // which is the last character in the run:
                        res = run.runEnd;
                    else
                        // continuing, the logic from the above comment, just this time
                        // for RTL runs in LTR paragraphs, the last character will be handled
                        // at the "end of movement" over this run, so when entering the run
                        // we enter one before the last char:
                        res = TextUtils.getOffsetBefore(mText,run.runEnd);
                }
            }
            else if (!useLineDir) {
                // exiting directional run in oppisite direction as paragraph,
                // so we either covered only the last letter of a LTR run in a
                // RTL line and need to go to the start of this run (offset-runLength+1)
                // or we went over all of a RTL run in a LTR line except its
                // last letter so move to this letter (offset+runLength-1):
                res = runDir > 0 ?
                    TextUtils.getOffsetAfter(mText, offset-run.runLength) :
                    TextUtils.getOffsetBefore(mText, offset+run.runLength) ;
            }
        }

        // Check if overflowed line to any direction:
        int originalLine = line;
        line = run.line; // run may have been updated above to a new line
                         // if it has, we trust its new value, otherwise:
        if (line==originalLine) {
            if (res >= run.lineEnd)
                line = run.nextNonEmptyLine(line);
            if (res < run.lineStart)
                line = run.prevNonEmptyLine(line);
        }
        if (line!=originalLine) { // if we did reach a new line, update position in new line:
            int newLineDir = getParagraphDirection(line);
            boolean lastLine = line == getLineCount()-1;
            if (newLineDir>0) {
                // go to leftmost position of new LTR line:
                run.findFirstRun(line);
                res = run.runDir>0 || run.runDegenerate ? run.runStart : TextUtils.getOffsetBefore(mText,run.runEnd);
            } else {
                // go to leftmost position of new RTL line:
                run.findLastRun(line);
                res = run.runDir>0 && !run.runDegenerate ? TextUtils.getOffsetAfter(mText,run.runStart) : run.runEnd;
                if (res==run.runEnd && !lastLine)
                    res = TextUtils.getOffsetBefore(mText,res);
            }
        }

            here = there;
        return res;
    }

        float h = getPrimaryHorizontal(end);

        if (h > horiz && h < besth) {
            best = end;
            besth = h;
        }

        if (best != offset)
            return best;

        int dir = getParagraphDirection(line);

        if (dir > 0) {
            if (line == getLineCount() - 1)
                return best;
    /**
     * Return the text offset that would be reached by moving left
     * (possibly onto another line) from the specified offset.
     */
    public int getOffsetToLeftOf(int offset) {
        DirectionalRunFinder run = new DirectionalRunFinder(offset);
        int runDir = run.runDir;
        boolean mainDir = runDir==run.lineDir;
        int line = run.line;
        // If run is only one character or if this is the last character
        // of a LTR run in a RTL paragraph, the use the paragraph direction:
        boolean useLineDir = run.runDegenerate ||
            offset==run.runEnd && runDir>0 && !mainDir;

        int dir = useLineDir ? run.lineDir : runDir;
        int res = dir > 0 ?
            TextUtils.getOffsetBefore(mText, offset) :
            TextUtils.getOffsetAfter(mText, offset) ;

        // if moved to a new run (or if stuck - i.e. end of text and going wrong way):
        if (!run.containedInRun(res,mainDir)) {
            if (mainDir) { // exiting directional run in same direction as paragraph:
                // find the new directional run (notice it may be in a different line)
                run.find(res);
                if (run.runDir != runDir && run.line==line && !run.runDegenerate) {
                    // if switched run direction:
                    if (run.runDir > 0)
                        // entering a LTR run in a RTL paragraph, actually move to one
                        // before last character, complementary to the behaviour of
                        // getOffsetToRightOf() above:
                        res = TextUtils.getOffsetBefore(mText,run.runEnd);
                    else
                return getOffsetForHorizontal(line + 1, -10000);
                        // otherwise entering RTL run, move to its rightmost character:
                        res = TextUtils.getOffsetAfter(mText,run.runStart);
                }
            } else if (!useLineDir) {
                // exiting directional run in oppisite direction as paragraph,
                // simlarly to the getOffsetAfterOf() implementation above:
                res = runDir > 0 ?
                    TextUtils.getOffsetBefore(mText, offset+run.runLength) :
                    TextUtils.getOffsetAfter(mText, offset-run.runLength) ;
            }
        }

        // Check if overflowed line to any direction:
        int originalLine = line;
        line = run.line; // run may have been updated above to a new line
                         // if it has, we trust its new value, otherwise:
        if (line==originalLine) {
            if (res >= run.lineEnd)
                line = run.nextNonEmptyLine(line);
            if (res < run.lineStart)
                line = run.prevNonEmptyLine(line);
        }
        if (line!=originalLine) { // if we did reach a new line, update position in new line:
            int newLineDir = getParagraphDirection(line);
            boolean lastLine = line == getLineCount()-1;
            if (newLineDir<0) {
                // go to rightmost position of new RTL line:
                res = getLineStart(line);
            } else {
            if (line == 0)
                return best;
            else
                return getOffsetForHorizontal(line - 1, -10000);
                // go to rightmost position of new LTR line:
                run.findLastRun(line);
                res = run.runDir<0 && !run.runDegenerate ? TextUtils.getOffsetAfter(mText,run.runStart) : run.runEnd;
                if (res==run.runEnd && !lastLine)
                    res = TextUtils.getOffsetBefore(mText,res);
            }
        }

        return res;
    }

    private int getOffsetAtStartOf(int offset) {
        if (offset == 0)
            return 0;
+6 −6
Original line number Diff line number Diff line
@@ -846,10 +846,10 @@ public class TextUtils {
    }

    public static int getOffsetBefore(CharSequence text, int offset) {
        if (offset == 0)
            return 0;
        if (offset == 1)
        if (offset <= 1)
            return 0;
        if (offset > text.length())
            return text.length();

        char c = text.charAt(offset - 1);

@@ -883,10 +883,10 @@ public class TextUtils {
    public static int getOffsetAfter(CharSequence text, int offset) {
        int len = text.length();

        if (offset == len)
            return len;
        if (offset == len - 1)
        if (offset >= len-1)
            return len;
        if (offset < 0)
            return 0;

        char c = text.charAt(offset);