singerdmx / flutter-quill

Rich text editor for Flutter
https://pub.dev/packages/flutter_quill
MIT License
2.58k stars 828 forks source link

Add "Paste as plain text" feature #1946

Open Alspb opened 4 months ago

Alspb commented 4 months ago

Is there an existing issue for this?

Use case

It would be great to have "Paste as plain text" feature, as e.g. TextField on Android has. Sometimes format is not needed, like when text is copied from browser. Also, this should help avoid misunderstanding on when formatted or plain text is pasted, see #1772.

Proposal

Implement "Paste as plain text" feature and add it to the context menu for pasting. Maybe there should be an option on whether "Paste as plain text" should be included into that menu or not.

Alspb commented 4 months ago

It looks like to paste plain text one should disable the following code blocks from quill_controller.dart:

if (await _pasteHTML()) {
    updateEditor?.call();
    return true;
}
_applyPasteStyleAndEmbed(insertedText, index, containsEmbed);

But adding "Paste as plain text" to the context menu is not that straightforward, since the menu is implemented using EditableText.getEditableButtonItems.

singerdmx commented 4 months ago

@ellet0 Do you have any suggestion?

EchoEllet commented 4 months ago

If the user paste using the paste button above the keyboard, it should alwats paste as text. This behavior is specific to Android and will apply to all apps. As for the paste as plain text, we could also add the option to always paste as plain text and disable rich text pasting feature.

Notice that if you don't use flutter_quill_extensions, it will be displayed by default.

As for paste as plain text, it's might not be common or known by users, I would suggest having this option only if rich text paste is available.

Alspb commented 3 months ago

Indeed, Android keyboards (though, probably no all) allow to paste as plain text, as it in fact stored in the clipboard. So, it's some kind of an option too: "Paste" button pastes rich text while the keyboard pastes the plain one. However, it doesn't help on other OS.

Notice that if you don't use flutter_quill_extensions, it will be displayed by default.

Not quite sure how to check if flutter_quill_extensions is used. Do you mean that pasting as rich text is only available if flutter_quill_extensions is used? I can double-check but looks like text copied from the editor is always pasted as rich (but not sure about pasting from browser).

As for paste as plain text, it's might not be common or known by users, I would suggest having this option only if rich text paste is available.

Totally agree. But there're some technical problems I see: adding "Paste as plain text" to the context menu on Android, dynamically identifying if "Paste as plain text" button should be in the toolbar, and finding an appropriate icon for "Paste as plain text" button. Though, maybe there's no real need in "Paste as plain text" in the toolbar...

EchoEllet commented 3 months ago

Not quite sure how to check if flutter_quill_extensions is used.

There are different ways, one of them is to check the type of ClipboardService static instance.

