mirror of
https://github.com/status-im/react-native.git
synced 2025-01-10 09:35:48 +00:00
6b9e4ec4b2
Summary: public Added opacity property to RCTShadowText, and use it to adjust the alpha color component of nested text nodes when collapsing the RCTShadowText tree into an NSAttributedString. The opacity is propagated down the tree, multiplying the aggregate with the current node's opacity at each step. Also, foreground and background colors are propagated down the tree so that in case a node has an opacity style but no colors, the ancestor's colors can be used when adjusting the alpha components. Reviewed By: nicklockwood Differential Revision: D2600402 fb-gh-sync-id: 2adb7b598b0a73c984bb2edaab545c02ab911c6b
175 lines
5.7 KiB
Objective-C
175 lines
5.7 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTTextManager.h"
|
|
|
|
#import "RCTAccessibilityManager.h"
|
|
#import "RCTAssert.h"
|
|
#import "RCTConvert.h"
|
|
#import "RCTLog.h"
|
|
#import "RCTShadowRawText.h"
|
|
#import "RCTShadowText.h"
|
|
#import "RCTSparseArray.h"
|
|
#import "RCTText.h"
|
|
#import "RCTTextView.h"
|
|
#import "UIView+React.h"
|
|
|
|
@interface RCTShadowText (Private)
|
|
|
|
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width;
|
|
|
|
@end
|
|
|
|
|
|
@implementation RCTTextManager
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
- (UIView *)view
|
|
{
|
|
return [RCTText new];
|
|
}
|
|
|
|
- (RCTShadowView *)shadowView
|
|
{
|
|
return [RCTShadowText new];
|
|
}
|
|
|
|
#pragma mark - Shadow properties
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(color, UIColor)
|
|
RCT_EXPORT_SHADOW_PROPERTY(fontFamily, NSString)
|
|
RCT_EXPORT_SHADOW_PROPERTY(fontSize, CGFloat)
|
|
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
|
|
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
|
|
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
|
|
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
|
|
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
|
|
RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
|
|
RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize)
|
|
RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment)
|
|
RCT_EXPORT_SHADOW_PROPERTY(textDecorationStyle, NSUnderlineStyle)
|
|
RCT_EXPORT_SHADOW_PROPERTY(textDecorationColor, UIColor)
|
|
RCT_EXPORT_SHADOW_PROPERTY(textDecorationLine, RCTTextDecorationLineType)
|
|
RCT_EXPORT_SHADOW_PROPERTY(writingDirection, NSWritingDirection)
|
|
RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL)
|
|
RCT_EXPORT_SHADOW_PROPERTY(opacity, CGFloat)
|
|
|
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
|
{
|
|
NSMutableSet *textViewTagsToUpdate = [NSMutableSet new];
|
|
for (RCTShadowView *rootView in shadowViewRegistry.allObjects) {
|
|
if (![rootView isReactRootView]) {
|
|
// This isn't a root view
|
|
continue;
|
|
}
|
|
|
|
if (![rootView isTextDirty]) {
|
|
// No text processing to be done
|
|
continue;
|
|
}
|
|
|
|
NSMutableArray<RCTShadowView *> *queue = [NSMutableArray arrayWithObject:rootView];
|
|
for (NSInteger i = 0; i < queue.count; i++) {
|
|
RCTShadowView *shadowView = queue[i];
|
|
RCTAssert([shadowView isTextDirty], @"Don't process any nodes that don't have dirty text");
|
|
|
|
if ([shadowView isKindOfClass:[RCTShadowText class]]) {
|
|
((RCTShadowText *)shadowView).fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
|
[(RCTShadowText *)shadowView recomputeText];
|
|
} else if ([shadowView isKindOfClass:[RCTShadowRawText class]]) {
|
|
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",
|
|
[(RCTShadowRawText *)shadowView text]);
|
|
} else {
|
|
NSNumber *reactTag = shadowView.reactTag;
|
|
// This isn't pretty, but hopefully it's temporary
|
|
// the problem is, there's no easy way (besides the viewName)
|
|
// to tell from the shadowView if the view is an RKTextView
|
|
if ([shadowView.viewName hasSuffix:@"TextView"]) {
|
|
// Add to textViewTagsToUpdate only if has a RCTShadowText subview
|
|
for (RCTShadowView *subview in shadowView.reactSubviews) {
|
|
if ([subview isKindOfClass:[RCTShadowText class]]) {
|
|
[textViewTagsToUpdate addObject:reactTag];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (RCTShadowView *child in [shadowView reactSubviews]) {
|
|
if ([child isTextDirty]) {
|
|
[queue addObject:child];
|
|
}
|
|
}
|
|
}
|
|
|
|
[shadowView setTextComputed];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* NOTE: this logic is included to support rich text editing inside multiline
|
|
* `<TextInput>` controls, a feature which is not yet supported in open source.
|
|
* It is required in order to ensure that the textStorage (aka attributed
|
|
* string) is copied over from the RCTShadowText to the RCTText view in time
|
|
* to be used to update the editable text content.
|
|
*/
|
|
if (textViewTagsToUpdate.count) {
|
|
|
|
NSMutableArray<RCTViewManagerUIBlock> *uiBlocks = [NSMutableArray new];
|
|
for (NSNumber *reactTag in textViewTagsToUpdate) {
|
|
RCTShadowView *shadowTextView = shadowViewRegistry[reactTag];
|
|
RCTShadowText *shadowText;
|
|
for (RCTShadowText *subview in shadowTextView.reactSubviews) {
|
|
if ([subview isKindOfClass:[RCTShadowText class]]) {
|
|
shadowText = subview;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UIEdgeInsets padding = shadowText.paddingAsInsets;
|
|
CGFloat width = shadowText.frame.size.width - (padding.left + padding.right);
|
|
NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width];
|
|
|
|
[uiBlocks addObject:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
RCTTextView *textView = viewRegistry[reactTag];
|
|
RCTText *text;
|
|
for (RCTText *subview in textView.reactSubviews) {
|
|
if ([subview isKindOfClass:[RCTText class]]) {
|
|
text = subview;
|
|
break;
|
|
}
|
|
}
|
|
|
|
text.textStorage = textStorage;
|
|
[textView performTextUpdate];
|
|
}];
|
|
}
|
|
|
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
for (RCTViewManagerUIBlock uiBlock in uiBlocks) {
|
|
uiBlock(uiManager, viewRegistry);
|
|
}
|
|
};
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView
|
|
{
|
|
NSNumber *reactTag = shadowView.reactTag;
|
|
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
|
|
|
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
RCTText *text = viewRegistry[reactTag];
|
|
text.contentInset = padding;
|
|
};
|
|
}
|
|
|
|
@end
|