Skip to content

Commit 4972cab

Browse files
Kudofacebook-github-bot-9
authored andcommitted
Add <Text> shadow support
Summary: Add three new TextStylePropTypes for \<Text> - textShadowOffset - textShadowRadius - textShadowColor Closes facebook/react-native#4975 Reviewed By: svcscm Differential Revision: D2796278 Pulled By: nicklockwood fb-gh-sync-id: f8c3fa210e664428b029b9fba8eca4a8eb81c08d
1 parent 718cd79 commit 4972cab

9 files changed

Lines changed: 126 additions & 7 deletions

File tree

Examples/UIExplorer/TextExample.android.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ var TextExample = React.createClass({
365365
This text contains an inline image <Image source={require('./flux.png')}/>. Neat, huh?
366366
</Text>
367367
</UIExplorerBlock>
368+
<UIExplorerBlock title="Text shadow">
369+
<Text style={{fontSize: 20, textShadowOffset: {width: 2, height: 2}, textShadowRadius: 1, textShadowColor: '#00cccc'}}>
370+
Demo text shadow
371+
</Text>
372+
</UIExplorerBlock>
368373
</UIExplorerPage>
369374
);
370375
}

Examples/UIExplorer/TextExample.ios.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,17 @@ exports.examples = [
424424
</View>
425425
);
426426
},
427+
}, {
428+
title: 'Text shadow',
429+
render: function() {
430+
return (
431+
<View>
432+
<Text style={{fontSize: 20, textShadowOffset: {width: 2, height: 2}, textShadowRadius: 1, textShadowColor: '#00cccc'}}>
433+
Demo text shadow
434+
</Text>
435+
</View>
436+
);
437+
},
427438
}];
428439

