Unverified Commit 26021191 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Cupertino text edit tooltip rework (#34095)

parent 7472fad1
......@@ -141,6 +141,7 @@ class _MaterialTextSelectionControls extends TextSelectionControls {
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset position,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
......
......@@ -108,12 +108,16 @@ abstract class TextSelectionControls {
/// [globalEditableRegion] is the TextField size of the global coordinate system
/// in logical pixels.
///
/// [textLineHeight] is the `preferredLineHeight` of the [RenderEditable] we
/// are building a toolbar for.
///
/// The [position] is a general calculation midpoint parameter of the toolbar.
/// If you want more detailed position information, can use [endpoints]
/// to calculate it.
Widget buildToolbar(
BuildContext context,
Rect globalEditableRegion,
double textLineHeight,
Offset position,
List<TextSelectionPoint> endpoints,
TextSelectionDelegate delegate,
......@@ -509,19 +513,29 @@ class TextSelectionOverlay {
return Container();
// Find the horizontal midpoint, just above the selected text.
final List<TextSelectionPoint> endpoints = renderObject.getEndpointsForSelection(_selection);
final Offset midpoint = Offset(
(endpoints.length == 1) ?
endpoints[0].point.dx :
(endpoints[0].point.dx + endpoints[1].point.dx) / 2.0,
endpoints[0].point.dy - renderObject.preferredLineHeight,
);
final List<TextSelectionPoint> endpoints =
renderObject.getEndpointsForSelection(_selection);
final Rect editingRegion = Rect.fromPoints(
renderObject.localToGlobal(Offset.zero),
renderObject.localToGlobal(renderObject.size.bottomRight(Offset.zero)),
);
final bool isMultiline = endpoints.last.point.dy - endpoints.first.point.dy >
renderObject.preferredLineHeight / 2;
// If the selected text spans more than 1 line, horizontally center the toolbar.
// Derived from both iOS and Android.
final double midX = isMultiline
? editingRegion.width / 2
: (endpoints.first.point.dx + endpoints.last.point.dx) / 2;
final Offset midpoint = Offset(
midX,
// The y-coordinate won't be made use of most likely.
endpoints[0].point.dy - renderObject.preferredLineHeight,
);
return FadeTransition(
opacity: _toolbarOpacity,
child: CompositedTransformFollower(
......@@ -531,6 +545,7 @@ class TextSelectionOverlay {
child: selectionControls.buildToolbar(
context,
editingRegion,
renderObject.preferredLineHeight,
midpoint,
endpoints,
selectionDelegate,
......
......@@ -15,6 +15,7 @@ import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior, PointerDeviceKind;
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
......@@ -864,7 +865,7 @@ void main() {
expect(find.text('CUT'), findsNothing);
});
testWidgets('text field build empty tool bar when no options available ios', (WidgetTester tester) async {
testWidgets('does not paint tool bar when no options available on ios', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(platform: TargetPlatform.iOS),
......@@ -882,11 +883,8 @@ void main() {
await tester.tap(find.byType(TextField));
// Wait for context menu to be built.
await tester.pumpAndSettle();
final RenderBox container = tester.renderObject(find.descendant(
of: find.byType(FadeTransition),
matching: find.byType(Container),
));
expect(container.size, Size.zero);
expect(find.byType(CupertinoTextSelectionToolbar), paintsNothing);
});
testWidgets('text field build empty tool bar when no options available android', (WidgetTester tester) async {
......
......@@ -1546,7 +1546,7 @@ void main() {
controls = MockTextSelectionControls();
when(controls.buildHandle(any, any, any)).thenReturn(Container());
when(controls.buildToolbar(any, any, any, any, any))
when(controls.buildToolbar(any, any, any, any, any, any))
.thenReturn(Container());
});
......
......@@ -1108,7 +1108,8 @@ class _IsWithinDistance<T> extends Matcher {
}
class _MoreOrLessEquals extends Matcher {
const _MoreOrLessEquals(this.value, this.epsilon);
const _MoreOrLessEquals(this.value, this.epsilon)
: assert(epsilon >= 0);
final double value;
final double epsilon;
......@@ -1125,6 +1126,12 @@ class _MoreOrLessEquals extends Matcher {
@override
Description describe(Description description) => description.add('$value$epsilon)');
@override
Description describeMismatch(Object item, Description mismatchDescription, Map<dynamic, dynamic> matchState, bool verbose) {
return super.describeMismatch(item, mismatchDescription, matchState, verbose)
..add('$item is not in the range of $value$epsilon).');
}
}
class _IsMethodCall extends Matcher {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment