update method
Updates the component state in response to a message.
Returns the updated component (often this) and an optional command.
Implementation
@override
(ViewportModel, Cmd?) update(Msg msg) {
final Stopwatch? sw = TuiTrace.enabled ? Stopwatch() : null;
sw?.start();
switch (msg) {
case KeyMsg(:final key):
if (key.matchesSingle(keyMap.pageDown)) {
final next = pageDown();
_traceUpdate('pageDown', next, sw);
return (next, null);
}
if (key.matchesSingle(keyMap.pageUp)) {
final next = pageUp();
_traceUpdate('pageUp', next, sw);
return (next, null);
}
if (key.matchesSingle(keyMap.halfPageDown)) {
final next = halfPageDown();
_traceUpdate('halfPageDown', next, sw);
return (next, null);
}
if (key.matchesSingle(keyMap.halfPageUp)) {
final next = halfPageUp();
_traceUpdate('halfPageUp', next, sw);
return (next, null);
}
if (key.matchesSingle(keyMap.down)) {
final next = scrollDown(1);
_traceUpdate('down', next, sw);
return (next, null);
}
if (key.matchesSingle(keyMap.up)) {
final next = scrollUp(1);
_traceUpdate('up', next, sw);
return (next, null);
}
if (horizontalStep > 0) {
if (!softWrap && key.matchesSingle(keyMap.left)) {
final next = scrollLeft(horizontalStep);
_traceUpdate('left', next, sw);
return (next, null);
}
if (!softWrap && key.matchesSingle(keyMap.right)) {
final next = scrollRight(horizontalStep);
_traceUpdate('right', next, sw);
return (next, null);
}
}
if (key.matchesSingle(keyMap.copy)) {
final text = getSelectedText();
if (text.isNotEmpty) {
_traceUpdate('copy', this, sw);
return (this, Cmd.setClipboard(text));
}
}
_traceUpdate('key', this, sw);
return (this, null);
case MouseMsg(
:final button,
:final action,
:final x,
:final y,
:final shift,
):
if (button == MouseButton.left) {
final isOutside =
y < 0 || (height != null ? y >= height! : y >= lines.length);
if (isOutside) {
if (action == MouseAction.press) {
final next = clearSelection();
_traceUpdate('mouse-clear', next, sw);
return (next, null);
}
_traceUpdate('mouse-outside', this, sw);
return (this, null);
}
if (action == MouseAction.press) {
final contentX = x - gutter + xOffset;
final contentY = y + yOffset;
final now = DateTime.now();
// Check for double click
if (lastClickTime != null &&
now.difference(lastClickTime!) <
const Duration(milliseconds: 500) &&
lastClickPos == (contentX, contentY)) {
final (start, end) = _findWordAt(contentX, contentY);
final next = copyWith(
selectionStart: (start, contentY),
selectionEnd: (end, contentY),
lastClickTime: now,
lastClickPos: (contentX, contentY),
);
_traceUpdate('mouse-double', next, sw);
return (next, null);
}
// Start selection
final next = copyWith(
selectionStart: (contentX, contentY),
selectionEnd: (contentX, contentY),
lastClickTime: now,
lastClickPos: (contentX, contentY),
);
_traceUpdate('mouse-press', next, sw);
return (next, null);
}
if (action == MouseAction.motion && selectionStart != null) {
// Update selection
final contentX = x - gutter + xOffset;
final contentY = y + yOffset;
final next = copyWith(selectionEnd: (contentX, contentY));
_traceUpdate('mouse-drag', next, sw);
return (next, null);
}
if (action == MouseAction.release && button == MouseButton.left) {
// Finalize selection (keep it for copying)
_traceUpdate('mouse-release', this, sw);
return (this, null);
}
}
if (!mouseWheelEnabled ||
(action != MouseAction.press && action != MouseAction.wheel)) {
_traceUpdate('mouse-ignore', this, sw);
return (this, null);
}
switch (button) {
case MouseButton.wheelUp:
if (!softWrap && shift && horizontalStep > 0) {
final next = scrollLeft(horizontalStep);
_traceUpdate('wheel-left', next, sw);
return (next, null);
}
final next = scrollUp(mouseWheelDelta);
_traceUpdate('wheel-up', next, sw);
return (next, null);
case MouseButton.wheelDown:
if (!softWrap && shift && horizontalStep > 0) {
final next = scrollRight(horizontalStep);
_traceUpdate('wheel-right', next, sw);
return (next, null);
}
final next = scrollDown(mouseWheelDelta);
_traceUpdate('wheel-down', next, sw);
return (next, null);
case MouseButton.wheelLeft:
if (!softWrap && horizontalStep > 0) {
final next = scrollLeft(horizontalStep);
_traceUpdate('wheel-left', next, sw);
return (next, null);
}
_traceUpdate('wheel-left', this, sw);
return (this, null);
case MouseButton.wheelRight:
if (!softWrap && horizontalStep > 0) {
final next = scrollRight(horizontalStep);
_traceUpdate('wheel-right', next, sw);
return (next, null);
}
_traceUpdate('wheel-right', this, sw);
return (this, null);
default:
_traceUpdate('mouse-default', this, sw);
return (this, null);
}
default:
_traceUpdate('msg', this, sw);
return (this, null);
}
}