Add letterSpacing style property for Text

Summary:
Fixes #457
Closes https://github.com/facebook/react-native/pull/482
Github Author: Vladimir Kurchatkin <vladimir.kurchatkin@gmail.com>

Test Plan: Imported from GitHub, without a `Test Plan:` line.
This commit is contained in:
Vladimir Kurchatkin
2015-05-12 00:25:08 -07:00
committed by Christopher Chedeau
parent 02d875869a
commit a142ed50ff
8 changed files with 57 additions and 36 deletions

View File

@@ -218,6 +218,26 @@ exports.examples = [
</View> </View>
); );
}, },
}, {
title: 'Letter Spacing',
render: function() {
return (
<View>
<Text style={{letterSpacing: 0}}>
letterSpacing = 0
</Text>
<Text style={{letterSpacing: 2, marginTop: 5}}>
letterSpacing = 2
</Text>
<Text style={{letterSpacing: 9, marginTop: 5}}>
letterSpacing = 9
</Text>
<Text style={{letterSpacing: -1, marginTop: 5}}>
letterSpacing = -1
</Text>
</View>
);
},
}, { }, {
title: 'Spaces', title: 'Spaces',
render: function() { render: function() {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 273 KiB

View File

@@ -88,35 +88,19 @@
XCTAssertTrue(foundElement, @"Cound't find element with '<View>' text in %d seconds", TIMEOUT_SECONDS); XCTAssertTrue(foundElement, @"Cound't find element with '<View>' text in %d seconds", TIMEOUT_SECONDS);
} }
- (void)testViewExampleSnapshot #define RCT_SNAPSHOT_TEST(name, reRecord) \
{ - (void)test##name##Snapshot \
[_runner runTest:_cmd module:@"ViewExample"]; { \
_runner.recordMode |= reRecord; \
[_runner runTest:_cmd module:@#name]; \
} }
- (void)testLayoutExampleSnapshot RCT_SNAPSHOT_TEST(ViewExample, NO)
{ RCT_SNAPSHOT_TEST(LayoutExample, NO)
[_runner runTest:_cmd module:@"LayoutExample"]; RCT_SNAPSHOT_TEST(TextExample, NO)
} RCT_SNAPSHOT_TEST(SwitchExample, NO)
RCT_SNAPSHOT_TEST(SliderExample, NO)
- (void)testTextExampleSnapshot RCT_SNAPSHOT_TEST(TabBarExample, NO)
{
[_runner runTest:_cmd module:@"TextExample"];
}
- (void)testSwitchExampleSnapshot
{
[_runner runTest:_cmd module:@"SwitchExample"];
}
- (void)testSliderExampleSnapshot
{
[_runner runTest:_cmd module:@"SliderExample"];
}
- (void)testTabBarExampleSnapshot
{
[_runner runTest:_cmd module:@"TabBarExample"];
}
// Make sure this test runs last // Make sure this test runs last
- (void)testZZZ_NotInRecordMode - (void)testZZZ_NotInRecordMode

View File

@@ -22,6 +22,7 @@ extern NSString *const RCTReactTagAttributeName;
@property (nonatomic, copy) NSString *fontWeight; @property (nonatomic, copy) NSString *fontWeight;
@property (nonatomic, copy) NSString *fontStyle; @property (nonatomic, copy) NSString *fontStyle;
@property (nonatomic, assign) BOOL isHighlighted; @property (nonatomic, assign) BOOL isHighlighted;
@property (nonatomic, assign) CGFloat letterSpacing;
@property (nonatomic, assign) CGFloat lineHeight; @property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSUInteger maximumNumberOfLines; @property (nonatomic, assign) NSUInteger maximumNumberOfLines;
@property (nonatomic, assign) CGSize shadowOffset; @property (nonatomic, assign) CGSize shadowOffset;
@@ -30,6 +31,7 @@ extern NSString *const RCTReactTagAttributeName;
// Not exposed to JS // Not exposed to JS
@property (nonatomic, strong) UIFont *font; @property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSLineBreakMode truncationMode; @property (nonatomic, assign) NSLineBreakMode truncationMode;
@property (nonatomic, assign) CGFloat effectiveLetterSpacing;
@property (nonatomic, copy, readonly) NSAttributedString *attributedString; @property (nonatomic, copy, readonly) NSAttributedString *attributedString;
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager; @property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;

View File

@@ -40,11 +40,15 @@ static css_dim_t RCTMeasure(void *context, float width)
css_dim_t result; css_dim_t result;
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width); result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
if (shadowText.effectiveLetterSpacing < 0) {
result.dimensions[CSS_WIDTH] -= shadowText.effectiveLetterSpacing;
}
result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height); result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height);
return result; return result;
} }
@implementation RCTShadowText { @implementation RCTShadowText
{
NSLayoutManager *_layoutManager; NSLayoutManager *_layoutManager;
NSTextContainer *_textContainer; NSTextContainer *_textContainer;
NSAttributedString *_cachedAttributedString; NSAttributedString *_cachedAttributedString;
@@ -55,6 +59,7 @@ static css_dim_t RCTMeasure(void *context, float width)
{ {
if ((self = [super init])) { if ((self = [super init])) {
_fontSize = NAN; _fontSize = NAN;
_letterSpacing = NAN;
_isHighlighted = NO; _isHighlighted = NO;
_textContainer = [[NSTextContainer alloc] init]; _textContainer = [[NSTextContainer alloc] init];
@@ -71,22 +76,24 @@ static css_dim_t RCTMeasure(void *context, float width)
- (NSAttributedString *)attributedString - (NSAttributedString *)attributedString
{ {
return [self _attributedStringWithFontFamily:nil return [self _attributedStringWithFontFamily:nil
fontSize:0 fontSize:nil
fontWeight:nil fontWeight:nil
fontStyle:nil]; fontStyle:nil
letterSpacing:nil];
} }
- (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
fontSize:(CGFloat)fontSize fontSize:(NSNumber *)fontSize
fontWeight:(NSString *)fontWeight fontWeight:(NSString *)fontWeight
fontStyle:(NSString *)fontStyle fontStyle:(NSString *)fontStyle
letterSpacing:(NSNumber *)letterSpacing
{ {
if (![self isTextDirty] && _cachedAttributedString) { if (![self isTextDirty] && _cachedAttributedString) {
return _cachedAttributedString; return _cachedAttributedString;
} }
if (_fontSize && !isnan(_fontSize)) { if (_fontSize && !isnan(_fontSize)) {
fontSize = _fontSize; fontSize = @(_fontSize);
} }
if (_fontWeight) { if (_fontWeight) {
fontWeight = _fontWeight; fontWeight = _fontWeight;
@@ -97,12 +104,17 @@ static css_dim_t RCTMeasure(void *context, float width)
if (_fontFamily) { if (_fontFamily) {
fontFamily = _fontFamily; fontFamily = _fontFamily;
} }
if (!isnan(_letterSpacing)) {
letterSpacing = @(_letterSpacing);
}
_effectiveLetterSpacing = letterSpacing.doubleValue;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init]; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
for (RCTShadowView *child in [self reactSubviews]) { for (RCTShadowView *child in [self reactSubviews]) {
if ([child isKindOfClass:[RCTShadowText class]]) { if ([child isKindOfClass:[RCTShadowText class]]) {
RCTShadowText *shadowText = (RCTShadowText *)child; RCTShadowText *shadowText = (RCTShadowText *)child;
[attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle]]; [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle letterSpacing:letterSpacing]];
} else if ([child isKindOfClass:[RCTShadowRawText class]]) { } else if ([child isKindOfClass:[RCTShadowRawText class]]) {
RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child; RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child;
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[shadowRawText text] ?: @""]]; [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[shadowRawText text] ?: @""]];
@@ -123,8 +135,9 @@ static css_dim_t RCTMeasure(void *context, float width)
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString]; [self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString];
} }
_font = [RCTConvert UIFont:nil withFamily:fontFamily size:@(fontSize) weight:fontWeight style:fontStyle]; _font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
[self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString]; [self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString];
[self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString]; [self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
[self _setParagraphStyleOnAttributedString:attributedString]; [self _setParagraphStyleOnAttributedString:attributedString];
@@ -143,7 +156,7 @@ static css_dim_t RCTMeasure(void *context, float width)
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString - (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
{ {
[attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { [attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
if (!value) { if (!value && attributeValue) {
[attributedString addAttribute:attribute value:attributeValue range:range]; [attributedString addAttribute:attribute value:attributeValue range:range];
} }
}]; }];
@@ -223,6 +236,7 @@ RCT_TEXT_PROPERTY(Color, _color, UIColor *);
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *); RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *);
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat); RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat);
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *); RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *);
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat);
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat); RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat);
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize); RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize);
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment); RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment);

View File

@@ -14,7 +14,6 @@
@property (nonatomic, strong) NSLayoutManager *layoutManager; @property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer; @property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, copy) NSAttributedString *attributedText; @property (nonatomic, copy) NSAttributedString *attributedText;
@property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, assign) UIEdgeInsets contentInset;
@end @end

View File

@@ -45,6 +45,7 @@ RCT_EXPORT_SHADOW_PROPERTY(fontSize, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString) RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString) RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL) RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat) RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger) RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger)
RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize) RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize)

View File

@@ -32,6 +32,7 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
writingDirection: ReactPropTypes.oneOf( writingDirection: ReactPropTypes.oneOf(
['auto' /*default*/, 'ltr', 'rtl'] ['auto' /*default*/, 'ltr', 'rtl']
), ),
letterSpacing: ReactPropTypes.number,
}); });
// Text doesn't support padding correctly (#4841912) // Text doesn't support padding correctly (#4841912)