Unverified Commit a21a1f41 authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

iOS selection handles are invisible (#31332)

Fix a bug where text selection handles were invisible in iOS
parent 6b191841
......@@ -654,4 +654,4 @@ class TextPainter {
final List<int> indices = _paragraph.getWordBoundary(position.offset);
return TextRange(start: indices[0], end: indices[1]);
}
}
\ No newline at end of file
}
......@@ -302,15 +302,25 @@ class RenderEditable extends RenderBox {
TextPosition(offset: _selection.start, affinity: _selection.affinity),
Rect.zero,
);
_selectionStartInViewport.value = visibleRegion.contains(startOffset + effectiveOffset);
// TODO(justinmc): https://github.com/flutter/flutter/issues/31495
// Check if the selection is visible with an approximation because a
// difference between rounded and unrounded values causes the caret to be
// reported as having a slightly (< 0.5) negative y offset. This rounding
// happens in paragraph.cc's layout and TextPainer's
// _applyFloatingPointHack. Ideally, the rounding mismatch will be fixed and
// this can be changed to be a strict check instead of an approximation.
const double visibleRegionSlop = 0.5;
_selectionStartInViewport.value = visibleRegion
.inflate(visibleRegionSlop)
.contains(startOffset + effectiveOffset);
final Offset endOffset = _textPainter.getOffsetForCaret(
TextPosition(offset: _selection.end, affinity: _selection.affinity),
Rect.zero,
);
_selectionEndInViewport.value = visibleRegion.contains(endOffset + effectiveOffset);
_selectionEndInViewport.value = visibleRegion
.inflate(visibleRegionSlop)
.contains(endOffset + effectiveOffset);
}
static const int _kLeftArrowCode = 21;
......
......@@ -612,7 +612,6 @@ class _TextSelectionHandleOverlayState
point.dy.clamp(0.0, viewport.height),
);
return CompositedTransformFollower(
link: widget.layerLink,
showWhenUnlinked: false,
......
......@@ -2091,4 +2091,39 @@ void main() {
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
expect(editableText.cursorColor, const Color(0xFFF44336));
});
testWidgets('iOS shows selection handles', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const String testText = 'lorem ipsum';
final TextEditingController controller = TextEditingController(text: testText);
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(),
home: Center(
child: CupertinoTextField(
controller: controller,
),
),
),
);
final RenderEditable renderEditable =
tester.state<EditableTextState>(find.byType(EditableText)).renderEditable;
await tester.tapAt(textOffsetToPosition(tester, 5));
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
await tester.pumpAndSettle();
final List<Widget> transitions =
find.byType(FadeTransition).evaluate().map((Element e) => e.widget).toList();
expect(transitions.length, 2);
final FadeTransition left = transitions[0];
final FadeTransition right = transitions[1];
expect(left.opacity.value, equals(1.0));
expect(right.opacity.value, equals(1.0));
debugDefaultTargetPlatformOverride = null;
});
}
......@@ -5676,4 +5676,71 @@ void main() {
);
expect(topLeft.dx, equals(383)); // Should be same as equivalent in 'Caret center position'
});
testWidgets('selection handles are rendered and not faded away', (WidgetTester tester) async {
const String testText = 'lorem ipsum';
final TextEditingController controller = TextEditingController(text: testText);
await tester.pumpWidget(
MaterialApp(
home: Material(
child: TextField(
controller: controller,
),
),
),
);
final RenderEditable renderEditable =
tester.state<EditableTextState>(find.byType(EditableText)).renderEditable;
await tester.tapAt(const Offset(20, 10));
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
await tester.pumpAndSettle();
final List<Widget> transitions =
find.byType(FadeTransition).evaluate().map((Element e) => e.widget).toList();
// On Android, an empty app contains a single FadeTransition. The following
// two are the left and right text selection handles, respectively.
expect(transitions.length, 3);
final FadeTransition left = transitions[1];
final FadeTransition right = transitions[2];
expect(left.opacity.value, equals(1.0));
expect(right.opacity.value, equals(1.0));
});
testWidgets('iOS selection handles are rendered and not faded away', (WidgetTester tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
const String testText = 'lorem ipsum';
final TextEditingController controller = TextEditingController(text: testText);
await tester.pumpWidget(
MaterialApp(
home: Material(
child: TextField(
controller: controller,
),
),
),
);
final RenderEditable renderEditable =
tester.state<EditableTextState>(find.byType(EditableText)).renderEditable;
await tester.tapAt(const Offset(20, 10));
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
await tester.pumpAndSettle();
final List<Widget> transitions =
find.byType(FadeTransition).evaluate().map((Element e) => e.widget).toList();
expect(transitions.length, 2);
final FadeTransition left = transitions[0];
final FadeTransition right = transitions[1];
expect(left.opacity.value, equals(1.0));
expect(right.opacity.value, equals(1.0));
debugDefaultTargetPlatformOverride = null;
});
}
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