Unverified Commit 3900d42b authored by jslavitz's avatar jslavitz Committed by GitHub

Control, Shift and Arrow Key functionality for Chromebook (#20204)

* added keyboard functionatliy to android builds

* Added tests

* almost ready for review

* ready for review

* Fixes

* final comments

* final commit

* removing raw keyboard changes

* removing raw keyboard changes

* removing raw keyboard changes

* actual last commit

* fixed the imports

* a few more changes

* A few more changes

* a few changes

* Final changes

* Final changes2

* final actual commit for real

* final actual commit for real2

* final actual commit for real3

* final actual commit for real4

* final

* final 2

* f

* f2

* fin

* fin 2

* fin3

* fin4
parent dc5a5c18
......@@ -198,6 +198,186 @@ class RenderEditable extends RenderBox {
Rect _lastCaretRect;
static const int _kLeftArrowCode = 21;
static const int _kRightArrowCode = 22;
static const int _kUpArrowCode = 19;
static const int _kDownArrowCode = 20;
// The extent offset of the current selection
int _extentOffset = -1;
// The base offset of the current selection
int _baseOffset = -1;
// Holds the last location the user selected in the case that he selects all
// the way to the end or beginning of the field.
int _previousCursorLocation = -1;
// Whether we should reset the location of the cursor in the case the user
// selects all the way to the end or the beginning of a field.
bool _resetCursor = false;
static const int _kShiftMask = 1; // https://developer.android.com/reference/android/view/KeyEvent.html#META_SHIFT_ON
static const int _kControlMask = 1 << 12; // https://developer.android.com/reference/android/view/KeyEvent.html#META_CTRL_ON
// TODO(goderbauer): doesn't handle extended grapheme clusters with more than one Unicode scalar value (https://github.com/flutter/flutter/issues/13404).
void _handleKeyEvent(RawKeyEvent keyEvent){
if (defaultTargetPlatform != TargetPlatform.android)
return;
if (keyEvent is RawKeyUpEvent)
return;
final RawKeyEventDataAndroid rawAndroidEvent = keyEvent.data;
final int pressedKeyCode = rawAndroidEvent.keyCode;
final int pressedKeyMetaState = rawAndroidEvent.metaState;
if (selection.isCollapsed) {
_extentOffset = selection.extentOffset;
_baseOffset = selection.baseOffset;
}
// Update current key states
final bool shift = pressedKeyMetaState & _kShiftMask > 0;
final bool ctrl = pressedKeyMetaState & _kControlMask > 0;
final bool rightArrow = pressedKeyCode == _kRightArrowCode;
final bool leftArrow = pressedKeyCode == _kLeftArrowCode;
final bool upArrow = pressedKeyCode == _kUpArrowCode;
final bool downArrow = pressedKeyCode == _kDownArrowCode;
final bool arrow = leftArrow || rightArrow || upArrow || downArrow;
// We will only move select or more the caret if an arrow is pressed
if (arrow) {
int newOffset = _extentOffset;
// Because the user can use multiple keys to change how he selects
// the new offset variable is threaded through these four functions
// and potentially changes after each one.
if (ctrl)
newOffset = _handleControl(rightArrow, leftArrow, ctrl, newOffset);
newOffset = _handleHorizontalArrows(rightArrow, leftArrow, shift, newOffset);
if (downArrow || upArrow)
newOffset = _handleVerticalArrows(upArrow, downArrow, shift, newOffset);
newOffset = _handleShift(rightArrow, leftArrow, shift, newOffset);
_extentOffset = newOffset;
}
}
// Handles full word traversal using control.
int _handleControl(bool rightArrow, bool leftArrow, bool ctrl, int newOffset) {
// If control is pressed, we will decide which way to look for a word
// based on which arrow is pressed.
if (leftArrow && _extentOffset > 2) {
final TextSelection textSelection = _selectWordAtOffset(new TextPosition(offset: _extentOffset - 2));
newOffset = textSelection.baseOffset + 1;
} else if (rightArrow && _extentOffset < text.text.length - 2) {
final TextSelection textSelection = _selectWordAtOffset(new TextPosition(offset: _extentOffset + 1));
newOffset = textSelection.extentOffset - 1;
}
return newOffset;
}
int _handleHorizontalArrows(bool rightArrow, bool leftArrow, bool shift, int newOffset) {
// Set the new offset to be +/- 1 depending on which arrow is pressed
// If shift is down, we also want to update the previous cursor location
if (rightArrow && _extentOffset < text.text.length) {
newOffset += 1;
if (shift)
_previousCursorLocation += 1;
}
if (leftArrow && _extentOffset > 0) {
newOffset -= 1;
if (shift)
_previousCursorLocation -= 1;
}
return newOffset;
}
// Handles moving the cursor vertically as well as taking care of the
// case where the user moves the cursor to the end or beginning of the text
// and then back up or down.
int _handleVerticalArrows(bool upArrow, bool downArrow, bool shift, int newOffset) {
// The caret offset gives a location in the upper left hand corner of
// the caret so the middle of the line above is a half line above that
// point and the line below is 1.5 lines below that point.
final double plh = _textPainter.preferredLineHeight;
final double verticalOffset = upArrow ? -0.5 * plh : 1.5 * plh;
final Offset caretOffset = _textPainter.getOffsetForCaret(new TextPosition(offset: _extentOffset), _caretPrototype);
final Offset caretOffsetTranslated = caretOffset.translate(0.0, verticalOffset);
final TextPosition position = _textPainter.getPositionForOffset(caretOffsetTranslated);
// To account for the possibility where the user vertically highlights
// all the way to the top or bottom of the text, we hold the previous
// cursor location. This allows us to restore to this position in the
// case that the user wants to unhighlight some text.
if (position.offset == _extentOffset) {
if (downArrow)
newOffset = text.text.length;
else if (upArrow)
newOffset = 0;
_resetCursor = shift;
} else if (_resetCursor && shift) {
newOffset = _previousCursorLocation;
_resetCursor = false;
} else {
newOffset = position.offset;
_previousCursorLocation = newOffset;
}
return newOffset;
}
// Handles the selection of text or removal of the selection and placing
// of the caret.
int _handleShift(bool rightArrow, bool leftArrow, bool shift, int newOffset) {
if (onSelectionChanged == null)
return newOffset;
// In the text_selection class, a TextSelection is defined such that the
// base offset is always less than the extent offset.
if (shift) {
if (_baseOffset < newOffset) {
onSelectionChanged(
new TextSelection(
baseOffset: _baseOffset,
extentOffset: newOffset
),
this,
SelectionChangedCause.keyboard,
);
} else {
onSelectionChanged(
new TextSelection(
baseOffset: newOffset,
extentOffset: _baseOffset
),
this,
SelectionChangedCause.keyboard,
);
}
} else {
// We want to put the cursor at the correct location depending on which
// arrow is used while there is a selection.
if (!selection.isCollapsed) {
if (leftArrow)
newOffset = _baseOffset < _extentOffset ? _baseOffset : _extentOffset;
else if (rightArrow)
newOffset = _baseOffset > _extentOffset ? _baseOffset : _extentOffset;
}
onSelectionChanged(
new TextSelection.fromPosition(
new TextPosition(
offset: newOffset
)
),
this,
SelectionChangedCause.keyboard,
);
}
return newOffset;
}
/// Marks the render object as needing to be laid out again and have its text
/// metrics recomputed.
///
......@@ -300,11 +480,23 @@ class RenderEditable extends RenderBox {
/// Whether the editable is currently focused.
bool get hasFocus => _hasFocus;
bool _hasFocus;
bool _listenerAttached = false;
set hasFocus(bool value) {
assert(value != null);
if (_hasFocus == value)
return;
_hasFocus = value;
if (_hasFocus) {
assert(!_listenerAttached);
RawKeyboard.instance.addListener(_handleKeyEvent);
_listenerAttached = true;
}
else {
assert(_listenerAttached);
RawKeyboard.instance.removeListener(_handleKeyEvent);
_listenerAttached = false;
}
markNeedsSemanticsUpdate();
}
......@@ -574,6 +766,8 @@ class RenderEditable extends RenderBox {
void detach() {
_offset.removeListener(markNeedsPaint);
_showCursor.removeListener(markNeedsPaint);
if (_listenerAttached)
RawKeyboard.instance.removeListener(_handleKeyEvent);
super.detach();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment