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

Commit f2b233d4 authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Introduce Ctrl+Backspace/Ctrl+ForwardDelete shortcut.

Ctrl+Backspace deletes preceding one word and Ctrl+ForwardDelete delets
following one word.
Used Chrome's implementation/behvior as a reference.

Bug: 17810742
Change-Id: I7b2ca515b2f017e457f7104ec7b8e7473ca4e081
parent d119e520
Loading
Loading
Loading
Loading
+68 −8
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import android.text.*;
import android.text.method.TextKeyListener.Capitalize;
import android.widget.TextView;

import java.text.BreakIterator;

/**
 * Abstract base class for key listeners.
 *
@@ -63,9 +65,9 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener

    private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode,
            KeyEvent event, boolean isForwardDelete) {
        // Ensure the key event does not have modifiers except ALT or SHIFT.
        // Ensure the key event does not have modifiers except ALT or SHIFT or CTRL.
        if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState()
                & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) {
                & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK))) {
            return false;
        }

@@ -74,18 +76,28 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
            return true;
        }

        // MetaKeyKeyListener doesn't track control key state. Need to check the KeyEvent instead.
        boolean isCtrlActive = ((event.getMetaState() & KeyEvent.META_CTRL_ON) != 0);
        boolean isShiftActive = (getMetaState(content, META_SHIFT_ON, event) == 1);
        boolean isAltActive = (getMetaState(content, META_ALT_ON, event) == 1);

        if (isCtrlActive) {
            if (isAltActive || isShiftActive) {
                // Ctrl+Alt, Ctrl+Shift, Ctrl+Alt+Shift should not delete any characters.
                return false;
            }
            return deleteUntilWordBoundary(view, content, isForwardDelete);
        }

        // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible.
        if (getMetaState(content, META_ALT_ON, event) == 1) {
            if (deleteLine(view, content)) {
        if (isAltActive && deleteLine(view, content)) {
            return true;
        }
        }

        // Delete a character.
        final int start = Selection.getSelectionEnd(content);
        final int end;
        if (isForwardDelete || event.isShiftPressed()
                || getMetaState(content, META_SHIFT_ON) == 1) {
        if (isForwardDelete || event.isShiftPressed() || isShiftActive) {
            end = TextUtils.getOffsetAfter(content, start);
        } else {
            end = TextUtils.getOffsetBefore(content, start);
@@ -97,6 +109,54 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
        return false;
    }

    private boolean deleteUntilWordBoundary(View view, Editable content, boolean isForwardDelete) {
        int currentCursorOffset = Selection.getSelectionStart(content);

        // If there is a selection, do nothing.
        if (currentCursorOffset != Selection.getSelectionEnd(content)) {
            return false;
        }

        // Early exit if there is no contents to delete.
        if ((!isForwardDelete && currentCursorOffset == 0) ||
            (isForwardDelete && currentCursorOffset == content.length())) {
            return false;
        }

        WordIterator wordIterator = null;
        if (view instanceof TextView) {
            wordIterator = ((TextView)view).getWordIterator();
        }

        if (wordIterator == null) {
            // Default locale is used for WordIterator since the appropriate locale is not clear
            // here.
            // TODO: Use appropriate locale for WordIterator.
            wordIterator = new WordIterator();
        }

        int deleteFrom;
        int deleteTo;

        if (isForwardDelete) {
            deleteFrom = currentCursorOffset;
            wordIterator.setCharSequence(content, deleteFrom, content.length());
            deleteTo = wordIterator.following(currentCursorOffset);
            if (deleteTo == BreakIterator.DONE) {
                deleteTo = content.length();
            }
        } else {
            deleteTo = currentCursorOffset;
            wordIterator.setCharSequence(content, 0, deleteTo);
            deleteFrom = wordIterator.preceding(currentCursorOffset);
            if (deleteFrom == BreakIterator.DONE) {
                deleteFrom = 0;
            }
        }
        content.delete(deleteFrom, deleteTo);
        return true;
    }

    private boolean deleteSelection(View view, Editable content) {
        int selectionStart = Selection.getSelectionStart(content);
        int selectionEnd = Selection.getSelectionEnd(content);