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) {
switch (msg) {
case TextAreaPasteMsg(:final content):
insertString(content);
return (this, null);
case PasteMsg(:final content):
insertString(content);
return (this, null);
case KeyMsg(key: final key):
// deletion
if (key.matchesSingle(keyMap.deleteBeforeCursor)) {
_backspace();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteCharacterForward)) {
_deleteCharForward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordBackward)) {
_deleteWordBackward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteWordForward)) {
_deleteWordForward();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineStart)) {
_deleteToLineStart();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteToLineEnd)) {
_deleteToLineEnd();
return (this, null);
}
if (key.matchesSingle(keyMap.deleteAfterCursor)) {
_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) {
_deleteWordForward();
return (this, null);
}
if (key.ctrl && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x74) {
// ctrl+t
_transposeBackward();
return (this, null);
}
}
if (key.alt && key.type == KeyType.runes && key.runes.isNotEmpty) {
final r = key.runes.first;
if (r == 0x75) {
_uppercaseWordForward();
return (this, null);
}
if (r == 0x6c) {
_lowercaseWordForward();
return (this, null);
}
if (r == 0x63) {
_capitalizeWordForward();
return (this, null);
}
}
if (key.type == KeyType.space) {
_insertChar(' ');
return (this, null);
}
if (key.type == KeyType.enter && keyMap.insertNewline.enabled) {
_newline();
return (this, null);
}
if (key.type == KeyType.runes && key.runes.isNotEmpty) {
final rune = key.runes.first;
if (rune == 0x0a) {
_newline();
} else {
_insertChar(String.fromCharCode(rune));
}
return (this, null);
}
}
if (msg is MouseMsg) {
final lineNumberDigits = showLineNumbers ? '${_lines.length}'.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) {
_selectionStart = null;
_selectionEnd = null;
_focused = false;
}
return (this, null);
}
final dl = displayLines[y];
if (action == MouseAction.press && button == MouseButton.left) {
_focused = true;
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final localX = x - promptW - lineNumberW;
final contentX = localX + dl.charOffset;
final contentY = dl.rowIndex;
final now = DateTime.now();
if (_lastClickTime != null &&
now.difference(_lastClickTime!) <
const Duration(milliseconds: 500) &&
_lastClickPos == (contentX, contentY)) {
// Double click: select word
final (start, end) = _findWordAt(contentX, contentY);
_selectionStart = (start, contentY);
_selectionEnd = (end, contentY);
_lastClickTime = now;
_lastClickPos = (contentX, contentY);
return (this, null);
}
// Start selection
_selectionStart = (contentX, contentY);
_selectionEnd = (contentX, contentY);
_lastClickTime = now;
_lastClickPos = (contentX, contentY);
return (this, null);
}
if (action == MouseAction.motion && _selectionStart != null) {
// Update selection
final promptW = _getPromptWidth(y);
final lineNumberW = showLineNumbers ? (lineNumberDigits + 1) : 0;
final localX = x - promptW - lineNumberW;
final contentX = localX + dl.charOffset;
final contentY = dl.rowIndex;
_selectionEnd = (contentX, contentY);
return (this, null);
}
if (action == MouseAction.release && button == MouseButton.left) {
// Finalize selection
return (this, null);
}
}
return (this, null);
}