Fix cursor is not centered when cursorHeight is set (non-Apple platforms). (#145829)

## Description

This PRs fixes the cursor vertical position when a custom cursor height is set on non-Apple platforms (on Apple platforms the cursor is already centered) .

| Before | After |
|--------|--------|
| ![image](https://github.com/flutter/flutter/assets/840911/2d1b855d-d36c-4941-85be-5044ea0e9bb2) | ![image](https://github.com/flutter/flutter/assets/840911/306510c8-42ca-45c7-8c25-ddfa2e22c7f3) | 

<details><summary>Code sample used for the screenshots (cursorHeight 18, font size 16, line height 2)</summary>

```dart
import 'package:flutter/material.dart';

void main() async {
  runApp(
    const MaterialApp(
      title: 'Flutter Demo',
      home: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: TextField(
          cursorHeight: 18,
          style: TextStyle(fontSize: 16, height: 2),
        ),
      ),
    );
  }
}
``` 

</details> 

## Related Issue

Fixes https://github.com/flutter/flutter/issues/143480

## Tests

Adds 2 tests, updates 1.
This commit is contained in:
Bruno Leroux 2024-04-05 06:01:35 +02:00 committed by GitHub
parent 9c6fcdac6f
commit a5ea2ef1d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 98 additions and 6 deletions

View File

@ -1793,13 +1793,12 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
final double caretX = clampDouble(caretRect.left, 0, math.max(scrollableWidth - _caretMargin, 0)); final double caretX = clampDouble(caretRect.left, 0, math.max(scrollableWidth - _caretMargin, 0));
caretRect = Offset(caretX, caretRect.top) & caretRect.size; caretRect = Offset(caretX, caretRect.top) & caretRect.size;
final double caretHeight = cursorHeight; final double fullHeight = _textPainter.getFullHeightForCaret(caretPosition, caretPrototype);
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: case TargetPlatform.macOS:
final double fullHeight = _textPainter.getFullHeightForCaret(caretPosition, caretPrototype);
final double heightDiff = fullHeight - caretRect.height;
// Center the caret vertically along the text. // Center the caret vertically along the text.
final double heightDiff = fullHeight - caretRect.height;
caretRect = Rect.fromLTWH( caretRect = Rect.fromLTWH(
caretRect.left, caretRect.left,
caretRect.top + heightDiff / 2, caretRect.top + heightDiff / 2,
@ -1812,10 +1811,13 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
case TargetPlatform.windows: case TargetPlatform.windows:
// Override the height to take the full height of the glyph at the TextPosition // Override the height to take the full height of the glyph at the TextPosition
// when not on iOS. iOS has special handling that creates a taller caret. // when not on iOS. iOS has special handling that creates a taller caret.
// TODO(garyq): See the TODO for _computeCaretPrototype(). // TODO(garyq): see https://github.com/flutter/flutter/issues/120836.
final double caretHeight = cursorHeight;
// Center the caret vertically along the text.
final double heightDiff = fullHeight - caretHeight;
caretRect = Rect.fromLTWH( caretRect = Rect.fromLTWH(
caretRect.left, caretRect.left,
caretRect.top - _kCaretHeightOffset, caretRect.top - _kCaretHeightOffset + heightDiff / 2,
caretRect.width, caretRect.width,
caretHeight, caretHeight,
); );

View File

@ -1236,7 +1236,7 @@ void main() {
backgroundCursorColor: Colors.grey, backgroundCursorColor: Colors.grey,
controller: controller, controller: controller,
focusNode: focusNode, focusNode: focusNode,
style: const TextStyle(), style: const TextStyle(fontSize: 17),
textAlign: TextAlign.center, textAlign: TextAlign.center,
keyboardType: TextInputType.text, keyboardType: TextInputType.text,
cursorColor: cursorColor, cursorColor: cursorColor,
@ -1267,6 +1267,96 @@ void main() {
skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308 skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308
); );
testWidgets(
'Caret with a cursorHeight smaller than font size is vertically centered on non-Apple platforms',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/143480.
final TextEditingController controller = TextEditingController.fromValue(
const TextEditingValue(selection: TextSelection.collapsed(offset: 0)),
);
addTearDown(controller.dispose);
const double cursorHeight = 12.0;
const double cursorWidth = 4.0;
const double fontSize = 16.0;
final Widget widget = EditableText(
autofocus: true,
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: const TextStyle(fontSize: fontSize),
keyboardType: TextInputType.text,
cursorColor: cursorColor,
cursorHeight: cursorHeight,
cursorWidth: cursorWidth,
);
await tester.pumpWidget(MaterialApp(home: widget));
final EditableTextState editableTextState = tester.firstState(find.byWidget(widget));
final RenderEditable renderEditable = editableTextState.renderEditable;
// The caretRect is vertically centered.
const Rect caretRect = Rect.fromLTWH(
0.0,
(fontSize - cursorHeight) / 2,
cursorWidth,
cursorHeight,
);
expect(
renderEditable,
paints..rect(color: cursorColor, rect: caretRect),
);
},
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{TargetPlatform.macOS, TargetPlatform.iOS}),
skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308
);
testWidgets(
'Caret with a cursorHeight bigger than font size is vertically centered on non-Apple platforms',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/143480.
final TextEditingController controller = TextEditingController.fromValue(
const TextEditingValue(selection: TextSelection.collapsed(offset: 0)),
);
addTearDown(controller.dispose);
const double cursorHeight = 24.0;
const double cursorWidth = 4.0;
const double fontSize = 16.0;
final Widget widget = EditableText(
autofocus: true,
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: const TextStyle(fontSize: fontSize),
keyboardType: TextInputType.text,
cursorColor: cursorColor,
cursorHeight: cursorHeight,
cursorWidth: cursorWidth,
);
await tester.pumpWidget(MaterialApp(home: widget));
final EditableTextState editableTextState = tester.firstState(find.byWidget(widget));
final RenderEditable renderEditable = editableTextState.renderEditable;
// The caretRect is vertically centered.
const Rect caretRect = Rect.fromLTWH(
0.0,
(fontSize - cursorHeight) / 2,
cursorWidth,
cursorHeight,
);
expect(
renderEditable,
paints..rect(color: cursorColor, rect: caretRect),
);
},
variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{TargetPlatform.macOS, TargetPlatform.iOS}),
skip: isBrowser && !isCanvasKit, // https://github.com/flutter/flutter/issues/56308
);
testWidgets('getLocalRectForCaret reports the real caret Rect', (WidgetTester tester) async { testWidgets('getLocalRectForCaret reports the real caret Rect', (WidgetTester tester) async {
EditableText.debugDeterministicCursor = true; EditableText.debugDeterministicCursor = true;
addTearDown(() { EditableText.debugDeterministicCursor = false; }); addTearDown(() { EditableText.debugDeterministicCursor = false; });