-
Notifications
You must be signed in to change notification settings - Fork 847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added an option for ClipboardPaste button to disable ClipboardMonitor #2427
base: master
Are you sure you want to change the base?
Conversation
Thanks for your contribution.
A breaking change is not only the kind that requires users to update their code to compile, but to also keep the current state without any behavior breaking. Changing the default of a value is also a breaking change. What would happen if the user pressed the paste button and there was nothing to paste? I suggest providing the option to override the I'm with the idea of disabling something that's causing issues. However, users should have the option to revert to the old behavior in case they still want |
Ok I understand
Nothing will happen. There are few checks that prevent breaking everything on calling "paste" function. I double checked it with Web & Android version and I strongly believe that iOS will not break either (don't have it right now, so I can't check) QuillController.clipboardPaste handles this quite well, here is the full function: /// Returns whether paste operation was handled here.
/// [updateEditor] is called if paste operation was successful.
@experimental
Future<bool> clipboardPaste({void Function()? updateEditor}) async {
if (readOnly || !selection.isValid) return true;
final clipboardConfig = config.clipboardConfig;
if (await clipboardConfig?.onClipboardPaste?.call() == true) {
updateEditor?.call();
return true;
}
final pasteInternalImageSuccess = await _pasteInternalImage();
if (pasteInternalImageSuccess) {
updateEditor?.call();
return true;
}
const enableExternalRichPasteDefault = true;
if (clipboardConfig?.enableExternalRichPaste ??
enableExternalRichPasteDefault) {
final pasteHtmlSuccess = await pasteHTML();
if (pasteHtmlSuccess) {
updateEditor?.call();
return true;
}
final pasteMarkdownSuccess = await pasteMarkdown();
if (pasteMarkdownSuccess) {
updateEditor?.call();
return true;
}
}
final clipboardService = ClipboardServiceProvider.instance;
final onImagePaste = clipboardConfig?.onImagePaste;
if (onImagePaste != null) {
final imageBytes = await clipboardService.getImageFile();
if (imageBytes != null) {
final imageUrl = await onImagePaste(imageBytes);
if (imageUrl != null) {
replaceText(
plainTextEditingValue.selection.end,
0,
BlockEmbed.image(imageUrl),
null,
);
updateEditor?.call();
return true;
}
}
}
final onGifPaste = clipboardConfig?.onGifPaste;
if (onGifPaste != null) {
final gifBytes = await clipboardService.getGifFile();
if (gifBytes != null) {
final gifUrl = await onGifPaste(gifBytes);
if (gifUrl != null) {
replaceText(
plainTextEditingValue.selection.end,
0,
BlockEmbed.image(gifUrl),
null,
);
updateEditor?.call();
return true;
}
}
}
// Only process plain text if no image/gif was pasted.
// Snapshot the input before using `await`.
// See https://github.com/flutter/flutter/issues/11427
final plainText = (await Clipboard.getData(Clipboard.kTextPlain))?.text;
if (plainText != null) {
final plainTextToPaste = await getTextToPaste(plainText);
if (pastePlainTextOrDelta(plainTextToPaste,
pastePlainText: _pastePlainText, pasteDelta: _pasteDelta)) {
updateEditor?.call();
return true;
}
}
final onUnprocessedPaste = clipboardConfig?.onUnprocessedPaste;
if (onUnprocessedPaste != null) {
if (await onUnprocessedPaste()) {
updateEditor?.call();
return true;
}
}
return false;
}
I will then provide such option. |
Please check the most recent commit. This is passed directly to ClipboardButton with .paste action As for the ClipboardButton itself and monitor:
/// If null, uses in-built [ClipboardMonitor]
/// If true, paste button is enabled (if true & not readonly)
/// If false, paste button is disabled
final bool? enableClipboardPaste;
return !controller.readOnly &&
(kIsWeb || (widget.enableClipboardPaste ?? _monitor.canPaste)); |
This is still a breaking change.
This option should be only in the button config, not the toolbar config. |
Reverted to 1 second |
I agree that it would be the best way to do it. Unfortunately I couldn't find the proper place to add a new value, could you elaborate here? final QuillToolbarToggleStyleButtonOptions clipboardPaste; import 'package:flutter/foundation.dart' show immutable;
import 'package:meta/meta.dart';
import '../base_button_options.dart';
class QuillToolbarToggleStyleButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions
implements QuillToolbarBaseButtonExtraOptionsIsToggled {
const QuillToolbarToggleStyleButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
required this.isToggled,
});
@override
final bool isToggled;
}
@immutable
class QuillToolbarToggleStyleButtonOptions
extends QuillToolbarBaseButtonOptions<QuillToolbarToggleStyleButtonOptions,
QuillToolbarToggleStyleButtonExtraOptions> {
const QuillToolbarToggleStyleButtonOptions({
super.iconData,
super.iconSize,
super.iconButtonFactor,
super.tooltip,
super.afterButtonPressed,
super.iconTheme,
super.childBuilder,
});
} |
I would love to, but since it's coded in the way that we have little control over ClipboardMonitor it's difficult to update it's state or to stop the timer when the button already exists. To stop the timer we need to trigger "didUpdateWidget" and add/remove extra listeners but it only happens when controller changes. @override
void didUpdateWidget(QuillToolbarClipboardButton oldWidget) {
super.didUpdateWidget(oldWidget);
// Default didUpdateWidget handler, otherwise simple flag change didn't stop the monitor
if (oldWidget.controller != controller) {
oldWidget.controller.removeListener(didChangeEditingValue);
removeExtraListener(oldWidget);
controller.addListener(didChangeEditingValue);
addExtraListener();
currentValue = currentStateValue;
}
// If controller didn't change, but something else did (enableClipboardPaste)
else if (widget.clipboardAction == ClipboardAction.paste) {
// If enableClipboardPaste is not null, disable monitors
if (widget.enableClipboardPaste != null) {
_monitor.monitorClipboard(false, _listenClipboardStatus);
currentValue = currentStateValue;
} else {
// Otherwise remove old listener and add a new one
_monitor.monitorClipboard(true, _listenClipboardStatus);
currentValue = currentStateValue;
}
}
} |
I think adding "isEnabled" to QuillToolbarBaseButtonOptions would be the best option, but this would require adding the functionality of this option to all the buttons. I can do it if you give me a green light. |
On the video you can see that it works for all 3 cases (null, true, false) and the timer will continue as normal if it is set back to "null" flutter.quill.clipboard.monitor.mp4 |
@EchoEllet can we merge this? |
This is still a breaking change.
This option should be only in the button config, not the toolbar config.
I will work on it soon. However, it's not a high-priorty issue given that I have disabled the buttons by default (revert to the default behavior). |
Description
According to the issue #2421 ClipboardMonitor was causing lag if "paste" button was enabled in QuillSimpleToolbar.
That was because of the periodic 1 second check for Clipboard content. In case with large strings or images in clipboard, it would lag out the application and slow down everything drastically.
This is not a breaking change, however it will change the behavior of availability for "paste" button for QuillSimpleToolbar since it always be enabled (except of readonly mode).
Related Issues
Type of Change