update method
Updates the component state in response to a message.
Returns the updated component (often this) and an optional command.
Implementation
@override
(TextAreaModel, Cmd?) update(Msg msg) {
return _runEditFrame(() {
switch (msg) {
case TextAreaPasteMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case PasteMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case PasteTextMsg(:final content):
_beginHistoryAction(_TextAreaHistoryAction.paste, breakChain: true);
return (this, _pasteContent(content));
case _TextAreaPasteChunkMsg():
_beginHistoryAction(_TextAreaHistoryAction.paste);
_applyNextPasteChunk();
if (_pasteController.hasPendingChunkedPaste) {
return (this, _schedulePasteChunk());
}
return (this, null);
case KeyMsg(key: final key):
if (key.matchesSingle(keyMap.undo)) {
undo();
return (this, null);
}
if (key.matchesSingle(keyMap.redo)) {
redo();
return (this, null);
}
if (key.matchesSingle(keyMap.selectAll)) {
selectAll();
return (this, null);
}
if (key.matchesSingle(keyMap.selectLine)) {
selectCurrentLine();
return (this, null);
}
// deletion
if (key.matchesSingle(keyMap.deleteBeforeCursor)) {
_beginHistoryAction(_TextAreaHistoryAction.deleteBackward);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_backspace();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteCharacterForward)) {
_beginHistoryAction(_TextAreaHistoryAction.deleteForward);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteCharForward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordBackward)) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteBackward,
breakChain: true,
);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteWordBackward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordForward)) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteForward,
breakChain: true,
);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineStart)) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteBackward,
breakChain: true,
);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteToLineStart();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineEnd)) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteForward,
breakChain: true,
);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteToLineEnd();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteAfterCursor)) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteForward,
breakChain: true,
);
if (_deleteSelectionIfAny()) {
return (this, null);
}
_deleteToLineEnd();
return (this, null);
}
// navigation
if (key.matchesSingle(keyMap.wordForward)) {
_moveWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.wordBackward)) {
_moveWordBackward();
return (this, null);
}
if (key.matchesSingle(keyMap.lineStart)) {
_cursorStartOfLine();
return (this, null);
}
if (key.matchesSingle(keyMap.lineEnd)) {
_cursorEndOfLine();
return (this, null);
}
if (key.matchesSingle(keyMap.inputBegin)) {
_cursorStartOfInput();
return (this, null);
}
if (key.matchesSingle(keyMap.inputEnd)) {
_cursorEndOfInput();
return (this, null);
}
if (key.matchesSingle(keyMap.characterForward)) {
_moveRight();
return (this, null);
}
if (key.matchesSingle(keyMap.characterBackward)) {
_moveLeft();
return (this, null);
}
if (key.matchesSingle(keyMap.lineNext)) {
_lineNext();
return (this, null);
}
if (key.matchesSingle(keyMap.linePrevious)) {
_linePrev();
return (this, null);
}
if (key.matchesSingle(keyMap.transposeCharacterBackward)) {
_transposeBackward();
return (this, null);
}
if (key.matchesSingle(keyMap.uppercaseWordForward)) {
_uppercaseWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.lowercaseWordForward)) {
_lowercaseWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.capitalizeWordForward)) {
_capitalizeWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.copy)) {
final text = getSelectedText();
if (text.isNotEmpty) {
return (this, Cmd.setClipboard(text));
}
}
// Fallback direct modifier checks for common combos.
if (key.type == KeyType.delete && key.alt) {
_beginHistoryAction(
_TextAreaHistoryAction.deleteForward,
breakChain: true,
);
_deleteWordForward();
return (this, null);
}
if (key.ctrl && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x74) {
// ctrl+t
_beginHistoryAction(
_TextAreaHistoryAction.transform,
breakChain: true,
);
_transposeBackward();
return (this, null);
}
}
if (key.alt && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x75) {
_beginHistoryAction(
_TextAreaHistoryAction.transform,
breakChain: true,
);
_uppercaseWordForward();
return (this, null);
}
if (r == 0x6c) {
_beginHistoryAction(
_TextAreaHistoryAction.transform,
breakChain: true,
);
_lowercaseWordForward();
return (this, null);
}
if (r == 0x63) {
_beginHistoryAction(
_TextAreaHistoryAction.transform,
breakChain: true,
);
_capitalizeWordForward();
return (this, null);
}
}
if (key.type == KeyType.space) {
_beginHistoryAction(_TextAreaHistoryAction.insert);
_insertChar(' ');
return (this, null);
}
if (key.type == KeyType.enter && keyMap.insertNewline.enabled) {
_beginHistoryAction(
_TextAreaHistoryAction.insert,
breakChain: true,
);
_newline();
return (this, null);
}
if (key.type == KeyType.runes && key.runes.isNotEmpty) {
_beginHistoryAction(_TextAreaHistoryAction.insert);
final rune = key.runes.first;
if (rune == 0x0a) {
_newline();
} else {
_insertChar(String.fromCharCode(rune));
}
return (this, null);
}
}
if (msg is MouseMsg) {
final lineNumberDigits = showLineNumbers ? '$lineCount'.length : 0;
final displayLines = _softWrappedLines(lineNumberDigits);
final action = msg.action;
final button = msg.button;
final x = msg.x;
final y = msg.y;
if (y < 0 || y >= displayLines.length) {
if (action == MouseAction.press && button == MouseButton.left) {
_mouseSelecting = false;
_clearLineSelection();
_focused = false;
_syncCoreState();
}
if (action == MouseAction.release && button == MouseButton.left) {
_mouseSelecting = false;
if (!_hasSelection()) {
_clearLineSelection();
_syncCoreState();
}
}
return (this, null);
}
if (action == MouseAction.press && button == MouseButton.left) {
_focused = true;
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final displayLine = displayLines[y];
final inLineNumberGutter =
showLineNumbers &&
x >= promptW &&
x < promptW + lineNumberW &&
displayLine.charOffset == 0;
if (inLineNumberGutter &&
selectDiagnosticAtLine(displayLine.rowIndex)) {
_mouseSelecting = false;
return (this, null);
}
final hit = _textView.hitTestContent(
_document,
_editorState,
localX: x - promptW - lineNumberW,
visualRow: y,
);
if (hit == null) {
_mouseSelecting = false;
return (this, null);
}
final contentX = hit.column;
final contentY = hit.line;
final now = DateTime.now();
final clickCount =
_lastClickTime != null &&
now.difference(_lastClickTime!) <
const Duration(milliseconds: 500) &&
_lastClickPos == (contentX, contentY)
? (_lastClickCount + 1).clamp(1, 3)
: 1;
_lastClickTime = now;
_lastClickPos = (contentX, contentY);
_lastClickCount = clickCount;
if (clickCount == 2) {
_mouseSelecting = false;
final (start, end) = _findWordAt(contentX, contentY);
_selectLineState(
base: TextPosition(line: contentY, column: start),
extent: TextPosition(line: contentY, column: end),
);
_syncCoreState();
return (this, null);
}
if (clickCount >= 3) {
_mouseSelecting = false;
_selectLineState(
base: TextPosition(line: contentY, column: 0),
extent: TextPosition(
line: contentY,
column: _document.lineLength(contentY),
),
);
_syncCoreState();
return (this, null);
}
// Start selection
_mouseSelecting = true;
_selectLineState(
base: TextPosition(line: contentY, column: contentX),
extent: TextPosition(line: contentY, column: contentX),
preserveCollapsedSelection: true,
);
_syncCoreState();
return (this, null);
}
if (action == MouseAction.motion &&
_mouseSelecting &&
_selectionStart != null) {
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final hit = _textView.hitTestContent(
_document,
_editorState,
localX: x - promptW - lineNumberW,
visualRow: y,
);
if (hit == null) {
return (this, null);
}
final contentX = hit.column;
final contentY = hit.line;
_selectLineState(
base: TextPosition(
line: _selectionStart!.$2,
column: _selectionStart!.$1,
),
extent: TextPosition(line: contentY, column: contentX),
);
_syncCoreState();
return (this, null);
}
if (action == MouseAction.release && button == MouseButton.left) {
_mouseSelecting = false;
if (!_hasSelection()) {
_clearLineSelection();
_syncCoreState();
}
return (this, null);
}
}
return (this, null);
});
}