TextLayout: take full width if text wrapped (#47435)

Summary:
### Problem
The calculated width for a multiline text is based on the longest line. However it does not account for text that wraps.

**Example** if numberOfLines=1 and the text wraps

```
+---------------------------+
This is a long text that
will wrap
+---------------------------+
```

The TextView will render
```
+---------------------------+
This is a long text t...
+---------------------------+
```

because the `calculatedWidth` took the width of the first line.

Also see https://github.com/facebook/react-native/pull/41770#issuecomment-2453611554 for additional context.

### Solution

If the text wraps, take the whole width.

```
+---------------------------+
This is a long text that w...
+---------------------------+
```

Fixes https://github.com/facebook/react-native/issues/39722
Fixes https://github.com/facebook/yoga/issues/1730

## Changelog:

[GENERAL] [FIXED] - Fix text not taking full width

Pull Request resolved: https://github.com/facebook/react-native/pull/47435

Test Plan:
```tsx
        <Text
          numberOfLines={1}
          style={{
            backgroundColor: 'red',
            alignSelf: 'flex-start',
            color: 'white',
            fontSize: 34,
          }}>
          {'This is a long text that will wrap.'}
        </Text>
        <Text
          numberOfLines={3}
          style={{
            backgroundColor: 'red',
            alignSelf: 'flex-start',
            color: 'white',
            fontSize: 34,
          }}>
          {
            '1\n\ntest\nThis is a long text that will wrap.This is a long text that will wrap.This is a long text that will wrap.\n'
          }
        </Text>
        <Text
          numberOfLines={3}
          style={{
            backgroundColor: 'red',
            alignSelf: 'flex-start',
            color: 'white',
            fontSize: 34,
          }}>
          {
            '1\n\nThis is a long text that will wrap.This is a long text that will wrap.This is a long text that will wrap.\n'
          }
        </Text>
```
1. Verify that the first and third text take full width
2. Verify that the second text does not take full width

| Before | After |
|:------:|:-----:|
|   <img width="480" alt="Screenshot 2024-11-05 at 9 00 24 PM" src="https://github.com/user-attachments/assets/b8d765c0-f4b1-42c6-afc7-75862c52612a">     |    <img width="480" alt="Screenshot 2024-11-05 at 9 01 49 PM" src="https://github.com/user-attachments/assets/f1534c14-a56a-4d44-8edc-4d9f75166cb2">   |

Reviewed By: NickGerleman

Differential Revision: D65521732

Pulled By: realsoelynn

fbshipit-source-id: 0bb0bb306445e73e8b24ff4c02921739d15ee07e
This commit is contained in:
Abdelhafidh Belalia 2024-11-21 03:16:30 -08:00 committed by Facebook GitHub Bot
parent 105ad855d9
commit 550b0c0ed1
2 changed files with 29 additions and 0 deletions

View File

@ -707,6 +707,10 @@ public class TextLayoutManager {
for (int lineIndex = 0; lineIndex < calculatedLineCount; lineIndex++) {
boolean endsWithNewLine =
text.length() > 0 && text.charAt(layout.getLineEnd(lineIndex) - 1) == '\n';
if (!endsWithNewLine && lineIndex + 1 < layout.getLineCount()) {
calculatedWidth = width;
break;
}
float lineWidth =
endsWithNewLine ? layout.getLineMax(lineIndex) : layout.getLineWidth(lineIndex);
if (lineWidth > calculatedWidth) {

View File

@ -347,8 +347,33 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
[layoutManager ensureLayoutForTextContainer:textContainer];
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
__block BOOL textDidWrap = NO;
[layoutManager
enumerateLineFragmentsForGlyphRange:glyphRange
usingBlock:^(
CGRect overallRect,
CGRect usedRect,
NSTextContainer *_Nonnull usedTextContainer,
NSRange lineGlyphRange,
BOOL *_Nonnull stop) {
NSRange range = [layoutManager characterRangeForGlyphRange:lineGlyphRange
actualGlyphRange:nil];
NSUInteger lastCharacterIndex = range.location + range.length - 1;
BOOL endsWithNewLine =
[textStorage.string characterAtIndex:lastCharacterIndex] == '\n';
if (!endsWithNewLine && textStorage.string.length > lastCharacterIndex + 1) {
textDidWrap = YES;
*stop = YES;
}
}];
CGSize size = [layoutManager usedRectForTextContainer:textContainer].size;
if (textDidWrap) {
size.width = textContainer.size.width;
}
size = (CGSize){RCTCeilPixelValue(size.width), RCTCeilPixelValue(size.height)};
__block auto attachments = TextMeasurement::Attachments{};