| /* |
| * 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 "WXComponent+Layout.h" |
| #import "WXComponent_internal.h" |
| #import "WXTransform.h" |
| #import "WXAssert.h" |
| #import "WXSDKInstance_private.h" |
| #import "WXComponent+BoxShadow.h" |
| #import "WXLog.h" |
| #import "WXMonitor.h" |
| #import "WXSDKInstance_performance.h" |
| #import "WXCellComponent.h" |
| #import "WXCoreBridge.h" |
| |
| bool flexIsUndefined(float value) { |
| return isnan(value); |
| } |
| |
| @implementation WXComponent (Layout) |
| |
| #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" |
| |
| #pragma mark Public |
| |
| - (void)setNeedsLayout |
| { |
| if (_flexCssNode != nullptr) { |
| _flexCssNode->markDirty(); |
| } |
| } |
| |
| - (BOOL)needsLayout |
| { |
| if (_flexCssNode != nullptr) { |
| return _flexCssNode->isDirty(); |
| } |
| else { |
| return false; |
| } |
| } |
| |
| - (CGSize (^)(CGSize))measureBlock |
| { |
| return nil; |
| } |
| |
| - (void)layoutDidFinish |
| { |
| WXAssertMainThread(); |
| } |
| |
| - (void)updateLayoutStyles:(NSDictionary*)styles |
| { |
| WXPerformBlockOnComponentThread(^{ |
| [WXCoreBridge callUpdateStyle:self.weexInstance.instanceId ref:self.ref data:styles]; |
| }); |
| } |
| |
| #pragma mark Private |
| |
| - (void)_setRenderObject:(void *)object |
| { |
| if (object) { |
| _flexCssNode = static_cast<WeexCore::WXCoreLayoutNode*>(object); |
| _flexCssNode->setContext((__bridge void *)self); // bind |
| if ([self measureBlock]) { |
| _flexCssNode->setMeasureFunc(flexCssNodeMeasure); |
| } |
| } |
| else if (_flexCssNode) { |
| _flexCssNode->setContext(nullptr); |
| _flexCssNode = nullptr; |
| } |
| } |
| |
| - (void)_updateCSSNodeStyles:(NSDictionary *)styles |
| { |
| [self _fillCSSNode:styles isUpdate:YES]; |
| } |
| |
| -(void)_resetCSSNodeStyles:(NSArray *)styles |
| { |
| [self _resetCSSNode:styles]; |
| } |
| |
| - (void)_frameDidCalculated:(BOOL)isChanged |
| { |
| WXAssertComponentThread(); |
| if (isChanged && [self isKindOfClass:[WXCellComponent class]]) { |
| CGFloat mainScreenWidth = [[UIScreen mainScreen] bounds].size.width; |
| CGFloat mainScreenHeight = [[UIScreen mainScreen] bounds].size.height; |
| if (mainScreenHeight/2 < _calculatedFrame.size.height && mainScreenWidth/2 < _calculatedFrame.size.width) { |
| [self weexInstance].performance.cellExceedNum++; |
| [self.weexInstance.apmInstance updateFSDiffStats:KEY_PAGE_STATS_CELL_EXCEED_NUM withDiffValue:1]; |
| } |
| } |
| |
| if ([self isViewLoaded] && isChanged && [self isViewFrameSyncWithCalculated]) { |
| |
| __weak typeof(self) weakSelf = self; |
| [self.weexInstance.componentManager _addUITask:^{ |
| __strong typeof(weakSelf) strongSelf = weakSelf; |
| |
| if (strongSelf == nil) { |
| return; |
| } |
| |
| // Check again incase that this property is set to NO in another UI task. |
| if (![strongSelf isViewFrameSyncWithCalculated]) { |
| return; |
| } |
| |
| if (strongSelf->_transform && !CATransform3DEqualToTransform(strongSelf.layer.transform, CATransform3DIdentity)) { |
| // From the UIView's frame documentation: |
| // https://developer.apple.com/reference/uikit/uiview#//apple_ref/occ/instp/UIView/frame |
| // Warning : If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored. |
| // So layer's transform must be reset to CATransform3DIdentity before setFrame, otherwise frame will be incorrect |
| strongSelf.layer.transform = CATransform3DIdentity; |
| } |
| |
| WXBoxShadow* usingBoxShadow = nil; |
| if (strongSelf->_darkThemeBoxShadow && [strongSelf.weexInstance isDarkTheme]) { |
| usingBoxShadow = strongSelf->_darkThemeBoxShadow; |
| } |
| else { |
| usingBoxShadow = strongSelf->_boxShadow; |
| } |
| |
| if (!CGRectEqualToRect(strongSelf.view.frame,strongSelf.calculatedFrame)) { |
| strongSelf.view.frame = strongSelf.calculatedFrame; |
| strongSelf->_absolutePosition = CGPointMake(NAN, NAN); |
| [strongSelf configBoxShadow:usingBoxShadow]; |
| } else { |
| if (![strongSelf equalBoxShadow:usingBoxShadow withBoxShadow:strongSelf->_lastBoxShadow]) { |
| [strongSelf configBoxShadow:usingBoxShadow]; |
| } |
| } |
| |
| [strongSelf _resetNativeBorderRadius]; |
| |
| if ([WXUtility enableRTLLayoutDirection]) { |
| if ([strongSelf isDirectionRTL] != strongSelf -> _isLastLayoutDirectionRTL) { |
| strongSelf -> _isLastLayoutDirectionRTL = [strongSelf isDirectionRTL]; |
| [strongSelf _layoutDirectionDidChanged:[strongSelf isDirectionRTL]]; |
| } |
| } |
| |
| if (strongSelf->_transform) { |
| [strongSelf->_transform applyTransformForView:strongSelf.view]; |
| } |
| |
| [strongSelf _adjustForRTL]; |
| |
| if (strongSelf->_backgroundImage) { |
| [strongSelf setGradientLayer]; |
| } |
| |
| [strongSelf setNeedsDisplay]; |
| }]; |
| } else if ([WXUtility enableRTLLayoutDirection]) { |
| // if frame is not change, we still need check was layoutDirection changed |
| if ([self isDirectionRTL] != _isLastLayoutDirectionRTL) { |
| self -> _isLastLayoutDirectionRTL = [self isDirectionRTL]; |
| __weak typeof(self) weakSelf = self; |
| [self.weexInstance.componentManager _addUITask:^{ |
| __strong typeof(weakSelf) strongSelf = weakSelf; |
| [strongSelf _layoutDirectionDidChanged:[strongSelf isDirectionRTL]]; |
| if (strongSelf->_transform) { |
| [strongSelf->_transform applyTransformForView:strongSelf.view]; |
| } |
| [strongSelf _adjustForRTL]; |
| }]; |
| } |
| } |
| } |
| |
| - (void)_layoutDirectionDidChanged:(BOOL)isRTL { |
| WXAssertMainThread(); |
| [self layoutDirectionDidChanged:isRTL]; |
| } |
| |
| - (void)layoutDirectionDidChanged:(BOOL)isRTL { |
| |
| } |
| |
| - (void)_layoutDidFinish |
| { |
| WXAssertMainThread(); |
| |
| if (_positionType == WXPositionTypeSticky) { |
| [self.ancestorScroller adjustSticky]; |
| } |
| |
| [self layoutDidFinish]; |
| } |
| |
| #define WX_STYLE_FLEX_NODE_JUDGE_LEGAL(key) styles[key]&&!isnan([WXConvert WXPixelType:styles[key] scaleFactor:self.weexInstance.pixelScaleFactor]) |
| |
| - (CGFloat)WXPixelType:(id)value |
| { |
| return [WXConvert WXPixelType:value scaleFactor:self.weexInstance.pixelScaleFactor]; |
| } |
| |
| - (void)_fillCSSNode:(NSDictionary *)styles isUpdate:(BOOL)isUpdate |
| { |
| if (_flexCssNode == nullptr) { |
| return; |
| } |
| |
| BOOL needLayout = NO; |
| |
| // CSS direction for RTL Layout |
| if (styles[@"direction"]) { |
| _flexCssNode->setDirection([self fxDirection:styles[@"direction"]], isUpdate); |
| needLayout = YES; |
| } |
| |
| // flex |
| if (styles[@"flex"]) { |
| _flexCssNode->set_flex([WXConvert CGFloat:styles[@"flex"]]); |
| needLayout = YES; |
| } |
| if (isnan(_flexCssNode->getFlex())) { |
| // to make the default flex value is zero, yoga is nan, maybe this can configured by yoga config |
| _flexCssNode->set_flex(0); |
| needLayout = YES; |
| } |
| |
| if (styles[@"flexDirection"]) { |
| _flexCssNode->setFlexDirection([self fxFlexDirection:styles[@"flexDirection"]], isUpdate); |
| needLayout = YES; |
| } |
| if (styles[@"alignItems"]) { |
| _flexCssNode->setAlignItems([self fxAlign:styles[@"alignItems"]]); |
| needLayout = YES; |
| } |
| if (styles[@"alignSelf"]) { |
| _flexCssNode->setAlignSelf([self fxAlignSelf:styles[@"alignSelf"]]); |
| needLayout = YES; |
| } |
| if (styles[@"flexWrap"]) { |
| _flexCssNode->setFlexWrap([self fxWrap:styles[@"flexWrap"]]); |
| needLayout = YES; |
| } |
| if (styles[@"justifyContent"]) { |
| _flexCssNode->setJustifyContent([self fxJustify:styles[@"justifyContent"]]); |
| needLayout = YES; |
| } |
| |
| // position |
| if (styles[@"position"]) { |
| _flexCssNode->setStylePositionType([self fxPositionType:styles[@"position"]]); |
| needLayout = YES; |
| } |
| if (styles[@"top"]) { |
| _flexCssNode->setStylePosition(WeexCore::kPositionEdgeTop, |
| [self judgePropValuePropValue:styles[@"top"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| if (styles[@"left"]) { |
| _flexCssNode->setStylePosition(WeexCore::kPositionEdgeLeft, |
| [self judgePropValuePropValue:styles[@"left"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| if(styles[@"right"]) { |
| _flexCssNode->setStylePosition(WeexCore::kPositionEdgeRight, |
| [self judgePropValuePropValue:styles[@"right"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| if (styles[@"bottom"]) { |
| _flexCssNode->setStylePosition(WeexCore::kPositionEdgeBottom, |
| [self judgePropValuePropValue:styles[@"bottom"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| |
| // dimension |
| if (styles[@"width"]) { |
| _flexCssNode->setStyleWidth([self judgePropValuePropValue:styles[@"width"] defaultValue:NAN], isUpdate); |
| needLayout = YES; |
| } |
| if (styles[@"height"]) { |
| _flexCssNode->setStyleHeight([self judgePropValuePropValue:styles[@"height"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| if (styles[@"minWidth"]) { |
| _flexCssNode->setMinWidth([self judgePropValuePropValue:styles[@"minWidth"] defaultValue:NAN], isUpdate); |
| needLayout = YES; |
| } |
| if (styles[@"minHeight"]) { |
| _flexCssNode->setMinHeight([self judgePropValuePropValue:styles[@"minHeight"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| if (styles[@"maxWidth"]) { |
| _flexCssNode->setMaxWidth([self judgePropValuePropValue:styles[@"maxWidth"] defaultValue:NAN], isUpdate); |
| needLayout = YES; |
| } |
| if (styles[@"maxHeight"]) { |
| _flexCssNode->setMaxHeight([self judgePropValuePropValue:styles[@"maxHeight"] defaultValue:NAN]); |
| needLayout = YES; |
| } |
| |
| // margin |
| if (styles[@"margin"]) { |
| _flexCssNode->setMargin(WeexCore::kMarginALL, |
| [self judgePropValuePropValue:styles[@"margin"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"marginTop"]) { |
| _flexCssNode->setMargin(WeexCore::kMarginTop, |
| [self judgePropValuePropValue:styles[@"marginTop"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"marginBottom"]) { |
| _flexCssNode->setMargin(WeexCore::kMarginBottom, |
| [self judgePropValuePropValue:styles[@"marginBottom"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"marginRight"]) { |
| _flexCssNode->setMargin(WeexCore::kMarginRight, |
| [self judgePropValuePropValue:styles[@"marginRight"] defaultValue:0]); |
| } |
| if (styles[@"marginLeft"]) { |
| _flexCssNode->setMargin(WeexCore::kMarginLeft, |
| [self judgePropValuePropValue:styles[@"marginLeft"] defaultValue:0]); |
| needLayout = YES; |
| } |
| |
| // border |
| if (styles[@"borderWidth"]) { |
| _flexCssNode->setBorderWidth(WeexCore::kBorderWidthALL, |
| [self judgePropValuePropValue:styles[@"borderWidth"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"borderTopWidth"]) { |
| _flexCssNode->setBorderWidth(WeexCore::kBorderWidthTop, |
| [self judgePropValuePropValue:styles[@"borderTopWidth"] defaultValue:0]); |
| needLayout = YES; |
| } |
| |
| if (styles[@"borderLeftWidth"]) { |
| _flexCssNode->setBorderWidth(WeexCore::kBorderWidthLeft, |
| [self judgePropValuePropValue:styles[@"borderLeftWidth"] defaultValue:0]); |
| needLayout = YES; |
| } |
| |
| if (styles[@"borderBottomWidth"]) { |
| _flexCssNode->setBorderWidth(WeexCore::kBorderWidthBottom, |
| [self judgePropValuePropValue:styles[@"borderBottomWidth"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"borderRightWidth"]) { |
| _flexCssNode->setBorderWidth(WeexCore::kBorderWidthRight, |
| [self judgePropValuePropValue:styles[@"borderRightWidth"] defaultValue:0]); |
| needLayout = YES; |
| } |
| |
| // padding |
| if (styles[@"padding"]) { |
| _flexCssNode->setPadding(WeexCore::kPaddingALL, |
| [self judgePropValuePropValue:styles[@"padding"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"paddingTop"]) { |
| _flexCssNode->setPadding(WeexCore::kPaddingTop, |
| [self judgePropValuePropValue:styles[@"paddingTop"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"paddingLeft"]) { |
| _flexCssNode->setPadding(WeexCore::kPaddingLeft, |
| [self judgePropValuePropValue:styles[@"paddingLeft"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"paddingBottom"]) { |
| _flexCssNode->setPadding(WeexCore::kPaddingBottom, |
| [self judgePropValuePropValue:styles[@"paddingBottom"] defaultValue:0]); |
| needLayout = YES; |
| } |
| if (styles[@"paddingRight"]) { |
| _flexCssNode->setPadding(WeexCore::kPaddingRight, |
| [self judgePropValuePropValue:styles[@"paddingRight"] defaultValue:0]); |
| needLayout = YES; |
| } |
| |
| if (needLayout) { |
| [self setNeedsLayout]; |
| } |
| } |
| |
| - (CGFloat)judgePropValuePropValue:(id)propValue defaultValue:(CGFloat)defaultValue |
| { |
| CGFloat convertValue = (CGFloat)[WXConvert WXFlexPixelType:propValue scaleFactor:self.weexInstance.pixelScaleFactor]; |
| if (!isnan(convertValue)) { |
| return convertValue; |
| } |
| return defaultValue; |
| } |
| |
| - (float)getCssStyleValueForKey:(NSString *)key |
| { |
| /* |
| * width, height, min-width, min-height, max-width, max-height, |
| * margin-(left/right/top/bottom) |
| * padding-(left/right/top/bottom) |
| * border-(left/right/top/bottom)-width |
| * left, right, top, bottom |
| * flex-grow |
| */ |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| if (_flexCssNode == nullptr) { |
| return NAN; |
| } |
| |
| std::string ckey = [key UTF8String]; |
| if (ckey == "width") return _flexCssNode->getStyleWidth(); |
| if (ckey == "height") return _flexCssNode->getStyleHeight(); |
| if (ckey == "min-width") return _flexCssNode->getMinWidth(); |
| if (ckey == "min-height") return _flexCssNode->getMinHeight(); |
| if (ckey == "max-width") return _flexCssNode->getMaxWidth(); |
| if (ckey == "max-height") return _flexCssNode->getMaxHeight(); |
| if (ckey == "margin-left") return _flexCssNode->getMarginLeft(); |
| if (ckey == "margin-right") return _flexCssNode->getMarginRight(); |
| if (ckey == "margin-top") return _flexCssNode->getMarginTop(); |
| if (ckey == "margin-bottom") return _flexCssNode->getMarginBottom(); |
| if (ckey == "padding-left") return _flexCssNode->getPaddingLeft(); |
| if (ckey == "padding-right") return _flexCssNode->getPaddingRight(); |
| if (ckey == "padding-top") return _flexCssNode->getPaddingTop(); |
| if (ckey == "padding-bottom") return _flexCssNode->getPaddingBottom(); |
| if (ckey == "border-left-width") return _flexCssNode->getBorderWidthLeft(); |
| if (ckey == "border-right-width") return _flexCssNode->getBorderWidthRight(); |
| if (ckey == "border-top-width") return _flexCssNode->getBorderWidthTop(); |
| if (ckey == "border-bottom-width") return _flexCssNode->getBorderWidthBottom(); |
| if (ckey == "left") return _flexCssNode->getStylePositionLeft(); |
| if (ckey == "right") return _flexCssNode->getStylePositionRight(); |
| if (ckey == "top") return _flexCssNode->getStylePositionTop(); |
| if (ckey == "bottom") return _flexCssNode->getStylePositionBottom(); |
| if (ckey == "flex-grow") return _flexCssNode->getFlex(); |
| |
| WXAssert(NO, @"Invalid css style key %@", key); |
| return NAN; |
| } |
| |
| - (WXCoreFlexDirection)getCssStyleFlexDirection |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getFlexDirection() : kFlexDirectionColumn; |
| } |
| |
| - (WXCoreFlexWrap)getCssStyleFlexWrap |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getFlexWrap() : kNoWrap; |
| } |
| |
| - (WXCoreJustifyContent)getCssStyleJustifyContent |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getJustifyContent() : kJustifyFlexStart; |
| } |
| |
| - (WXCoreAlignItems)getCssStyleAlignItems |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getAlignItems() : kAlignItemsStretch; |
| } |
| |
| - (WXCoreAlignSelf)getCssStyleAlignSelf |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getAlignSelf() : kAlignSelfAuto; |
| } |
| |
| - (WXCorePositionType)getCssStylePositionType |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getStylePositionType() : kRelative; |
| } |
| |
| - (WXCoreDirection)getCssDirection |
| { |
| WXAssert(_flexCssNode != nullptr, @"Css node is null."); |
| return _flexCssNode ? _flexCssNode->getDirection() : WEEXCORE_CSS_DEFAULT_DIRECTION; |
| } |
| |
| - (NSString*)convertLayoutValueToStyleValue:(NSString*)valueName |
| { |
| if (_flexCssNode == nullptr) { |
| return @"0"; |
| } |
| |
| float layoutValue = 0; |
| if ([valueName isEqualToString:@"left"]) |
| layoutValue = _flexCssNode->getLayoutPositionLeft(); |
| else if ([valueName isEqualToString:@"right"]) |
| layoutValue = _flexCssNode->getLayoutPositionRight(); |
| else if ([valueName isEqualToString:@"top"]) |
| layoutValue = _flexCssNode->getLayoutPositionTop(); |
| else if ([valueName isEqualToString:@"bottom"]) |
| layoutValue = _flexCssNode->getLayoutPositionBottom(); |
| else if ([valueName isEqualToString:@"width"]) |
| layoutValue = _flexCssNode->getLayoutWidth(); |
| else if ([valueName isEqualToString:@"height"]) |
| layoutValue = _flexCssNode->getLayoutHeight(); |
| else |
| return @"0"; |
| |
| layoutValue /= self.weexInstance.pixelScaleFactor; |
| return [NSString stringWithFormat:@"%f", layoutValue]; |
| } |
| |
| - (CGFloat)safeContainerStyleWidth |
| { |
| if (_flexCssNode == nullptr) { |
| return 0.0f; |
| } |
| |
| CGFloat thisValue = _flexCssNode->getStyleWidth(); |
| if (isnan(thisValue)) { |
| if (_flexCssNode->getParent()) { |
| thisValue = _flexCssNode->getParent()->getLayoutWidth(); // parent may be layout done |
| if (isnan(thisValue)) { |
| thisValue = _flexCssNode->getParent()->getStyleWidth(); |
| } |
| } |
| } |
| |
| if (isnan(thisValue)) { |
| thisValue = self.weexInstance.frame.size.width; |
| } |
| |
| if (isnan(thisValue) || thisValue == 0.0f) { |
| thisValue = [UIScreen mainScreen].bounds.size.width; |
| } |
| |
| return thisValue; |
| } |
| |
| #define WX_FLEX_STYLE_RESET_CSS_NODE(key, defaultValue)\ |
| do {\ |
| WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_KEY(key,key,defaultValue)\ |
| } while(0); |
| |
| #define WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_KEY(judgeKey, propKey, defaultValue)\ |
| do {\ |
| if (styles && [styles containsObject:@#judgeKey]) {\ |
| NSMutableDictionary *resetStyleDic = [[NSMutableDictionary alloc] init];\ |
| [resetStyleDic setValue:defaultValue forKey:@#propKey];\ |
| [self _updateCSSNodeStyles:resetStyleDic];\ |
| [self setNeedsLayout];\ |
| }\ |
| } while(0); |
| |
| #define WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(judgeKey, propTopKey,propLeftKey,propRightKey,propBottomKey, defaultValue)\ |
| do {\ |
| if (styles && [styles containsObject:@#judgeKey]) {\ |
| NSMutableDictionary *resetStyleDic = [[NSMutableDictionary alloc] init];\ |
| [resetStyleDic setValue:defaultValue forKey:@#propTopKey];\ |
| [resetStyleDic setValue:defaultValue forKey:@#propLeftKey];\ |
| [resetStyleDic setValue:defaultValue forKey:@#propRightKey];\ |
| [resetStyleDic setValue:defaultValue forKey:@#propBottomKey];\ |
| [self _updateCSSNodeStyles:resetStyleDic];\ |
| [self setNeedsLayout];\ |
| }\ |
| } while(0); |
| |
| |
| - (void)_resetCSSNode:(NSArray *)styles |
| { |
| if (styles.count<=0) { |
| return; |
| } |
| |
| WX_FLEX_STYLE_RESET_CSS_NODE(direction, @(WeexCore::kDirectionInherit)) |
| |
| WX_FLEX_STYLE_RESET_CSS_NODE(flex, @0.0) |
| WX_FLEX_STYLE_RESET_CSS_NODE(flexDirection, @(WeexCore::kFlexDirectionColumn)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(alignItems, @(WeexCore::kAlignItemsStretch)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(alignSelf, @(WeexCore::kAlignSelfAuto)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(flexWrap, @(WeexCore::kNoWrap)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(justifyContent, @(WeexCore::kJustifyFlexStart)) |
| |
| // position |
| WX_FLEX_STYLE_RESET_CSS_NODE(position, @(WeexCore::kRelative)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(top, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(left, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(right, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(bottom, @(NAN)) |
| |
| // dimension |
| WX_FLEX_STYLE_RESET_CSS_NODE(width, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(height, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(minWidth, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(minHeight, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(maxWidth, @(NAN)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(maxHeight, @(NAN)) |
| |
| // margin |
| WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(margin |
| ,marginTop |
| ,marginLeft |
| ,marginRight |
| ,marginBottom |
| , @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(marginTop, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(marginLeft, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(marginRight, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(marginBottom, @(0.0)) |
| |
| // border |
| WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(borderWidth |
| , borderTopWidth |
| , borderLeftWidth |
| , borderRightWidth |
| , borderBottomWidth |
| , @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(borderTopWidth, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(borderLeftWidth, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(borderRightWidth, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(borderBottomWidth, @(0.0)) |
| |
| // padding |
| WX_FLEX_STYLE_RESET_CSS_NODE_GIVEN_DIRECTION_KEY(padding |
| , paddingTop |
| , paddingLeft |
| , paddingRight |
| , paddingBottom |
| , @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(paddingTop, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(paddingLeft, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(paddingRight, @(0.0)) |
| WX_FLEX_STYLE_RESET_CSS_NODE(paddingBottom, @(0.0)) |
| |
| } |
| |
| #pragma mark CSS Node Override |
| |
| static WeexCore::WXCoreSize flexCssNodeMeasure(WeexCore::WXCoreLayoutNode *node, float width, WeexCore::MeasureMode widthMeasureMode,float height, WeexCore::MeasureMode heightMeasureMode){ |
| |
| if (node->getContext() == nullptr) { //为空 |
| return WeexCore::WXCoreSize(); |
| } |
| WXComponent *component = (__bridge WXComponent *)(node->getContext()); |
| |
| if (![component respondsToSelector:@selector(measureBlock)]) { |
| return WeexCore::WXCoreSize(); |
| } |
| |
| CGSize (^measureBlock)(CGSize) = [component measureBlock]; |
| |
| if (!measureBlock) { |
| return WeexCore::WXCoreSize(); |
| } |
| |
| CGSize constrainedSize = CGSizeMake(width, height); |
| CGSize resultSize = measureBlock(constrainedSize); |
| #ifdef DEBUG |
| WXLogDebug(@"flexLayout -> measureblock %@, resultSize:%@", |
| component.type, |
| NSStringFromCGSize(resultSize) |
| ); |
| #endif |
| WeexCore::WXCoreSize size; |
| size.height=(float)resultSize.height; |
| size.width=(float)resultSize.width; |
| return size; |
| } |
| |
| -(WeexCore::WXCorePositionType)fxPositionType:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"absolute"]) { |
| return WeexCore::kAbsolute; |
| } else if ([value isEqualToString:@"relative"]) { |
| return WeexCore::kRelative; |
| } else if ([value isEqualToString:@"fixed"]) { |
| return WeexCore::kFixed; |
| } else if ([value isEqualToString:@"sticky"]) { |
| return WeexCore::kRelative; |
| } |
| } |
| return WeexCore::kRelative; |
| } |
| |
| - (WeexCore::WXCoreDirection)fxDirection:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"rtl"]) { |
| return WeexCore::kDirectionRTL; |
| } else if ([value isEqualToString:@"ltr"]) { |
| return WeexCore::kDirectionLTR; |
| } |
| } |
| return WeexCore::kDirectionInherit; |
| } |
| |
| - (WeexCore::WXCoreFlexDirection)fxFlexDirection:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"column"]) { |
| return WeexCore::kFlexDirectionColumn; |
| } else if ([value isEqualToString:@"column-reverse"]) { |
| return WeexCore::kFlexDirectionColumnReverse; |
| } else if ([value isEqualToString:@"row"]) { |
| return WeexCore::kFlexDirectionRow; |
| } else if ([value isEqualToString:@"row-reverse"]) { |
| return WeexCore::kFlexDirectionRowReverse; |
| } |
| } |
| return WeexCore::kFlexDirectionColumn; |
| } |
| |
| //TODO |
| - (WeexCore::WXCoreAlignItems)fxAlign:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"stretch"]) { |
| return WeexCore::kAlignItemsStretch; |
| } else if ([value isEqualToString:@"flex-start"]) { |
| return WeexCore::kAlignItemsFlexStart; |
| } else if ([value isEqualToString:@"flex-end"]) { |
| return WeexCore::kAlignItemsFlexEnd; |
| } else if ([value isEqualToString:@"center"]) { |
| return WeexCore::kAlignItemsCenter; |
| //return WXCoreFlexLayout::WXCore_AlignItems_Center; |
| } else if ([value isEqualToString:@"auto"]) {//!OCLint |
| // return YGAlignAuto; |
| } else if ([value isEqualToString:@"baseline"]) {//!OCLint |
| // return YGAlignBaseline; |
| } |
| } |
| |
| return WeexCore::kAlignItemsStretch; |
| } |
| |
| - (WeexCore::WXCoreAlignSelf)fxAlignSelf:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"stretch"]) { |
| return WeexCore::kAlignSelfStretch; |
| } else if ([value isEqualToString:@"flex-start"]) { |
| return WeexCore::kAlignSelfFlexStart; |
| } else if ([value isEqualToString:@"flex-end"]) { |
| return WeexCore::kAlignSelfFlexEnd; |
| } else if ([value isEqualToString:@"center"]) { |
| return WeexCore::kAlignSelfCenter; |
| } else if ([value isEqualToString:@"auto"]) { |
| return WeexCore::kAlignSelfAuto; |
| } else if ([value isEqualToString:@"baseline"]) {//!OCLint |
| // return YGAlignBaseline; |
| } |
| } |
| |
| return WeexCore::kAlignSelfStretch; |
| } |
| |
| - (WeexCore::WXCoreFlexWrap)fxWrap:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]) { |
| if ([value isEqualToString:@"nowrap"]) { |
| return WeexCore::kNoWrap; |
| } else if ([value isEqualToString:@"wrap"]) { |
| return WeexCore::kWrap; |
| } else if ([value isEqualToString:@"wrap-reverse"]) { |
| return WeexCore::kWrapReverse; |
| } |
| } |
| return WeexCore::kNoWrap; |
| } |
| |
| - (WeexCore::WXCoreJustifyContent)fxJustify:(id)value |
| { |
| if([value isKindOfClass:[NSString class]]){ |
| if ([value isEqualToString:@"flex-start"]) { |
| return WeexCore::kJustifyFlexStart; |
| } else if ([value isEqualToString:@"center"]) { |
| return WeexCore::kJustifyCenter; |
| } else if ([value isEqualToString:@"flex-end"]) { |
| return WeexCore::kJustifyFlexEnd; |
| } else if ([value isEqualToString:@"space-between"]) { |
| return WeexCore::kJustifySpaceBetween; |
| } else if ([value isEqualToString:@"space-around"]) { |
| return WeexCore::kJustifySpaceAround; |
| } |
| } |
| return WeexCore::kJustifyFlexStart; |
| } |
| |
| - (void)removeSubcomponentCssNode:(WXComponent *)subcomponent |
| { |
| auto node = subcomponent->_flexCssNode; |
| if (node) { |
| if (_flexCssNode) { |
| _flexCssNode->removeChild(node); |
| } |
| |
| [subcomponent _setRenderObject:nullptr]; |
| |
| // unbind subcomponents of subcomponent |
| NSMutableArray* sub_subcomponents = [[NSMutableArray alloc] init]; |
| [subcomponent _collectSubcomponents:sub_subcomponents]; |
| for (WXComponent* c in sub_subcomponents) { |
| [c _setRenderObject:nullptr]; |
| } |
| |
| [WXCoreBridge removeRenderObjectFromMap:subcomponent.weexInstance.instanceId object:node]; |
| delete node; // also will delete all children recursively |
| } |
| } |
| |
| #pragma mark - RTL |
| |
| - (BOOL)isDirectionRTL { |
| if (![WXUtility enableRTLLayoutDirection]) return NO; |
| |
| return _isLayoutDirectionRTL; |
| } |
| |
| - (void)_adjustForRTL { |
| if (![WXUtility enableRTLLayoutDirection]) return; |
| |
| if (self->_positionType == WXPositionTypeFixed) return; |
| |
| if (self.supercomponent && self.supercomponent.isDirectionRTL && [self.supercomponent shouldTransformSubviewsWhenRTL]) { |
| if (_transform) { |
| self.view.layer.transform = CATransform3DConcat(self.view.layer.transform, CATransform3DScale(CATransform3DIdentity, -1, 1, 1)); |
| } else { |
| self.view.layer.transform = CATransform3DScale(CATransform3DIdentity, -1, 1, 1); |
| } |
| } else { |
| if (!_transform) { |
| self.view.layer.transform = CATransform3DIdentity; |
| } |
| } |
| } |
| |
| // Now we scrollView RTL solution is tranform |
| // so scrollView need tranform subviews when RTL by default |
| // if your component view is not scrollView but also implement RTL layout by tranform,you need return YES |
| - (BOOL)shouldTransformSubviewsWhenRTL { |
| return [self.view isKindOfClass:[UIScrollView class]]; |
| } |
| |
| - (void)_setIsLayoutRTL:(BOOL)isRTL { |
| _isLayoutDirectionRTL = isRTL; |
| } |
| |
| @end |