import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; import 'package:xterm/src/core/mouse/button.dart'; import 'package:xterm/src/core/mouse/button_state.dart'; import 'package:xterm/src/terminal_view.dart'; import 'package:xterm/src/ui/controller.dart'; import 'package:xterm/src/ui/gesture/gesture_detector.dart'; import 'package:xterm/src/ui/pointer_input.dart'; import 'package:xterm/src/ui/render.dart'; class TerminalGestureHandler extends StatefulWidget { const TerminalGestureHandler({ super.key, required this.terminalView, required this.terminalController, this.child, this.onTapUp, this.onSingleTapUp, this.onTapDown, this.onSecondaryTapDown, this.onSecondaryTapUp, this.onTertiaryTapDown, this.onTertiaryTapUp, this.readOnly = false, }); final TerminalViewState terminalView; final TerminalController terminalController; final Widget? child; final GestureTapUpCallback? onTapUp; final GestureTapUpCallback? onSingleTapUp; final GestureTapDownCallback? onTapDown; final GestureTapDownCallback? onSecondaryTapDown; final GestureTapUpCallback? onSecondaryTapUp; final GestureTapDownCallback? onTertiaryTapDown; final GestureTapUpCallback? onTertiaryTapUp; final bool readOnly; @override State createState() => _TerminalGestureHandlerState(); } class _TerminalGestureHandlerState extends State { TerminalViewState get terminalView => widget.terminalView; RenderTerminal get renderTerminal => terminalView.renderTerminal; DragStartDetails? _lastDragStartDetails; LongPressStartDetails? _lastLongPressStartDetails; @override Widget build(BuildContext context) { return TerminalGestureDetector( child: widget.child, onTapUp: widget.onTapUp, onSingleTapUp: onSingleTapUp, onTapDown: onTapDown, onSecondaryTapDown: onSecondaryTapDown, onSecondaryTapUp: onSecondaryTapUp, onTertiaryTapDown: onSecondaryTapDown, onTertiaryTapUp: onSecondaryTapUp, onLongPressStart: onLongPressStart, onLongPressMoveUpdate: onLongPressMoveUpdate, // onLongPressUp: onLongPressUp, onDragStart: onDragStart, onDragUpdate: onDragUpdate, onDoubleTapDown: onDoubleTapDown, ); } bool get _shouldSendTapEvent => !widget.readOnly && widget.terminalController.shouldSendPointerInput(PointerInput.tap); void _tapDown( GestureTapDownCallback? callback, TapDownDetails details, TerminalMouseButton button, { bool forceCallback = false, }) { // Check if the terminal should and can handle the tap down event. var handled = false; if (_shouldSendTapEvent) { handled = renderTerminal.mouseEvent( button, TerminalMouseButtonState.down, details.localPosition, ); } // If the event was not handled by the terminal, use the supplied callback. if (!handled || forceCallback) { callback?.call(details); } } void _tapUp( GestureTapUpCallback? callback, TapUpDetails details, TerminalMouseButton button, { bool forceCallback = false, }) { // Check if the terminal should and can handle the tap up event. var handled = false; if (_shouldSendTapEvent) { handled = renderTerminal.mouseEvent( button, TerminalMouseButtonState.up, details.localPosition, ); } // If the event was not handled by the terminal, use the supplied callback. if (!handled && forceCallback) { callback?.call(details); } } void onTapDown(TapDownDetails details) { // onTapDown is special, as it will always call the supplied callback. // The TerminalView depends on it to bring the terminal into focus. _tapDown( widget.onTapDown, details, TerminalMouseButton.left, forceCallback: true, ); } void onSingleTapUp(TapUpDetails details) { _tapUp(widget.onSingleTapUp, details, TerminalMouseButton.left); } void onSecondaryTapDown(TapDownDetails details) { _tapDown(widget.onSecondaryTapDown, details, TerminalMouseButton.right); } void onSecondaryTapUp(TapUpDetails details) { _tapUp(widget.onSecondaryTapUp, details, TerminalMouseButton.right); } void onTertiaryTapDown(TapDownDetails details) { _tapDown(widget.onTertiaryTapDown, details, TerminalMouseButton.middle); } void onTertiaryTapUp(TapUpDetails details) { _tapUp(widget.onTertiaryTapUp, details, TerminalMouseButton.right); } void onDoubleTapDown(TapDownDetails details) { renderTerminal.selectWord(details.localPosition); } void onLongPressStart(LongPressStartDetails details) { _lastLongPressStartDetails = details; renderTerminal.selectWord(details.localPosition); } void onLongPressMoveUpdate(LongPressMoveUpdateDetails details) { renderTerminal.selectWord( _lastLongPressStartDetails!.localPosition, details.localPosition, ); } // void onLongPressUp() {} void onDragStart(DragStartDetails details) { _lastDragStartDetails = details; details.kind == PointerDeviceKind.mouse ? renderTerminal.selectCharacters(details.localPosition) : renderTerminal.selectWord(details.localPosition); } void onDragUpdate(DragUpdateDetails details) { renderTerminal.selectCharacters( _lastDragStartDetails!.localPosition, details.localPosition, ); } }