Fix cursor position when Unicode Zs category is entered in TextField (#152215)

Changed the cursor position to be the same as before flutter 3.22.0 when 17 character codes in the Unicode Zs category are entered into a TextField.

Extend the support for https://github.com/flutter/flutter/pull/149698. As a result, https://github.com/flutter/flutter/issues/149099 is resolved.

The code for the Unicode-Zs category is based on the following page.
https://www.compart.com/en/unicode/category/Zs

Fixes https://github.com/flutter/flutter/issues/149099
This commit is contained in:
Koji Wakamiya 2024-07-27 04:55:49 +09:00 committed by GitHub
parent 1115a0d9f2
commit 112e4087e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 17 deletions

View File

@ -356,11 +356,15 @@ class _TextLayout {
// Luckily they have the same bidi embedding level as the paragraph as per // Luckily they have the same bidi embedding level as the paragraph as per
// https://unicode.org/reports/tr9/#L1, so we can anchor the caret to the // https://unicode.org/reports/tr9/#L1, so we can anchor the caret to the
// last logical trailing space. // last logical trailing space.
final bool hasTrailingSpaces = switch (rawString.codeUnitAt(rawString.length - 1)) { // Whitespace character definitions refer to Java/ICU, not Unicode-Zs.
0x9 || // horizontal tab // https://github.com/unicode-org/icu/blob/23d9628f88a2d0127c564ad98297061c36d3ce77/icu4c/source/common/unicode/uchar.h#L3388-L3425
0x3000 || // ideographic space final String lastCodeUnit = rawString[rawString.length - 1];
0x20 => true, // space final bool hasTrailingSpaces = switch (lastCodeUnit.codeUnitAt(0)) {
_ => false, 0x0009 => true, // horizontal tab
0x00A0 || // no-break space
0x2007 || // figure space
0x202F => false, // narrow no-break space
_ => RegExp(r'\p{Space_Separator}', unicode: true).hasMatch(lastCodeUnit),
}; };
final double baseline = lineMetrics.baseline; final double baseline = lineMetrics.baseline;

View File

@ -145,20 +145,77 @@ void main() {
caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width); expect(caretOffset.dx, painter.width);
// Test with trailing full-width space /// Verify the handling of spaces by SkParagraph and TextPainter.
const String textWithFullWidthSpace = 'A\u{3000}'; ///
checkCaretOffsetsLtr(textWithFullWidthSpace); /// Test characters that are in the Unicode-Zs category but are not treated as whitespace characters by SkParagraph.
painter.text = const TextSpan(text: textWithFullWidthSpace); /// The following character codes are intentionally excluded from the test target.
painter.layout(); /// * '\u{00A0}' (no-break space)
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero); /// * '\u{2007}' (figure space)
expect(caretOffset.dx, 0); /// * '\u{202F}' (narrow no-break space)
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero); void verifyCharacterIsConsideredTrailingSpace(String character) {
expect(caretOffset.dx, painter.width / 2); final String reason = 'character: ${character.codeUnitAt(0).toRadixString(16)}';
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: textWithFullWidthSpace.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width); text = 'A$character';
checkCaretOffsetsLtr(text);
painter.text = TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, 0.0, reason: reason);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
expect(caretOffset.dx, 14.0, reason: reason);
caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width, reason: reason);
painter.layout(maxWidth: 14.0);
final List<ui.LineMetrics> lines = painter.computeLineMetrics();
expect(lines.length, 1, reason: reason);
expect(lines.first.width, 14.0, reason: reason);
}
// Test with trailing space.
verifyCharacterIsConsideredTrailingSpace('\u{0020}');
// Test with trailing full-width space.
verifyCharacterIsConsideredTrailingSpace('\u{3000}');
// Test with trailing ogham space mark.
verifyCharacterIsConsideredTrailingSpace('\u{1680}');
// Test with trailing en quad.
verifyCharacterIsConsideredTrailingSpace('\u{2000}');
// Test with trailing em quad.
verifyCharacterIsConsideredTrailingSpace('\u{2001}');
// Test with trailing en space.
verifyCharacterIsConsideredTrailingSpace('\u{2002}');
// Test with trailing em space.
verifyCharacterIsConsideredTrailingSpace('\u{2003}');
// Test with trailing three-per-em space.
verifyCharacterIsConsideredTrailingSpace('\u{2004}');
// Test with trailing four-per-em space.
verifyCharacterIsConsideredTrailingSpace('\u{2005}');
// Test with trailing six-per-em space.
verifyCharacterIsConsideredTrailingSpace('\u{2006}');
// Test with trailing punctuation space.
verifyCharacterIsConsideredTrailingSpace('\u{2008}');
// Test with trailing thin space.
verifyCharacterIsConsideredTrailingSpace('\u{2009}');
// Test with trailing hair space.
verifyCharacterIsConsideredTrailingSpace('\u{200A}');
// Test with trailing medium mathematical space(MMSP).
verifyCharacterIsConsideredTrailingSpace('\u{205F}');
painter.dispose(); painter.dispose();
}); }, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/56308
test('TextPainter caret test with WidgetSpan', () { test('TextPainter caret test with WidgetSpan', () {
// Regression test for https://github.com/flutter/flutter/issues/98458. // Regression test for https://github.com/flutter/flutter/issues/98458.