diff --git a/CHANGELOG.md b/CHANGELOG.md index 96a2e2217..fc60e661f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `enableClipboardPaste` flag in `QuillToolbarClipboardButton` to determine if the button defaults to `null,` which will use `ClipboardMonitor`, which checks every second if the clipboard has content to paste [#2427](https://github.com/singerdmx/flutter-quill/pull/2427). + ## [11.0.0-dev.20] - 2025-01-19 ### Changed diff --git a/lib/src/toolbar/buttons/clipboard_button.dart b/lib/src/toolbar/buttons/clipboard_button.dart index 1b631e8ce..1e070de20 100644 --- a/lib/src/toolbar/buttons/clipboard_button.dart +++ b/lib/src/toolbar/buttons/clipboard_button.dart @@ -22,8 +22,11 @@ class ClipboardMonitor { bool get canPaste => _canPaste; Timer? _timer; + bool _isCheckingClipboard = false; + void monitorClipboard(bool add, void Function() listener) { if (kIsWeb) return; + if (add) { _timer = Timer.periodic( const Duration(seconds: 1), (timer) => _update(listener)); @@ -33,17 +36,27 @@ class ClipboardMonitor { } Future _update(void Function() listener) async { + if (_isCheckingClipboard) { + return; + } + + _isCheckingClipboard = true; + final clipboardService = ClipboardServiceProvider.instance; + if (await clipboardService.hasClipboardContent) { _canPaste = true; + listener(); } + + _isCheckingClipboard = false; } } @experimental class QuillToolbarClipboardButton extends QuillToolbarToggleStyleBaseButton { - QuillToolbarClipboardButton({ + const QuillToolbarClipboardButton({ required super.controller, required this.clipboardAction, QuillToolbarClipboardButtonOptions? options, @@ -55,14 +68,10 @@ class QuillToolbarClipboardButton extends QuillToolbarToggleStyleBaseButton { }) : _options = options, super(options: options ?? const QuillToolbarClipboardButtonOptions()); - // TODO: This field will be used by the PR: https://github.com/singerdmx/flutter-quill/pull/2427 - // ignore: unused_field final QuillToolbarClipboardButtonOptions? _options; final ClipboardAction clipboardAction; - final ClipboardMonitor _monitor = ClipboardMonitor(); - @override State createState() => QuillToolbarClipboardButtonState(); } @@ -70,6 +79,8 @@ class QuillToolbarClipboardButton extends QuillToolbarToggleStyleBaseButton { class QuillToolbarClipboardButtonState extends QuillToolbarToggleStyleBaseButtonState< QuillToolbarClipboardButton> { + final ClipboardMonitor _monitor = ClipboardMonitor(); + @override bool get currentStateValue { switch (widget.clipboardAction) { @@ -78,23 +89,54 @@ class QuillToolbarClipboardButtonState case ClipboardAction.copy: return !controller.selection.isCollapsed; case ClipboardAction.paste: - return !controller.readOnly && (kIsWeb || widget._monitor.canPaste); + return !controller.readOnly && + (kIsWeb || + (widget._options?.enableClipboardPaste ?? _monitor.canPaste)); } } void _listenClipboardStatus() => didChangeEditingValue(); + @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; + } + // The controller didn't change, but enableClipboardPaste did. + else if (widget.clipboardAction == ClipboardAction.paste) { + final isTimerActive = _monitor._timer?.isActive ?? false; + + // Enable clipboard monitoring if not active and should be monitored. + if (_shouldUseClipboardMonitor && !isTimerActive) { + _monitor.monitorClipboard(true, _listenClipboardStatus); + } + // Disable clipboard monitoring if active and should not be monitored. + else if (!_shouldUseClipboardMonitor && isTimerActive) { + _monitor.monitorClipboard(false, _listenClipboardStatus); + } + + currentValue = currentStateValue; + } + } + @override void addExtraListener() { - if (widget.clipboardAction == ClipboardAction.paste) { - widget._monitor.monitorClipboard(true, _listenClipboardStatus); + if (_shouldUseClipboardMonitor) { + _monitor.monitorClipboard(true, _listenClipboardStatus); } } @override void removeExtraListener(covariant QuillToolbarClipboardButton oldWidget) { - if (widget.clipboardAction == ClipboardAction.paste) { - oldWidget._monitor.monitorClipboard(false, _listenClipboardStatus); + if (_shouldUseClipboardMonitor) { + _monitor.monitorClipboard(false, _listenClipboardStatus); } } @@ -112,6 +154,11 @@ class QuillToolbarClipboardButtonState ClipboardAction.paste => Icons.paste_outlined, }; + bool get _shouldUseClipboardMonitor { + return widget.clipboardAction == ClipboardAction.paste && + (widget._options?.enableClipboardPaste == null); + } + void _onPressed() { switch (widget.clipboardAction) { case ClipboardAction.cut: