Browse Source

Merge pull request #40 from devmil/feature/double-tap-select

Select word / whole row via double tap
xuty 4 years ago
parent
commit
c0634ba09f

+ 18 - 6
lib/frontend/terminal_view.dart

@@ -97,6 +97,7 @@ class _TerminalViewState extends State<TerminalView> {
   }
 
   late CellSize _cellSize;
+  Position? _tapPosition;
 
   /// Scroll position from the terminal. Not null if terminal scroll extent has
   /// been updated and needs to be syncronized to flutter side.
@@ -222,19 +223,30 @@ class _TerminalViewState extends State<TerminalView> {
     return GestureDetector(
       behavior: HitTestBehavior.deferToChild,
       dragStartBehavior: DragStartBehavior.down,
-      onDoubleTapDown: (details) {
-        print('details : $details');
+      onDoubleTapDown: (detail) {
+        final pos = detail.localPosition;
+        _tapPosition = getMouseOffset(pos.dx, pos.dy);
       },
       onTapDown: (detail) {
+        final pos = detail.localPosition;
+        _tapPosition = getMouseOffset(pos.dx, pos.dy);
+      },
+      onDoubleTap: () {
+        if (_tapPosition != null) {
+          widget.terminal.onMouseDoubleTap(_tapPosition!);
+          widget.terminal.refresh();
+        }
+      },
+      onTap: () {
         if (widget.terminal.selection?.isEmpty ?? true) {
           InputListener.of(context)!.requestKeyboard();
         } else {
           widget.terminal.clearSelection();
         }
-        final pos = detail.localPosition;
-        final offset = getMouseOffset(pos.dx, pos.dy);
-        widget.terminal.onMouseTap(offset);
-        widget.terminal.refresh();
+        if (_tapPosition != null) {
+          widget.terminal.onMouseTap(_tapPosition!);
+          widget.terminal.refresh();
+        }
       },
       onPanStart: (detail) {
         final pos = detail.localPosition;

+ 6 - 0
lib/mouse/mouse_mode.dart

@@ -10,6 +10,7 @@ abstract class MouseMode {
   // static const buttonEvent = MouseModeX10();
 
   void onTap(Terminal terminal, Position offset);
+  void onDoubleTap(Terminal terminal, Position offset) {}
   void onPanStart(Terminal terminal, Position offset) {}
   void onPanUpdate(Terminal terminal, Position offset) {}
 }
@@ -22,6 +23,11 @@ class MouseModeNone extends MouseMode {
     terminal.debug.onMsg('tap: $offset');
   }
 
+  @override
+  void onDoubleTap(Terminal terminal, Position offset) {
+    terminal.selectWordOrRow(offset);
+  }
+
   @override
   void onPanStart(Terminal terminal, Position offset) {
     terminal.selection!.init(offset);

+ 64 - 0
lib/terminal/terminal.dart

@@ -466,6 +466,65 @@ class Terminal with Observable implements TerminalUiInteraction {
     }
   }
 
+  final wordSeparatorCodes = <String>[
+    String.fromCharCode(0),
+    ' ',
+    '.',
+    ':',
+    '/'
+  ];
+
+  void selectWordOrRow(Position position) {
+    if (position.y > buffer.lines.length) {
+      return;
+    }
+
+    final row = position.y;
+
+    final line = buffer.lines[row];
+
+    final positionIsInSelection = _selection.contains(position);
+    final completeLineIsSelected =
+        _selection.start?.x == 0 && _selection.end?.x == terminalWidth;
+
+    if (positionIsInSelection && !completeLineIsSelected) {
+      // select area on an already existing selection extends it to the full line
+      _selection.clear();
+      _selection.init(Position(0, row));
+      _selection.update(Position(terminalWidth, row));
+    } else {
+      // select the word that is under position
+
+      var start = position.x;
+      var end = position.x;
+
+      do {
+        if (start == 0) {
+          break;
+        }
+        final content = line.cellGetContent(start - 1);
+        if (wordSeparatorCodes.contains(String.fromCharCode(content))) {
+          break;
+        }
+        start--;
+      } while (true);
+      do {
+        if (end >= terminalWidth - 1) {
+          break;
+        }
+        final content = line.cellGetContent(end + 1);
+        if (wordSeparatorCodes.contains(String.fromCharCode(content))) {
+          break;
+        }
+        end++;
+      } while (true);
+
+      _selection.clear();
+      _selection.init(Position(start, row));
+      _selection.update(Position(end, row));
+    }
+  }
+
   String? getSelectedText() {
     if (_selection.isEmpty) {
       return null;
@@ -591,6 +650,11 @@ class Terminal with Observable implements TerminalUiInteraction {
     mouseMode.onTap(this, position);
   }
 
+  @override
+  onMouseDoubleTap(Position position) {
+    mouseMode.onDoubleTap(this, position);
+  }
+
   @override
   void onPanStart(Position position) {
     mouseMode.onPanStart(this, position);

+ 8 - 0
lib/terminal/terminal_isolate.dart

@@ -21,6 +21,7 @@ enum _IsolateCommand {
   refresh,
   clearSelection,
   mouseTap,
+  mouseDoubleTap,
   mousePanStart,
   mousePanUpdate,
   setScrollOffsetFromTop,
@@ -94,6 +95,9 @@ void terminalMain(SendPort port) async {
       case _IsolateCommand.mouseTap:
         _terminal?.mouseMode.onTap(_terminal, msg[1]);
         break;
+      case _IsolateCommand.mouseDoubleTap:
+        _terminal?.mouseMode.onDoubleTap(_terminal, msg[1]);
+        break;
       case _IsolateCommand.mousePanStart:
         _terminal?.mouseMode.onPanStart(_terminal, msg[1]);
         break;
@@ -405,6 +409,10 @@ class TerminalIsolate with Observable implements TerminalUiInteraction {
     _sendPort?.send([_IsolateCommand.mouseTap, position]);
   }
 
+  void onMouseDoubleTap(Position position) {
+    _sendPort?.send([_IsolateCommand.mouseDoubleTap, position]);
+  }
+
   void onPanStart(Position position) {
     _sendPort?.send([_IsolateCommand.mousePanStart, position]);
   }

+ 3 - 0
lib/terminal/terminal_ui_interaction.dart

@@ -71,6 +71,9 @@ abstract class TerminalUiInteraction with Observable {
   /// notify the Terminal about a mouse tap
   void onMouseTap(Position position);
 
+  /// notify the Terminal about a mouse double tap
+  void onMouseDoubleTap(Position position);
+
   /// notify the Terminal about a pan start
   void onPanStart(Position position);