未验证 提交 7a5df94c 编写于 作者: L LongCatIsLooong 提交者: GitHub

Passthrough movement keys when theres no selection (#24581)

上级 90618237
......@@ -735,5 +735,11 @@ public class TextInputChannel {
this.composingStart = composingStart;
this.composingEnd = composingEnd;
}
public boolean hasSelection() {
// When selectionStart == -1, it's guaranteed that selectionEnd will also
// be -1.
return selectionStart >= 0;
}
}
}
......@@ -306,57 +306,13 @@ class InputConnectionAdaptor extends BaseInputConnection
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart == selEnd && !event.isShiftPressed()) {
int newSel = Math.max(flutterTextUtils.getOffsetBefore(mEditable, selStart), 0);
setSelection(newSel, newSel);
} else {
int newSelEnd = Math.max(flutterTextUtils.getOffsetBefore(mEditable, selEnd), 0);
setSelection(selStart, newSelEnd);
}
return true;
return handleHorizontalMovement(true, event.isShiftPressed());
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart == selEnd && !event.isShiftPressed()) {
int newSel =
Math.min(flutterTextUtils.getOffsetAfter(mEditable, selStart), mEditable.length());
setSelection(newSel, newSel);
} else {
int newSelEnd =
Math.min(flutterTextUtils.getOffsetAfter(mEditable, selEnd), mEditable.length());
setSelection(selStart, newSelEnd);
}
return true;
return handleHorizontalMovement(false, event.isShiftPressed());
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart == selEnd && !event.isShiftPressed()) {
Selection.moveUp(mEditable, mLayout);
int newSelStart = Selection.getSelectionStart(mEditable);
setSelection(newSelStart, newSelStart);
} else {
Selection.extendUp(mEditable, mLayout);
int newSelStart = Selection.getSelectionStart(mEditable);
int newSelEnd = Selection.getSelectionEnd(mEditable);
setSelection(newSelStart, newSelEnd);
}
return true;
return handleVerticalMovement(true, event.isShiftPressed());
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart == selEnd && !event.isShiftPressed()) {
Selection.moveDown(mEditable, mLayout);
int newSelStart = Selection.getSelectionStart(mEditable);
setSelection(newSelStart, newSelStart);
} else {
Selection.extendDown(mEditable, mLayout);
int newSelStart = Selection.getSelectionStart(mEditable);
int newSelEnd = Selection.getSelectionEnd(mEditable);
setSelection(newSelStart, newSelEnd);
}
return true;
return handleVerticalMovement(false, event.isShiftPressed());
// When the enter key is pressed on a non-multiline field, consider it a
// submit instead of a newline.
} else if ((event.getKeyCode() == KeyEvent.KEYCODE_ENTER
......@@ -366,17 +322,20 @@ class InputConnectionAdaptor extends BaseInputConnection
return true;
} else {
// Enter a character.
int character = event.getUnicodeChar();
if (character == 0) {
final int selStart = Selection.getSelectionStart(mEditable);
final int selEnd = Selection.getSelectionEnd(mEditable);
final int character = event.getUnicodeChar();
if (selStart < 0 || selEnd < 0 || character == 0) {
return false;
}
int selStart = Math.max(0, Selection.getSelectionStart(mEditable));
int selEnd = Math.max(0, Selection.getSelectionEnd(mEditable));
int selMin = Math.min(selStart, selEnd);
int selMax = Math.max(selStart, selEnd);
final int selMin = Math.min(selStart, selEnd);
final int selMax = Math.max(selStart, selEnd);
beginBatchEdit();
if (selMin != selMax) mEditable.delete(selMin, selMax);
mEditable.insert(selMin, String.valueOf((char) character));
setSelection(selMin + 1, selMin + 1);
endBatchEdit();
return true;
}
}
......@@ -390,6 +349,60 @@ class InputConnectionAdaptor extends BaseInputConnection
return false;
}
private boolean handleHorizontalMovement(boolean isLeft, boolean isShiftPressed) {
final int selStart = Selection.getSelectionStart(mEditable);
final int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart < 0 || selEnd < 0) {
return false;
}
final int newSelectionEnd =
isLeft
? Math.max(flutterTextUtils.getOffsetBefore(mEditable, selEnd), 0)
: Math.min(flutterTextUtils.getOffsetAfter(mEditable, selEnd), mEditable.length());
final boolean shouldCollapse = selStart == selEnd && !isShiftPressed;
if (shouldCollapse) {
setSelection(newSelectionEnd, newSelectionEnd);
} else {
setSelection(selStart, newSelectionEnd);
}
return true;
};
private boolean handleVerticalMovement(boolean isUp, boolean isShiftPressed) {
final int selStart = Selection.getSelectionStart(mEditable);
final int selEnd = Selection.getSelectionEnd(mEditable);
if (selStart < 0 || selEnd < 0) {
return false;
}
final boolean shouldCollapse = selStart == selEnd && !isShiftPressed;
beginBatchEdit();
if (shouldCollapse) {
if (isUp) {
Selection.moveUp(mEditable, mLayout);
} else {
Selection.moveDown(mEditable, mLayout);
}
final int newSelection = Selection.getSelectionStart(mEditable);
setSelection(newSelection, newSelection);
} else {
if (isUp) {
Selection.extendUp(mEditable, mLayout);
} else {
Selection.extendDown(mEditable, mLayout);
}
setSelection(Selection.getSelectionStart(mEditable), Selection.getSelectionEnd(mEditable));
}
endBatchEdit();
return true;
}
@Override
public boolean performContextMenuAction(int id) {
beginBatchEdit();
......
......@@ -137,7 +137,7 @@ class ListenableEditingState extends SpannableStringBuilder {
beginBatchEdit();
replace(0, length(), newState.text);
if (newState.selectionStart >= 0) {
if (newState.hasSelection()) {
Selection.setSelection(this, newState.selectionStart, newState.selectionEnd);
} else {
Selection.removeSelection(this);
......
......@@ -84,6 +84,7 @@ public class InputConnectionAdaptorTest {
TextInputChannel textInputChannel = new TextInputChannel(dartExecutor);
AndroidKeyProcessor mockKeyProcessor = mock(AndroidKeyProcessor.class);
ListenableEditingState mEditable = new ListenableEditingState(null, testView);
Selection.setSelection(mEditable, 0, 0);
ListenableEditingState spyEditable = spy(mEditable);
EditorInfo outAttrs = new EditorInfo();
outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE;
......@@ -911,6 +912,37 @@ public class InputConnectionAdaptorTest {
assertTrue(Selection.getSelectionStart(editable) > selStart);
}
@Test
public void testSendKeyEvent_MovementKeysAreNopWhenNoSelection() {
// Regression test for https://github.com/flutter/flutter/issues/76283.
ListenableEditingState editable = sampleEditable(-1, -1);
InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable);
KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
boolean didConsume = adaptor.sendKeyEvent(keyEvent);
assertFalse(didConsume);
assertEquals(Selection.getSelectionStart(editable), -1);
assertEquals(Selection.getSelectionEnd(editable), -1);
keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
didConsume = adaptor.sendKeyEvent(keyEvent);
assertFalse(didConsume);
assertEquals(Selection.getSelectionStart(editable), -1);
assertEquals(Selection.getSelectionEnd(editable), -1);
keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
didConsume = adaptor.sendKeyEvent(keyEvent);
assertFalse(didConsume);
assertEquals(Selection.getSelectionStart(editable), -1);
assertEquals(Selection.getSelectionEnd(editable), -1);
keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
didConsume = adaptor.sendKeyEvent(keyEvent);
assertFalse(didConsume);
assertEquals(Selection.getSelectionStart(editable), -1);
assertEquals(Selection.getSelectionEnd(editable), -1);
}
@Test
public void testMethod_getExtractedText() {
int selStart = 5;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册