| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| /// The position information for a text selection toolbar. |
| /// |
| /// Typically, a menu will attempt to position itself at [primaryAnchor], and |
| /// if that's not possible, then it will use [secondaryAnchor] instead, if it |
| /// exists. |
| /// |
| /// See also: |
| /// |
| /// * [AdaptiveTextSelectionToolbar.anchors], which is of this type. |
| @immutable |
| class TextSelectionToolbarAnchors { |
| /// Creates an instance of [TextSelectionToolbarAnchors] directly from the |
| /// anchor points. |
| const TextSelectionToolbarAnchors({ |
| required this.primaryAnchor, |
| this.secondaryAnchor, |
| }); |
| |
| /// Creates an instance of [TextSelectionToolbarAnchors] for some selection. |
| factory TextSelectionToolbarAnchors.fromSelection({ |
| required RenderBox renderBox, |
| required double startGlyphHeight, |
| required double endGlyphHeight, |
| required List<TextSelectionPoint> selectionEndpoints, |
| }) { |
| final Rect editingRegion = Rect.fromPoints( |
| renderBox.localToGlobal(Offset.zero), |
| renderBox.localToGlobal(renderBox.size.bottomRight(Offset.zero)), |
| ); |
| |
| if (editingRegion.left.isNaN || editingRegion.top.isNaN |
| || editingRegion.right.isNaN || editingRegion.bottom.isNaN) { |
| return const TextSelectionToolbarAnchors(primaryAnchor: Offset.zero); |
| } |
| |
| final bool isMultiline = selectionEndpoints.last.point.dy - selectionEndpoints.first.point.dy > |
| endGlyphHeight / 2; |
| |
| final Rect selectionRect = Rect.fromLTRB( |
| isMultiline |
| ? editingRegion.left |
| : editingRegion.left + selectionEndpoints.first.point.dx, |
| editingRegion.top + selectionEndpoints.first.point.dy - startGlyphHeight, |
| isMultiline |
| ? editingRegion.right |
| : editingRegion.left + selectionEndpoints.last.point.dx, |
| editingRegion.top + selectionEndpoints.last.point.dy, |
| ); |
| |
| return TextSelectionToolbarAnchors( |
| primaryAnchor: Offset( |
| selectionRect.left + selectionRect.width / 2, |
| clampDouble(selectionRect.top, editingRegion.top, editingRegion.bottom), |
| ), |
| secondaryAnchor: Offset( |
| selectionRect.left + selectionRect.width / 2, |
| clampDouble(selectionRect.bottom, editingRegion.top, editingRegion.bottom), |
| ), |
| ); |
| } |
| |
| /// The location that the toolbar should attempt to position itself at. |
| /// |
| /// If the toolbar doesn't fit at this location, use [secondaryAnchor] if it |
| /// exists. |
| final Offset primaryAnchor; |
| |
| /// The fallback position that should be used if [primaryAnchor] doesn't work. |
| final Offset? secondaryAnchor; |
| } |