Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,13 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
}
}

inline static UIFont *RCTEffectiveFontFromTextAttributes(const TextAttributes &textAttributes)
inline static UIFont *RCTEffectiveFontFromTextAttributes(
const TextAttributes &textAttributes,
CGFloat fontSizeMultiplier)
Comment on lines +141 to +143

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why extracting this and changing the function signature?

{
NSString *fontFamily = [NSString stringWithUTF8String:textAttributes.fontFamily.c_str()];
// An unset family resolves to the default one; no need to build a string for it.
NSString *fontFamily =
textAttributes.fontFamily.empty() ? nil : [NSString stringWithUTF8String:textAttributes.fontFamily.c_str()];

RCTFontProperties fontProperties;
fontProperties.family = fontFamily;
Expand All @@ -158,7 +162,7 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
NSString *variationSettings = [NSString stringWithUTF8String:textAttributes.fontVariationSettings->c_str()];
fontProperties.variations = RCTParseFontVariationSettings(variationSettings);
}
fontProperties.sizeMultiplier = RCTEffectiveFontSizeMultiplierFromTextAttributes(textAttributes);
fontProperties.sizeMultiplier = fontSizeMultiplier;

return RCTFontWithFontProperties(fontProperties);
}
Expand Down Expand Up @@ -193,7 +197,8 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
NSMutableDictionary<NSAttributedStringKey, id> *attributes = [NSMutableDictionary dictionaryWithCapacity:10];

// Font
UIFont *font = RCTEffectiveFontFromTextAttributes(textAttributes);
CGFloat fontSizeMultiplier = RCTEffectiveFontSizeMultiplierFromTextAttributes(textAttributes);
UIFont *font = RCTEffectiveFontFromTextAttributes(textAttributes, fontSizeMultiplier);
if (font) {
attributes[NSFontAttributeName] = font;
}
Expand All @@ -215,42 +220,38 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
}

// Paragraph Style
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
BOOL isParagraphStyleUsed = NO;
const bool isRTL = textAttributes.layoutDirection == LayoutDirection::RightToLeft;
if (textAttributes.alignment.has_value() || isRTL) {
TextAlignment textAlignment =
RCTResolveTextAlignment(textAttributes.alignment.value_or(TextAlignment::Natural), isRTL);

paragraphStyle.alignment = RCTNSTextAlignmentFromTextAlignment(textAlignment);
isParagraphStyleUsed = YES;
}
const bool hasLineHeight = !isnan(textAttributes.lineHeight);
if (textAttributes.alignment.has_value() || isRTL || textAttributes.baseWritingDirection.has_value() ||
textAttributes.lineBreakStrategy.has_value() || textAttributes.lineBreakMode.has_value() || hasLineHeight) {
Comment on lines +225 to +226

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why conflating all the checks in this one if we also have to check every item singularly anyway?

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];

if (textAttributes.alignment.has_value() || isRTL) {
TextAlignment textAlignment =
RCTResolveTextAlignment(textAttributes.alignment.value_or(TextAlignment::Natural), isRTL);
paragraphStyle.alignment = RCTNSTextAlignmentFromTextAlignment(textAlignment);
}

if (textAttributes.baseWritingDirection.has_value()) {
paragraphStyle.baseWritingDirection =
RCTNSWritingDirectionFromWritingDirection(textAttributes.baseWritingDirection.value());
isParagraphStyleUsed = YES;
}
if (textAttributes.baseWritingDirection.has_value()) {
paragraphStyle.baseWritingDirection =
RCTNSWritingDirectionFromWritingDirection(textAttributes.baseWritingDirection.value());
}

if (textAttributes.lineBreakStrategy.has_value()) {
paragraphStyle.lineBreakStrategy =
RCTNSLineBreakStrategyFromLineBreakStrategy(textAttributes.lineBreakStrategy.value());
isParagraphStyleUsed = YES;
}
if (textAttributes.lineBreakStrategy.has_value()) {
paragraphStyle.lineBreakStrategy =
RCTNSLineBreakStrategyFromLineBreakStrategy(textAttributes.lineBreakStrategy.value());
}

if (textAttributes.lineBreakMode.has_value()) {
paragraphStyle.lineBreakMode = RCTNSLineBreakModeFromLineBreakMode(textAttributes.lineBreakMode.value());
isParagraphStyleUsed = YES;
}
if (textAttributes.lineBreakMode.has_value()) {
paragraphStyle.lineBreakMode = RCTNSLineBreakModeFromLineBreakMode(textAttributes.lineBreakMode.value());
}

