2019-10-21 09:59:22 +08:00
|
|
|
/*
|
|
|
|
* Copyright [2019] [Doric.Pub]
|
|
|
|
*
|
|
|
|
* Licensed 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.
|
|
|
|
*/
|
2019-07-30 15:20:11 +08:00
|
|
|
//
|
|
|
|
// DoricViewNode.m
|
|
|
|
// Doric
|
|
|
|
//
|
|
|
|
// Created by pengfei.zhou on 2019/7/30.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "DoricViewNode.h"
|
|
|
|
#import "DoricUtil.h"
|
|
|
|
#import "DoricGroupNode.h"
|
2019-07-30 21:05:27 +08:00
|
|
|
#import "DoricRootNode.h"
|
|
|
|
#import "DoricConstant.h"
|
2019-07-30 15:20:11 +08:00
|
|
|
|
2019-08-02 20:24:05 +08:00
|
|
|
void DoricAddEllipticArcPath(CGMutablePathRef path,
|
2019-10-12 14:48:19 +08:00
|
|
|
CGPoint origin,
|
|
|
|
CGFloat radius,
|
|
|
|
CGFloat startAngle,
|
|
|
|
CGFloat endAngle) {
|
2019-08-02 20:24:05 +08:00
|
|
|
CGAffineTransform t = CGAffineTransformMakeTranslation(origin.x, origin.y);
|
|
|
|
CGPathAddArc(path, &t, 0, 0, radius, startAngle, endAngle, NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CGPathRef DoricCreateRoundedRectPath(CGRect bounds,
|
2019-10-12 14:48:19 +08:00
|
|
|
CGFloat leftTop,
|
|
|
|
CGFloat rightTop,
|
|
|
|
CGFloat rightBottom,
|
|
|
|
CGFloat leftBottom) {
|
2019-08-02 20:24:05 +08:00
|
|
|
const CGFloat minX = CGRectGetMinX(bounds);
|
|
|
|
const CGFloat minY = CGRectGetMinY(bounds);
|
|
|
|
const CGFloat maxX = CGRectGetMaxX(bounds);
|
|
|
|
const CGFloat maxY = CGRectGetMaxY(bounds);
|
2019-10-12 14:48:19 +08:00
|
|
|
|
2019-08-02 20:24:05 +08:00
|
|
|
CGMutablePathRef path = CGPathCreateMutable();
|
2019-10-12 14:48:19 +08:00
|
|
|
DoricAddEllipticArcPath(path, (CGPoint) {
|
|
|
|
minX + leftTop, minY + leftTop
|
2019-08-02 20:24:05 +08:00
|
|
|
}, leftTop, M_PI, 3 * M_PI_2);
|
2019-10-12 14:48:19 +08:00
|
|
|
DoricAddEllipticArcPath(path, (CGPoint) {
|
|
|
|
maxX - rightTop, minY + rightTop
|
2019-08-02 20:24:05 +08:00
|
|
|
}, rightTop, 3 * M_PI_2, 0);
|
2019-10-12 14:48:19 +08:00
|
|
|
DoricAddEllipticArcPath(path, (CGPoint) {
|
|
|
|
maxX - rightBottom, maxY - rightBottom
|
2019-08-02 20:24:05 +08:00
|
|
|
}, rightBottom, 0, M_PI_2);
|
2019-10-12 14:48:19 +08:00
|
|
|
DoricAddEllipticArcPath(path, (CGPoint) {
|
|
|
|
minX + leftBottom, maxY - leftBottom
|
2019-08-02 20:24:05 +08:00
|
|
|
}, leftBottom, M_PI_2, M_PI);
|
|
|
|
CGPathCloseSubpath(path);
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-12 14:48:19 +08:00
|
|
|
@interface DoricViewNode ()
|
|
|
|
@property(nonatomic, strong) NSMutableDictionary *callbackIds;
|
2019-07-31 19:22:00 +08:00
|
|
|
@end
|
|
|
|
|
2019-07-30 15:20:11 +08:00
|
|
|
@implementation DoricViewNode
|
|
|
|
|
2019-07-31 19:22:00 +08:00
|
|
|
- (instancetype)initWithContext:(DoricContext *)doricContext {
|
2019-10-12 14:48:19 +08:00
|
|
|
if (self = [super initWithContext:doricContext]) {
|
2019-07-31 19:22:00 +08:00
|
|
|
_callbackIds = [[NSMutableDictionary alloc] init];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
2019-10-12 14:48:19 +08:00
|
|
|
|
2019-07-30 15:20:11 +08:00
|
|
|
- (UIView *)build:(NSDictionary *)props {
|
|
|
|
return [[UIView alloc] init];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)blend:(NSDictionary *)props {
|
2019-10-12 14:48:19 +08:00
|
|
|
if (self.view == nil) {
|
2019-07-30 15:20:11 +08:00
|
|
|
self.view = [self build:props];
|
|
|
|
}
|
|
|
|
for (NSString *key in props) {
|
|
|
|
id value = props[key];
|
|
|
|
[self blendView:self.view forPropName:key propValue:value];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop {
|
2019-10-12 14:48:19 +08:00
|
|
|
if ([name isEqualToString:@"width"]) {
|
|
|
|
NSNumber *width = (NSNumber *) prop;
|
2019-07-31 14:18:20 +08:00
|
|
|
if ([width integerValue] < 0) {
|
|
|
|
self.layoutParams.width = [width integerValue];
|
|
|
|
} else {
|
2019-07-31 17:43:26 +08:00
|
|
|
self.layoutParams.width = LAYOUT_ABSOLUTE;
|
2019-07-31 14:18:20 +08:00
|
|
|
view.width = [width floatValue];
|
|
|
|
}
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"height"]) {
|
|
|
|
NSNumber *height = (NSNumber *) prop;
|
2019-07-31 14:18:20 +08:00
|
|
|
if ([height integerValue] < 0) {
|
|
|
|
self.layoutParams.height = [height integerValue];
|
|
|
|
} else {
|
2019-07-31 17:43:26 +08:00
|
|
|
self.layoutParams.height = LAYOUT_ABSOLUTE;
|
2019-07-31 14:18:20 +08:00
|
|
|
view.height = [height floatValue];
|
|
|
|
}
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"x"]) {
|
|
|
|
view.x = [(NSNumber *) prop floatValue];
|
|
|
|
} else if ([name isEqualToString:@"y"]) {
|
|
|
|
view.y = [(NSNumber *) prop floatValue];
|
|
|
|
} else if ([name isEqualToString:@"bgColor"]) {
|
2019-07-30 15:20:11 +08:00
|
|
|
view.backgroundColor = DoricColor(prop);
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"layoutConfig"]) {
|
|
|
|
if (self.parent && [prop isKindOfClass:[NSDictionary class]]) {
|
2019-07-30 15:20:11 +08:00
|
|
|
[self.parent blendChild:self layoutConfig:prop];
|
|
|
|
}
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"onClick"]) {
|
2019-07-31 19:22:00 +08:00
|
|
|
[self.callbackIds setObject:prop forKey:@"onClick"];
|
|
|
|
view.userInteractionEnabled = YES;
|
2019-10-12 14:48:19 +08:00
|
|
|
UITapGestureRecognizer *tapGesturRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
|
2019-07-31 19:22:00 +08:00
|
|
|
[view addGestureRecognizer:tapGesturRecognizer];
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"border"]) {
|
2019-08-02 17:45:47 +08:00
|
|
|
NSDictionary *dic = prop;
|
2019-10-12 14:48:19 +08:00
|
|
|
CGFloat width = [(NSNumber *) [dic objectForKey:@"width"] floatValue];
|
|
|
|
UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]);
|
2019-08-02 17:45:47 +08:00
|
|
|
view.layer.borderWidth = width;
|
|
|
|
view.layer.borderColor = color.CGColor;
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"corners"]) {
|
|
|
|
if ([prop isKindOfClass:NSNumber.class]) {
|
|
|
|
view.layer.cornerRadius = [(NSNumber *) prop floatValue];
|
|
|
|
} else if ([prop isKindOfClass:NSDictionary.class]) {
|
2019-08-02 20:24:05 +08:00
|
|
|
NSDictionary *dic = prop;
|
2019-10-12 14:48:19 +08:00
|
|
|
CGFloat leftTop = [(NSNumber *) [dic objectForKey:@"leftTop"] floatValue];
|
|
|
|
CGFloat rightTop = [(NSNumber *) [dic objectForKey:@"rightTop"] floatValue];
|
|
|
|
CGFloat rightBottom = [(NSNumber *) [dic objectForKey:@"rightBottom"] floatValue];
|
|
|
|
CGFloat leftBottom = [(NSNumber *) [dic objectForKey:@"leftBottom"] floatValue];
|
2019-08-02 20:24:05 +08:00
|
|
|
CALayer *mask = nil;
|
2019-10-12 14:48:19 +08:00
|
|
|
if (ABS(leftTop - rightTop) > CGFLOAT_MIN
|
|
|
|
|| ABS(leftTop - rightBottom) > CGFLOAT_MIN
|
|
|
|
|| ABS(leftTop - leftBottom) > CGFLOAT_MIN) {
|
2019-08-02 20:24:05 +08:00
|
|
|
view.layer.cornerRadius = 0;
|
|
|
|
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
|
|
|
|
CGPathRef path = DoricCreateRoundedRectPath(self.view.bounds, leftTop, rightTop, rightBottom, leftBottom);
|
|
|
|
shapeLayer.path = path;
|
|
|
|
CGPathRelease(path);
|
|
|
|
mask = shapeLayer;
|
|
|
|
} else {
|
|
|
|
view.layer.cornerRadius = leftTop;
|
|
|
|
}
|
|
|
|
view.layer.mask = mask;
|
2019-08-02 17:45:47 +08:00
|
|
|
}
|
2019-10-12 14:48:19 +08:00
|
|
|
} else if ([name isEqualToString:@"shadow"]) {
|
2019-08-02 17:45:47 +08:00
|
|
|
NSDictionary *dic = prop;
|
2019-10-12 14:48:19 +08:00
|
|
|
CGFloat opacity = [(NSNumber *) [dic objectForKey:@"opacity"] floatValue];
|
2019-08-02 17:45:47 +08:00
|
|
|
if (opacity > CGFLOAT_MIN) {
|
|
|
|
view.clipsToBounds = NO;
|
2019-10-12 14:48:19 +08:00
|
|
|
UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]);
|
2019-08-02 17:45:47 +08:00
|
|
|
view.layer.shadowColor = color.CGColor;
|
2019-10-12 14:48:19 +08:00
|
|
|
view.layer.shadowRadius = [(NSNumber *) [dic objectForKey:@"radius"] floatValue];
|
|
|
|
view.layer.shadowOffset = CGSizeMake([(NSNumber *) [dic objectForKey:@"offsetX"] floatValue], [(NSNumber *) [dic objectForKey:@"offsetY"] floatValue]);
|
2019-08-02 17:45:47 +08:00
|
|
|
view.layer.shadowOpacity = opacity;
|
|
|
|
} else {
|
|
|
|
view.clipsToBounds = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2019-07-30 21:05:27 +08:00
|
|
|
DoricLog(@"Blend View error for View Type :%@, prop is %@", self.class, name);
|
2019-07-30 15:20:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-31 19:22:00 +08:00
|
|
|
- (void)onClick:(UIView *)view {
|
2019-10-12 14:48:19 +08:00
|
|
|
[self callJSResponse:[self.callbackIds objectForKey:@"onClick"], nil];
|
2019-07-31 19:22:00 +08:00
|
|
|
}
|
|
|
|
|
2019-07-31 17:43:26 +08:00
|
|
|
- (CGFloat)measuredWidth {
|
2019-10-12 14:48:19 +08:00
|
|
|
if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) {
|
|
|
|
MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams;
|
2019-07-31 17:43:26 +08:00
|
|
|
return self.width + marginParams.margin.left + marginParams.margin.right;
|
|
|
|
}
|
|
|
|
return self.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)measuredHeight {
|
2019-10-12 14:48:19 +08:00
|
|
|
if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) {
|
|
|
|
MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams;
|
2019-07-31 17:43:26 +08:00
|
|
|
return self.height + marginParams.margin.top + marginParams.margin.bottom;
|
|
|
|
}
|
|
|
|
return self.height;
|
|
|
|
}
|
|
|
|
|
2019-07-31 14:18:20 +08:00
|
|
|
- (void)measureByParent:(DoricGroupNode *)parent {
|
2019-08-06 20:06:34 +08:00
|
|
|
DoricLayoutDesc widthSpec = self.layoutParams.width;
|
|
|
|
DoricLayoutDesc heightSpec = self.layoutParams.height;
|
|
|
|
if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) {
|
|
|
|
[self.view sizeToFit];
|
|
|
|
}
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)layoutByParent:(DoricGroupNode *)parent {
|
2019-10-12 14:48:19 +08:00
|
|
|
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
2019-07-30 21:05:27 +08:00
|
|
|
- (NSArray<NSString *> *)idList {
|
|
|
|
NSMutableArray *ret = [[NSMutableArray alloc] init];
|
|
|
|
DoricViewNode *node = self;
|
|
|
|
do {
|
|
|
|
[ret addObject:node.viewId];
|
|
|
|
node = node.parent;
|
|
|
|
} while (node && ![node isKindOfClass:[DoricRootNode class]]);
|
2019-10-12 14:48:19 +08:00
|
|
|
|
2019-07-31 19:22:00 +08:00
|
|
|
return [[ret reverseObjectEnumerator] allObjects];
|
2019-07-30 21:05:27 +08:00
|
|
|
}
|
|
|
|
|
2019-10-12 14:48:19 +08:00
|
|
|
- (void)callJSResponse:(NSString *)funcId, ... {
|
2019-07-30 21:05:27 +08:00
|
|
|
NSMutableArray *array = [[NSMutableArray alloc] init];
|
|
|
|
[array addObject:self.idList];
|
|
|
|
[array addObject:funcId];
|
|
|
|
va_list args;
|
|
|
|
va_start(args, funcId);
|
|
|
|
id arg;
|
|
|
|
while ((arg = va_arg(args, id)) != nil) {
|
|
|
|
[array addObject:arg];
|
|
|
|
}
|
|
|
|
[self.doricContext callEntity:DORIC_ENTITY_RESPONSE withArgumentsArray:array];
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type {
|
|
|
|
DoricRegistry *registry = context.driver.registry;
|
2019-10-12 14:48:19 +08:00
|
|
|
Class clz = [registry acquireViewNode:type];
|
2019-07-30 21:05:27 +08:00
|
|
|
return [[clz alloc] initWithContext:context];
|
|
|
|
}
|
|
|
|
|
2019-07-31 14:18:20 +08:00
|
|
|
- (CGFloat)x {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).x;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)y {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).y;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)width {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).width;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)height {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).height;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)top {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).top;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)bottom {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).bottom;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)left {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).left;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)right {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).right;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)centerX {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).centerX;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (CGFloat)centerY {
|
2019-10-12 14:48:19 +08:00
|
|
|
return ((UIView *) self.view).centerY;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setX:(CGFloat)x {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).x = x;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setY:(CGFloat)y {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).y = y;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setWidth:(CGFloat)width {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).width = width;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setHeight:(CGFloat)height {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).height = height;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setLeft:(CGFloat)left {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).left = left;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setRight:(CGFloat)right {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).right = right;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setTop:(CGFloat)top {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).top = top;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setBottom:(CGFloat)bottom {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).bottom = bottom;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setCenterX:(CGFloat)centerX {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).centerX = centerX;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setCenterY:(CGFloat)centerY {
|
2019-10-12 14:48:19 +08:00
|
|
|
((UIView *) self.view).centerY = centerY;
|
2019-07-31 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
2019-08-06 20:06:34 +08:00
|
|
|
- (void)requestLayout {
|
|
|
|
[self.parent requestLayout];
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:20:11 +08:00
|
|
|
@end
|