Open jsamr opened 4 years ago
hey guys, I created a component that worked for me, I'm sharing https://github.com/daviddossantos/textinput-multiline-multiplataform
@daviddossantos thanks for sharing but this is unrelated! The bug I reported was on Android and targeted Text
component. Your workaround seems to target iOS and the TextInput
component.
sorry, I didn't pay attention
unluckily @hoda-oz is right
Even if Text components are nested in other Text components, they are merged into a single TextView of Android So you cannot manipulate text position in nested Text components. In this case, you can manipulate vertical position by lineHeight property.
I have been reading https://github.com/facebook/react-native/pull/23195/files and https://github.com/facebook/react-native/pull/8619/files to understand how nested Text
are combined in one TextView
<Text>
<Text>This is text</Text>
<Text style={{textAlignVertical: 'top', fontSize: 10, backgroundColor: "red" }}>sup!</Text>
</Text>
The code below loops the different text and probably is responsible for aggregating them in 1 TextView.
each one of the below code-snippets adds a different text property to the Text
sup!
using the Android Spans API. Hopefully I will be able to use AlignmentSpan to add the required functionality...
https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L174 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L178-L179 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L186 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L193 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L199-L207 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L210 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L213 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L220-L227 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L233 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L235
the line below changes the text sup!
to background color red
each one of the below code-snippets adds a different text property to the Text
sup!
using the Android Spans API. Hopefully I will be able to use AlignmentSpan to add the required functionality...
https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L174 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L178-L179 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L186 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L193 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L199-L207 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L210 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L213 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L220-L227 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L233 https://github.com/facebook/react-native/blob/28fb41a0ab48cc01d606b64744c84e2ac3805f3f/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L235
I further researched and found the AlignmentSpan
as explained in ParagraphSpans they need a new line \n
like Spantastic\ntext
.
as explained in AlignmentSpan.Standard we can use the following Layout.Alignment
ALIGN_CENTER
ALIGN_OPPOSITE
ALIGN_NORMAL
This are examples of ALIGN_OPPOSITE
The functionality is not available in AOSP so I should create a CustomSpan which inherits from AlignmentSpan
and changes the text-alignment to top
.
AlignmentSpan
uses mAlignment.name()
to retrieve the Span
with the correct alignment
public interface AlignmentSpan extends ParagraphStyle {
class Standard implements AlignmentSpan, ParcelableSpan {
@Override
public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mAlignment.name());
}
}
}
```java public abstract class Layout { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public void drawText(Canvas canvas, int firstLine, int lastLine) { // not relevant logic // Determine whether the line aligns to normal, opposite, or center. Alignment align = paraAlign; if (align == Alignment.ALIGN_LEFT) { align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; } else if (align == Alignment.ALIGN_RIGHT) { align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; } int x; final int indentWidth; if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); x = left + indentWidth; } else { indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); x = right - indentWidth; } } else { int max = (int)getLineExtent(lineNum, tabStops, false); if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { indentWidth = -getIndentAdjust(lineNum, Alignment.ALIGN_RIGHT); x = right - max - indentWidth; } else { indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_LEFT); x = left - max + indentWidth; } } else { // Alignment.ALIGN_CENTER indentWidth = getIndentAdjust(lineNum, Alignment.ALIGN_CENTER); max = max & ~1; x = ((right + left - max) >> 1) + indentWidth; } } Directions directions = getLineDirections(lineNum); if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab && !justify) { // XXX: assumes there's nothing additional to be done canvas.drawText(buf, start, end, x, lbaseline, paint); } else { tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops, getEllipsisStart(lineNum), getEllipsisStart(lineNum) + getEllipsisCount(lineNum)); if (justify) { tl.justify(right - left - indentWidth); } tl.draw(canvas, x, ltop, lbaseline, lbottom); } } TextLine.recycle(tl); } ```
@fabriziobertoglio1987 Awesome! Thanks for your research. Looking forwards to your PR :slightly_smiling_face:
@jsamr thanks a lot Jules Randolph for the sponsorship. I will give priority to this issue and publish a pull request. :pray: :peace_symbol: :heart:
thanks a lot @jsamr
Yes, I believe you are right .. We can use SuperscriptSpan and SubscriptSpan
CLICK TO OPEN TESTS RESULTS
TEST SCENARIO
- we use [SuperscriptSpan](https://developer.android.com/reference/android/text/style/SuperscriptSpan) to top align the text
RESULT
- the text is top aligned
```javascript
@fabriziobertoglio1987 That is promising! Although it would be more like super / sub support rather than a fix for this peculiar issue. But a very exciting new feature! Below just a recap on the distinction between super and top.
In CSS, top, bottom are quite different than sub and sup:
The baseline-relative values %, sub, super shift the box relative to its baseline-aligned position, whereas the line-relative values top, center, and bottom shift the inline box and its contents relative to the bounds of its line box.
What that basically mean is that, if you have a sibling which expands the line box such as a big image, top
would put the content aligned with the top of the image (which coincide with the line box) while super
would only shift the content a little bit up from the baseline.
But honestly, I have no idea what are the specs of React Native inline layout! There certainly are models for native platforms.
I was able to prepare a fix for this issue, the problem is that I don't know how to pass the prop.
The prop textAlignVertical: 'super'
needs to be passed to the span
(the nested/child <Text>sup!</Text>
) and not the parent android TextView
(the parent react <Text>This is text</Text>
).
The class that handle TextView
attributes is ReactTextAnchorViewManager
which for example applies the prop TEXT_ALIGN_VERTICAL
and changes the alignment of the parent Text
.
while the second child/nexted text <Text>This is text<Text>sup!</Text></Text>
is just part of the original TextView
, but it is styled differently using an android SuperscriptSpan
insted of for example CustomLetterSpacingSpan
This properties are applied by the ReactBaseTextShadowNode and are taked from the textAttributes properties listed below
I still did not find the logic that adds this properties to the ReactBaseTextShadowNode, but I imagine may be in the ReactTextViewAnchorManager
I will have to further investigate were to add this property, probably I will have to investigate how the style properties are used to add the spans. Thanks a lot for the patience in adding this feature :pray:
@fabriziobertoglio1987 Any updates?
I'm searching for a way to pass the props to the span Android element so that it is displayed as super
or subscript
.
As explained in the notes above, I analyzed the way the style prop backgroundColor
is passed:
1) textAttributes
includes the backgroundColor
passed with the style
prop.
2) explanation of how in point 1) the textAttributes
variables includes also the style.backgroundColor
value. The method TextAttributesProps.fromReadableMap
retrieves the backgroundColor
and saves it in result
variable.
3) in the case of ReactBaseTextShadowNode
the textAttributes
are taken from the parentTextAttributes
4) a setter is available to update the backgroundColor
I will try to add the prop
super/subscript to the textAttributes
. Thanks a lot for the support. I will update you and try to prepare the pull request for today.
The backgroundColor is saved in textShadowNode.mBackgroundColor
https://github.com/facebook/react-native/blob/40a93914c69fc46cd4c7ba0ed025a1f4f29453ff/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java#L96
CLICK TO OPEN TESTS RESULTS
FLog.w("TESTING::ReactBaseTextShadowNode", "text: " + (text));
FLog.w("TESTING::ReactBaseTextShadowNode", "textShadowNode.mIsBackgroundColorSet: " + (textShadowNode.mIsBackgroundColorSet));
String hexColor = String.format("#%06X", (0xFFFFFF & textShadowNode.mBackgroundColor));
FLog.w("TESTING::ReactBaseTextShadowNode", "textShadowNode.mBackgroundColor: " + (hexColor));
logcat return
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: text: Parent
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: textShadowNode.mIsBackgroundColorSet: false
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: textShadowNode.mBackgroundColor: #000000
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: text: Child
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: textShadowNode.mIsBackgroundColorSet: true
07-01 07:44:54.972 5186 5238 W unknown:TESTING::ReactBaseTextShadowNode: textShadowNode.mBackgroundColor: #0000FF
I will continue investigating how to pass props through textShadowNode
Following up here, still encounter this issue. Any update? Any workaround?
As discussed in this comment in the conversation 2022: How can we improve React Native? #528, I'm starting to work on this issue, and I will try to publish a PR. A summary of all the updates will be posted in https://github.com/react-native-community/discussions-and-proposals/issues/495#issuecomment-1363027413
@fabriziobertoglio1987 That is promising! Although it would be more like super / sub support rather than a fix for this peculiar issue. But a very exciting new feature! Below just a recap on the distinction between super and top.
In CSS, top, bottom are quite different than sub and sup:
The baseline-relative values %, sub, super shift the box relative to its baseline-aligned position, whereas the line-relative values top, center, and bottom shift the inline box and its contents relative to the bounds of its line box.
What that basically mean is that, if you have a sibling which expands the line box such as a big image,
top
would put the content aligned with the top of the image (which coincide with the line box) whilesuper
would only shift the content a little bit up from the baseline.But honestly, I have no idea what are the specs of React Native inline layout! There certainly are models for native platforms.
@jsamr in react-native we use gravity to align the parent text top/center/bottom Android Gravity behaves like flex box https://github.com/facebook/react-native/pull/35704#issuecomment-1369192954
For this reason a Text with height 250px and textAlignVertical bottom aligns to the bottom (baseline -250px). CSS behaves different from react-native. The same result is obtained with flex style instead of vertical-align
I think the expected behaviour for this functionality is aligning the text relatively to the lineHeight and not using Gravity (alignment relative to the parent component) https://jsfiddle.net/Lbc0y7ph/
https://www.w3schools.com/cssref/pr_pos_vertical-align.php
I will update my existing solution, which somehow implements the same flex layout logic to nested text (align nested text top/bottom of the parent component).
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
Still an issue
Still a problem with 0.72.6.
Hello,
Thanks for the issue. I have been contributing to facebook/react-native for 4 years and specialize in the Text/TextInput components. I currently have 58 facebook/react-native PRs:
https://github.com/facebook/react-native/pulls?q=is%3Apr+author%3Afabriziobertoglio1987+
This is the suggested approach from the react-native core team (see this discussion):
I'm publishing several fixes for Text and TextInput component in a separate library react-native-improved.
The advantages would be:
What do you think about this? Should I keep working on this? Thanks
I'm the author of https://github.com/facebook/react-native/pull/35949 and https://github.com/facebook/react-native/pull/35704. The two PRs fix this issue.
still getting this issue when render sub and sup html tag using react-native-render-html
@fabOnReact Both those PRs were closed as stale. Are there new PRs or is the project stalled?
This issue has already been reported (#18790), but closed for "stalling". As of React Native 0.63.3, the bug still exists.
Description
textAlignVertical
works fine when set on the top-most Text component. However, if you have any nested texts (a common use case), you can not override the alignVertical in the child Text component.React Native version:
System: OS: Linux 5.4 Manjaro Linux CPU: (8) x64 Intel(R) Core(TM) i7-8809G CPU @ 3.10GHz Memory: 1.54 GB / 31.29 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.15.0 - /tmp/xfs-2fbb69bb/node Yarn: 2.2.2 - /tmp/xfs-2fbb69bb/yarn npm: 6.14.8 - /usr/bin/npm Watchman: 4.9.0 - /usr/bin/watchman SDKs: Android SDK: API Levels: 28, 29 Build Tools: 28.0.3, 29.0.0, 29.0.2, 29.0.3, 30.0.0, 30.0.1 System Images: android-22 | Google APIs Intel x86 Atom, android-23 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom Android NDK: Not Found IDEs: Android Studio: 4.1 AI-201.8743.12.41.6858069 Languages: Java: 1.8.0_265 - /usr/bin/javac Python: 3.8.6 - /usr/bin/python npmPackages: @react-native-community/cli: Not Found react: 16.13.1 => 16.13.1 react-native: ^0.63.3 => 0.63.3 npmGlobalPackages: react-native: Not Found
Steps To Reproduce
Create an app which renders the following component:
Expected Results
The "sup!" Text node should be aligned to the top edge of the line. Instead, "sup!" is aligned with the baseline:
Web (expected)
Android
Snack, code example, screenshot, or link to a repository:
https://snack.expo.io/@jsamr/android-textalignvertical