From 550b0c0ed16a64ce58102f15d4657abe92aac71c Mon Sep 17 00:00:00 2001
From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com>
Date: Thu, 21 Nov 2024 03:16:30 -0800
Subject: [PATCH] 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
{'This is a long text that will wrap.'}
{
'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'
}
{
'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'
}
```
1. Verify that the first and third text take full width
2. Verify that the second text does not take full width
| Before | After |
|:------:|:-----:|
| | |
Reviewed By: NickGerleman
Differential Revision: D65521732
Pulled By: realsoelynn
fbshipit-source-id: 0bb0bb306445e73e8b24ff4c02921739d15ee07e
---
.../react/views/text/TextLayoutManager.java | 4 +++
.../textlayoutmanager/RCTTextLayoutManager.mm | 25 +++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
index c5e232cf734..3d420f8b9ea 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java
@@ -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) {
diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
index 8b00c5f8e2b..cc080f5257a 100644
--- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
+++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm
@@ -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{};