Do you mean that pasting as rich text is only available if flutter_quill_extensions is used? I can double-check but looks like text copied from the editor is always pasted as rich (but not sure about pasting from browser

Starting from 9.4.0, the usages of super_clipboard have been moved from flutter_quill to flutter_quill_extensions.

The rich text, gif, and image features are disabled by default. You will have to use the extensions package and call a function to override the default implementation.

The example already utilized the extensions package and called the FlutterQuillExtensions.useSuperClipboardPlugin.

Alspb commented 3 months ago

Thanks, I'm able to change clipboard plugins by adding/removing FlutterQuillExtensions.useSuperClipboardPlugin, ClipboardService instance in the code confirms it. However, for some reason I can't reproduce pasting formatted text copied from the browser, no matter which clipboard plugin I use. I mean that getHTML function inside _pasteHTML method always returns null, not sure why.

Nevertheless, if I copy some inline text from the editor (i.e., several words on the same line), the format is preserved (for example, bold words remain bold) after paste, even for the default clipboard plugin. In fact, it looks like clipboard is not used here since the copied text and it's format are stored in the editor itself, see clipboardSelection method. So, at least pasting from the editor is always rich. At the same time, if several paragraphs are copied, the format is not preserved after paste. Maybe there's a reason behind this behavior but looks like a bug.

By the way, "instance" is misspelled in the code, we should correct it in the same PR: ClipboardServiceProvider.instacne.

EchoEllet commented 3 months ago

However, for some reason I can't reproduce pasting formatted text copied from the browser, no matter which clipboard plugin I use. I mean that getHTML function inside _pasteHTML method always returns null, not sure why.

That's usually because of the browser restrictions on using the clipboard on the Web.

Take a look at Super Clipboard Accessing clipboard on Web

Web browsers impose some restrictions when reading from clipboard. If value in clipboard has been copied from another application, user needs to confirm clipboard access (usually in form of popover). Asynchronous clipboard API is unavailable by default on Firefox. To get around this limitation, super_clipboard provides a way to listen to a browser clipboard event, which is triggered when user presses the appropriate keyboard shortcut in browser window (or selects the option from menu).

Previously the Editor requested permission by default, then later it was disabled by default.

Nevertheless, if I copy some inline text from the editor (i.e., several words on the same line), the format is preserved (for example, bold words remain bold) after paste, even for the default clipboard plugin. In fact, it looks like clipboard is not used here since the copied text and it's format are stored in the editor itself

Yes, this was a feature before ClipboardService and super_clipboard integration. However, we might want to document this to be more specific. Developers might use flutter_quill_extensions for this feature even if they are only interested in it for the Editor itself.

By the way, "instance" is misspelled in the code, we should correct it in the same PR: ClipboardServiceProvider.instacne.

Luckily we haven't exported the ClipboardService or related classes yet, so we can correct and update them at any time, I knew we would make a breaking change after a short time after introducing it in 9.4.0 so I decided not to export them for know.

EchoEllet commented 3 months ago

By the way, "instance" is misspelled in the code, we should correct it in the same PR: ClipboardServiceProvider.instacne.

https://github.com/singerdmx/flutter-quill/blob/ce5eb86b10ffc0cbccd1074b24e6028e3b2ea84d/lib/src/services/clipboard/clipboard_service_provider.dart#L7-L19

It seems that static ClipboardService get instacne => _instance; is the only typo.

The private _instance is correct.

Alspb commented 3 months ago

Do I understand correctly that currently copy+paste is supposed have following features?

  1. Base editor version: no style is preserved, including when a text is copied from the editor itself.
  2. flutter_quill_extensions: style is preserved when text is copied from the editor itself, including embeds.
  3. flutter_quill_extensions + super_clipboard: style is preserved also when copying from web or markdown.

If so, it looks reasonable. Sure, as for me, the most ideal solution would be to preserve text style (but not embeds!) even without using flutter_quill_extensions. So that without flutter_quill_extensions the editor becomes simple but powerful rich text editor, which in particular "remembers" style when text is copied and pasted. However, not sure if it's easy to split styles and embeds in the code. For example, methods like _applyPasteStyleAndEmbed should be split into two, and so on. If it's hard to implement, the approach above looks like literally the only way.

EchoEllet commented 3 months ago

Base editor version: no style is preserved, including when a text is copied from the editor itself.

If you copy anything from the editor and paste it somewhere else in the document of the editor, it should paste it as it was before since it it will retrive it from the QuillController itself and not from the system Clipboard

flutter_quill_extensions: style is preserved when text is copied from the editor itself, including embeds.

The styles should be already preserved without the extensions package.

flutter_quill_extensions + super_clipboard: style is preserved also when copying from web or markdown.

super_clipboard is a dependency of flutter_quill_extensions, so you don't need to explicitly include it once you include flutter_quill_extensions and call FlutterQuillExtensions.useSuperClipboard()

You should be able to paste HTML and Markdown files. And HTML from other apps and websites.

If you're testing on the web, then it doesn't work and doesn't require super_clipboard, instead, a paste event should be triggered and read the HTML text from the event.

We haven't implemented this feature yet, though.

See PR #2001 and Issue #1998 for details.

Alspb commented 2 months ago

Thanks, it's much clearer now. Seems that "Paste as plain text" feature might only be useful when using flutter_quill_extensions + super_clipboard.

EchoEllet commented 2 months ago

flutter_quill_extensions already have super_clipboard as a dependency.

Alspb commented 2 months ago

Sure, I mean that super_clipboard shouldn't work without running FlutterQuillExtensions.useSuperClipboardPlugin().

EchoEllet commented 1 month ago

Seems like an android feature, will add it and provide a property that should be enabled and user can enable it on other platforms, by default will be for Android.