Skip to content

Commit

Permalink
Added support for textDecorationLine style prop on Android
Browse files Browse the repository at this point in the history
Summary:As suggested by kmagiera in #3819, I've implemented `textDecorationLine` style for Android in `ReactTextShadowNode` using span operations so we can support nested Text components.

![Demo](http://c.hlp.sc/022k2l033p0n/Image%202016-01-03%20at%2011.17.15%20PM.png)
Closes #5105

Differential Revision: D3167756

Pulled By: andreicoman11

fb-gh-sync-id: 122701a53d50f47f89b49e1f343c97db5fa2323d
fbshipit-source-id: 122701a53d50f47f89b49e1f343c97db5fa2323d
  • Loading branch information
Charca authored and Facebook Github Bot 9 committed Apr 12, 2016
1 parent eac617d commit 2039be9
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 3 deletions.
17 changes: 17 additions & 0 deletions Examples/UIExplorer/TextExample.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,23 @@ var TextExample = React.createClass({
Move fast and be bold
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Text Decoration">
<Text style={{textDecorationLine: 'underline'}}>
Solid underline
</Text>
<Text style={{textDecorationLine: 'none'}}>
None textDecoration
</Text>
<Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'solid'}}>
Solid line-through
</Text>
<Text style={{textDecorationLine: 'underline line-through'}}>
Both underline and line-through
</Text>
<Text>
Mixed text with <Text style={{textDecorationLine: 'underline'}}>underline</Text> and <Text style={{textDecorationLine: 'line-through'}}>line-through</Text> text nodes
</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Nested">
<Text onPress={() => console.log('1st')}>
(Normal text,
Expand Down
3 changes: 0 additions & 3 deletions Libraries/Text/TextStylePropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
textAlignVertical: ReactPropTypes.oneOf(
['auto' /*default*/, 'top', 'bottom', 'center']
),
/**
* @platform ios
*/
textDecorationLine: ReactPropTypes.oneOf(
['none' /*default*/, 'underline', 'line-through', 'underline line-through']
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class ViewProps {
public static final String RESIZE_MODE = "resizeMode";
public static final String TEXT_ALIGN = "textAlign";
public static final String TEXT_ALIGN_VERTICAL = "textAlignVertical";
public static final String TEXT_DECORATION_LINE = "textDecorationLine";

public static final String BORDER_WIDTH = "borderWidth";
public static final String BORDER_LEFT_WIDTH = "borderLeftWidth";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.UnderlineSpan;
import android.widget.TextView;

import com.facebook.csslayout.CSSConstants;
Expand Down Expand Up @@ -146,6 +148,12 @@ private static void buildSpannedFromTextCSSNode(
textCSSNode.mFontFamily,
textCSSNode.getThemedContext().getAssets())));
}
if (textCSSNode.mIsUnderlineTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new UnderlineSpan()));
}
if (textCSSNode.mIsLineThroughTextDecorationSet) {
ops.add(new SetSpanOperation(start, end, new StrikethroughSpan()));
}
if (textCSSNode.mTextShadowOffsetDx != 0 || textCSSNode.mTextShadowOffsetDy != 0) {
ops.add(new SetSpanOperation(
start,
Expand Down Expand Up @@ -287,6 +295,9 @@ private static int parseNumericFontWeight(String fontWeightString) {
private float mTextShadowRadius = 1;
private int mTextShadowColor = DEFAULT_TEXT_SHADOW_COLOR;

private boolean mIsUnderlineTextDecorationSet = false;
private boolean mIsLineThroughTextDecorationSet = false;

/**
* mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}.
* mFontWeight can be {@link Typeface#NORMAL} or {@link Typeface#BOLD}.
Expand Down Expand Up @@ -428,6 +439,22 @@ public void setFontStyle(@Nullable String fontStyleString) {
}
}

@ReactProp(name = ViewProps.TEXT_DECORATION_LINE)
public void setTextDecorationLine(@Nullable String textDecorationLineString) {
mIsUnderlineTextDecorationSet = false;
mIsLineThroughTextDecorationSet = false;
if (textDecorationLineString != null) {
for (String textDecorationLineSubString : textDecorationLineString.split(" ")) {
if ("underline".equals(textDecorationLineSubString)) {
mIsUnderlineTextDecorationSet = true;
} else if ("line-through".equals(textDecorationLineSubString)) {
mIsLineThroughTextDecorationSet = true;
}
}
}
markUpdated();
}

@ReactProp(name = PROP_SHADOW_OFFSET)
public void setTextShadowOffset(ReadableMap offsetMap) {
if (offsetMap == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.UnderlineSpan;
import android.util.DisplayMetrics;
import android.view.Choreographer;
import android.widget.TextView;
Expand Down Expand Up @@ -279,6 +281,60 @@ public void testFontFamilyBoldItalicStyleApplied() {
assertThat(customStyleSpan.getWeight() & Typeface.BOLD).isNotZero();
}

@Test
public void testTextDecorationLineUnderlineApplied() {
UIManagerModule uiManager = getUIManagerModule();

ReactRootView rootView = createText(
uiManager,
JavaOnlyMap.of(ViewProps.TEXT_DECORATION_LINE, "underline"),
JavaOnlyMap.of(ReactTextShadowNode.PROP_TEXT, "test text"));

TextView textView = (TextView) rootView.getChildAt(0);
Spanned text = (Spanned) textView.getText();
UnderlineSpan underlineSpan = getSingleSpan(textView, UnderlineSpan.class);
StrikethroughSpan[] strikeThroughSpans =
text.getSpans(0, text.length(), StrikethroughSpan.class);
assertThat(underlineSpan instanceof UnderlineSpan).isTrue();
assertThat(strikeThroughSpans).hasSize(0);
}

@Test
public void testTextDecorationLineLineThroughApplied() {
UIManagerModule uiManager = getUIManagerModule();

ReactRootView rootView = createText(
uiManager,
JavaOnlyMap.of(ViewProps.TEXT_DECORATION_LINE, "line-through"),
JavaOnlyMap.of(ReactTextShadowNode.PROP_TEXT, "test text"));

TextView textView = (TextView) rootView.getChildAt(0);
Spanned text = (Spanned) textView.getText();
UnderlineSpan[] underlineSpans =
text.getSpans(0, text.length(), UnderlineSpan.class);
StrikethroughSpan strikeThroughSpan =
getSingleSpan(textView, StrikethroughSpan.class);
assertThat(underlineSpans).hasSize(0);
assertThat(strikeThroughSpan instanceof StrikethroughSpan).isTrue();
}

@Test
public void testTextDecorationLineUnderlineLineThroughApplied() {
UIManagerModule uiManager = getUIManagerModule();

ReactRootView rootView = createText(
uiManager,
JavaOnlyMap.of(ViewProps.TEXT_DECORATION_LINE, "underline line-through"),
JavaOnlyMap.of(ReactTextShadowNode.PROP_TEXT, "test text"));

UnderlineSpan underlineSpan =
getSingleSpan((TextView) rootView.getChildAt(0), UnderlineSpan.class);
StrikethroughSpan strikeThroughSpan =
getSingleSpan((TextView) rootView.getChildAt(0), StrikethroughSpan.class);
assertThat(underlineSpan instanceof UnderlineSpan).isTrue();
assertThat(strikeThroughSpan instanceof StrikethroughSpan).isTrue();
}

@Test
public void testBackgroundColorStyleApplied() {
UIManagerModule uiManager = getUIManagerModule();
Expand Down

0 comments on commit 2039be9

Please sign in to comment.