blob: 616a13aea1a5e3e9e69dc2de95b041f734edeae5 [file] [log] [blame]
// 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;
}