429440
var styles = StyleSheet.create({

Libraries/Components/View/ReactNativeStyleAttributes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ ReactNativeStyleAttributes.color = colorAttributes;
4646
ReactNativeStyleAttributes.shadowColor = colorAttributes;
4747
ReactNativeStyleAttributes.textDecorationColor = colorAttributes;
4848
ReactNativeStyleAttributes.tintColor = colorAttributes;
49+
ReactNativeStyleAttributes.textShadowColor = colorAttributes;
4950

5051
module.exports = ReactNativeStyleAttributes;

Libraries/Text/RCTShadowText.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ extern NSString *const RCTReactTagAttributeName;
3333
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
3434
@property (nonatomic, assign) BOOL allowFontScaling;
3535
@property (nonatomic, assign) CGFloat opacity;
36+
@property (nonatomic, assign) CGSize textShadowOffset;
37+
@property (nonatomic, assign) CGFloat textShadowRadius;
38+
@property (nonatomic, strong) UIColor *textShadowColor;
3639

3740
- (void)recomputeText;
3841

Libraries/Text/RCTShadowText.m

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,22 +305,31 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib
305305
}
306306

307307
// Text decoration
308-
if(_textDecorationLine == RCTTextDecorationLineTypeUnderline ||
309-
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough) {
308+
if (_textDecorationLine == RCTTextDecorationLineTypeUnderline ||
309+
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough) {
310310
[self _addAttribute:NSUnderlineStyleAttributeName withValue:@(_textDecorationStyle)
311311
toAttributedString:attributedString];
312312
}
313-
if(_textDecorationLine == RCTTextDecorationLineTypeStrikethrough ||
314-
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough){
313+
if (_textDecorationLine == RCTTextDecorationLineTypeStrikethrough ||
314+
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough){
315315
[self _addAttribute:NSStrikethroughStyleAttributeName withValue:@(_textDecorationStyle)
316316
toAttributedString:attributedString];
317317
}
318-
if(_textDecorationColor) {
318+
if (_textDecorationColor) {
319319
[self _addAttribute:NSStrikethroughColorAttributeName withValue:_textDecorationColor
320320
toAttributedString:attributedString];
321321
[self _addAttribute:NSUnderlineColorAttributeName withValue:_textDecorationColor
322322
toAttributedString:attributedString];
323323
}
324+
325+
// Text shadow
326+
if (!CGSizeEqualToSize(_textShadowOffset, CGSizeZero)) {
327+
NSShadow *shadow = [NSShadow new];
328+
shadow.shadowOffset = _textShadowOffset;
329+
shadow.shadowBlurRadius = _textShadowRadius;
330+
shadow.shadowColor = _textShadowColor;
331+
[self _addAttribute:NSShadowAttributeName withValue:shadow toAttributedString:attributedString];
332+
}
324333
}
325334

326335
- (void)fillCSSNode:(css_node_t *)node
@@ -371,6 +380,9 @@ - (void)set##setProp:(type)value; \
371380
RCT_TEXT_PROPERTY(TextDecorationStyle, _textDecorationStyle, NSUnderlineStyle);
372381
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
373382
RCT_TEXT_PROPERTY(Opacity, _opacity, CGFloat)
383+
RCT_TEXT_PROPERTY(TextShadowOffset, _textShadowOffset, CGSize);
384+
RCT_TEXT_PROPERTY(TextShadowRadius, _textShadowRadius, CGFloat);
385+
RCT_TEXT_PROPERTY(TextShadowColor, _textShadowColor, UIColor *);
374386

375387
- (void)setAllowFontScaling:(BOOL)allowFontScaling
376388
{

Libraries/Text/RCTTextManager.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ - (RCTShadowView *)shadowView
5959
RCT_EXPORT_SHADOW_PROPERTY(writingDirection, NSWritingDirection)
6060
RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL)
6161
RCT_EXPORT_SHADOW_PROPERTY(opacity, CGFloat)
62+
RCT_EXPORT_SHADOW_PROPERTY(textShadowOffset, CGSize)
63+
RCT_EXPORT_SHADOW_PROPERTY(textShadowRadius, CGFloat)
64+
RCT_EXPORT_SHADOW_PROPERTY(textShadowColor, UIColor)
6265

6366
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
6467
{

Libraries/Text/TextStylePropTypes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
3030
['normal' /*default*/, 'bold',
3131
'100', '200', '300', '400', '500', '600', '700', '800', '900']
3232
),
33+
textShadowOffset: ReactPropTypes.shape(
34+
{width: ReactPropTypes.number, height: ReactPropTypes.number}
35+
),
36+
textShadowRadius: ReactPropTypes.number,
37+
textShadowColor: ColorPropType,
3338
/**
3439
* @platform ios
3540
*/

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.facebook.csslayout.CSSNode;
3333
import com.facebook.csslayout.MeasureOutput;
3434
import com.facebook.infer.annotation.Assertions;
35+
import com.facebook.react.bridge.ReadableMap;
3536
import com.facebook.react.common.annotations.VisibleForTesting;
3637
import com.facebook.react.uimanager.IllegalViewOperationException;
3738
import com.facebook.react.uimanager.LayoutShadowNode;
@@ -63,6 +64,11 @@ public class ReactTextShadowNode extends LayoutShadowNode {
6364
@VisibleForTesting
6465
public static final String PROP_TEXT = "text";
6566

67+
public static final String PROP_SHADOW_OFFSET = "textShadowOffset";
68+
public static final String PROP_SHADOW_RADIUS = "textShadowRadius";
69+
public static final String PROP_SHADOW_COLOR = "textShadowColor";
70+
public static final int DEFAULT_TEXT_SHADOW_COLOR = 0x55000000;
71+
6672
private static final TextPaint sTextPaintInstance = new TextPaint();
6773

6874
static {
@@ -114,8 +120,7 @@ private static final void buildSpannedFromTextCSSNode(
114120
ops.add(new SetSpanOperation(start, end, new ForegroundColorSpan(textCSSNode.mColor)));
115121
}
116122
if (textCSSNode.mIsBackgroundColorSet) {
117-
ops.add(
118-
new SetSpanOperation(
123+
ops.add(new SetSpanOperation(
119124
start,
120125
end,
121126
new BackgroundColorSpan(textCSSNode.mBackgroundColor)));
@@ -135,6 +140,16 @@ private static final void buildSpannedFromTextCSSNode(
135140
textCSSNode.mFontFamily,
136141
textCSSNode.getThemedContext().getAssets())));
137142
}
143+
if (textCSSNode.mTextShadowOffsetDx != 0 || textCSSNode.mTextShadowOffsetDy != 0) {
144+
ops.add(new SetSpanOperation(
145+
start,
146+
end,
147+
new ShadowStyleSpan(
148+
textCSSNode.mTextShadowOffsetDx,
149+
textCSSNode.mTextShadowOffsetDy,
150+
textCSSNode.mTextShadowRadius,
151+
textCSSNode.mTextShadowColor)));
152+
}
138153
ops.add(new SetSpanOperation(start, end, new ReactTagSpan(textCSSNode.getReactTag())));
139154
}
140155
}
@@ -279,6 +294,11 @@ private static int parseNumericFontWeight(String fontWeightString) {
279294
protected int mNumberOfLines = UNSET;
280295
protected int mFontSize = UNSET;
281296

297+
private float mTextShadowOffsetDx = 0;
298+
private float mTextShadowOffsetDy = 0;
299+
private float mTextShadowRadius = 1;
300+
private int mTextShadowColor = DEFAULT_TEXT_SHADOW_COLOR;
301+
282302
/**
283303
* mFontStyle can be {@link Typeface#NORMAL} or {@link Typeface#ITALIC}.
284304
* mFontWeight can be {@link Typeface#NORMAL} or {@link Typeface#BOLD}.
@@ -413,6 +433,34 @@ public void setFontStyle(@Nullable String fontStyleString) {
413433
}
414434
}
415435

436+
@ReactProp(name = PROP_SHADOW_OFFSET)
437+
public void setTextShadowOffset(ReadableMap offsetMap) {
438+
if (offsetMap == null) {
439+
mTextShadowOffsetDx = 0;
440+
mTextShadowOffsetDy = 0;
441+
} else {
442+
mTextShadowOffsetDx = PixelUtil.toPixelFromDIP(offsetMap.getDouble("width"));
443+
mTextShadowOffsetDy = PixelUtil.toPixelFromDIP(offsetMap.getDouble("height"));
444+
}
445+
markUpdated();
446+
}
447+
448+
@ReactProp(name = PROP_SHADOW_RADIUS, defaultInt = 1)
449+
public void setTextShadowRadius(float textShadowRadius) {
450+
if (textShadowRadius != mTextShadowRadius) {
451+
mTextShadowRadius = textShadowRadius;
452+
markUpdated();
453+
}
454+
}
455+
456+
@ReactProp(name = PROP_SHADOW_COLOR, defaultInt = DEFAULT_TEXT_SHADOW_COLOR, customType = "Color")
457+
public void setTextShadowColor(int textShadowColor) {
458+
if (textShadowColor != mTextShadowColor) {
459+
mTextShadowColor = textShadowColor;
460+
markUpdated();
461+
}
462+
}
463+
416464
@Override
417465
public boolean isVirtualAnchor() {
418466
return !mIsVirtual;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
package com.facebook.react.views.text;
11+
12+
13+
import android.text.TextPaint;
14+
import android.text.style.CharacterStyle;
15+
16+
public class ShadowStyleSpan extends CharacterStyle {
17+
private final float mDx, mDy, mRadius;
18+
private final int mColor;
19+
20+
public ShadowStyleSpan(float dx, float dy, float radius, int color) {
21+
mDx = dx;
22+
mDy = dy;
23+
mRadius = radius;
24+
mColor = color;
25+
}
26+
27+
@Override
28+
public void updateDrawState(TextPaint textPaint) {
29+
textPaint.setShadowLayer(mRadius, mDx, mDy, mColor);
30+
}
31+
}

0 commit comments

Comments
 (0)