if (!isnan(textAttributes.lineHeight)) {
CGFloat lineHeight = textAttributes.lineHeight * RCTEffectiveFontSizeMultiplierFromTextAttributes(textAttributes);
paragraphStyle.minimumLineHeight = lineHeight;
paragraphStyle.maximumLineHeight = lineHeight;
isParagraphStyleUsed = YES;
}
if (hasLineHeight) {
CGFloat lineHeight = textAttributes.lineHeight * fontSizeMultiplier;
paragraphStyle.minimumLineHeight = lineHeight;
paragraphStyle.maximumLineHeight = lineHeight;
}

if (isParagraphStyleUsed) {
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
}

Expand Down Expand Up @@ -405,6 +406,9 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
const AttributedString::Fragment &fragment,
UIImage *placeholderImage)
{
id eventEmitterWrapper =
fragment.parentShadowView.componentHandle ? RCTWrapEventEmitter(fragment.parentShadowView.eventEmitter) : nil;

if (fragment.isAttachment()) {
auto layoutMetrics = fragment.parentShadowView.layoutMetrics;
CGRect bounds = {
Expand All @@ -415,41 +419,35 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
attachment.image = placeholderImage;
attachment.bounds = bounds;

return [[NSMutableAttributedString attributedStringWithAttachment:attachment] mutableCopy];
} else {
NSString *decoded = [[NSString alloc] initWithBytes:fragment.string.data()
length:fragment.string.size()
encoding:NSUTF8StringEncoding];
NSString *string = decoded != nil ? decoded : @"";

if (fragment.textAttributes.textTransform.has_value()) {
auto textTransform = fragment.textAttributes.textTransform.value();
string = RCTNSStringFromStringApplyingTextTransform(string, textTransform);
NSMutableAttributedString *nsAttributedStringFragment =
[[NSMutableAttributedString attributedStringWithAttachment:attachment] mutableCopy];
if (eventEmitterWrapper != nil) {
[nsAttributedStringFragment addAttribute:RCTAttributedStringEventEmitterKey
value:eventEmitterWrapper
range:NSMakeRange(0, nsAttributedStringFragment.length)];
Comment on lines +423 to +427

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this changes the behavior of React Native and has nothing to do with the PR title.

Before we were not attaching the event emitter if the fragment was an attachment, now we are.

If you want this change, please create a separate PR with the proper title and summary. Let's have small and focused PRs, please.

}

return [[NSMutableAttributedString alloc]
initWithString:string
attributes:RCTNSTextAttributesFromTextAttributes(fragment.textAttributes)];
return nsAttributedStringFragment;
}
}

static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFromFragment(
const AttributedString::Fragment &fragment,
UIImage *placeholderImage)
{
auto nsAttributedStringFragment = RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage);

if (fragment.parentShadowView.componentHandle) {
auto eventEmitterWrapper = RCTWrapEventEmitter(fragment.parentShadowView.eventEmitter);
NSString *decoded = [[NSString alloc] initWithBytes:fragment.string.data()
length:fragment.string.size()
encoding:NSUTF8StringEncoding];
NSString *string = decoded != nil ? decoded : @"";

NSDictionary<NSAttributedStringKey, id> *additionalTextAttributes =
@{RCTAttributedStringEventEmitterKey : eventEmitterWrapper};
if (fragment.textAttributes.textTransform.has_value()) {
auto textTransform = fragment.textAttributes.textTransform.value();
string = RCTNSStringFromStringApplyingTextTransform(string, textTransform);
}

[nsAttributedStringFragment addAttributes:additionalTextAttributes
range:NSMakeRange(0, nsAttributedStringFragment.length)];
// The event emitter goes into the initial attributes rather than a second
// pass over the string, which would split and re-merge its attribute runs.
NSMutableDictionary<NSAttributedStringKey, id> *attributes =
RCTNSTextAttributesFromTextAttributes(fragment.textAttributes);
if (eventEmitterWrapper != nil) {
attributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
}

return nsAttributedStringFragment;
return [[NSMutableAttributedString alloc] initWithString:string attributes:attributes];
}

NSAttributedString *RCTNSAttributedStringFromAttributedString(const AttributedString &attributedString)
Expand All @@ -460,15 +458,19 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
placeholderImage = [UIImage new];
});

const auto &fragments = attributedString.getFragments();

// Most paragraphs are a single run of text; it needs no enclosing string.
if (fragments.size() == 1) {
return RCTNSAttributedStringFragmentFromFragment(fragments.front(), placeholderImage);
}

NSMutableAttributedString *nsAttributedString = [NSMutableAttributedString new];

[nsAttributedString beginEditing];

for (auto fragment : attributedString.getFragments()) {
NSMutableAttributedString *nsAttributedStringFragment =
RCTNSAttributedStringFragmentWithAttributesFromFragment(fragment, placeholderImage);

[nsAttributedString appendAttributedString:nsAttributedStringFragment];
for (const auto &fragment : fragments) {
[nsAttributedString appendAttributedString:RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage)];
}
[nsAttributedString endEditing];

Expand Down
Loading