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 | |:------:|:-----:| | Screenshot 2024-11-05 at 9 00 24 PM | Screenshot 2024-11-05 at 9 01 49 PM | 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{};