in src/Avalonia.Controls/TextBox.cs [1279:1609]
protected override void OnKeyDown(KeyEventArgs e)
{
if (_presenter == null)
{
return;
}
if (!string.IsNullOrEmpty(_presenter.PreeditText))
{
return;
}
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
var movement = false;
var selection = false;
var handled = false;
var modifiers = e.KeyModifiers;
var keymap = Application.Current!.PlatformSettings!.HotkeyConfiguration;
using var _ = _imClient.BeginChange();
bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
bool DetectSelection() => e.KeyModifiers.HasAllFlags(keymap.SelectionModifiers);
if (Match(keymap.SelectAll))
{
SelectAll();
handled = true;
}
else if (Match(keymap.Copy))
{
if (!IsPasswordBox)
{
Copy();
}
handled = true;
}
else if (Match(keymap.Cut))
{
if (!IsPasswordBox)
{
Cut();
}
handled = true;
}
else if (Match(keymap.Paste))
{
Paste();
handled = true;
}
else if (Match(keymap.Undo) && IsUndoEnabled)
{
Undo();
handled = true;
}
else if (Match(keymap.Redo) && IsUndoEnabled)
{
Redo();
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfDocument))
{
MoveHome(true);
movement = true;
selection = false;
handled = true;
SetCurrentValue(CaretIndexProperty, _presenter.CaretIndex);
}
else if (Match(keymap.MoveCursorToTheEndOfDocument))
{
MoveEnd(true);
movement = true;
selection = false;
handled = true;
SetCurrentValue(CaretIndexProperty, _presenter.CaretIndex);
}
else if (Match(keymap.MoveCursorToTheStartOfLine))
{
MoveHome(false);
movement = true;
selection = false;
handled = true;
SetCurrentValue(CaretIndexProperty, _presenter.CaretIndex);
}
else if (Match(keymap.MoveCursorToTheEndOfLine))
{
MoveEnd(false);
movement = true;
selection = false;
handled = true;
SetCurrentValue(CaretIndexProperty, _presenter.CaretIndex);
}
else if (Match(keymap.MoveCursorToTheStartOfDocumentWithSelection))
{
SetCurrentValue(SelectionStartProperty, caretIndex);
MoveHome(true);
SetCurrentValue(SelectionEndProperty, _presenter.CaretIndex);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfDocumentWithSelection))
{
SetCurrentValue(SelectionStartProperty, caretIndex);
MoveEnd(true);
SetCurrentValue(SelectionEndProperty, _presenter.CaretIndex);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfLineWithSelection))
{
SetCurrentValue(SelectionStartProperty, caretIndex);
MoveHome(false);
SetCurrentValue(SelectionEndProperty, _presenter.CaretIndex);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfLineWithSelection))
{
SetCurrentValue(SelectionStartProperty, caretIndex);
MoveEnd(false);
SetCurrentValue(SelectionEndProperty, _presenter.CaretIndex);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.PageLeft))
{
MovePageLeft();
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.PageRight))
{
MovePageRight();
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.PageUp))
{
MovePageUp();
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.PageDown))
{
MovePageDown();
movement = true;
selection = false;
handled = true;
}
else
{
// It's not secure to rely on password field content when moving.
bool hasWholeWordModifiers = modifiers.HasAllFlags(keymap.WholeWordTextActionModifiers) && !IsPasswordBox;
switch (e.Key)
{
case Key.Left:
selection = DetectSelection();
MoveHorizontal(-1, hasWholeWordModifiers, selection, true);
if (caretIndex != _presenter.CaretIndex)
{
movement = true;
}
break;
case Key.Right:
selection = DetectSelection();
MoveHorizontal(1, hasWholeWordModifiers, selection, true);
if (caretIndex != _presenter.CaretIndex)
{
movement = true;
}
break;
case Key.Up:
selection = DetectSelection();
MoveVertical(LogicalDirection.Backward, selection);
if (caretIndex != _presenter.CaretIndex)
{
movement = true;
}
break;
case Key.Down:
selection = DetectSelection();
MoveVertical(LogicalDirection.Forward, selection);
if (caretIndex != _presenter.CaretIndex)
{
movement = true;
}
break;
case Key.Back:
{
SnapshotUndoRedo();
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
SetSelectionForControlBackspace();
}
if (!DeleteSelection())
{
var characterHit = _presenter.GetNextCharacterHit(LogicalDirection.Backward);
var backspacePosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(caretIndex, true);
var backspaceCharacterHit = _presenter.TextLayout.TextLines[lineIndex]
.GetBackspaceCaretCharacterHit(new CharacterHit(caretIndex));
if (backspaceCharacterHit.FirstCharacterIndex > backspacePosition &&
backspaceCharacterHit.FirstCharacterIndex < caretIndex)
{
backspacePosition = backspaceCharacterHit.FirstCharacterIndex;
}
if (caretIndex != backspacePosition)
{
var start = Math.Min(backspacePosition, caretIndex);
var end = Math.Max(backspacePosition, caretIndex);
var length = end - start;
var sb = StringBuilderCache.Acquire(text.Length);
sb.Append(text);
sb.Remove(start, end - start);
SetCurrentValue(TextProperty, StringBuilderCache.GetStringAndRelease(sb));
SetCurrentValue(CaretIndexProperty, start);
_presenter.MoveCaretToTextPosition(start);
}
}
SnapshotUndoRedo();
handled = true;
break;
}
case Key.Delete:
SnapshotUndoRedo();
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
SetSelectionForControlDelete();
}
if (!DeleteSelection())
{
var characterHit = _presenter.GetNextCharacterHit();
var nextPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
if (nextPosition != caretIndex)
{
var start = Math.Min(nextPosition, caretIndex);
var end = Math.Max(nextPosition, caretIndex);
var sb = StringBuilderCache.Acquire(text.Length);
sb.Append(text);
sb.Remove(start, end - start);
SetCurrentValue(TextProperty, StringBuilderCache.GetStringAndRelease(sb));
}
}
SnapshotUndoRedo();
handled = true;
break;
case Key.Enter:
if (AcceptsReturn)
{
SnapshotUndoRedo();
HandleTextInput(NewLine);
handled = true;
}
break;
case Key.Tab:
if (AcceptsTab)
{
SnapshotUndoRedo();
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
break;
case Key.Space:
SnapshotUndoRedo(); // always snapshot in between words
break;
default:
handled = false;
break;
}
}
if (movement && !selection)
{
ClearSelection();
}
if (handled || movement)
{
e.Handled = true;
}
}