From 2b3fa606ae76f533147222d6ac52f13f526908ee Mon Sep 17 00:00:00 2001 From: Michael Allen Date: Wed, 20 Apr 2022 15:28:01 -0700 Subject: [PATCH] - Fix selection handles show/hide after paste, backspace, copy (#769) --- lib/src/widgets/delegate.dart | 15 ++++++++--- lib/src/widgets/raw_editor.dart | 26 +++++++++---------- ...editor_state_selection_delegate_mixin.dart | 2 +- lib/src/widgets/text_selection.dart | 17 +++++++++++- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/lib/src/widgets/delegate.dart b/lib/src/widgets/delegate.dart index 21961871..82dfa2ca 100644 --- a/lib/src/widgets/delegate.dart +++ b/lib/src/widgets/delegate.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import '../../flutter_quill.dart'; import 'text_selection.dart'; @@ -252,9 +253,17 @@ class EditorTextSelectionGestureDetectorBuilder { void onDoubleTapDown(TapDownDetails details) { if (delegate.selectionEnabled) { renderEditor!.selectWord(SelectionChangedCause.tap); - if (shouldShowSelectionToolbar) { - editor!.showToolbar(); - } + // allow the selection to get updated before trying to bring up + // toolbars. + // + // if double tap happens on an editor that doesn't + // have focus, selection hasn't been set when the toolbars + // get added + SchedulerBinding.instance!.addPostFrameCallback((_) { + if (shouldShowSelectionToolbar) { + editor!.showToolbar(); + } + }); } } diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index 24c8bf3c..aa486afb 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -759,10 +759,10 @@ class RawEditorState extends EditorState void _updateOrDisposeSelectionOverlayIfNeeded() { if (_selectionOverlay != null) { - if (!_hasFocus) { + if (!_hasFocus || textEditingValue.selection.isCollapsed) { _selectionOverlay!.dispose(); _selectionOverlay = null; - } else if (!textEditingValue.selection.isCollapsed) { + } else { _selectionOverlay!.update(textEditingValue); } } else if (_hasFocus) { @@ -929,10 +929,6 @@ class RawEditorState extends EditorState if (cause == SelectionChangedCause.toolbar) { bringIntoView(textEditingValue.selection.extent); - // on iOS, Safari does not hide the selection after copy - // however, most other iOS apps do as well as other platforms - // so we'll hide toolbar & selection after copy - hideToolbar(false); // Collapse the selection and hide the toolbar and handles. userUpdateTextEditingValue( @@ -1008,13 +1004,17 @@ class RawEditorState extends EditorState _replaceText( ReplaceTextIntent(textEditingValue, data.text!, selection, cause)); - if (cause == SelectionChangedCause.toolbar) { - try { - // ignore exception when paste window is at end of document - bringIntoView(textEditingValue.selection.extent); - } catch (_) {} - hideToolbar(); - } + bringIntoView(textEditingValue.selection.extent); + + // Collapse the selection and hide the toolbar and handles. + userUpdateTextEditingValue( + TextEditingValue( + text: textEditingValue.text, + selection: TextSelection.collapsed( + offset: textEditingValue.selection.end), + ), + cause, + ); } /// Select the entire text value. 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 988a0da1..8ea0f337 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 @@ -136,7 +136,7 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState void hideToolbar([bool hideHandles = true]) { // If the toolbar is currently visible. if (selectionOverlay?.toolbar != null) { - selectionOverlay?.hideToolbar(); + hideHandles ? selectionOverlay?.hide() : selectionOverlay?.hideToolbar(); } } diff --git a/lib/src/widgets/text_selection.dart b/lib/src/widgets/text_selection.dart index 137979a0..03cc4c99 100644 --- a/lib/src/widgets/text_selection.dart +++ b/lib/src/widgets/text_selection.dart @@ -4,6 +4,7 @@ import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import '../models/documents/nodes/node.dart'; @@ -224,6 +225,11 @@ class EditorTextSelectionOverlay { Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor)! .insert(toolbar!); _toolbarController.forward(from: 0); + + // make sure handles are visible as well + if (_handles == null) { + showHandles(); + } } Widget _buildHandle( @@ -308,7 +314,16 @@ class EditorTextSelectionOverlay { Widget _buildToolbar(BuildContext context) { // Find the horizontal midpoint, just above the selected text. - final endpoints = renderObject.getEndpointsForSelection(_selection); + List endpoints; + + try { + // building with an invalid selection with throw an exception + // This happens where the selection has changed, but the toolbar + // hasn't been dismissed yet. + endpoints = renderObject.getEndpointsForSelection(_selection); + } catch (_) { + return Container(); + } final editingRegion = Rect.fromPoints( renderObject.localToGlobal(Offset.zero),