Răsfoiți Sursa

Implement default keyboard shortcuts

xuty 3 ani în urmă
părinte
comite
7f6c077438

+ 4 - 4
lib/src/core/buffer/range.dart

@@ -27,16 +27,16 @@ class BufferRange {
   }
 
   Iterable<BufferSegment> toSegments() sync* {
-    var start = this.begin;
+    var begin = this.begin;
     var end = this.end;
 
     if (!isNormalized) {
       end = this.begin;
-      start = this.end;
+      begin = this.end;
     }
 
-    for (var i = start.y; i <= end.y; i++) {
-      var startX = i == start.y ? start.x : null;
+    for (var i = begin.y; i <= end.y; i++) {
+      var startX = i == begin.y ? begin.x : null;
       var endX = i == end.y ? end.x : null;
       yield BufferSegment(this, i, startX, endX);
     }

+ 32 - 14
lib/src/terminal_view.dart

@@ -13,6 +13,7 @@ import 'package:xterm/src/ui/gesture/gesture_handler.dart';
 import 'package:xterm/src/ui/input_map.dart';
 import 'package:xterm/src/ui/keyboard_visibility.dart';
 import 'package:xterm/src/ui/render.dart';
+import 'package:xterm/src/ui/shortcut/actions.dart';
 import 'package:xterm/src/ui/shortcut/shortcuts.dart';
 import 'package:xterm/src/ui/terminal_text_style.dart';
 import 'package:xterm/src/ui/terminal_theme.dart';
@@ -110,8 +111,8 @@ class TerminalView extends StatefulWidget {
   /// default.
   final bool deleteDetection;
 
-  /// Shortcuts for this terminal. This has higher priority than the input
-  /// handler. If not provided, [defaultTerminalShortcuts] will be used.
+  /// Shortcuts for this terminal. This has higher priority than input handler
+  /// of the terminal If not provided, [defaultTerminalShortcuts] will be used.
   final Map<ShortcutActivator, Intent>? shortcuts;
 
   @override
@@ -121,6 +122,8 @@ class TerminalView extends StatefulWidget {
 class TerminalViewState extends State<TerminalView> {
   late final FocusNode _focusNode;
 
+  late final ShortcutManager _shortcutManager;
+
   final _customTextEditKey = GlobalKey<CustomTextEditState>();
 
   final _scrollableKey = GlobalKey<ScrollableState>();
@@ -141,6 +144,9 @@ class TerminalViewState extends State<TerminalView> {
     _focusNode = widget.focusNode ?? FocusNode();
     _controller = widget.controller ?? TerminalController();
     _scrollController = widget.scrollController ?? ScrollController();
+    _shortcutManager = ShortcutManager(
+      shortcuts: widget.shortcuts ?? defaultTerminalShortcuts,
+    );
     super.initState();
   }
 
@@ -161,6 +167,7 @@ class TerminalViewState extends State<TerminalView> {
   @override
   void dispose() {
     _focusNode.dispose();
+    _shortcutManager.dispose();
     super.dispose();
   }
 
@@ -188,17 +195,6 @@ class TerminalViewState extends State<TerminalView> {
       },
     );
 
-    child = Shortcuts(
-      shortcuts: widget.shortcuts ?? defaultTerminalShortcuts,
-      child: child,
-    );
-
-    child = Container(
-      color: widget.theme.background.withOpacity(widget.backgroundOpacity),
-      padding: widget.padding,
-      child: child,
-    );
-
     child = CustomTextEdit(
       key: _customTextEditKey,
       focusNode: _focusNode,
@@ -226,6 +222,12 @@ class TerminalViewState extends State<TerminalView> {
       child: child,
     );
 
+    child = TerminalActions(
+      terminal: widget.terminal,
+      controller: _controller,
+      child: child,
+    );
+
     child = KeyboardVisibilty(
       onKeyboardShow: _onKeyboardShow,
       child: child,
@@ -247,6 +249,12 @@ class TerminalViewState extends State<TerminalView> {
       child: child,
     );
 
+    child = Container(
+      color: widget.theme.background.withOpacity(widget.backgroundOpacity),
+      padding: widget.padding,
+      child: child,
+    );
+
     return child;
   }
 
@@ -285,7 +293,17 @@ class TerminalViewState extends State<TerminalView> {
     return _customTextEditKey.currentState?.hasInputConnection == true;
   }
 
-  KeyEventResult _onKeyEvent(RawKeyEvent event) {
+  KeyEventResult _onKeyEvent(FocusNode focusNode, RawKeyEvent event) {
+    // ignore: invalid_use_of_protected_member
+    final shortcutResult = _shortcutManager.handleKeypress(
+      focusNode.context!,
+      event,
+    );
+
+    if (shortcutResult == KeyEventResult.handled) {
+      return KeyEventResult.handled;
+    }
+
     if (event is! RawKeyDownEvent) {
       return KeyEventResult.ignored;
     }

+ 2 - 2
lib/src/ui/custom_text_edit.dart

@@ -31,7 +31,7 @@ class CustomTextEdit extends StatefulWidget {
 
   final void Function(TextInputAction) onAction;
 
-  final KeyEventResult Function(RawKeyEvent) onKey;
+  final KeyEventResult Function(FocusNode, RawKeyEvent) onKey;
 
   final FocusNode focusNode;
 
@@ -129,7 +129,7 @@ class CustomTextEditState extends State<CustomTextEdit>
 
   KeyEventResult _onKey(FocusNode focusNode, RawKeyEvent event) {
     if (_currentEditingState.composing.isCollapsed) {
-      return widget.onKey(event);
+      return widget.onKey(focusNode, event);
     }
 
     return KeyEventResult.skipRemainingHandlers;

+ 29 - 43
lib/src/ui/shortcut/actions.dart

@@ -1,17 +1,20 @@
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
-import 'package:xterm/core.dart';
 import 'package:xterm/src/ui/shortcut/intents.dart';
+import 'package:xterm/xterm.dart';
 
 class TerminalActions extends StatelessWidget {
   const TerminalActions({
     super.key,
     required this.terminal,
+    required this.controller,
     required this.child,
   });
 
   final Terminal terminal;
 
+  final TerminalController controller;
+
   final Widget child;
 
   @override
@@ -26,54 +29,37 @@ class TerminalActions extends StatelessWidget {
             final text = data?.text;
             if (text != null) {
               terminal.paste(text);
+              controller.clearSelection();
             }
+            return null;
           },
         ),
-        // TerminalCopyIntent: CallbackAction(
-        //   onInvoke: (Intent intent) => terminal.copy(),
-        // ),
-        // TerminalSelectAllIntent: CallbackAction(
-        //   onInvoke: (Intent intent) => terminal.selectAll(),
-        // ),
-      },
-      child: child,
-    );
-  }
-}
-
-class TerminalPasteAction extends Action<TerminalPasteIntent> {
-  TerminalPasteAction(this.terminal);
-
-  final Terminal terminal;
-
-  @override
-  void invoke(TerminalPasteIntent intent) async {
-    final data = await Clipboard.getData(Clipboard.kTextPlain);
-    final text = data?.text;
-    if (text != null) {
-      terminal.paste(text);
-    }
-  }
-}
-
-class TerminalCopyAction extends Action<TerminalCopyIntent> {
-  TerminalCopyAction(this.terminal);
-
-  final Terminal terminal;
+        TerminalCopyIntent: CallbackAction(
+          onInvoke: (Intent intent) async {
+            final selection = controller.selection;
 
-  @override
-  void invoke(TerminalCopyIntent intent) {
-    // terminal
-  }
-}
+            if (selection == null) {
+              return;
+            }
 
-class TerminalSelectAllAction extends Action<TerminalSelectAllIntent> {
-  TerminalSelectAllAction(this.terminal);
+            final text = terminal.buffer.getText(selection);
 
-  final Terminal terminal;
+            await Clipboard.setData(ClipboardData(text: text));
 
-  @override
-  void invoke(TerminalSelectAllIntent intent) {
-    // terminal.selectAll();
+            return null;
+          },
+        ),
+        TerminalSelectAllIntent: CallbackAction(onInvoke: (Intent intent) {
+          controller.setSelection(
+            BufferRange(
+              CellOffset(0, terminal.buffer.height - terminal.viewHeight),
+              CellOffset(terminal.viewWidth, terminal.buffer.height - 1),
+            ),
+          );
+          return null;
+        }),
+      },
+      child: child,
+    );
   }
 }

+ 14 - 14
pubspec.lock

@@ -42,7 +42,7 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.8.2"
+    version: "2.9.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -112,7 +112,7 @@ packages:
       name: characters
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0"
+    version: "1.2.1"
   charcode:
     dependency: transitive
     description:
@@ -133,7 +133,7 @@ packages:
       name: clock
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.1"
   code_builder:
     dependency: transitive
     description:
@@ -203,7 +203,7 @@ packages:
       name: fake_async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.0"
+    version: "1.3.1"
   file:
     dependency: transitive
     description:
@@ -311,21 +311,21 @@ packages:
       name: matcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.11"
+    version: "0.12.12"
   material_color_utilities:
     dependency: transitive
     description:
       name: material_color_utilities
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.1.4"
+    version: "0.1.5"
   meta:
     dependency: "direct main"
     description:
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.7.0"
+    version: "1.8.0"
   mime:
     dependency: transitive
     description:
@@ -360,7 +360,7 @@ packages:
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.1"
+    version: "1.8.2"
   pedantic:
     dependency: transitive
     description:
@@ -470,7 +470,7 @@ packages:
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.2"
+    version: "1.9.0"
   stack_trace:
     dependency: transitive
     description:
@@ -498,35 +498,35 @@ packages:
       name: string_scanner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.1"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0"
+    version: "1.2.1"
   test:
     dependency: "direct dev"
     description:
       name: test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.21.1"
+    version: "1.21.4"
   test_api:
     dependency: transitive
     description:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.9"
+    version: "0.4.12"
   test_core:
     dependency: transitive
     description:
       name: test_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.13"
+    version: "0.4.16"
   timing:
     dependency: transitive
     description: