浏览代码

Merge pull request #1550 from LucasXu0/fix_windows_copy_paste

fix: Clipboard does not work in Windows #1406
Lucas.Xu 2 年之前
父节点
当前提交
a507fb8ec6

+ 54 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/infra/clipboard.dart

@@ -0,0 +1,54 @@
+import 'dart:io' show Platform;
+
+import 'package:rich_clipboard/rich_clipboard.dart';
+
+class AppFlowyClipboardData {
+  const AppFlowyClipboardData({
+    required this.text,
+    required this.html,
+  });
+  final String? text;
+  final String? html;
+}
+
+class AppFlowyClipboard {
+  static Future<void> setData({
+    String? text,
+    String? html,
+  }) async {
+    // https://github.com/BringingFire/rich_clipboard/issues/13
+    // Wrapping a `<html><body>` tag for html in Windows,
+    //  otherwise it will raise an exception
+    if (Platform.isWindows && html != null) {
+      if (!html.startsWith('<html><body>')) {
+        html = '<html><body>$html</body></html>';
+      }
+    }
+
+    return RichClipboard.setData(
+      RichClipboardData(
+        text: text,
+        html: html,
+      ),
+    );
+  }
+
+  static Future<AppFlowyClipboardData> getData() async {
+    final data = await RichClipboard.getData();
+    final text = data.text;
+    var html = data.html;
+
+    // https://github.com/BringingFire/rich_clipboard/issues/13
+    // Remove all the fragment symbol in Windows.
+    if (Platform.isWindows && html != null) {
+      html = html
+          .replaceAll('<!--StartFragment-->', '')
+          .replaceAll('<!--EndFragment-->', '');
+    }
+
+    return AppFlowyClipboardData(
+      text: text,
+      html: html,
+    );
+  }
+}

+ 2 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart

@@ -1,7 +1,7 @@
 import 'package:appflowy_editor/src/core/document/node.dart';
+import 'package:appflowy_editor/src/infra/clipboard.dart';
 import 'package:appflowy_editor/src/service/render_plugin_service.dart';
 import 'package:flutter/material.dart';
-import 'package:rich_clipboard/rich_clipboard.dart';
 
 import 'image_node_widget.dart';
 
@@ -21,7 +21,7 @@ class ImageNodeBuilder extends NodeWidgetBuilder<Node> {
       width: width,
       alignment: _textToAlignment(align),
       onCopy: () {
-        RichClipboard.setData(RichClipboardData(text: src));
+        AppFlowyClipboard.setData(text: src);
       },
       onDelete: () {
         final transaction = context.editorState.transaction

+ 2 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart

@@ -2,6 +2,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/src/commands/text/text_commands.dart';
 import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
 import 'package:appflowy_editor/src/flutter/overlay.dart';
+import 'package:appflowy_editor/src/infra/clipboard.dart';
 import 'package:appflowy_editor/src/infra/flowy_svg.dart';
 import 'package:appflowy_editor/src/render/link_menu/link_menu.dart';
 import 'package:appflowy_editor/src/extensions/text_node_extensions.dart';
@@ -9,7 +10,6 @@ import 'package:appflowy_editor/src/extensions/editor_state_extensions.dart';
 import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart';
 
 import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
-import 'package:rich_clipboard/rich_clipboard.dart';
 
 typedef ToolbarItemEventHandler = void Function(
     EditorState editorState, BuildContext context);
@@ -363,7 +363,7 @@ void showLinkMenu(
             _dismissLinkMenu();
           },
           onCopyLink: () {
-            RichClipboard.setData(RichClipboardData(text: linkText));
+            AppFlowyClipboard.setData(text: linkText);
             _dismissLinkMenu();
           },
           onRemoveLink: () {

+ 14 - 10
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart

@@ -1,9 +1,9 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:appflowy_editor/src/infra/clipboard.dart';
 import 'package:appflowy_editor/src/infra/html_converter.dart';
 import 'package:appflowy_editor/src/core/document/node_iterator.dart';
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart';
 import 'package:flutter/material.dart';
-import 'package:rich_clipboard/rich_clipboard.dart';
 
 int _textLengthOfNode(Node node) {
   if (node is TextNode) {
@@ -38,14 +38,15 @@ void _handleCopy(EditorState editorState) async {
               startOffset: selection.start.offset,
               endOffset: selection.end.offset)
           .toHTMLString();
+      final textString = textNode.toPlainText().substring(
+            selection.startIndex,
+            selection.endIndex,
+          );
       Log.keyboard.debug('copy html: $htmlString');
-      RichClipboard.setData(RichClipboardData(
+      AppFlowyClipboard.setData(
+        text: textString,
         html: htmlString,
-        text: textNode.toPlainText().substring(
-              selection.startIndex,
-              selection.endIndex,
-            ),
-      ));
+      );
     } else {
       Log.keyboard.debug('unimplemented: copy non-text');
     }
@@ -79,7 +80,10 @@ void _handleCopy(EditorState editorState) async {
     }
     text += '\n';
   }
-  RichClipboard.setData(RichClipboardData(html: html, text: text));
+  AppFlowyClipboard.setData(
+    text: text,
+    html: html,
+  );
 }
 
 void _pasteHTML(EditorState editorState, String html) {
@@ -186,7 +190,7 @@ void _pasteMultipleLinesInText(
 }
 
 void _handlePaste(EditorState editorState) async {
-  final data = await RichClipboard.getData();
+  final data = await AppFlowyClipboard.getData();
 
   if (editorState.cursorSelection?.isCollapsed ?? false) {
     _pastRichClipboard(editorState, data);
@@ -200,7 +204,7 @@ void _handlePaste(EditorState editorState) async {
   });
 }
 
-void _pastRichClipboard(EditorState editorState, RichClipboardData data) {
+void _pastRichClipboard(EditorState editorState, AppFlowyClipboardData data) {
   if (data.html != null) {
     _pasteHTML(editorState, data.html!);
     return;