Unverified Commit ca6f23e8 authored by Tomasz Gucio's avatar Tomasz Gucio Committed by GitHub

Remove Editable.onCaretChanged callback (#109114)

parent 5ea2be69
...@@ -25,15 +25,10 @@ const double _kCaretHeightOffset = 2.0; // pixels ...@@ -25,15 +25,10 @@ const double _kCaretHeightOffset = 2.0; // pixels
// The additional size on the x and y axis with which to expand the prototype // The additional size on the x and y axis with which to expand the prototype
// cursor to render the floating cursor in pixels. // cursor to render the floating cursor in pixels.
const EdgeInsets _kFloatingCaretSizeIncrease = EdgeInsets.symmetric(horizontal: 0.5, vertical: 1.0); const EdgeInsets _kFloatingCursorSizeIncrease = EdgeInsets.symmetric(horizontal: 0.5, vertical: 1.0);
// The corner radius of the floating cursor in pixels. // The corner radius of the floating cursor in pixels.
const Radius _kFloatingCaretRadius = Radius.circular(1.0); const Radius _kFloatingCursorRadius = Radius.circular(1.0);
/// Signature for the callback that reports when the caret location changes.
///
/// Used by [RenderEditable.onCaretChanged].
typedef CaretChangedHandler = void Function(Rect caretRect);
/// Represents the coordinates of the point in a selection, and the text /// Represents the coordinates of the point in a selection, and the text
/// direction at that point, relative to top left of the [RenderEditable] that /// direction at that point, relative to top left of the [RenderEditable] that
...@@ -260,9 +255,6 @@ class VerticalCaretMovementRun implements Iterator<TextPosition> { ...@@ -260,9 +255,6 @@ class VerticalCaretMovementRun implements Iterator<TextPosition> {
/// position. The cursor is shown while [showCursor] is true. It is painted in /// position. The cursor is shown while [showCursor] is true. It is painted in
/// the [cursorColor]. /// the [cursorColor].
/// ///
/// If, when the render object paints, the caret is found to have changed
/// location, [onCaretChanged] is called.
///
/// Keyboard handling, IME handling, scrolling, toggling the [showCursor] value /// Keyboard handling, IME handling, scrolling, toggling the [showCursor] value
/// to actually blink the cursor, and other features not mentioned above are the /// to actually blink the cursor, and other features not mentioned above are the
/// responsibility of higher layers and not handled by this object. /// responsibility of higher layers and not handled by this object.
...@@ -299,7 +291,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -299,7 +291,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
double textScaleFactor = 1.0, double textScaleFactor = 1.0,
TextSelection? selection, TextSelection? selection,
required ViewportOffset offset, required ViewportOffset offset,
this.onCaretChanged,
this.ignorePointer = false, this.ignorePointer = false,
bool readOnly = false, bool readOnly = false,
bool forceLine = true, bool forceLine = true,
...@@ -474,8 +465,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -474,8 +465,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
} }
// Caret Painters: // Caret Painters:
// The floating painter. This painter paints the regular caret as well. // A single painter for both the regular caret and the floating cursor.
late final _FloatingCursorPainter _caretPainter = _FloatingCursorPainter(_onCaretChanged); late final _CaretPainter _caretPainter = _CaretPainter();
// Text Highlight painters: // Text Highlight painters:
final _TextHighlightPainter _selectionPainter = _TextHighlightPainter(); final _TextHighlightPainter _selectionPainter = _TextHighlightPainter();
...@@ -515,19 +506,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -515,19 +506,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
); );
} }
Rect? _lastCaretRect;
// TODO(LongCatIsLooong): currently EditableText uses this callback to keep
// the text field visible. But we don't always paint the caret, for example
// when the selection is not collapsed.
/// Called during the paint phase when the caret location changes.
CaretChangedHandler? onCaretChanged;
void _onCaretChanged(Rect caretRect) {
if (_lastCaretRect != caretRect) {
onCaretChanged?.call(caretRect);
}
_lastCaretRect = onCaretChanged == null ? null : caretRect;
}
/// Whether the [handleEvent] will propagate pointer events to selection /// Whether the [handleEvent] will propagate pointer events to selection
/// handlers. /// handlers.
/// ///
...@@ -2396,8 +2374,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -2396,8 +2374,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_floatingCursorTextPosition = lastTextPosition; _floatingCursorTextPosition = lastTextPosition;
final double? animationValue = _resetFloatingCursorAnimationValue; final double? animationValue = _resetFloatingCursorAnimationValue;
final EdgeInsets sizeAdjustment = animationValue != null final EdgeInsets sizeAdjustment = animationValue != null
? EdgeInsets.lerp(_kFloatingCaretSizeIncrease, EdgeInsets.zero, animationValue)! ? EdgeInsets.lerp(_kFloatingCursorSizeIncrease, EdgeInsets.zero, animationValue)!
: _kFloatingCaretSizeIncrease; : _kFloatingCursorSizeIncrease;
_caretPainter.floatingCursorRect = sizeAdjustment.inflateRect(_caretPrototype).shift(boundedOffset); _caretPainter.floatingCursorRect = sizeAdjustment.inflateRect(_caretPrototype).shift(boundedOffset);
} else { } else {
_caretPainter.floatingCursorRect = null; _caretPainter.floatingCursorRect = null;
...@@ -2777,8 +2755,8 @@ class _TextHighlightPainter extends RenderEditablePainter { ...@@ -2777,8 +2755,8 @@ class _TextHighlightPainter extends RenderEditablePainter {
} }
} }
class _FloatingCursorPainter extends RenderEditablePainter { class _CaretPainter extends RenderEditablePainter {
_FloatingCursorPainter(this.caretPaintCallback); _CaretPainter();
bool get shouldPaint => _shouldPaint; bool get shouldPaint => _shouldPaint;
bool _shouldPaint = true; bool _shouldPaint = true;
...@@ -2790,8 +2768,6 @@ class _FloatingCursorPainter extends RenderEditablePainter { ...@@ -2790,8 +2768,6 @@ class _FloatingCursorPainter extends RenderEditablePainter {
notifyListeners(); notifyListeners();
} }
CaretChangedHandler caretPaintCallback;
bool showRegularCaret = false; bool showRegularCaret = false;
final Paint caretPaint = Paint(); final Paint caretPaint = Paint();
...@@ -2863,7 +2839,6 @@ class _FloatingCursorPainter extends RenderEditablePainter { ...@@ -2863,7 +2839,6 @@ class _FloatingCursorPainter extends RenderEditablePainter {
canvas.drawRRect(caretRRect, caretPaint); canvas.drawRRect(caretRRect, caretPaint);
} }
} }
caretPaintCallback(integralRect);
} }
@override @override
...@@ -2897,7 +2872,7 @@ class _FloatingCursorPainter extends RenderEditablePainter { ...@@ -2897,7 +2872,7 @@ class _FloatingCursorPainter extends RenderEditablePainter {
} }
canvas.drawRRect( canvas.drawRRect(
RRect.fromRectAndRadius(floatingCursorRect, _kFloatingCaretRadius), RRect.fromRectAndRadius(floatingCursorRect, _kFloatingCursorRadius),
floatingCursorPaint..color = floatingCursorColor, floatingCursorPaint..color = floatingCursorColor,
); );
} }
...@@ -2911,7 +2886,7 @@ class _FloatingCursorPainter extends RenderEditablePainter { ...@@ -2911,7 +2886,7 @@ class _FloatingCursorPainter extends RenderEditablePainter {
if (oldDelegate == null) { if (oldDelegate == null) {
return shouldPaint; return shouldPaint;
} }
return oldDelegate is! _FloatingCursorPainter return oldDelegate is! _CaretPainter
|| oldDelegate.shouldPaint != shouldPaint || oldDelegate.shouldPaint != shouldPaint
|| oldDelegate.showRegularCaret != showRegularCaret || oldDelegate.showRegularCaret != showRegularCaret
|| oldDelegate.caretColor != caretColor || oldDelegate.caretColor != caretColor
......
...@@ -2819,17 +2819,17 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2819,17 +2819,17 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_formatAndSetValue(value, SelectionChangedCause.keyboard); _formatAndSetValue(value, SelectionChangedCause.keyboard);
} }
if (_showBlinkingCursor && _cursorTimer != null) {
// To keep the cursor from blinking while typing, restart the timer here.
_stopCursorBlink(resetCharTicks: false);
_startCursorBlink();
}
// Wherever the value is changed by the user, schedule a showCaretOnScreen // Wherever the value is changed by the user, schedule a showCaretOnScreen
// to make sure the user can see the changes they just made. Programmatic // to make sure the user can see the changes they just made. Programmatic
// changes to `textEditingValue` do not trigger the behavior even if the // changes to `textEditingValue` do not trigger the behavior even if the
// text field is focused. // text field is focused.
_scheduleShowCaretOnScreen(withAnimation: true); _scheduleShowCaretOnScreen(withAnimation: true);
if (_hasInputConnection) {
// To keep the cursor from blinking while typing, we want to restart the
// cursor timer every time a new character is typed.
_stopCursorBlink(resetCharTicks: false);
_startCursorBlink();
}
} }
bool _checkNeedsAdjustAffinity(TextEditingValue value) { bool _checkNeedsAdjustAffinity(TextEditingValue value) {
...@@ -3415,18 +3415,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -3415,18 +3415,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
} }
// To keep the cursor from blinking while it moves, restart the timer here. // To keep the cursor from blinking while it moves, restart the timer here.
if (_cursorTimer != null) { if (_showBlinkingCursor && _cursorTimer != null) {
_stopCursorBlink(resetCharTicks: false); _stopCursorBlink(resetCharTicks: false);
_startCursorBlink(); _startCursorBlink();
} }
} }
Rect? _currentCaretRect;
// ignore: use_setters_to_change_properties, (this is used as a callback, can't be a setter)
void _handleCaretChanged(Rect caretRect) {
_currentCaretRect = caretRect;
}
// Animation configuration for scrolling the caret back on screen. // Animation configuration for scrolling the caret back on screen.
static const Duration _caretAnimationDuration = Duration(milliseconds: 100); static const Duration _caretAnimationDuration = Duration(milliseconds: 100);
static const Curve _caretAnimationCurve = Curves.fastOutSlowIn; static const Curve _caretAnimationCurve = Curves.fastOutSlowIn;
...@@ -3440,7 +3434,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -3440,7 +3434,13 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_showCaretOnScreenScheduled = true; _showCaretOnScreenScheduled = true;
SchedulerBinding.instance.addPostFrameCallback((Duration _) { SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_showCaretOnScreenScheduled = false; _showCaretOnScreenScheduled = false;
if (_currentCaretRect == null || !_scrollController.hasClients) { // Since we are in a post frame callback, check currentContext in case
// RenderEditable has been disposed (in which case it will be null).
final RenderEditable? renderEditable =
_editableKey.currentContext?.findRenderObject() as RenderEditable?;
if (renderEditable == null
|| !(renderEditable.selection?.isValid ?? false)
|| !_scrollController.hasClients) {
return; return;
} }
...@@ -3471,7 +3471,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -3471,7 +3471,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final EdgeInsets caretPadding = widget.scrollPadding final EdgeInsets caretPadding = widget.scrollPadding
.copyWith(bottom: bottomSpacing); .copyWith(bottom: bottomSpacing);
final RevealedOffset targetOffset = _getOffsetToRevealCaret(_currentCaretRect!); final Rect caretRect = renderEditable.getLocalRectForCaret(renderEditable.selection!.extent);
final RevealedOffset targetOffset = _getOffsetToRevealCaret(caretRect);
final Rect rectToReveal; final Rect rectToReveal;
final TextSelection selection = textEditingValue.selection; final TextSelection selection = textEditingValue.selection;
...@@ -4644,7 +4645,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -4644,7 +4645,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
obscuringCharacter: widget.obscuringCharacter, obscuringCharacter: widget.obscuringCharacter,
obscureText: widget.obscureText, obscureText: widget.obscureText,
offset: offset, offset: offset,
onCaretChanged: _handleCaretChanged,
rendererIgnoresPointer: widget.rendererIgnoresPointer, rendererIgnoresPointer: widget.rendererIgnoresPointer,
cursorWidth: widget.cursorWidth, cursorWidth: widget.cursorWidth,
cursorHeight: widget.cursorHeight, cursorHeight: widget.cursorHeight,
...@@ -4769,7 +4769,6 @@ class _Editable extends MultiChildRenderObjectWidget { ...@@ -4769,7 +4769,6 @@ class _Editable extends MultiChildRenderObjectWidget {
required this.obscuringCharacter, required this.obscuringCharacter,
required this.obscureText, required this.obscureText,
required this.offset, required this.offset,
this.onCaretChanged,
this.rendererIgnoresPointer = false, this.rendererIgnoresPointer = false,
required this.cursorWidth, required this.cursorWidth,
this.cursorHeight, this.cursorHeight,
...@@ -4810,7 +4809,6 @@ class _Editable extends MultiChildRenderObjectWidget { ...@@ -4810,7 +4809,6 @@ class _Editable extends MultiChildRenderObjectWidget {
final TextHeightBehavior? textHeightBehavior; final TextHeightBehavior? textHeightBehavior;
final TextWidthBasis textWidthBasis; final TextWidthBasis textWidthBasis;
final ViewportOffset offset; final ViewportOffset offset;
final CaretChangedHandler? onCaretChanged;
final bool rendererIgnoresPointer; final bool rendererIgnoresPointer;
final double cursorWidth; final double cursorWidth;
final double? cursorHeight; final double? cursorHeight;
...@@ -4849,7 +4847,6 @@ class _Editable extends MultiChildRenderObjectWidget { ...@@ -4849,7 +4847,6 @@ class _Editable extends MultiChildRenderObjectWidget {
locale: locale ?? Localizations.maybeLocaleOf(context), locale: locale ?? Localizations.maybeLocaleOf(context),
selection: value.selection, selection: value.selection,
offset: offset, offset: offset,
onCaretChanged: onCaretChanged,
ignorePointer: rendererIgnoresPointer, ignorePointer: rendererIgnoresPointer,
obscuringCharacter: obscuringCharacter, obscuringCharacter: obscuringCharacter,
obscureText: obscureText, obscureText: obscureText,
...@@ -4894,7 +4891,6 @@ class _Editable extends MultiChildRenderObjectWidget { ...@@ -4894,7 +4891,6 @@ class _Editable extends MultiChildRenderObjectWidget {
..locale = locale ?? Localizations.maybeLocaleOf(context) ..locale = locale ?? Localizations.maybeLocaleOf(context)
..selection = value.selection ..selection = value.selection
..offset = offset ..offset = offset
..onCaretChanged = onCaretChanged
..ignorePointer = rendererIgnoresPointer ..ignorePointer = rendererIgnoresPointer
..textHeightBehavior = textHeightBehavior ..textHeightBehavior = textHeightBehavior
..textWidthBasis = textWidthBasis ..textWidthBasis = textWidthBasis
......
...@@ -276,7 +276,7 @@ void main() { ...@@ -276,7 +276,7 @@ void main() {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
controller.text = 'Start\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nEnd'; controller.text = "Start${'\n' * 39}End";
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Center( home: Center(
...@@ -322,6 +322,58 @@ void main() { ...@@ -322,6 +322,58 @@ void main() {
expect(scrollController.offset, greaterThan(0.0)); expect(scrollController.offset, greaterThan(0.0));
}); });
testWidgets('focused multi-line editable does not scroll to old position when non-collapsed selection set', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController();
final FocusNode focusNode = FocusNode();
final String text = "Start${'\n' * 39}End";
controller.value = TextEditingValue(text: text, selection: TextSelection.collapsed(offset: text.length - 3));
await tester.pumpWidget(MaterialApp(
home: Center(
child: SizedBox(
height: 300.0,
child: ListView(
controller: scrollController,
children: <Widget>[
EditableText(
backgroundCursorColor: Colors.grey,
maxLines: null, // multiline
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
],
),
),
),
));
// Bring keyboard up and verify that end of EditableText is not on screen.
await tester.showKeyboard(find.byType(EditableText));
await tester.pumpAndSettle();
scrollController.jumpTo(0.0);
await tester.pumpAndSettle();
final RenderBox render = tester.renderObject(find.byType(EditableText));
expect(render.size.height, greaterThan(500.0));
expect(scrollController.offset, 0.0);
// Change selection to non-collapased so that cursor isn't shown
// and the location requires a bit of scroll.
tester.testTextInput.updateEditingValue(TextEditingValue(
text: text,
selection: const TextSelection(baseOffset: 26, extentOffset: 27),
));
await tester.pumpAndSettle();
// Selection extent scrolls into view.
expect(find.byType(EditableText), findsOneWidget);
expect(render.size.height, greaterThan(500.0));
expect(scrollController.offset, 28.0);
});
testWidgets('scrolls into view with scrollInserts after the keyboard pops up', (WidgetTester tester) async { testWidgets('scrolls into view with scrollInserts after the keyboard pops up', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
......
...@@ -9652,7 +9652,7 @@ void main() { ...@@ -9652,7 +9652,7 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
await tester.pumpWidget(Container()); await tester.pumpWidget(Container());
expect(tester.takeException(), isNotNull); expect(tester.takeException(), isAssertionError);
}); });
}); });
......
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