-
Notifications
You must be signed in to change notification settings - Fork 25.1k
TextLayout: take full width if text wrapped #47435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
cc @cipolleschi or @realsoelynn You may want to review this since you have the context from #41770 |
|
@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
|
That also likely fixes issue reported here. facebook/yoga#1730 I think we may want this behavior regardless of max number of lines. Wrapping when we have AtMost constraint should be changed on iOS/Android to take full available width. |
|
@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
|
@NickGerleman It won't fix the other issue. That one is in yoga. I will look into it as well |
|
@realsoelynn I see a failing internal test. Does this require any action from my side? |
|
@s77rt @realsoelynn the other issue was reported to Yoga, but I highly suspect it is RN measure function issue related to what this change is addressing. |
|
Making this a draft, will work on what @NickGerleman suggested. It should fix both this issue and the other one |
|
Ready for review. cc @realsoelynn @NickGerleman |
|
FYI: @NickGerleman is on PTO this week. He should be back next week |
|
@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
NickGerleman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Back from PTO! Took a look, and I think we might need to handle max-content differently.
| if (!endsWithNewLine && lineIndex + 1 < layout.getLineCount()) { | ||
| calculatedWidth = width; | ||
| break; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may also reach this path when widthYogaMeasureMode is YogaMeasureMode.UNDEFINED, in which case we are asking for max-content size, and the width is NaN.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think on L604, we could replace:
if (widthYogaMeasureMode == YogaMeasureMode.AT_MOST && calculatedWidth > width)with
if (widthYogaMeasureMode == YogaMeasureMode.AT_MOST && (calculatedWidth > width || layout.getLineCount() > 1))There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may also reach this path when widthYogaMeasureMode is YogaMeasureMode.UNDEFINED
In this case, should we keep the calculated width as the max width of the lines?
I think on L604, we could replace
We set calculatedWidth = width only if the text wraps due to overflow. If the user write 2 short lines we still use the width of the maximum line
| CGSize size = [layoutManager usedRectForTextContainer:textContainer].size; | ||
|
|
||
| if (textDidWrap) { | ||
| size.width = textContainer.size.width; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may have similar case to the above, for measuring max-content, where we don't want to set to size. I think in this case, it looks like that might be represented as Infinity layout constraint which makes its way to the NSTextContainer.
| NSUInteger lastCharacterIndex = range.location + range.length - 1; | ||
| BOOL endsWithNewLine = | ||
| [textStorage.string characterAtIndex:lastCharacterIndex] == '\n'; | ||
| if (!endsWithNewLine && textStorage.string.length > lastCharacterIndex + 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this correctly detect wrapping for something like the below?
Hello\n
World\n
I think we may be able to just count lines. https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html#//apple_ref/doc/uid/20001810-CJBGBIBB
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's not considered text wrap. In this case we want the current behaviour (text width) otherwise we'd break case 2 from #47435 (comment)
NickGerleman
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you are correct about only wanting to do this when we need to break due to wrapping.
That also solves the max-content case, since we will never wrap in this case since we have unconstrained width.
So I think this is good!
|
@realsoelynn has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
|
This pull request was successfully merged by @s77rt in 550b0c0 When will my fix make it into a release? | How to file a pick request? |
|
@realsoelynn merged this pull request in 550b0c0. |
Summary: When we create a layout from measure constraints, we do some processing of the width, to return a different one, potentially smaller than the layout width, sometimes using line width, and sometimes using the container width. This logic has gotten spooooky over time, and after a series of changes, and bugfixes, now effectively does nothing! 1. Way back in 2020, yungsters made D21056031 introducing this logic to "shrink wrap" text which is wrapped. 2. "Shrink wrapping" is not how web works when text is wrapped, (though it is how it works when there is explicit newline), and facebook#47435 later undid this change 3. facebook#37790 made changes specific to the case of trailing newline, because the logic to "shrink wrap" did not handle correctly. After D74366936, which changes width used for layout creation to correctly respect `Layout.desiredWidth`, we should be back to multiline layouts, with no paragraph whose lines take up more than container width, being "shrink wrapped", while not doing so when there is wrapping or ellipsization, like current behavior. The desired width also excludes the non-visible trailing whitespace. It means we can remove all of this logic, while preserving the same behavior. Mismatched measure widths from those used in the intermediate layout may also result in issues for Facsimile (see example in last diff of stack). Changelog: [Internal] Differential Revision: D74368513
Summary: When we create a layout from measure constraints, we do some processing of the width, to return a different one, potentially smaller than the layout width, sometimes using line width, and sometimes using the container width. This logic has gotten spooooky over time, and after a series of changes, and bugfixes, now effectively does nothing! 1. Way back in 2020, yungsters made D21056031 introducing this logic to "shrink wrap" text which is wrapped. 2. "Shrink wrapping" is not how web works when text is wrapped, (though it is how it works when there is explicit newline), and facebook#47435 later undid this change 3. facebook#37790 made changes specific to the case of trailing newline, because the logic to "shrink wrap" did not handle correctly. After D74366936, which changes width used for layout creation to correctly respect `Layout.desiredWidth`, we should be back to multiline layouts, with no paragraph whose lines take up more than container width, being "shrink wrapped", while not doing so when there is wrapping or ellipsization, like current behavior. The desired width also excludes the non-visible trailing whitespace. It means we can remove all of this logic, while preserving the same behavior. Mismatched measure widths from those used in the intermediate layout may also result in issues for Facsimile (see example in last diff of stack). Changelog: [Internal] Differential Revision: D74368513
Summary: When we create a layout from measure constraints, we do some processing of the width, to return a different one, potentially smaller than the layout width, sometimes using line width, and sometimes using the container width. This logic has gotten spooooky over time, and after a series of changes, and bugfixes, now effectively does nothing! 1. Way back in 2020, yungsters made D21056031 introducing this logic to "shrink wrap" text which is wrapped. 2. "Shrink wrapping" is not how web works when text is wrapped, (though it is how it works when there is explicit newline), and facebook#47435 later undid this change 3. facebook#37790 made changes specific to the case of trailing newline, because the logic to "shrink wrap" did not handle correctly. After D74366936, which changes width used for layout creation to correctly respect `Layout.desiredWidth`, we should be back to multiline layouts, with no paragraph whose lines take up more than container width, being "shrink wrapped", while not doing so when there is wrapping or ellipsization, like current behavior. The desired width also excludes the non-visible trailing whitespace. It means we can remove all of this logic, while preserving the same behavior. Mismatched measure widths from those used in the intermediate layout may also result in issues for Facsimile (see example in last diff of stack). Changelog: [Internal] Differential Revision: D74368513
Summary: Pull Request resolved: facebook#51206 When we create a layout from measure constraints, we do some processing of the width, to return a different one, potentially smaller than the layout width, sometimes using line width, and sometimes using the container width. This logic has gotten spooooky over time, and after a series of changes, and bugfixes, now effectively does nothing! 1. Way back in 2020, yungsters made D21056031 introducing this logic to "shrink wrap" text which is wrapped. 2. "Shrink wrapping" is not how web works when text is wrapped, (though it is how it works when there is explicit newline), and facebook#47435 later undid this change 3. facebook#37790 made changes specific to the case of trailing newline, because the logic to "shrink wrap" did not handle correctly. After D74366936, which changes width used for layout creation to correctly respect `Layout.desiredWidth`, we should be back to multiline layouts, with no paragraph whose lines take up more than container width, being "shrink wrapped", while not doing so when there is wrapping or ellipsization, like current behavior. The desired width also excludes the non-visible trailing whitespace. It means we can remove all of this logic, while preserving the same behavior. Mismatched measure widths from those used in the intermediate layout may also result in issues for Facsimile (see example in last diff of stack). Changelog: [Internal] Differential Revision: D74368513
Summary: Pull Request resolved: #51206 When we create a layout from measure constraints, we do some processing of the width, to return a different one, potentially smaller than the layout width, sometimes using line width, and sometimes using the container width. This logic has gotten spooooky over time, and after a series of changes, and bugfixes, now effectively does nothing! 1. Way back in 2020, yungsters made D21056031 introducing this logic to "shrink wrap" text which is wrapped. 2. "Shrink wrapping" is not how web works when text is wrapped, (though it is how it works when there is explicit newline), and #47435 later undid this change 3. #37790 made changes specific to the case of trailing newline, because the logic to "shrink wrap" did not handle correctly. After D74366936, which changes width used for layout creation to correctly respect `Layout.desiredWidth`, we should be back to multiline layouts, with no paragraph whose lines take up more than container width, being "shrink wrapped", while not doing so when there is wrapping or ellipsization, like current behavior. The desired width also excludes the non-visible trailing whitespace. It means we can remove all of this logic, while preserving the same behavior. Mismatched measure widths from those used in the intermediate layout may also result in issues for Facsimile (see example in last diff of stack). Changelog: [Internal] Reviewed By: javache Differential Revision: D74368513 fbshipit-source-id: df5d7b773ad1888ebca1966ee4020a5c2ce7fd64


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
The TextView will render
because the
calculatedWidthtook the width of the first line.Also see #41770 (comment) for additional context.
Solution
If the text wraps, take the whole width.
Fixes #39722
Fixes facebook/yoga#1730
Changelog:
[GENERAL] [FIXED] - Fix text not taking full width
Test Plan: