diff --git a/lib/src/widgets/raw_editor.dart b/lib/src/widgets/raw_editor.dart index d6496ee6..22738b8f 100644 --- a/lib/src/widgets/raw_editor.dart +++ b/lib/src/widgets/raw_editor.dart @@ -1050,13 +1050,15 @@ class RawEditorState extends EditorState value: textEditingValue, context: context, debugRequiredFor: widget, - toolbarLayerLink: _toolbarLayerLink, startHandleLayerLink: _startHandleLayerLink, endHandleLayerLink: _endHandleLayerLink, renderObject: renderEditor, selectionCtrls: widget.selectionCtrls, selectionDelegate: this, clipboardStatus: _clipboardStatus, + contextMenuBuilder: widget.contextMenuBuilder == null + ? null + : (context) => widget.contextMenuBuilder!(context, this), ); _selectionOverlay!.handlesVisible = _shouldShowSelectionHandles(); _selectionOverlay!.showHandles(); diff --git a/lib/src/widgets/text_selection.dart b/lib/src/widgets/text_selection.dart index 7930d468..ad1398e0 100644 --- a/lib/src/widgets/text_selection.dart +++ b/lib/src/widgets/text_selection.dart @@ -69,7 +69,6 @@ class EditorTextSelectionOverlay { EditorTextSelectionOverlay({ required this.value, required this.context, - required this.toolbarLayerLink, required this.startHandleLayerLink, required this.endHandleLayerLink, required this.renderObject, @@ -77,12 +76,11 @@ class EditorTextSelectionOverlay { required this.selectionCtrls, required this.selectionDelegate, required this.clipboardStatus, + required this.contextMenuBuilder, this.onSelectionHandleTapped, this.dragStartBehavior = DragStartBehavior.start, this.handlesVisible = false, }) { - final overlay = Overlay.of(context, rootOverlay: true); - // Clipboard status is only checked on first instance of // ClipboardStatusNotifier // if state has changed after creation, but prior to @@ -90,9 +88,6 @@ class EditorTextSelectionOverlay { // we won't know the status unless there is forced update // i.e. occasionally no paste clipboardStatus.update(); - - _toolbarController = AnimationController( - duration: const Duration(milliseconds: 150), vsync: overlay); } TextEditingValue value; @@ -122,10 +117,6 @@ class EditorTextSelectionOverlay { /// Debugging information for explaining why the [Overlay] is required. final Widget debugRequiredFor; - /// The object supplied to the [CompositedTransformTarget] that wraps the text - /// field. - final LayerLink toolbarLayerLink; - /// The objects supplied to the [CompositedTransformTarget] that wraps the /// location of start selection handle. final LayerLink startHandleLayerLink; @@ -144,6 +135,11 @@ class EditorTextSelectionOverlay { /// text field. final TextSelectionDelegate selectionDelegate; + /// {@macro flutter.widgets.EditableText.contextMenuBuilder} + /// + /// If not provided, no context menu will be built. + final WidgetBuilder? contextMenuBuilder; + /// Determines the way that drag start behavior is handled. /// /// If set to [DragStartBehavior.start], handle drag behavior will @@ -177,7 +173,6 @@ class EditorTextSelectionOverlay { /// Useful because the actual value of the clipboard can only be checked /// asynchronously (see [Clipboard.getData]). final ClipboardStatusNotifier clipboardStatus; - late AnimationController _toolbarController; /// A pair of handles. If this is non-null, there are always 2, though the /// second is hidden when the selection is collapsed. @@ -188,8 +183,6 @@ class EditorTextSelectionOverlay { TextSelection get _selection => value.selection; - Animation get _toolbarOpacity => _toolbarController.view; - void setHandlesVisible(bool visible) { if (handlesVisible == visible) { return; @@ -220,7 +213,6 @@ class EditorTextSelectionOverlay { /// To hide the whole overlay, see [hide]. void hideToolbar() { assert(toolbar != null); - _toolbarController.stop(); toolbar!.remove(); toolbar = null; } @@ -228,10 +220,12 @@ class EditorTextSelectionOverlay { /// Shows the toolbar by inserting it into the [context]'s overlay. void showToolbar() { assert(toolbar == null); - toolbar = OverlayEntry(builder: _buildToolbar); + if (contextMenuBuilder == null) return; + toolbar = OverlayEntry(builder: (context) { + return contextMenuBuilder!(context); + }); Overlay.of(context, rootOverlay: true, debugRequiredFor: debugRequiredFor) .insert(toolbar!); - _toolbarController.forward(from: 0); // make sure handles are visible as well if (_handles == null) { @@ -319,63 +313,6 @@ class EditorTextSelectionOverlay { ..bringIntoView(textPosition); } - Widget _buildToolbar(BuildContext context) { - // Find the horizontal midpoint, just above the selected text. - 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), - renderObject.localToGlobal(renderObject.size.bottomRight(Offset.zero)), - ); - - final baseLineHeight = renderObject.preferredLineHeight(_selection.base); - final extentLineHeight = - renderObject.preferredLineHeight(_selection.extent); - final smallestLineHeight = math.min(baseLineHeight, extentLineHeight); - final isMultiline = endpoints.last.point.dy - endpoints.first.point.dy > - smallestLineHeight / 2; - - // If the selected text spans more than 1 line, - // horizontally center the toolbar. - // Derived from both iOS and Android. - final midX = isMultiline - ? editingRegion.width / 2 - : (endpoints.first.point.dx + endpoints.last.point.dx) / 2; - - final midpoint = Offset( - midX, - // The y-coordinate won't be made use of most likely. - endpoints[0].point.dy - baseLineHeight, - ); - - return FadeTransition( - opacity: _toolbarOpacity, - child: CompositedTransformFollower( - link: toolbarLayerLink, - showWhenUnlinked: false, - offset: -editingRegion.topLeft, - child: selectionCtrls.buildToolbar( - context, - editingRegion, - baseLineHeight, - midpoint, - endpoints, - selectionDelegate, - clipboardStatus, - null), - ), - ); - } - void markNeedsBuild([Duration? duration]) { if (_handles != null) { _handles![0].markNeedsBuild(); @@ -399,7 +336,6 @@ class EditorTextSelectionOverlay { /// Final cleanup. void dispose() { hide(); - _toolbarController.dispose(); } /// Builds the handles by inserting them into the [context]'s overlay.