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

Commit a18df8d6 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Consider punctuation treatment when selecting text." into mnc-dev

parents 636a0da8 6c7b4ad6
Loading
Loading
Loading
Loading
+39 −0
Original line number Original line Diff line number Diff line
@@ -95,6 +95,45 @@ public class WordIterator implements Selection.PositionIterator {
        } while (true);
        } while (true);
    }
    }


    /** {@inheritDoc} */
    public boolean isBoundary(int offset) {
        int shiftedOffset = offset - mOffsetShift;
        checkOffsetIsValid(shiftedOffset);
        return mIterator.isBoundary(shiftedOffset);
    }

    /**
     * Returns the position of next boundary after the given offset. Returns
     * {@code DONE} if there is no boundary after the given offset.
     *
     * @param offset the given start position to search from.
     * @return the position of the last boundary preceding the given offset.
     */
    public int nextBoundary(int offset) {
        int shiftedOffset = offset - mOffsetShift;
        shiftedOffset = mIterator.following(shiftedOffset);
        if (shiftedOffset == BreakIterator.DONE) {
            return BreakIterator.DONE;
        }
        return shiftedOffset + mOffsetShift;
    }

    /**
     * Returns the position of boundary preceding the given offset or
     * {@code DONE} if the given offset specifies the starting position.
     *
     * @param offset the given start position to search from.
     * @return the position of the last boundary preceding the given offset.
     */
    public int prevBoundary(int offset) {
        int shiftedOffset = offset - mOffsetShift;
        shiftedOffset = mIterator.preceding(shiftedOffset);
        if (shiftedOffset == BreakIterator.DONE) {
            return BreakIterator.DONE;
        }
        return shiftedOffset + mOffsetShift;
    }

    /** If <code>offset</code> is within a word, returns the index of the first character of that
    /** If <code>offset</code> is within a word, returns the index of the first character of that
     * word, otherwise returns BreakIterator.DONE.
     * word, otherwise returns BreakIterator.DONE.
     *
     *
+90 −34
Original line number Original line Diff line number Diff line
@@ -688,44 +688,101 @@ public class Editor {
    private int getWordStart(int offset) {
    private int getWordStart(int offset) {
        // FIXME - For this and similar methods we're not doing anything to check if there's
        // FIXME - For this and similar methods we're not doing anything to check if there's
        // a LocaleSpan in the text, this may be something we should try handling or checking for.
        // a LocaleSpan in the text, this may be something we should try handling or checking for.
        int retOffset = getWordIteratorWithText().getBeginning(offset);
        int retOffset = getWordIteratorWithText().prevBoundary(offset);
        if (retOffset == BreakIterator.DONE) retOffset = offset;
        if (isPunctBoundaryBehind(retOffset, true /* isStart */)) {
            // If we're on a punctuation boundary we should continue to get the
            // previous offset until we're not longer on a punctuation boundary.
            retOffset = getWordIteratorWithText().prevBoundary(retOffset);
            while (!isPunctBoundaryBehind(retOffset, false /* isStart */)
                    && retOffset != BreakIterator.DONE) {
                retOffset = getWordIteratorWithText().prevBoundary(retOffset);
            }
        }
        if (retOffset == BreakIterator.DONE) {
            return offset;
        }
        return retOffset;
        return retOffset;
    }
    }


    private int getWordEnd(int offset, boolean includePunctuation) {
    private int getWordEnd(int offset) {
        int retOffset = getWordIteratorWithText().getEnd(offset);
        int retOffset = getWordIteratorWithText().nextBoundary(offset);
        if (isPunctBoundaryForward(retOffset, true /* isStart */)) {
            // If we're on a punctuation boundary we should continue to get the
            // next offset until we're no longer on a punctuation boundary.
            retOffset = getWordIteratorWithText().nextBoundary(retOffset);
            while (!isPunctBoundaryForward(retOffset, false /* isStart */)
                    && retOffset != BreakIterator.DONE) {
                retOffset = getWordIteratorWithText().nextBoundary(retOffset);
            }
        }
        if (retOffset == BreakIterator.DONE) {
        if (retOffset == BreakIterator.DONE) {
            retOffset = offset;
            return offset;
        } else if (includePunctuation) {
            retOffset = handlePunctuation(retOffset);
        }
        }
        return retOffset;
        return retOffset;
    }
    }


    private boolean isEndBoundary(int offset) {
    /**
        int thisEnd = getWordEnd(offset, false);
     * Checks for punctuation boundaries for the provided offset and the
        return offset == thisEnd;
     * previous character.
     *
     * @param offset The offset to check from.
     * @param isStart Whether the boundary being checked for is at the start or
     *            end of a punctuation sequence.
     * @return Whether this is a punctuation boundary.
     */
    private boolean isPunctBoundaryBehind(int offset, boolean isStart) {
        CharSequence text = mTextView.getText();
        if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) {
            return false;
        }
        }
        int cp = Character.codePointAt(text, offset);
        int prevCp = Character.codePointBefore(text, offset);


    private boolean isStartBoundary(int offset) {
        if (isPunctuation(cp)) {
        int thisStart = getWordStart(offset);
            // If it's the start, the current cp and the prev cp are
        return thisStart == offset;
            // punctuation. If it's at the end of a punctuation sequence the
            // current is punctuation and the prev is not.
            return isStart ? isPunctuation(prevCp) : !isPunctuation(prevCp);
        }
        return false;
    }
    }


    private int handlePunctuation(int offset) {
    /**
        // FIXME - Check with UX how repeated ending punctuation should be handled.
     * Checks for punctuation boundaries for the provided offset and the next
        // FIXME - Check with UX if / how we would handle non sentence ending characters.
     * character.
        // FIXME - Consider punctuation in different languages.
     *
     * @param offset The offset to check from.
     * @param isStart Whether the boundary being checked for is at the start or
     *            end of a punctuation sequence.
     * @return Whether this is a punctuation boundary.
     */
    private boolean isPunctBoundaryForward(int offset, boolean isStart) {
        CharSequence text = mTextView.getText();
        CharSequence text = mTextView.getText();
        if (offset < text.length()) {
        if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) {
            int c = Character.codePointAt(text, offset);
            return false;
            if (c == 0x002e /* period */|| c == 0x003f /* question mark */
                    || c == 0x0021 /* exclamation mark */) {
                offset = Character.offsetByCodePoints(text, offset, 1);
        }
        }
        int cp = Character.codePointBefore(text, offset);
        int nextCpOffset = Math.min(offset + Character.charCount(cp), text.length() - 1);
        int nextCp = Character.codePointBefore(text, nextCpOffset);

        if (isPunctuation(cp)) {
            // If it's the start, the current cp and the next cp are
            // punctuation. If it's at the end of a punctuation sequence the
            // current is punctuation and the next is not.
            return isStart ? isPunctuation(nextCp) : !isPunctuation(nextCp);
        }
        }
        return offset;
        return false;
    }

    private boolean isPunctuation(int cp) {
        int type = Character.getType(cp);
        return (type == Character.CONNECTOR_PUNCTUATION ||
                type == Character.DASH_PUNCTUATION ||
                type == Character.END_PUNCTUATION ||
                type == Character.FINAL_QUOTE_PUNCTUATION ||
                type == Character.INITIAL_QUOTE_PUNCTUATION ||
                type == Character.OTHER_PUNCTUATION ||
                type == Character.START_PUNCTUATION);
    }
    }


    /**
    /**
@@ -3901,7 +3958,7 @@ public class Editor {
            final int currLine = mTextView.getLineAtCoordinate(y);
            final int currLine = mTextView.getLineAtCoordinate(y);
            boolean positionCursor = false;
            boolean positionCursor = false;
            int offset = trueOffset;
            int offset = trueOffset;
            int end = getWordEnd(offset, true);
            int end = getWordEnd(offset);
            int start = getWordStart(offset);
            int start = getWordStart(offset);


            if (offset < mPreviousOffset) {
            if (offset < mPreviousOffset) {
@@ -3917,7 +3974,7 @@ public class Editor {
                    }
                    }
                }
                }
                mTouchWordOffset = Math.max(trueOffset - offset, 0);
                mTouchWordOffset = Math.max(trueOffset - offset, 0);
                mInWord = !isStartBoundary(offset);
                mInWord = !getWordIteratorWithText().isBoundary(offset);
                positionCursor = true;
                positionCursor = true;
            } else if (offset - mTouchWordOffset > mPreviousOffset) {
            } else if (offset - mTouchWordOffset > mPreviousOffset) {
                // User is shrinking the selection.
                // User is shrinking the selection.
@@ -3926,7 +3983,7 @@ public class Editor {
                    offset = end;
                    offset = end;
                }
                }
                offset -= mTouchWordOffset;
                offset -= mTouchWordOffset;
                mInWord = !isEndBoundary(offset);
                mInWord = !getWordIteratorWithText().isBoundary(offset);
                positionCursor = true;
                positionCursor = true;
            }
            }


@@ -3999,8 +4056,7 @@ public class Editor {
            final int currLine = mTextView.getLineAtCoordinate(y);
            final int currLine = mTextView.getLineAtCoordinate(y);
            int offset = trueOffset;
            int offset = trueOffset;
            boolean positionCursor = false;
            boolean positionCursor = false;

            int end = getWordEnd(offset);
            int end = getWordEnd(offset, true);
            int start = getWordStart(offset);
            int start = getWordStart(offset);


            if (offset > mPreviousOffset) {
            if (offset > mPreviousOffset) {
@@ -4016,7 +4072,7 @@ public class Editor {
                    }
                    }
                }
                }
                mTouchWordOffset = Math.max(offset - trueOffset, 0);
                mTouchWordOffset = Math.max(offset - trueOffset, 0);
                mInWord = !isEndBoundary(offset);
                mInWord = !getWordIteratorWithText().isBoundary(offset);
                positionCursor = true;
                positionCursor = true;
            } else if (offset + mTouchWordOffset < mPreviousOffset) {
            } else if (offset + mTouchWordOffset < mPreviousOffset) {
                // User is shrinking the selection.
                // User is shrinking the selection.
@@ -4026,7 +4082,7 @@ public class Editor {
                }
                }
                offset += mTouchWordOffset;
                offset += mTouchWordOffset;
                positionCursor = true;
                positionCursor = true;
                mInWord = !isStartBoundary(offset);
                mInWord = !getWordIteratorWithText().isBoundary(offset);
            }
            }


            if (positionCursor) {
            if (positionCursor) {
@@ -4272,7 +4328,7 @@ public class Editor {
                        // We don't start "dragging" until the user is past the initial word that
                        // We don't start "dragging" until the user is past the initial word that
                        // gets selected on long press.
                        // gets selected on long press.
                        int firstWordStart = getWordStart(mStartOffset);
                        int firstWordStart = getWordStart(mStartOffset);
                        int firstWordEnd = getWordEnd(mStartOffset, false);
                        int firstWordEnd = getWordEnd(mStartOffset);
                        if (offset > firstWordEnd || offset < firstWordStart) {
                        if (offset > firstWordEnd || offset < firstWordStart) {


                            // Basically the goal in the below code is to have the highlight be
                            // Basically the goal in the below code is to have the highlight be
@@ -4312,7 +4368,7 @@ public class Editor {


                            // Need to adjust start offset based on direction of movement.
                            // Need to adjust start offset based on direction of movement.
                            int newStart = mStartOffset < offset ? getWordStart(mStartOffset)
                            int newStart = mStartOffset < offset ? getWordStart(mStartOffset)
                                    : getWordEnd(mStartOffset, true);
                                    : getWordEnd(mStartOffset);
                            Selection.setSelection((Spannable) mTextView.getText(), newStart,
                            Selection.setSelection((Spannable) mTextView.getText(), newStart,
                                    offset);
                                    offset);
                        }
                        }