update method

  1. @override
(TextAreaModel, Cmd?) update(
  1. Msg msg
)
override

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);
}