299 lines
11 KiB
Objective-C
299 lines
11 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RCTTextAttributes.h"
|
|
|
|
#import <React/RCTAssert.h>
|
|
#import <React/RCTFont.h>
|
|
#import <React/RCTLog.h>
|
|
|
|
NSString *const RCTTextAttributesIsHighlightedAttributeName = @"RCTTextAttributesIsHighlightedAttributeName";
|
|
NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttributeName";
|
|
|
|
@implementation RCTTextAttributes
|
|
|
|
- (instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
_fontSize = NAN;
|
|
_letterSpacing = NAN;
|
|
_lineHeight = NAN;
|
|
_textDecorationStyle = NSUnderlineStyleSingle;
|
|
_fontSizeMultiplier = NAN;
|
|
_maxFontSizeMultiplier = NAN;
|
|
_alignment = NSTextAlignmentNatural;
|
|
_baseWritingDirection = NSWritingDirectionNatural;
|
|
_textShadowRadius = NAN;
|
|
_opacity = NAN;
|
|
_textTransform = RCTTextTransformUndefined;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
|
|
{
|
|
// Note: All lines marked with `*` does not use explicit/correct rules to compare old and new values becuase
|
|
// their types do not have special designated value representing undefined/unspecified/inherit meaning.
|
|
// We will address this in the future.
|
|
|
|
// Color
|
|
_foregroundColor = textAttributes->_foregroundColor ?: _foregroundColor;
|
|
_backgroundColor = textAttributes->_backgroundColor ?: _backgroundColor;
|
|
_opacity = !isnan(textAttributes->_opacity) ? (isnan(_opacity) ? 1.0 : _opacity) * textAttributes->_opacity : _opacity;
|
|
|
|
// Font
|
|
_fontFamily = textAttributes->_fontFamily ?: _fontFamily;
|
|
_fontSize = !isnan(textAttributes->_fontSize) ? textAttributes->_fontSize : _fontSize;
|
|
_fontSizeMultiplier = !isnan(textAttributes->_fontSizeMultiplier) ? textAttributes->_fontSizeMultiplier : _fontSizeMultiplier;
|
|
_maxFontSizeMultiplier = !isnan(textAttributes->_maxFontSizeMultiplier) ? textAttributes->_maxFontSizeMultiplier : _maxFontSizeMultiplier;
|
|
_fontWeight = textAttributes->_fontWeight ?: _fontWeight;
|
|
_fontStyle = textAttributes->_fontStyle ?: _fontStyle;
|
|
_fontVariant = textAttributes->_fontVariant ?: _fontVariant;
|
|
_allowFontScaling = textAttributes->_allowFontScaling || _allowFontScaling; // *
|
|
_letterSpacing = !isnan(textAttributes->_letterSpacing) ? textAttributes->_letterSpacing : _letterSpacing;
|
|
|
|
// Paragraph Styles
|
|
_lineHeight = !isnan(textAttributes->_lineHeight) ? textAttributes->_lineHeight : _lineHeight;
|
|
_alignment = textAttributes->_alignment != NSTextAlignmentNatural ? textAttributes->_alignment : _alignment; // *
|
|
_baseWritingDirection = textAttributes->_baseWritingDirection != NSWritingDirectionNatural ? textAttributes->_baseWritingDirection : _baseWritingDirection; // *
|
|
|
|
// Decoration
|
|
_textDecorationColor = textAttributes->_textDecorationColor ?: _textDecorationColor;
|
|
_textDecorationStyle = textAttributes->_textDecorationStyle != NSUnderlineStyleSingle ? textAttributes->_textDecorationStyle : _textDecorationStyle; // *
|
|
_textDecorationLine = textAttributes->_textDecorationLine != RCTTextDecorationLineTypeNone ? textAttributes->_textDecorationLine : _textDecorationLine; // *
|
|
|
|
// Shadow
|
|
_textShadowOffset = !CGSizeEqualToSize(textAttributes->_textShadowOffset, CGSizeZero) ? textAttributes->_textShadowOffset : _textShadowOffset; // *
|
|
_textShadowRadius = !isnan(textAttributes->_textShadowRadius) ? textAttributes->_textShadowRadius : _textShadowRadius;
|
|
_textShadowColor = textAttributes->_textShadowColor ?: _textShadowColor;
|
|
|
|
// Special
|
|
_isHighlighted = textAttributes->_isHighlighted || _isHighlighted; // *
|
|
_tag = textAttributes->_tag ?: _tag;
|
|
_layoutDirection = textAttributes->_layoutDirection != UIUserInterfaceLayoutDirectionLeftToRight ? textAttributes->_layoutDirection : _layoutDirection;
|
|
_textTransform = textAttributes->_textTransform != RCTTextTransformUndefined ? textAttributes->_textTransform : _textTransform;
|
|
}
|
|
|
|
- (NSDictionary<NSAttributedStringKey, id> *)effectiveTextAttributes
|
|
{
|
|
NSMutableDictionary<NSAttributedStringKey, id> *attributes =
|
|
[NSMutableDictionary dictionaryWithCapacity:10];
|
|
|
|
// Font
|
|
UIFont *font = self.effectiveFont;
|
|
if (font) {
|
|
attributes[NSFontAttributeName] = font;
|
|
}
|
|
|
|
// Colors
|
|
UIColor *effectiveForegroundColor = self.effectiveForegroundColor;
|
|
|
|
if (_foregroundColor || !isnan(_opacity)) {
|
|
attributes[NSForegroundColorAttributeName] = effectiveForegroundColor;
|
|
}
|
|
|
|
if (_backgroundColor || !isnan(_opacity)) {
|
|
attributes[NSBackgroundColorAttributeName] = self.effectiveBackgroundColor;
|
|
}
|
|
|
|
// Kerning
|
|
if (!isnan(_letterSpacing)) {
|
|
attributes[NSKernAttributeName] = @(_letterSpacing);
|
|
}
|
|
|
|
// Paragraph Style
|
|
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
|
BOOL isParagraphStyleUsed = NO;
|
|
if (_alignment != NSTextAlignmentNatural) {
|
|
NSTextAlignment alignment = _alignment;
|
|
if (_layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
|
|
if (alignment == NSTextAlignmentRight) {
|
|
alignment = NSTextAlignmentLeft;
|
|
} else if (alignment == NSTextAlignmentLeft) {
|
|
alignment = NSTextAlignmentRight;
|
|
}
|
|
}
|
|
|
|
paragraphStyle.alignment = alignment;
|
|
isParagraphStyleUsed = YES;
|
|
}
|
|
|
|
if (_baseWritingDirection != NSWritingDirectionNatural) {
|
|
paragraphStyle.baseWritingDirection = _baseWritingDirection;
|
|
isParagraphStyleUsed = YES;
|
|
}
|
|
|
|
if (!isnan(_lineHeight)) {
|
|
CGFloat lineHeight = _lineHeight * self.effectiveFontSizeMultiplier;
|
|
paragraphStyle.minimumLineHeight = lineHeight;
|
|
paragraphStyle.maximumLineHeight = lineHeight;
|
|
isParagraphStyleUsed = YES;
|
|
}
|
|
|
|
if (isParagraphStyleUsed) {
|
|
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
|
|
}
|
|
|
|
// Decoration
|
|
BOOL isTextDecorationEnabled = NO;
|
|
if (_textDecorationLine == RCTTextDecorationLineTypeUnderline ||
|
|
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough) {
|
|
isTextDecorationEnabled = YES;
|
|
attributes[NSUnderlineStyleAttributeName] = @(_textDecorationStyle);
|
|
}
|
|
|
|
if (_textDecorationLine == RCTTextDecorationLineTypeStrikethrough ||
|
|
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough){
|
|
isTextDecorationEnabled = YES;
|
|
attributes[NSStrikethroughStyleAttributeName] = @(_textDecorationStyle);
|
|
}
|
|
|
|
if (_textDecorationColor || isTextDecorationEnabled) {
|
|
attributes[NSStrikethroughColorAttributeName] = _textDecorationColor ?: effectiveForegroundColor;
|
|
attributes[NSUnderlineColorAttributeName] = _textDecorationColor ?: effectiveForegroundColor;
|
|
}
|
|
|
|
// Shadow
|
|
if (!CGSizeEqualToSize(_textShadowOffset, CGSizeZero)) {
|
|
NSShadow *shadow = [NSShadow new];
|
|
shadow.shadowOffset = _textShadowOffset;
|
|
shadow.shadowBlurRadius = _textShadowRadius;
|
|
shadow.shadowColor = _textShadowColor;
|
|
attributes[NSShadowAttributeName] = shadow;
|
|
}
|
|
|
|
// Special
|
|
if (_isHighlighted) {
|
|
attributes[RCTTextAttributesIsHighlightedAttributeName] = @YES;
|
|
}
|
|
|
|
if (_tag) {
|
|
attributes[RCTTextAttributesTagAttributeName] = _tag;
|
|
}
|
|
|
|
return [attributes copy];
|
|
}
|
|
|
|
- (UIFont *)effectiveFont
|
|
{
|
|
// FIXME: RCTFont has thread-safety issues and must be rewritten.
|
|
return [RCTFont updateFont:nil
|
|
withFamily:_fontFamily
|
|
size:@(isnan(_fontSize) ? 0 : _fontSize)
|
|
weight:_fontWeight
|
|
style:_fontStyle
|
|
variant:_fontVariant
|
|
scaleMultiplier:self.effectiveFontSizeMultiplier];
|
|
}
|
|
|
|
- (CGFloat)effectiveFontSizeMultiplier
|
|
{
|
|
bool fontScalingEnabled = !RCTHasFontHandlerSet() && _allowFontScaling;
|
|
|
|
if (fontScalingEnabled) {
|
|
CGFloat fontSizeMultiplier = !isnan(_fontSizeMultiplier) ? _fontSizeMultiplier : 1.0;
|
|
CGFloat maxFontSizeMultiplier = !isnan(_maxFontSizeMultiplier) ? _maxFontSizeMultiplier : 0.0;
|
|
return maxFontSizeMultiplier >= 1.0 ? fminf(maxFontSizeMultiplier, fontSizeMultiplier) : fontSizeMultiplier;
|
|
} else {
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
- (UIColor *)effectiveForegroundColor
|
|
{
|
|
UIColor *effectiveForegroundColor = _foregroundColor ?: [UIColor blackColor];
|
|
|
|
if (!isnan(_opacity)) {
|
|
effectiveForegroundColor = [effectiveForegroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveForegroundColor.CGColor) * _opacity];
|
|
}
|
|
|
|
return effectiveForegroundColor;
|
|
}
|
|
|
|
- (UIColor *)effectiveBackgroundColor
|
|
{
|
|
UIColor *effectiveBackgroundColor = _backgroundColor;// ?: [[UIColor whiteColor] colorWithAlphaComponent:0];
|
|
|
|
if (effectiveBackgroundColor && !isnan(_opacity)) {
|
|
effectiveBackgroundColor = [effectiveBackgroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveBackgroundColor.CGColor) * _opacity];
|
|
}
|
|
|
|
return effectiveBackgroundColor ?: [UIColor clearColor];
|
|
}
|
|
|
|
- (NSString *)applyTextAttributesToText:(NSString *)text
|
|
{
|
|
switch (_textTransform) {
|
|
case RCTTextTransformUndefined:
|
|
case RCTTextTransformNone:
|
|
return text;
|
|
case RCTTextTransformLowercase:
|
|
return [text lowercaseString];
|
|
case RCTTextTransformUppercase:
|
|
return [text uppercaseString];
|
|
case RCTTextTransformCapitalize:
|
|
return [text capitalizedString];
|
|
}
|
|
}
|
|
|
|
- (RCTTextAttributes *)copyWithZone:(NSZone *)zone
|
|
{
|
|
RCTTextAttributes *textAttributes = [RCTTextAttributes new];
|
|
[textAttributes applyTextAttributes:self];
|
|
return textAttributes;
|
|
}
|
|
|
|
#pragma mark - NSObject
|
|
|
|
- (BOOL)isEqual:(RCTTextAttributes *)textAttributes
|
|
{
|
|
if (self == textAttributes) {
|
|
return YES;
|
|
}
|
|
|
|
#define RCTTextAttributesCompareFloats(a) ((a == textAttributes->a) || (isnan(a) && isnan(textAttributes->a)))
|
|
#define RCTTextAttributesCompareSize(a) CGSizeEqualToSize(a, textAttributes->a)
|
|
#define RCTTextAttributesCompareObjects(a) ((a == textAttributes->a) || [a isEqual:textAttributes->a])
|
|
#define RCTTextAttributesCompareStrings(a) ((a == textAttributes->a) || [a isEqualToString:textAttributes->a])
|
|
#define RCTTextAttributesCompareOthers(a) (a == textAttributes->a)
|
|
|
|
return
|
|
RCTTextAttributesCompareObjects(_foregroundColor) &&
|
|
RCTTextAttributesCompareObjects(_backgroundColor) &&
|
|
RCTTextAttributesCompareFloats(_opacity) &&
|
|
// Font
|
|
RCTTextAttributesCompareObjects(_fontFamily) &&
|
|
RCTTextAttributesCompareFloats(_fontSize) &&
|
|
RCTTextAttributesCompareFloats(_fontSizeMultiplier) &&
|
|
RCTTextAttributesCompareFloats(_maxFontSizeMultiplier) &&
|
|
RCTTextAttributesCompareStrings(_fontWeight) &&
|
|
RCTTextAttributesCompareObjects(_fontStyle) &&
|
|
RCTTextAttributesCompareObjects(_fontVariant) &&
|
|
RCTTextAttributesCompareOthers(_allowFontScaling) &&
|
|
RCTTextAttributesCompareFloats(_letterSpacing) &&
|
|
// Paragraph Styles
|
|
RCTTextAttributesCompareFloats(_lineHeight) &&
|
|
RCTTextAttributesCompareFloats(_alignment) &&
|
|
RCTTextAttributesCompareOthers(_baseWritingDirection) &&
|
|
// Decoration
|
|
RCTTextAttributesCompareObjects(_textDecorationColor) &&
|
|
RCTTextAttributesCompareOthers(_textDecorationStyle) &&
|
|
RCTTextAttributesCompareOthers(_textDecorationLine) &&
|
|
// Shadow
|
|
RCTTextAttributesCompareSize(_textShadowOffset) &&
|
|
RCTTextAttributesCompareFloats(_textShadowRadius) &&
|
|
RCTTextAttributesCompareObjects(_textShadowColor) &&
|
|
// Special
|
|
RCTTextAttributesCompareOthers(_isHighlighted) &&
|
|
RCTTextAttributesCompareObjects(_tag) &&
|
|
RCTTextAttributesCompareOthers(_layoutDirection) &&
|
|
RCTTextAttributesCompareOthers(_textTransform);
|
|
}
|
|
|
|
@end
|