Skip to content

Commit 15768b5

Browse files
Fix textTransform when used with other text styles on Android
1 parent 24f8d4d commit 15768b5

File tree

5 files changed

+79
-106
lines changed

5 files changed

+79
-106
lines changed

RNTester/js/TextExample.android.js

+12
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,18 @@ class TextExample extends React.Component<{}> {
584584
'.aa\tbb\t\tcc dd EE \r\nZZ I like to eat apples. \n中文éé 我喜欢吃苹果。awdawd '
585585
}
586586
</Text>
587+
<Text
588+
style={{
589+
textTransform: 'uppercase',
590+
fontSize: 16,
591+
color: 'turquoise',
592+
backgroundColor: 'blue',
593+
lineHeight: 32,
594+
letterSpacing: 2,
595+
alignSelf: 'flex-start',
596+
}}>
597+
Works with other text styles
598+
</Text>
587599
</RNTesterBlock>
588600
</RNTesterPage>
589601
);

ReactAndroid/src/main/java/com/facebook/react/views/text/CustomTextTransformSpan.java

-83
This file was deleted.

ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java

+13-12
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,25 @@ public void execute(SpannableStringBuilder sb, int priority) {
8585
private static void buildSpannedFromShadowNode(
8686
ReactBaseTextShadowNode textShadowNode,
8787
SpannableStringBuilder sb,
88-
List<SetSpanOperation> ops) {
88+
List<SetSpanOperation> ops,
89+
TextTransform inheritedTextTransform) {
8990

9091
int start = sb.length();
9192

93+
if (textShadowNode.mTextTransform != TextTransform.UNSET) {
94+
inheritedTextTransform = textShadowNode.mTextTransform;
95+
}
96+
9297
for (int i = 0, length = textShadowNode.getChildCount(); i < length; i++) {
9398
ReactShadowNode child = textShadowNode.getChildAt(i);
9499

95100
if (child instanceof ReactRawTextShadowNode) {
96-
sb.append(((ReactRawTextShadowNode) child).getText());
101+
sb.append(
102+
TextTransform.apply(
103+
((ReactRawTextShadowNode) child).getText(),
104+
inheritedTextTransform));
97105
} else if (child instanceof ReactBaseTextShadowNode) {
98-
buildSpannedFromShadowNode((ReactBaseTextShadowNode) child, sb, ops);
106+
buildSpannedFromShadowNode((ReactBaseTextShadowNode) child, sb, ops, inheritedTextTransform);
99107
} else if (child instanceof ReactTextInlineImageShadowNode) {
100108
// We make the image take up 1 character in the span and put a corresponding character into
101109
// the text so that the image doesn't run over any following text.
@@ -174,13 +182,6 @@ private static void buildSpannedFromShadowNode(
174182
new SetSpanOperation(
175183
start, end, new CustomLineHeightSpan(textShadowNode.getEffectiveLineHeight())));
176184
}
177-
if (textShadowNode.mTextTransform != TextTransform.UNSET) {
178-
ops.add(
179-
new SetSpanOperation(
180-
start,
181-
end,
182-
new CustomTextTransformSpan(textShadowNode.mTextTransform)));
183-
}
184185
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textShadowNode.getReactTag())));
185186
}
186187
}
@@ -201,10 +202,10 @@ protected static Spannable spannedFromShadowNode(
201202
// a new spannable will be wiped out
202203
List<SetSpanOperation> ops = new ArrayList<>();
203204

204-
buildSpannedFromShadowNode(textShadowNode, sb, ops);
205+
buildSpannedFromShadowNode(textShadowNode, sb, ops, TextTransform.UNSET);
205206

206207
if (text != null) {
207-
sb.append(text);
208+
sb.append(TextTransform.apply(text, textShadowNode.mTextTransform));
208209
}
209210

210211
if (textShadowNode.mFontSize == UNSET) {

ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java

+6-10
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,12 @@ private static void buildSpannableFromFragment(
6262
ReadableMap fragment = fragments.getMap(i);
6363
int start = sb.length();
6464

65-
//ReactRawText
66-
sb.append(fragment.getString("string"));
65+
// ReactRawText
66+
TextAttributeProps textAttributes = new TextAttributeProps(new ReactStylesDiffMap(fragment.getMap("textAttributes")));
67+
68+
sb.append(TextTransform.apply(
69+
fragment.getString("string"),
70+
textAttributes.mTextTransform));
6771

6872
// TODO: add support for TextInlineImage and BaseText
6973
// if (child instanceof ReactRawTextShadowNode) {
@@ -84,7 +88,6 @@ private static void buildSpannableFromFragment(
8488
// "Unexpected view type nested under text node: " + child.getClass());
8589
// }
8690

87-
TextAttributeProps textAttributes = new TextAttributeProps(new ReactStylesDiffMap(fragment.getMap("textAttributes")));
8891
int end = sb.length();
8992
if (end >= start) {
9093
if (textAttributes.mIsColorSet) {
@@ -141,13 +144,6 @@ private static void buildSpannableFromFragment(
141144
new SetSpanOperation(
142145
start, end, new CustomLineHeightSpan(textAttributes.getEffectiveLineHeight())));
143146
}
144-
if (textAttributes.mTextTransform != TextTransform.UNSET && textAttributes.mTextTransform != TextTransform.NONE) {
145-
ops.add(
146-
new SetSpanOperation(
147-
start,
148-
end,
149-
new CustomTextTransformSpan(textAttributes.mTextTransform)));
150-
}
151147

152148
int reactTag = fragment.getInt("reactTag");
153149
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(reactTag)));

ReactAndroid/src/main/java/com/facebook/react/views/text/TextTransform.java

+48-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,54 @@
77

88
package com.facebook.react.views.text;
99

10+
import java.text.BreakIterator;
11+
1012
/**
1113
* Types of text transforms for CustomTextTransformSpan
1214
*/
13-
public enum TextTransform { NONE, UPPERCASE, LOWERCASE, CAPITALIZE, UNSET };
15+
public enum TextTransform {
16+
NONE, UPPERCASE, LOWERCASE, CAPITALIZE, UNSET;
17+
18+
public static String apply(String text, TextTransform textTransform) {
19+
if (text == null) {
20+
return null;
21+
}
22+
23+
String transformed;
24+
switch(textTransform) {
25+
case UPPERCASE:
26+
transformed = text.toUpperCase();
27+
break;
28+
case LOWERCASE:
29+
transformed = text.toLowerCase();
30+
break;
31+
case CAPITALIZE:
32+
transformed = capitalize(text);
33+
break;
34+
default:
35+
transformed = text;
36+
}
37+
38+
return transformed;
39+
}
40+
41+
private static String capitalize(String text) {
42+
BreakIterator wordIterator = BreakIterator.getWordInstance();
43+
wordIterator.setText(text);
44+
45+
StringBuilder res = new StringBuilder(text.length());
46+
int start = wordIterator.first();
47+
for (int end = wordIterator.next(); end != BreakIterator.DONE; end = wordIterator.next()) {
48+
String word = text.substring(start, end);
49+
if (Character.isLetterOrDigit(word.charAt(0))) {
50+
res.append(Character.toUpperCase(word.charAt(0)));
51+
res.append(word.substring(1).toLowerCase());
52+
} else {
53+
res.append(word);
54+
}
55+
start = end;
56+
}
57+
58+
return res.toString();
59+
}
60+
};

0 commit comments

Comments
 (0)