diff --git a/lib/src/models/documents/nodes/line.dart b/lib/src/models/documents/nodes/line.dart index 55f13bb4..475b97d7 100644 --- a/lib/src/models/documents/nodes/line.dart +++ b/lib/src/models/documents/nodes/line.dart @@ -393,12 +393,17 @@ class Line extends Container { final data = queryChild(offset, true); var node = data.node as Leaf?; if (node != null) { - var pos = node.length - data.offset; - result.add(Tuple2(beg, node.style)); + var pos = 0; + if (node is Text) { + pos = node.length - data.offset; + result.add(Tuple2(beg, node.style)); + } while (!node!.isLast && pos < local) { node = node.next as Leaf; - result.add(Tuple2(pos + beg, node.style)); - pos += node.length; + if (node is Text) { + result.add(Tuple2(pos + beg, node.style)); + pos += node.length; + } } } @@ -462,19 +467,23 @@ class Line extends Container { // Adjust first node final firstNodeLen = res[1].item1; var text = res[0].item2; - total.add(text.substring(text.length - firstNodeLen)); + if (text != Embed.kObjectReplacementCharacter) { + total.add(text.substring(text.length - firstNodeLen)); + } for (var i = 1; i < res.length - 1; i++) { - total.add(res[i].item2); + if (res[i].item2 != Embed.kObjectReplacementCharacter) { + total.add(res[i].item2); + } } // Adjust last node final lastNodeLen = len - res[res.length - 1].item1; text = res[res.length - 1].item2; - total.add(text.substring(0, lastNodeLen)); - return total - .skipWhile((value) => value == Embed.kObjectReplacementCharacter) - .join(); + if (text != Embed.kObjectReplacementCharacter) { + total.add(text.substring(0, lastNodeLen)); + } + return total.join(); } List> _getPlainText(int offset, int len, {int beg = 0}) { diff --git a/lib/src/widgets/editor.dart b/lib/src/widgets/editor.dart index c4933dfe..d5eab93a 100644 --- a/lib/src/widgets/editor.dart +++ b/lib/src/widgets/editor.dart @@ -6,6 +6,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import '../models/documents/style.dart'; +import 'package:tuple/tuple.dart'; import '../models/documents/document.dart'; import '../models/documents/nodes/container.dart' as container_node; @@ -48,6 +50,10 @@ abstract class EditorState extends State EditorTextSelectionOverlay? get selectionOverlay; + List> get pasteStyle; + + String get pastePlainText; + /// Controls the floating cursor animation when it is released. /// The floating cursor is animated to merge with the regular cursor. AnimationController get floatingCursorResetController; diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index bca95269..5ce4f400 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -17,6 +17,7 @@ import '../models/documents/document.dart'; import '../models/documents/nodes/block.dart'; import '../models/documents/nodes/line.dart'; import '../models/documents/nodes/node.dart'; +import '../models/documents/style.dart'; import '../utils/platform.dart'; import 'controller.dart'; import 'cursor.dart'; @@ -259,6 +260,14 @@ class RawEditorState extends EditorState // Theme DefaultStyles? _styles; + // for pasting style + @override + List> get pasteStyle => _pasteStyle; + List> _pasteStyle = >[]; + @override + String get pastePlainText => _pastePlainText; + String _pastePlainText = ''; + final ClipboardStatusNotifier _clipboardStatus = ClipboardStatusNotifier(); final LayerLink _toolbarLayerLink = LayerLink(); final LayerLink _startHandleLayerLink = LayerLink(); @@ -843,6 +852,8 @@ class RawEditorState extends EditorState @override void copySelection(SelectionChangedCause cause) { + _pastePlainText = widget.controller.getPlainText(); + _pasteStyle = widget.controller.getAllIndividualSelectionStyles(); // Copied straight from EditableTextState super.copySelection(cause); if (cause == SelectionChangedCause.toolbar) { @@ -865,6 +876,8 @@ class RawEditorState extends EditorState @override void cutSelection(SelectionChangedCause cause) { + _pastePlainText = widget.controller.getPlainText(); + _pasteStyle = widget.controller.getAllIndividualSelectionStyles(); // Copied straight from EditableTextState super.cutSelection(cause); if (cause == SelectionChangedCause.toolbar) { diff --git a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart index 4982c801..f22e417a 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart @@ -4,6 +4,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import '../../models/documents/nodes/leaf.dart'; +import '../../models/documents/style.dart'; import '../../utils/delta.dart'; import '../editor.dart'; @@ -24,6 +25,21 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState widget.controller.replaceText( diff.start, diff.deleted.length, insertedText, value.selection); + + if (insertedText == pastePlainText && pastePlainText != '') { + var pos = diff.start; + for ( var i = 0; i < pasteStyle.length; i++){ + int offset = pasteStyle[i].item1; + Style style = pasteStyle[i].item2; + if (i == pasteStyle.length - 1) { + style.attributes.forEach((k, v) => widget.controller + .formatText(pos + offset, pastePlainText.length - offset, v)); + } else { + style.attributes.forEach((k, v) => widget.controller + .formatText(pos + offset, pasteStyle[i + 1].item1, v)); + } + } + } } String _adjustInsertedText(String text) {