diff --git a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart index c0c76c18d8..6b1f89b45b 100644 --- a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart +++ b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart @@ -55,7 +55,7 @@ class _CupertinoDesktopTextSelectionControls extends TextSelectionControls { clipboardStatus: clipboardStatus, endpoints: endpoints, globalEditableRegion: globalEditableRegion, - handleCut: canCut(delegate) ? () => handleCut(delegate) : null, + handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null, handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null, handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null, handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null, diff --git a/packages/flutter/lib/src/cupertino/text_selection.dart b/packages/flutter/lib/src/cupertino/text_selection.dart index 45e88a841f..6b3dddeec2 100644 --- a/packages/flutter/lib/src/cupertino/text_selection.dart +++ b/packages/flutter/lib/src/cupertino/text_selection.dart @@ -236,7 +236,7 @@ class CupertinoTextSelectionControls extends TextSelectionControls { clipboardStatus: clipboardStatus, endpoints: endpoints, globalEditableRegion: globalEditableRegion, - handleCut: canCut(delegate) ? () => handleCut(delegate) : null, + handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null, handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null, handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null, handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null, diff --git a/packages/flutter/lib/src/material/desktop_text_selection.dart b/packages/flutter/lib/src/material/desktop_text_selection.dart index dde064040c..e24f4dbb26 100644 --- a/packages/flutter/lib/src/material/desktop_text_selection.dart +++ b/packages/flutter/lib/src/material/desktop_text_selection.dart @@ -41,7 +41,7 @@ class _DesktopTextSelectionControls extends TextSelectionControls { clipboardStatus: clipboardStatus, endpoints: endpoints, globalEditableRegion: globalEditableRegion, - handleCut: canCut(delegate) ? () => handleCut(delegate) : null, + handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null, handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null, handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null, handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null, diff --git a/packages/flutter/lib/src/material/text_selection.dart b/packages/flutter/lib/src/material/text_selection.dart index f1def2a5dc..ca1d892a90 100644 --- a/packages/flutter/lib/src/material/text_selection.dart +++ b/packages/flutter/lib/src/material/text_selection.dart @@ -45,7 +45,7 @@ class MaterialTextSelectionControls extends TextSelectionControls { endpoints: endpoints, delegate: delegate, clipboardStatus: clipboardStatus, - handleCut: canCut(delegate) ? () => handleCut(delegate) : null, + handleCut: canCut(delegate) ? () => handleCut(delegate, clipboardStatus) : null, handleCopy: canCopy(delegate) ? () => handleCopy(delegate, clipboardStatus) : null, handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null, handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null, diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 2995b5b1cf..a0cb911868 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2808,7 +2808,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien VoidCallback? _semanticsOnCut(TextSelectionControls? controls) { return widget.selectionEnabled && cutEnabled && _hasFocus && controls?.canCut(this) == true - ? () => controls!.handleCut(this) + ? () => controls!.handleCut(this, _clipboardStatus) : null; } diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 971d9c94f8..9feb7fe024 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -204,8 +204,9 @@ abstract class TextSelectionControls { /// /// This is called by subclasses when their cut affordance is activated by /// the user. - void handleCut(TextSelectionDelegate delegate) { + void handleCut(TextSelectionDelegate delegate, ClipboardStatusNotifier? clipboardStatus) { delegate.cutSelection(SelectionChangedCause.toolbar); + clipboardStatus?.update(); } /// Call [TextSelectionDelegate.copySelection] to copy current selection. diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 7f28c9dd8c..3ccb3c219c 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -8724,7 +8724,7 @@ class MockTextSelectionControls extends Fake implements TextSelectionControls { } @override - void handleCut(TextSelectionDelegate delegate) { + void handleCut(TextSelectionDelegate delegate, ClipboardStatusNotifier? clipboardStatus) { cutCount += 1; } diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart index ba3b28fb85..04f7327dde 100644 --- a/packages/flutter/test/widgets/text_selection_test.dart +++ b/packages/flutter/test/widgets/text_selection_test.dart @@ -759,6 +759,28 @@ void main() { }); }); }); + + group('TextSelectionControls', () { + test('ClipboardStatusNotifier is updated on handleCut', () async { + final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier(); + final FakeTextSelectionDelegate delegate = FakeTextSelectionDelegate(); + final CustomTextSelectionControls textSelectionControls = CustomTextSelectionControls(); + + expect(clipboardStatus.updateCalled, false); + textSelectionControls.handleCut(delegate, clipboardStatus); + expect(clipboardStatus.updateCalled, true); + }); + + test('ClipboardStatusNotifier is updated on handleCopy', () async { + final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier(); + final FakeTextSelectionDelegate delegate = FakeTextSelectionDelegate(); + final CustomTextSelectionControls textSelectionControls = CustomTextSelectionControls(); + + expect(clipboardStatus.updateCalled, false); + textSelectionControls.handleCopy(delegate, clipboardStatus); + expect(clipboardStatus.updateCalled, true); + }); + }); } class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGestureDetectorBuilderDelegate { @@ -872,3 +894,55 @@ class FakeRenderEditable extends RenderEditable { selectWordCalled = true; } } + +class CustomTextSelectionControls extends TextSelectionControls { + @override + Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight, [VoidCallback? onTap, double? startGlyphHeight, double? endGlyphHeight]) { + throw UnimplementedError(); + } + + @override + Widget buildToolbar( + BuildContext context, + Rect globalEditableRegion, + double textLineHeight, + Offset position, + List endpoints, + TextSelectionDelegate delegate, + ClipboardStatusNotifier clipboardStatus, + Offset? lastSecondaryTapDownPosition, + ) { + throw UnimplementedError(); + } + + @override + Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight, [double? startGlyphHeight, double? endGlyphHeight]) { + throw UnimplementedError(); + } + + @override + Size getHandleSize(double textLineHeight) { + throw UnimplementedError(); + } +} + +class FakeClipboardStatusNotifier extends ClipboardStatusNotifier { + FakeClipboardStatusNotifier() : super(value: ClipboardStatus.unknown); + + @override + bool get disposed => false; + + bool updateCalled = false; + @override + Future update() async { + updateCalled = true; + } +} + +class FakeTextSelectionDelegate extends Fake implements TextSelectionDelegate { + @override + void cutSelection(SelectionChangedCause cause) { } + + @override + void copySelection(SelectionChangedCause cause) { } +}