Skip to content

Commit

Permalink
Fix cursor position when Unicode Zs category is entered in TextField …
Browse files Browse the repository at this point in the history
…(#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 flutter/flutter#149698. As a result, flutter/flutter#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 flutter/flutter#149099
  • Loading branch information
koji-1009 authored Jul 26, 2024
1 parent 1115a0d commit 112e408
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 17 deletions.
14 changes: 9 additions & 5 deletions packages/flutter/lib/src/painting/text_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,15 @@ class _TextLayout {
// 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
// last logical trailing space.
final bool hasTrailingSpaces = switch (rawString.codeUnitAt(rawString.length - 1)) {
0x9 || // horizontal tab
0x3000 || // ideographic space
0x20 => true, // space
_ => false,
// Whitespace character definitions refer to Java/ICU, not Unicode-Zs.
// https://github.com/unicode-org/icu/blob/23d9628f88a2d0127c564ad98297061c36d3ce77/icu4c/source/common/unicode/uchar.h#L3388-L3425
final String lastCodeUnit = rawString[rawString.length - 1];
final bool hasTrailingSpaces = switch (lastCodeUnit.codeUnitAt(0)) {
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;
Expand Down
81 changes: 69 additions & 12 deletions packages/flutter/test/painting/text_painter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,77 @@ void main() {
caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width);

// Test with trailing full-width space
const String textWithFullWidthSpace = 'A\u{3000}';
checkCaretOffsetsLtr(textWithFullWidthSpace);
painter.text = const TextSpan(text: textWithFullWidthSpace);
painter.layout();
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, 0);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
expect(caretOffset.dx, painter.width / 2);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: textWithFullWidthSpace.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width);
/// Verify the handling of spaces by SkParagraph and TextPainter.
///
/// Test characters that are in the Unicode-Zs category but are not treated as whitespace characters by SkParagraph.
/// The following character codes are intentionally excluded from the test target.
/// * '\u{00A0}' (no-break space)
/// * '\u{2007}' (figure space)
/// * '\u{202F}' (narrow no-break space)
void verifyCharacterIsConsideredTrailingSpace(String character) {
final String reason = 'character: ${character.codeUnitAt(0).toRadixString(16)}';

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();
});
}, skip: isBrowser && !isSkiaWeb); // https://github.com/flutter/flutter/issues/56308

test('TextPainter caret test with WidgetSpan', () {
// Regression test for https://github.com/flutter/flutter/issues/98458.
Expand Down

0 comments on commit 112e408

Please sign in to comment.