You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2018/04/26 12:39:59 UTC
[07/16] incubator-weex git commit: [WEEX-311] [iOS] use new
layoutEngin to replace yoga
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
new file mode 100644
index 0000000..bb263c8
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextAreaComponent.mm
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "WXTextAreaComponent.h"
+#import "WXUtility.h"
+#import "WXComponent+Layout.h"
+#import "WXComponent_internal.h"
+#import "WXComponent+Layout.h"
+
+#define CorrectX 4 //textview fill text 4 pixel from left. so placeholderlabel have 4 pixel too
+#define CorrectY 8 // textview fill text 8 pixel from top
+typedef UITextView WXTextAreaView;
+
+@interface WXTextAreaComponent()
+
+@property (nonatomic, strong) WXTextAreaView *textView;
+@property (nonatomic) NSUInteger rows;
+
+@end
+
+@implementation WXTextAreaComponent {
+ UIEdgeInsets _border;
+ UIEdgeInsets _padding;
+}
+
+-(void)viewDidLoad
+{
+ _padding = UIEdgeInsetsZero;
+ _border = UIEdgeInsetsZero;
+ if (self.placeholderString) {
+ self.placeHolderLabel = [[UILabel alloc] init];
+ self.placeHolderLabel.numberOfLines = 0;
+ [_textView addSubview:self.placeHolderLabel];
+ }
+ // default placeholder hide from voice over
+ self.placeHolderLabel.isAccessibilityElement = NO;
+ _textView.isAccessibilityElement = YES;
+ _textView.delegate = self;
+ [_textView setNeedsDisplay];
+ [_textView setClipsToBounds:YES];
+ [super viewDidLoad];
+}
+
+- (void)viewWillUnload
+{
+ _textView = nil;
+}
+
+- (UIView *)loadView
+{
+ _textView = [[WXTextAreaView alloc] init];
+ return _textView;
+}
+
+#pragma mark measure frame
+- (CGSize (^)(CGSize))measureBlock
+{
+ __weak typeof(self) weakSelf = self;
+ return ^CGSize (CGSize constrainedSize) {
+
+ CGSize computedSize = [[[NSString alloc] init]sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:weakSelf.textView.font.pointSize]}];
+ computedSize.height = _rows? computedSize.height *weakSelf.rows + (CorrectY + CorrectY/2):0;
+//#ifndef USE_FLEX
+
+ if (![WXComponent isUseFlex])
+ {
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
+ computedSize.width = MAX(computedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
+ computedSize.width = MIN(computedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
+ computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
+ }
+
+ if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
+ computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
+ }
+ }
+
+//#else
+
+ else
+ {
+ if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+ computedSize.width = MAX(computedSize.width, weakSelf.flexCssNode->getMinWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+ computedSize.width = MIN(computedSize.width, weakSelf.flexCssNode->getMaxWidth());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMinHeight())) {
+ computedSize.height = MAX(computedSize.height, weakSelf.flexCssNode->getMinHeight());
+ }
+
+ if (!isnan(weakSelf.flexCssNode->getMaxHeight())) {
+ computedSize.height = MIN(computedSize.height, weakSelf.flexCssNode->getMaxHeight());
+ }
+ }
+
+//#endif
+ return (CGSize) {
+ WXCeilPixelValue(computedSize.width),
+ WXCeilPixelValue(computedSize.height)
+ };
+ };
+}
+
+#pragma mark -Overwrite method
+-(NSString *)text
+{
+ return _textView.text;
+}
+
+- (void)setText:(NSString *)text
+{
+ _textView.text = text;
+ if ([text length] >0) {
+ self.placeHolderLabel.text = @"";
+ }
+}
+
+-(void)setTextColor:(UIColor *)color
+{
+ [_textView setTextColor:color];
+}
+
+-(void)setTextAlignment:(NSTextAlignment)textAlignForStyle
+{
+ [_textView setTextAlignment:textAlignForStyle];
+}
+
+-(void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
+{
+ [_textView setUserInteractionEnabled:userInteractionEnabled];
+}
+
+-(void)setEnabled:(BOOL)enabled
+{
+ _textView.editable = enabled;
+ _textView.selectable = enabled;
+}
+
+-(void)setReturnKeyType:(UIReturnKeyType)returnKeyType
+{
+ [_textView setReturnKeyType:returnKeyType];
+}
+
+-(void)setInputAccessoryView:(UIView *)inputAccessoryView
+{
+ [_textView setInputAccessoryView:inputAccessoryView];
+}
+
+-(void)setEditSelectionRange:(NSInteger)selectionStart selectionEnd:(NSInteger)selectionEnd
+{
+ [self.textView becomeFirstResponder];
+ UITextPosition *startPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionStart];
+ UITextPosition *endPos = [self.textView positionFromPosition:self.textView.beginningOfDocument offset:selectionEnd];
+ UITextRange *textRange = [self.textView textRangeFromPosition:startPos
+ toPosition:endPos];
+ self.textView.selectedTextRange = textRange;
+}
+
+-(NSDictionary *)getEditSelectionRange
+{
+ NSInteger selectionStart = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.start];
+ NSInteger selectionEnd = [self.textView offsetFromPosition:self.textView.beginningOfDocument toPosition:self.textView.selectedTextRange.end];
+ NSDictionary *res = @{@"selectionStart":@(selectionStart),@"selectionEnd":@(selectionEnd)};
+ return res;
+}
+
+-(void)setKeyboardType:(UIKeyboardType)keyboardType
+{
+ [_textView setKeyboardType:keyboardType];
+}
+
+-(void)setSecureTextEntry:(BOOL)secureTextEntry
+{
+ [_textView setSecureTextEntry:secureTextEntry];
+}
+
+-(void)setEditPadding:(UIEdgeInsets)padding
+{
+ _padding = padding;
+ [self _updateTextContentInset];
+}
+
+-(void)setEditBorder:(UIEdgeInsets)border
+{
+ _border = border;
+ [self _updateTextContentInset];
+}
+
+-(void)setAttributedPlaceholder:(NSMutableAttributedString *)attributedString font:(UIFont *)font
+{
+ if (self.placeholderColor) {
+ [attributedString addAttribute:NSForegroundColorAttributeName value:self.placeholderColor range:NSMakeRange(0, self.placeholderString.length)];
+ [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, self.placeholderString.length)];
+ }
+ self.placeHolderLabel.backgroundColor = [UIColor clearColor];
+ CGRect expectedLabelSize = [attributedString boundingRectWithSize:(CGSize){self.view.frame.size.width, CGFLOAT_MAX}
+ options:NSStringDrawingUsesLineFragmentOrigin
+ context:nil];
+
+ self.placeHolderLabel.clipsToBounds = NO;
+ CGRect newFrame = self.placeHolderLabel.frame;
+ newFrame.size.height = ceil(expectedLabelSize.size.height);
+ newFrame.size.width = _textView.frame.size.width- CorrectX*2;
+ newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
+ self.placeHolderLabel.frame = newFrame;
+ self.placeHolderLabel.attributedText = attributedString;
+}
+
+-(void)setFont:(UIFont *)font
+{
+ [_textView setFont:font];
+}
+
+-(void)setRows:(NSUInteger)rows
+{
+ _rows = rows;
+ [self setNeedsLayout];
+}
+
+#pragma mark -Private Method
+- (void)_updateTextContentInset
+{
+ [_textView setTextContainerInset:UIEdgeInsetsMake(_padding.top + _border.top,
+ _padding.left + _border.left,
+ _padding.bottom + _border.bottom,
+ _border.right + _border.right)];
+
+ //when textview update, placeHolderLabel update too
+ CGRect newFrame = self.placeHolderLabel.frame;
+ newFrame.size.width = self.textView.frame.size.width - (_padding.left + _border.left) -CorrectX*2;
+ newFrame.origin.x = CorrectX + _padding.left + _border.left; // the cursor origin.x
+ newFrame.origin.y = _padding.top + _border.top;
+ self.placeHolderLabel.frame = newFrame;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b77b4259/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
deleted file mode 100644
index 2a948e2..0000000
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.m
+++ /dev/null
@@ -1,1076 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#import "WXTextComponent.h"
-#import "WXSDKInstance_private.h"
-#import "WXComponent_internal.h"
-#import "WXLayer.h"
-#import "WXUtility.h"
-#import "WXConvert.h"
-#import "WXRuleManager.h"
-#import "WXDefine.h"
-#import "WXView.h"
-#import <pthread/pthread.h>
-#import <CoreText/CoreText.h>
-
-// WXText is a non-public is not permitted
-@interface WXTextView : WXView
-@property (nonatomic, strong) NSTextStorage *textStorage;
-@end
-
-@implementation WXTextView
-
-- (instancetype)initWithFrame:(CGRect)frame
-{
- if ((self = [super initWithFrame:frame])) {
- self.accessibilityTraits |= UIAccessibilityTraitStaticText;
-
- self.opaque = NO;
- self.contentMode = UIViewContentModeRedraw;
- self.textStorage = [NSTextStorage new];
- }
- return self;
-}
-
-+ (Class)layerClass
-{
- return [WXLayer class];
-}
-
-- (void)copy:(id)sender
-{
- [[UIPasteboard generalPasteboard] setString:((WXTextComponent*)self.wx_component).text];
-}
-
-- (void)setTextStorage:(NSTextStorage *)textStorage
-{
- if (_textStorage != textStorage) {
- _textStorage = textStorage;
- [self.wx_component setNeedsDisplay];
- }
-}
-
-- (BOOL)canBecomeFirstResponder
-{
- return YES;
-}
-
-- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
-{
- if (action == @selector(copy:)) {
- return [[self.wx_component valueForKey:@"_enableCopy"] boolValue];
- }
- return [super canPerformAction:action withSender:sender];
-}
-
-- (NSString *)description
-{
- NSString *superDescription = super.description;
- NSRange semicolonRange = [superDescription rangeOfString:@";"];
- NSString * content = _textStorage.string;
- if ([(WXTextComponent*)self.wx_component useCoreText]) {
- content = ((WXTextComponent*)self.wx_component).text;
- }
- NSString *replacement = [NSString stringWithFormat:@"; text: %@; frame:%f,%f,%f,%f", content, self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height];
- return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement];
-}
-
-- (NSString *)accessibilityValue
-{
- if (self.wx_component && self.wx_component->_ariaLabel) {
- return [super accessibilityValue];
- }
- if (![(WXTextComponent*)self.wx_component useCoreText]) {
- return _textStorage.string;
- }
- return ((WXTextComponent*)self.wx_component).text;
-}
-
-- (NSString *)accessibilityLabel
-{
- if (self.wx_component) {
- if (self.wx_component->_ariaLabel) {
- return self.wx_component->_ariaLabel;
- }
- }
- return [super accessibilityLabel];
-}
-
-@end
-
-static BOOL textRenderUsingCoreText = YES;
-
-NSString *const WXTextTruncationToken = @"\u2026";
-CGFloat WXTextDefaultLineThroughWidth = 1.2;
-
-@interface WXTextComponent()
-@property (nonatomic, strong) NSString *useCoreTextAttr;
-@end
-
-@implementation WXTextComponent
-{
- UIEdgeInsets _border;
- UIEdgeInsets _padding;
- NSTextStorage *_textStorage;
- CGFloat _textStorageWidth;
-
- UIColor *_color;
- NSString *_fontFamily;
- CGFloat _fontSize;
- CGFloat _fontWeight;
- WXTextStyle _fontStyle;
- NSUInteger _lines;
- NSTextAlignment _textAlign;
- NSString *_direction;
- WXTextDecoration _textDecoration;
- NSString *_textOverflow;
- CGFloat _lineHeight;
- CGFloat _letterSpacing;
- BOOL _truncationLine; // support trunk tail
-
- NSAttributedString * _ctAttributedString;
- NSString *_wordWrap;
-
- pthread_mutex_t _ctAttributedStringMutex;
- pthread_mutexattr_t _propertMutexAttr;
- BOOL _observerIconfont;
- BOOL _enableCopy;
-}
-
-+ (void)setRenderUsingCoreText:(BOOL)usingCoreText
-{
- textRenderUsingCoreText = usingCoreText;
-}
-
-+ (BOOL)textRenderUsingCoreText
-{
- return textRenderUsingCoreText;
-}
-
-- (instancetype)initWithRef:(NSString *)ref
- type:(NSString *)type
- styles:(NSDictionary *)styles
- attributes:(NSDictionary *)attributes
- events:(NSArray *)events
- weexInstance:(WXSDKInstance *)weexInstance
-{
- self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
- if (self) {
- // just for coretext and textkit render replacement
- pthread_mutexattr_init(&(_propertMutexAttr));
- pthread_mutexattr_settype(&(_propertMutexAttr), PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&(_ctAttributedStringMutex), &(_propertMutexAttr));
-
- if ([attributes objectForKey:@"coretext"]) {
- _useCoreTextAttr = [WXConvert NSString:attributes[@"coretext"]];
- } else {
- _useCoreTextAttr = nil;
- }
-
- [self fillCSSStyles:styles];
- [self fillAttributes:attributes];
- }
-
- return self;
-}
-
-- (BOOL)useCoreText
-{
- if ([_useCoreTextAttr isEqualToString:@"true"]) {
- return YES;
- }
- if ([_useCoreTextAttr isEqualToString:@"false"]) {
- return NO;
- }
-
- if ([WXTextComponent textRenderUsingCoreText]) {
- return YES;
- }
- return NO;
-}
-
-- (void)dealloc
-{
- if (_fontFamily && _observerIconfont) {
- [[NSNotificationCenter defaultCenter] removeObserver:self name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
- }
- pthread_mutex_destroy(&_ctAttributedStringMutex);
- pthread_mutexattr_destroy(&_propertMutexAttr);
-}
-
-#define WX_STYLE_FILL_TEXT(key, prop, type, needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- _##prop = [WXConvert type:value];\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
- }\
-} while(0);
-
-#define WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(key, prop, type, defaultValue,needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- if([WXUtility isBlankString:value]){\
- _##prop = defaultValue;\
- }else {\
- _##prop = [WXConvert type:value];\
- }\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
- }\
-} while(0);
-
-
-#define WX_STYLE_FILL_TEXT_PIXEL(key, prop, needLayout)\
-do {\
- id value = styles[@#key];\
- if (value) {\
- _##prop = [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor];\
- [self setNeedsRepaint];\
- if (needLayout) {\
- [self setNeedsLayout];\
- }\
-}\
-} while(0);
-
-- (void)fillCSSStyles:(NSDictionary *)styles
-{
- WX_STYLE_FILL_TEXT_WITH_DEFAULT_VALUE(color, color, UIColor, [UIColor blackColor], NO)
- WX_STYLE_FILL_TEXT(fontFamily, fontFamily, NSString, YES)
- WX_STYLE_FILL_TEXT_PIXEL(fontSize, fontSize, YES)
- WX_STYLE_FILL_TEXT(fontWeight, fontWeight, WXTextWeight, YES)
- WX_STYLE_FILL_TEXT(fontStyle, fontStyle, WXTextStyle, YES)
- WX_STYLE_FILL_TEXT(lines, lines, NSUInteger, YES)
- WX_STYLE_FILL_TEXT(textAlign, textAlign, NSTextAlignment, NO)
- WX_STYLE_FILL_TEXT(textDecoration, textDecoration, WXTextDecoration, YES)
- WX_STYLE_FILL_TEXT(textOverflow, textOverflow, NSString, NO)
- WX_STYLE_FILL_TEXT_PIXEL(lineHeight, lineHeight, YES)
- WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES)
- WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES);
- WX_STYLE_FILL_TEXT(direction, direction, NSString, YES)
- if (_fontFamily && !_observerIconfont) {
- // notification received when custom icon font file download finish
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION object:nil];
- _observerIconfont = YES;
- }
- UIEdgeInsets padding = {
- WXFloorPixelValue(self.cssNode->style.padding[CSS_TOP] + self.cssNode->style.border[CSS_TOP]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_LEFT] + self.cssNode->style.border[CSS_LEFT]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_BOTTOM] + self.cssNode->style.border[CSS_BOTTOM]),
- WXFloorPixelValue(self.cssNode->style.padding[CSS_RIGHT] + self.cssNode->style.border[CSS_RIGHT])
- };
-
- if (!UIEdgeInsetsEqualToEdgeInsets(padding, _padding)) {
- _padding = padding;
- [self setNeedsRepaint];
- }
-}
-
-- (void)fillAttributes:(NSDictionary *)attributes
-{
- id text = [WXConvert NSString:attributes[@"value"]];
- if (text && ![self.text isEqualToString:text]) {
- self.text = text;
- [self setNeedsRepaint];
- [self setNeedsLayout];
- }
- if (attributes[@"enableCopy"]) {
- _enableCopy = [WXConvert BOOL:attributes[@"enableCopy"]];
- }
-}
-
-- (void)setNeedsRepaint
-{
- _textStorage = nil;
-
- pthread_mutex_lock(&(_ctAttributedStringMutex));
- _ctAttributedString = nil;
- pthread_mutex_unlock(&(_ctAttributedStringMutex));
-
-}
-
-#pragma mark - Subclass
-
-- (void)setNeedsLayout
-{
- [super setNeedsLayout];
-}
-
-- (void)viewDidLoad
-{
- [super viewDidLoad];
- BOOL useCoreText = NO;
- if ([self.view.wx_component isKindOfClass:NSClassFromString(@"WXTextComponent")] && [self.view.wx_component respondsToSelector:@selector(useCoreText)]) {
- useCoreText = [(WXTextComponent*)self.view.wx_component useCoreText];
- }
- if (!useCoreText) {
- ((WXTextView *)self.view).textStorage = _textStorage;
- }
- if (_enableCopy) {
- UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(displayMenuController:)];
- [self.view addGestureRecognizer:longPress];
- }
- self.view.isAccessibilityElement = YES;
-
- [self setNeedsDisplay];
-}
-
-- (void)displayMenuController:(id)sender
-{
- if ([self.view becomeFirstResponder] && ((UILongPressGestureRecognizer*)sender).state == UIGestureRecognizerStateBegan) {
- UIMenuController *theMenu = [UIMenuController sharedMenuController];
- CGSize size = [self ctAttributedString].size;
- CGRect selectionRect = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, size.width, size.height);
- [theMenu setTargetRect:selectionRect inView:self.view.superview];
- [theMenu setMenuVisible:YES animated:YES];
- }
-}
-
-- (UIView *)loadView
-{
- return [[WXTextView alloc] init];
-}
-
-- (BOOL)needsDrawRect
-{
- return YES;
-}
-
-- (UIImage *)drawRect:(CGRect)rect;
-{
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (_isCompositingChild) {
- [self drawTextWithContext:context bounds:rect padding:_padding view:nil];
- } else {
- WXTextView *textView = (WXTextView *)_view;
- [self drawTextWithContext:context bounds:rect padding:_padding view:textView];
- }
-
- return nil;
-}
-
-- (CGSize (^)(CGSize))measureBlock
-{
- __weak typeof(self) weakSelf = self;
- return ^CGSize (CGSize constrainedSize) {
- CGSize computedSize = CGSizeZero;
- NSTextStorage *textStorage = nil;
-
- //TODO:more elegant way to use max and min constrained size
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_WIDTH])) {
- constrainedSize.width = MAX(constrainedSize.width, weakSelf.cssNode->style.minDimensions[CSS_WIDTH]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_WIDTH])) {
- constrainedSize.width = MIN(constrainedSize.width, weakSelf.cssNode->style.maxDimensions[CSS_WIDTH]);
- }
-
- if (![self useCoreText]) {
- textStorage = [weakSelf textStorageWithWidth:constrainedSize.width];
- NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
- NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
- computedSize = [layoutManager usedRectForTextContainer:textContainer].size;
- } else {
- computedSize = [weakSelf calculateTextHeightWithWidth:constrainedSize.width];
- }
-
- if (!isnan(weakSelf.cssNode->style.minDimensions[CSS_HEIGHT])) {
- computedSize.height = MAX(computedSize.height, weakSelf.cssNode->style.minDimensions[CSS_HEIGHT]);
- }
-
- if (!isnan(weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT])) {
- computedSize.height = MIN(computedSize.height, weakSelf.cssNode->style.maxDimensions[CSS_HEIGHT]);
- }
- if (textStorage && [WXUtility isBlankString:textStorage.string]) {
- // if the text value is empty or nil, then set the height is 0.
- computedSize.height = 0;
- }
-
- return (CGSize) {
- WXCeilPixelValue(computedSize.width),
- WXCeilPixelValue(computedSize.height)
- };
- };
-}
-
-#pragma mark Text Building
-
-- (NSAttributedString *)ctAttributedString
-{
- if (!self.text) {
- return nil;
- }
- NSAttributedString * attributedString = nil;
- pthread_mutex_lock(&(_ctAttributedStringMutex));
- if (!_ctAttributedString) {
- _ctAttributedString = [self buildCTAttributeString];
- WXPerformBlockOnComponentThread(^{
- [self.weexInstance.componentManager startComponentTasks];
- });
- }
- attributedString = [_ctAttributedString copy];
- pthread_mutex_unlock(&(_ctAttributedStringMutex));
- return attributedString;
-}
-
-- (void)repaintText:(NSNotification *)notification
-{
- if (![_fontFamily isEqualToString:notification.userInfo[@"fontFamily"]]) {
- return;
- }
- [self setNeedsRepaint];
- WXPerformBlockOnComponentThread(^{
- [self.weexInstance.componentManager startComponentTasks];
- WXPerformBlockOnMainThread(^{
- [self setNeedsLayout];
- [self setNeedsDisplay];
- });
- });
-}
-
-- (NSMutableAttributedString *)buildCTAttributeString
-{
- NSString * string = self.text;
- if (![string isKindOfClass:[NSString class]]) {
- WXLogError(@"text %@ is invalid", self.text);
- string = @"";
- }
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string];
- if (_color) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
- }
-
- // set font
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor useCoreText:[self useCoreText]];
- CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName,
- font.pointSize,
- NULL);
- if (ctFont) {
- [attributedString addAttribute:(id)kCTFontAttributeName value:(__bridge id)(ctFont) range:NSMakeRange(0, string.length)];
- CFRelease(ctFont);
- }
-
- if(_textDecoration == WXTextDecorationUnderline){
- [attributedString addAttribute:(id)kCTUnderlineStyleAttributeName value:@(kCTUnderlinePatternSolid | kCTUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- } else if(_textDecoration == WXTextDecorationLineThrough){
- [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- }
-
- NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
-
- // handle text direction style, default ltr
- if (_cssNode->layout.direction == CSS_DIRECTION_RTL) {
- if (0 == _textAlign) {
- //force text right-align if don't specified any align.
- _textAlign = NSTextAlignmentRight;
- }
- paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
- } else {
- //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
- //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
- //depending on the direction for the user’s language preference setting.
- paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
- }
-
- if (_textAlign) {
- paragraphStyle.alignment = _textAlign;
- }
-
- if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
- paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
- } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
- paragraphStyle.lineBreakMode = NSLineBreakByClipping;
- } else {
- // set default lineBreakMode
- paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping;
- }
- _truncationLine = NO;
- if (_textOverflow && [_textOverflow length] > 0) {
- if (_lines && [_textOverflow isEqualToString:@"ellipsis"])
- _truncationLine = YES;
- }
-
- if (_lineHeight) {
- paragraphStyle.maximumLineHeight = _lineHeight;
- paragraphStyle.minimumLineHeight = _lineHeight;
- }
- if (_lineHeight || _textAlign || [_textOverflow length] > 0) {
- [attributedString addAttribute:NSParagraphStyleAttributeName
- value:paragraphStyle
- range:(NSRange){0, attributedString.length}];
- }
-
- if (_letterSpacing) {
- [attributedString addAttribute:NSKernAttributeName value:@(_letterSpacing) range:(NSRange){0, attributedString.length}];
- }
-
- if ([self adjustLineHeight]) {
- if (_lineHeight > font.lineHeight) {
- [attributedString addAttribute:NSBaselineOffsetAttributeName
- value:@((_lineHeight - font.lineHeight)/ 2)
- range:(NSRange){0, attributedString.length}];
- }
- }
-
- return attributedString;
-}
-
-- (NSAttributedString *)buildAttributeString
-{
- NSString *string = self.text ?: @"";
-
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
-
- // set textColor
- if(_color) {
- [attributedString addAttribute:NSForegroundColorAttributeName value:_color range:NSMakeRange(0, string.length)];
- }
-
- // set font
- UIFont *font = [WXUtility fontWithSize:_fontSize textWeight:_fontWeight textStyle:_fontStyle fontFamily:_fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
- if (font) {
- [attributedString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, string.length)];
- }
-
- if(_textDecoration == WXTextDecorationUnderline){
- [attributedString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- } else if(_textDecoration == WXTextDecorationLineThrough){
- [attributedString addAttribute:NSStrikethroughStyleAttributeName value:@(NSUnderlinePatternSolid | NSUnderlineStyleSingle) range:NSMakeRange(0, string.length)];
- }
-
- NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
-
- // handle text direction style, default ltr
- if (_cssNode->layout.direction == CSS_DIRECTION_RTL) {
- if (0 == _textAlign) {
- //force text right-align if don't specified any align.
- _textAlign = NSTextAlignmentRight;
- }
- paragraphStyle.baseWritingDirection = NSWritingDirectionRightToLeft;
- } else {
- //if you specify NSWritingDirectionNaturalDirection, the receiver resolves the writing
- //directionto eitherNSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft,
- //depending on the direction for the user’s language preference setting.
- paragraphStyle.baseWritingDirection = NSWritingDirectionNatural;
- }
-
- if (_textAlign) {
- paragraphStyle.alignment = _textAlign;
- }
-
- if (_lineHeight) {
- paragraphStyle.maximumLineHeight = _lineHeight;
- paragraphStyle.minimumLineHeight = _lineHeight;
- }
-
- if (_lineHeight || _textAlign) {
- [attributedString addAttribute:NSParagraphStyleAttributeName
- value:paragraphStyle
- range:(NSRange){0, attributedString.length}];
- }
- if ([self adjustLineHeight]) {
- if (_lineHeight > font.lineHeight) {
- [attributedString addAttribute:NSBaselineOffsetAttributeName
- value:@((_lineHeight - font.lineHeight)/ 2)
- range:(NSRange){0, attributedString.length}];
- }
- }
-
- return attributedString;
-}
-
-- (BOOL)adjustLineHeight
-{
- if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
- return true;
- }
- return ![self useCoreText];
-}
-
-- (NSTextStorage *)textStorageWithWidth:(CGFloat)width
-{
- if (_textStorage && width == _textStorageWidth) {
- return _textStorage;
- }
-
- NSLayoutManager *layoutManager = [NSLayoutManager new];
-
- // build AttributeString
- NSAttributedString *attributedString = [self buildAttributeString];
-
- NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
- [textStorage addLayoutManager:layoutManager];
-
- NSTextContainer *textContainer = [NSTextContainer new];
- textContainer.lineFragmentPadding = 0.0;
-
- if ([[_wordWrap lowercaseString] isEqualToString:@"break-word"]) {
- textContainer.lineBreakMode = NSLineBreakByWordWrapping;
- } else if ([[_wordWrap lowercaseString] isEqualToString:@"normal"]){
- textContainer.lineBreakMode = NSLineBreakByClipping;
- } else {
- // set default lineBreakMode
- textContainer.lineBreakMode = NSLineBreakByCharWrapping;
- }
-
- if (_textOverflow && [_textOverflow length] > 0) {
- if ([_textOverflow isEqualToString:@"ellipsis"])
- textContainer.lineBreakMode = NSLineBreakByTruncatingTail;
- }
- textContainer.maximumNumberOfLines = _lines > 0 ? _lines : 0;
- textContainer.size = (CGSize){isnan(width) ? CGFLOAT_MAX : width, CGFLOAT_MAX};
-
- [layoutManager addTextContainer:textContainer];
- [layoutManager ensureLayoutForTextContainer:textContainer];
-
- _textStorageWidth = width;
- _textStorage = textStorage;
-
- return textStorage;
-}
-
-- (void)syncTextStorageForView
-{
- CGFloat width = self.calculatedFrame.size.width - (_padding.left + _padding.right);
- NSTextStorage *textStorage = nil;
- if (![self useCoreText]) {
- textStorage = [self textStorageWithWidth:width];
- }
- [self.weexInstance.componentManager _addUITask:^{
- if ([self isViewLoaded]) {
- if (![self useCoreText]) {
- ((WXTextView *)self.view).textStorage = textStorage;
- }
- [self readyToRender]; // notify super component
- [self setNeedsDisplay];
- }
- }];
-}
-
-- (void)_frameDidCalculated:(BOOL)isChanged
-{
- [super _frameDidCalculated:isChanged];
- [self syncTextStorageForView];
-}
-
-- (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
-{
- [super _updateStylesOnComponentThread:styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:isUpdateStyles];
- NSMutableDictionary * newStyles = [styles mutableCopy];
- for (NSString * key in [resetStyles copy]) {
- [newStyles setObject:@"" forKey:key];
- }
- [self fillCSSStyles:newStyles];
-
- [self syncTextStorageForView];
-}
-
-- (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
-{
- [super _updateAttributesOnComponentThread:attributes];
-
- [self fillAttributes:attributes];
-
- [self syncTextStorageForView];
-}
-
-- (void)drawTextWithContext:(CGContextRef)context bounds:(CGRect)bounds padding:(UIEdgeInsets)padding view:(WXTextView *)view
-{
- if (bounds.size.width <= 0 || bounds.size.height <= 0) {
- return;
- }
-
- if ([self _needsDrawBorder]) {
- [self _drawBorderWithContext:context size:bounds.size];
- } else {
- WXPerformBlockOnMainThread(^{
- [self _resetNativeBorderRadius];
- });
- }
- if (![self useCoreText]) {
- NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject;
- NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
-
- CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
- NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
-
- [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
- [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
- } else {
- CGRect textFrame = UIEdgeInsetsInsetRect(bounds, padding);
- // sufficient height for text to draw, or frame lines will be empty
- textFrame.size.height = bounds.size.height * 2;
- CGContextSaveGState(context);
- //flip the coordinate system
- CGContextSetTextMatrix(context, CGAffineTransformIdentity);
- CGContextTranslateCTM(context, 0, textFrame.size.height);
- CGContextScaleCTM(context, 1.0, -1.0);
-
- NSAttributedString * attributedStringCopy = [self ctAttributedString];
- //add path
- CGPathRef cgPath = NULL;
- cgPath = CGPathCreateWithRect(textFrame, NULL);
- CTFrameRef _coreTextFrameRef = NULL;
- if (_coreTextFrameRef) {
- CFRelease(_coreTextFrameRef);
- _coreTextFrameRef = NULL;
- }
- if(!attributedStringCopy) {
- return;
- }
- CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCopy));
- _coreTextFrameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCopy.length), cgPath, NULL);
- CFArrayRef ctLines = NULL;
- if (NULL == _coreTextFrameRef) {
- // try to protect crash from frame is NULL
- return;
- }
- CFRelease(ctframesetterRef);
- ctframesetterRef = NULL;
- ctLines = CTFrameGetLines(_coreTextFrameRef);
- CFIndex lineCount = CFArrayGetCount(ctLines);
- NSMutableArray * mutableLines = [NSMutableArray new];
- CGPoint lineOrigins[lineCount];
- NSUInteger rowCount = 0;
- BOOL needTruncation = NO;
- CTLineRef ctTruncatedLine = NULL;
- CTFrameGetLineOrigins(_coreTextFrameRef, CFRangeMake(0, 0), lineOrigins);
- for (CFIndex lineIndex = 0;(!_lines || _lines > lineIndex) && lineIndex < lineCount; lineIndex ++) {
- CTLineRef lineRef = NULL;
- lineRef = CFArrayGetValueAtIndex(ctLines, lineIndex);
- if (!lineRef) {
- break;
- }
- CGPoint lineOrigin = lineOrigins[lineIndex];
- lineOrigin.x += padding.left;
- lineOrigin.y -= padding.top;
- CFArrayRef runs = CTLineGetGlyphRuns(lineRef);
- [mutableLines addObject:(__bridge id _Nonnull)(lineRef)];
- // lineIndex base 0
- rowCount = lineIndex + 1;
- if (_lines > 0 && _truncationLine) {
- if (_truncationLine && rowCount > _lines) {
- needTruncation = YES;
- do {
- NSUInteger lastRow = [mutableLines count];
- if (lastRow < rowCount) {
- break;
- }
- [mutableLines removeLastObject];
- } while (1);
-
- }
- }
- if (_lines > 0 && _truncationLine) {
- if (rowCount >= _lines &&!needTruncation && (CTLineGetStringRange(lineRef).length + CTLineGetStringRange(lineRef).location) < attributedStringCopy.length) {
- needTruncation = YES;
- }
- }
-
- if (needTruncation) {
- CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
- ctTruncatedLine = [self buildTruncatedLineWithRuns:runs lines:mutableLines path:cgPath];
- if (ctTruncatedLine) {
- CFArrayRef truncatedRuns = CTLineGetGlyphRuns(ctTruncatedLine);
- [self drawTextWithRuns:truncatedRuns context:context lineOrigin:lineOrigin];
- CFRelease(ctTruncatedLine);
- ctTruncatedLine = NULL;
- continue;
- }
- }else {
- [self drawTextWithRuns:runs context:context lineOrigin:lineOrigin];
- }
- }
-
- [mutableLines removeAllObjects];
- CGPathRelease(cgPath);
- CFRelease(_coreTextFrameRef);
- _coreTextFrameRef = NULL;
- cgPath = NULL;
- CGContextRestoreGState(context);
- }
-}
-
-- (void)drawTextWithRuns:(CFArrayRef)runs context:(CGContextRef)context lineOrigin:(CGPoint)lineOrigin
-{
- for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runs); runIndex ++) {
- CTRunRef run = NULL;
- run = CFArrayGetValueAtIndex(runs, runIndex);
- CFDictionaryRef attr = NULL;
- attr = CTRunGetAttributes(run);
- if (0 == runIndex) {
- NSNumber *baselineOffset = (NSNumber*)CFDictionaryGetValue(attr, NSBaselineOffsetAttributeName);
- if (baselineOffset) {
- lineOrigin.y += [baselineOffset doubleValue];
- }
- }
- CGContextSetTextPosition(context, lineOrigin.x, lineOrigin.y);
- CTRunDraw(run, context, CFRangeMake(0, 0));
- CFIndex glyphCount = CTRunGetGlyphCount(run);
- if (glyphCount <= 0) continue;
-
- NSUnderlineStyle strikethrough = (NSUnderlineStyle)CFDictionaryGetValue(attr, NSStrikethroughStyleAttributeName);
-
- if (strikethrough) {
- // draw strikethrough
- [self drawLineThroughWithRun:runs context:context index:runIndex origin:lineOrigin];
- }
- }
-}
-
-- (CTLineRef)buildTruncatedLineWithRuns:(CFArrayRef)runs lines:(NSMutableArray*)mutableLines path:(CGPathRef)cgPath
-{
- NSAttributedString * truncationToken = nil;
- CTLineRef ctTruncatedLine = NULL;
- CTLineRef lastLine = (__bridge CTLineRef)(mutableLines.lastObject);
-
- CFArrayRef lastLineRuns = CTLineGetGlyphRuns(lastLine);
- NSUInteger lastLineRunCount = CFArrayGetCount(lastLineRuns);
-
- CTLineRef truncationTokenLine = NULL;
- NSMutableDictionary *attrs = nil;
- if (lastLineRunCount > 0) {
- CTRunRef run = CFArrayGetValueAtIndex(runs, lastLineRunCount - 1);
- attrs = (id)CTRunGetAttributes(run);
- attrs = attrs ? attrs.mutableCopy : [NSMutableDictionary new];
- CTFontRef font = (__bridge CTFontRef)(attrs[(id)kCTFontAttributeName]);
- CGFloat fontSize = font ? CTFontGetSize(font):32 * self.weexInstance.pixelScaleFactor;
- UIFont * uiFont = [UIFont systemFontOfSize:fontSize];
- if (uiFont) {
- font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
- }
- if (font) {
- attrs[(id)kCTFontAttributeName] = (__bridge id)(font);
- uiFont = nil;
- CFRelease(font);
- }
- CGColorRef color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]);
- if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) {
- [attrs removeObjectForKey:(id)kCTForegroundColorAttributeName];
- }
-
- attrs = attrs?:[NSMutableDictionary new];
- truncationToken = [[NSAttributedString alloc] initWithString:WXTextTruncationToken attributes:attrs];
- truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)truncationToken);
- }
-
- if (truncationTokenLine) {
- // default truncationType is kCTLineTruncationEnd
- CTLineTruncationType truncationType = kCTLineTruncationEnd;
- NSAttributedString *attributedString = [self ctAttributedString];
- NSAttributedString * lastLineText = nil;
- NSRange lastLineTextRange = WXNSRangeFromCFRange(CTLineGetStringRange(lastLine));
- NSRange attributeStringRange = NSMakeRange(0, attributedString.string.length);
- NSRange interSectionRange = NSIntersectionRange(lastLineTextRange, attributeStringRange);
- if (!NSEqualRanges(interSectionRange, lastLineTextRange)) {
- // out of bounds
- lastLineTextRange = interSectionRange;
- }
- lastLineText = [attributedString attributedSubstringFromRange: lastLineTextRange];
- if (!lastLineText) {
- lastLineText = attributedString;
- }
- NSMutableAttributedString *mutableLastLineText = lastLineText.mutableCopy;
- [mutableLastLineText appendAttributedString:truncationToken];
- CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)[mutableLastLineText copy]);
- if (ctLastLineExtend) {
- CGRect cgPathRect = CGRectZero;
- CGFloat truncatedWidth = 0;
- if (CGPathIsRect(cgPath, &cgPathRect)) {
- truncatedWidth = cgPathRect.size.width;
- }
- ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, truncationType, truncationTokenLine);
- CFRelease(ctLastLineExtend);
- ctLastLineExtend = NULL;
- CFRelease(truncationTokenLine);
- truncationTokenLine = NULL;
- }
- }
-
- return ctTruncatedLine;
-}
-
-- (void)drawLineThroughWithRun:(CFArrayRef)runs context:(CGContextRef)context index:(CFIndex)runIndex origin:(CGPoint)lineOrigin
-{
- CFRetain(runs);
- CGContextRetain(context);
-
- CGContextSaveGState(context);
- CGFloat xHeight = 0, underLinePosition = 0, lineThickness = 0;
- CTRunRef run = CFArrayGetValueAtIndex(runs, runIndex);
- WXTextGetRunsMaxMetric(runs, &xHeight, &underLinePosition, &lineThickness);
-
- CGPoint strikethroughStart;
- strikethroughStart.x = lineOrigin.x - underLinePosition;
- strikethroughStart.y = lineOrigin.y + xHeight/2;
- CGPoint runPosition = CGPointZero;
- CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
- strikethroughStart.x = lineOrigin.x + runPosition.x;
- CGContextSetLineWidth(context, WXTextDefaultLineThroughWidth);
- double length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL);
- CGContextMoveToPoint(context, strikethroughStart.x, strikethroughStart.y);
- CGContextAddLineToPoint(context, strikethroughStart.x + length, strikethroughStart.y);
- CGContextStrokePath(context);
-
- CGContextRestoreGState(context);
- CFRelease(runs);
- CGContextRelease(context);
-}
-
-- (CGSize)calculateTextHeightWithWidth:(CGFloat)aWidth
-{
- CGFloat totalHeight = 0;
- CGSize suggestSize = CGSizeZero;
- NSAttributedString * attributedStringCpy = [self ctAttributedString];
- if (!attributedStringCpy) {
- return CGSizeZero;
- }
- if (isnan(aWidth)) {
- aWidth = CGFLOAT_MAX;
- }
- aWidth = [attributedStringCpy boundingRectWithSize:CGSizeMake(aWidth, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil].size.width;
- CTFramesetterRef ctframesetterRef = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attributedStringCpy));
- suggestSize = CTFramesetterSuggestFrameSizeWithConstraints(ctframesetterRef, CFRangeMake(0, 0), NULL, CGSizeMake(aWidth, MAXFLOAT), NULL);
-
- CGMutablePathRef path = NULL;
- path = CGPathCreateMutable();
- // sufficient height to draw text
- CGPathAddRect(path, NULL, CGRectMake(0, 0, aWidth, suggestSize.height * 10));
-
- CTFrameRef frameRef = NULL;
- frameRef = CTFramesetterCreateFrame(ctframesetterRef, CFRangeMake(0, attributedStringCpy.length), path, NULL);
- CGPathRelease(path);
-
- CFArrayRef lines = NULL;
- if (NULL == frameRef) {
- //try to protect unexpected crash.
- return suggestSize;
- }
- CFRelease(ctframesetterRef);
- ctframesetterRef = NULL;
- lines = CTFrameGetLines(frameRef);
- CFIndex lineCount = CFArrayGetCount(lines);
- CGFloat ascent = 0;
- CGFloat descent = 0;
- CGFloat leading = 0;
-
- // height = ascent + descent + lineCount*leading
- // ignore linespaing
- NSUInteger actualLineCount = 0;
- for (CFIndex lineIndex = 0; (!_lines|| lineIndex < _lines) && lineIndex < lineCount; lineIndex ++)
- {
- CTLineRef lineRef = NULL;
- lineRef = CFArrayGetValueAtIndex(lines, lineIndex);
- CTLineGetTypographicBounds(lineRef, &ascent, &descent, &leading);
- totalHeight += ascent + descent;
- actualLineCount ++;
- }
-
- totalHeight = totalHeight + actualLineCount * leading;
- CFRelease(frameRef);
- frameRef = NULL;
-
- if (WX_SYS_VERSION_LESS_THAN(@"10.0")) {
- // there is something wrong with coreText drawing text height, trying to fix this with more efficent way.
- if(actualLineCount && actualLineCount < lineCount) {
- suggestSize.height = suggestSize.height * actualLineCount / lineCount;
- }
- return CGSizeMake(aWidth, suggestSize.height);
- }
- return CGSizeMake(aWidth, totalHeight);
-}
-
-static void WXTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness)
-{
- CFRetain(runs);
- CGFloat maxXHeight = 0;
- CGFloat maxUnderlinePos = 0;
- CGFloat maxLineThickness = 0;
- for (NSUInteger index = 0, runsCount = CFArrayGetCount(runs); index < runsCount; index ++) {
- CTRunRef run = CFArrayGetValueAtIndex(runs, index);
- CFDictionaryRef attrs = CTRunGetAttributes(run);
- if (attrs) {
- CTFontRef font = CFDictionaryGetValue(attrs, kCTFontAttributeName);
- if (font) {
- CGFloat xHeight = CTFontGetXHeight(font);
- if (xHeight > maxXHeight) {
- maxXHeight = xHeight;
- }
-
- CGFloat underlinePos = CTFontGetUnderlinePosition(font);
- if (underlinePos < maxUnderlinePos) {
- maxUnderlinePos = underlinePos;
- }
-
- CGFloat lineThickness = CTFontGetUnderlineThickness(font);
- if (lineThickness > maxLineThickness) {
- maxLineThickness = lineThickness;
- }
- }
- }
- }
-
- if (xHeight) {
- *xHeight = maxXHeight;
- }
-
- if (underlinePosition) {
- *underlinePosition = maxUnderlinePos;
- }
-
- if (lineThickness) {
- *lineThickness = maxLineThickness;
- }
-
- CFRelease(runs);
-}
-
-NS_INLINE NSRange WXNSRangeFromCFRange(CFRange range) {
- return NSMakeRange(range.location, range.length);
-}
-
-#ifdef UITEST
-- (NSString *)description
-{
- return super.description;
-}
-#endif
-
-- (void)_resetCSSNodeStyles:(NSArray *)styles
-{
- [super _resetCSSNodeStyles:styles];
- if ([styles containsObject:@"color"]) {
- _color = [UIColor blackColor];
- [self setNeedsRepaint];
- }
- if ([styles containsObject:@"fontSize"]) {
- _fontSize = WX_TEXT_FONT_SIZE;
- [self setNeedsRepaint];
- [self setNeedsLayout];
- }
-}
-
-@end
-