diff --git a/Android/doric/build.gradle b/Android/doric/build.gradle index 06e6b084..43aa1284 100644 --- a/Android/doric/build.gradle +++ b/Android/doric/build.gradle @@ -26,7 +26,7 @@ android { afterEvaluate { buildJSBundle.exec() buildDemo.exec() - buildDebugger.exec() + //buildDebugger.exec() } task buildJSBundle(type: Exec) { diff --git a/Android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java b/Android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java index 20acbb84..c7cc65e0 100644 --- a/Android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java +++ b/Android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java @@ -67,8 +67,8 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time private void initJSExecutor() { -// mDoricJSE = new DoricNativeJSExecutor(); - mDoricJSE = new DoricRemoteJSExecutor(); + mDoricJSE = new DoricNativeJSExecutor(); +// mDoricJSE = new DoricRemoteJSExecutor(); mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_LOG, new JavaFunction() { @Override public JavaValue exec(JSDecoder[] args) { diff --git a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java index bfe28c75..81cd53b5 100644 --- a/Android/doric/src/main/java/pub/doric/shader/GroupNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/GroupNode.java @@ -116,12 +116,42 @@ public abstract class GroupNode extends ViewNode { } protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + return new ViewGroup.LayoutParams(0, 0); } protected void blendChild(ViewNode viewNode, JSObject jsObject) { + + JSValue jsValue = jsObject.getProperty("margin"); + JSValue widthSpec = jsObject.getProperty("widthSpec"); + JSValue heightSpec = jsObject.getProperty("widthSpec"); + ViewGroup.LayoutParams layoutParams = viewNode.getLayoutParams(); + if (widthSpec.isNumber()) { + switch (widthSpec.asNumber().toInt()) { + case 1: + layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; + break; + case 2: + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + break; + default: + break; + + } + } + if (heightSpec.isNumber()) { + switch (heightSpec.asNumber().toInt()) { + case 1: + layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; + break; + case 2: + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + break; + default: + break; + } + } if (jsValue.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) { JSValue topVal = jsValue.asObject().getProperty("top"); if (topVal.isNumber()) { diff --git a/Android/doric/src/main/java/pub/doric/shader/LinearNode.java b/Android/doric/src/main/java/pub/doric/shader/LinearNode.java index 2a54cd00..80e7bb24 100644 --- a/Android/doric/src/main/java/pub/doric/shader/LinearNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/LinearNode.java @@ -50,7 +50,7 @@ public class LinearNode extends GroupNode { @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + return new LinearLayout.LayoutParams(0, 0); } @Override diff --git a/Android/doric/src/main/java/pub/doric/shader/StackNode.java b/Android/doric/src/main/java/pub/doric/shader/StackNode.java index ff5417d6..4c781889 100644 --- a/Android/doric/src/main/java/pub/doric/shader/StackNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/StackNode.java @@ -62,6 +62,6 @@ public class StackNode extends GroupNode { @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + return new FrameLayout.LayoutParams(0, 0); } } diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index e03d3ad6..6f3bda4d 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -86,16 +86,12 @@ public abstract class ViewNode extends DoricContextHolder { protected void blend(T view, ViewGroup.LayoutParams layoutParams, String name, JSValue prop) { switch (name) { case "width": - if (prop.asNumber().toInt() < 0) { - layoutParams.width = prop.asNumber().toInt(); - } else { + if (layoutParams.width >= 0) { layoutParams.width = DoricUtils.dp2px(prop.asNumber().toFloat()); } break; case "height": - if (prop.asNumber().toInt() < 0) { - layoutParams.height = prop.asNumber().toInt(); - } else { + if (layoutParams.height >= 0) { layoutParams.height = DoricUtils.dp2px(prop.asNumber().toFloat()); } break; diff --git a/demo/rollup.config.js b/demo/rollup.config.js index 3e8b4c13..e267034d 100644 --- a/demo/rollup.config.js +++ b/demo/rollup.config.js @@ -8,6 +8,7 @@ export default bundles.map(bundle => { output: { format: "cjs", file: `bundle/${bundle}.js`, + sourcemap: true, }, plugins: [ resolve({ jsnext: true, main: true }), diff --git a/demo/src/Counter.ts b/demo/src/Counter.ts index 8107b065..5da84dc6 100644 --- a/demo/src/Counter.ts +++ b/demo/src/Counter.ts @@ -1,4 +1,4 @@ -import { Image, ViewHolder, VMPanel, ViewModel, Gravity, NativeCall, Text, Color, VLayout, log, logw, loge, Group, } from "doric" +import { Image, ViewHolder, VMPanel, ViewModel, Gravity, NativeCall, Text, Color, VLayout, log, logw, loge, Group, LayoutSpec, } from "doric" interface CountModel { @@ -57,6 +57,10 @@ class CounterView extends ViewHolder { // iv.width = iv.height = 100 iv.imageUrl = "https://misc.aotu.io/ONE-SUNDAY/SteamEngine.png" //iv.bgColor = Color.parse('#00ff00') + iv.layoutConfig = { + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, + } root.addChild(iv) } diff --git a/demo/src/Snake.ts b/demo/src/Snake.ts index 68e6bde8..617d0306 100644 --- a/demo/src/Snake.ts +++ b/demo/src/Snake.ts @@ -1,4 +1,4 @@ -import { loge, log, ViewHolder, Stack, ViewModel, Gravity, Text, Color, HLayout, VLayout, Group, VMPanel } from "doric"; +import { loge, log, ViewHolder, Stack, ViewModel, Gravity, Text, Color, HLayout, VLayout, Group, VMPanel, LayoutSpec } from "doric"; type SnakeNode = { x: number @@ -154,10 +154,14 @@ class SnakeView extends ViewHolder { margin: { top: 20 }, + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, } vlayout.space = 20 vlayout.layoutConfig = { - alignment: new Gravity().centerX().top() + alignment: new Gravity().centerX().top(), + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, } this.panel.bgColor = Color.parse('#00ff00') vlayout.addChild(title) @@ -165,11 +169,20 @@ class SnakeView extends ViewHolder { root.addChild(vlayout) const hlayout = new HLayout - this.start.text = "Start" - this.start.textSize = 30 - this.start.textColor = Color.parse("#ffffff") - - hlayout.addChild(this.start) + hlayout.layoutConfig = { + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, + } + hlayout.addChild(this.start.also( + it => { + it.text = "Start" + it.textSize = 30 + it.textColor = Color.parse("#ffffff") + it.layoutConfig = { + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, + } + })) vlayout.addChild(hlayout) @@ -182,7 +195,9 @@ class SnakeView extends ViewHolder { controlArea.gravity = new Gravity().centerX() controlArea.space = 10 controlArea.layoutConfig = { - alignment: new Gravity().centerX() + alignment: new Gravity().centerX(), + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, } const line1 = new HLayout const line2 = new HLayout @@ -191,6 +206,14 @@ class SnakeView extends ViewHolder { line2.addChild(this.left) line2.addChild(this.down) line2.addChild(this.right) + line1.layoutConfig = { + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, + } + line2.layoutConfig = { + widthSpec: LayoutSpec.WRAP_CONTENT, + heightSpec: LayoutSpec.WRAP_CONTENT, + } controlArea.addChild(line1) controlArea.addChild(line2) vlayout.addChild(controlArea) diff --git a/demo/tsconfig.json b/demo/tsconfig.json index d511f6a0..4d880db4 100644 --- a/demo/tsconfig.json +++ b/demo/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "experimentalDecorators": true, /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ @@ -11,7 +10,7 @@ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "build/", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ @@ -53,7 +52,7 @@ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ }, "include": [ diff --git a/doric-cli/contents/_rollup.config.js b/doric-cli/contents/_rollup.config.js index 3e8b4c13..e267034d 100644 --- a/doric-cli/contents/_rollup.config.js +++ b/doric-cli/contents/_rollup.config.js @@ -8,6 +8,7 @@ export default bundles.map(bundle => { output: { format: "cjs", file: `bundle/${bundle}.js`, + sourcemap: true, }, plugins: [ resolve({ jsnext: true, main: true }), diff --git a/iOS/Example/Example/ViewController.m b/iOS/Example/Example/ViewController.m index dabf77b3..e638a42a 100644 --- a/iOS/Example/Example/ViewController.m +++ b/iOS/Example/Example/ViewController.m @@ -13,21 +13,26 @@ #import "DoricNativePlugin.h" #import "DoricRootNode.h" #import "DoricLocalServer.h" +#import "DoricLayouts.h" +#import "DoricExtensions.h" @interface ViewController () -@property (nonatomic,strong) DoricContext *doricContext; -@property (nonatomic,strong) DoricLocalServer *localServer; +@property(nonatomic, strong) DoricContext *doricContext; +@property(nonatomic, strong) DoricLocalServer *localServer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; - + NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"Snake" ofType:@"js"]; NSString *jsContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; self.doricContext = [[DoricContext alloc] initWithScript:jsContent source:@"test.js"]; - self.doricContext.rootNode.view = self.view; + [self.doricContext.rootNode setupRootView:[[DoricStackView new] also:^(DoricStackView *it) { + it.layoutConfig = [[DoricStackConfig alloc] initWithWidth:DoricLayoutAtMost height:DoricLayoutAtMost]; + [self.view addSubview:it]; + }]]; [self.doricContext initContextWithWidth:self.view.width height:self.view.height]; [self.doricContext.driver connectDevKit:@"ws://192.168.11.38:7777"]; self.localServer = [[DoricLocalServer alloc] init]; diff --git a/iOS/Example/Podfile.lock b/iOS/Example/Podfile.lock index 9c0eaa93..a41b13c0 100644 --- a/iOS/Example/Podfile.lock +++ b/iOS/Example/Podfile.lock @@ -25,7 +25,7 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - Doric: 1dae6e8a19a32a27af975f787509cc0658dde095 + Doric: f96b77d435e836e88cf02319e3c9ebc08cab65f6 GCDWebServer: ead88cd14596dd4eae4f5830b8877c87c8728990 SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965 SocketRocket: d57c7159b83c3c6655745cd15302aa24b6bae531 diff --git a/iOS/Pod/Classes/DoricContext.h b/iOS/Pod/Classes/DoricContext.h index 0829fff0..6607ba4b 100644 --- a/iOS/Pod/Classes/DoricContext.h +++ b/iOS/Pod/Classes/DoricContext.h @@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) DoricRootNode *rootNode; @property(nonatomic, strong) NSString *source; @property(nonatomic, strong) NSString *script;; -@property(nonatomic, strong) NSDictionary *initialParams; +@property(nonatomic, strong) NSMutableDictionary *initialParams; - (instancetype)initWithScript:(NSString *)script source:(NSString *)source; diff --git a/iOS/Pod/Classes/DoricContext.m b/iOS/Pod/Classes/DoricContext.m index 222b131d..fa07d1df 100644 --- a/iOS/Pod/Classes/DoricContext.m +++ b/iOS/Pod/Classes/DoricContext.m @@ -24,6 +24,7 @@ #import "DoricContextManager.h" #import "DoricRootNode.h" #import "DoricConstant.h" +#import "DoricExtensions.h" @implementation DoricContext @@ -35,7 +36,7 @@ - (instancetype)initWithScript:(NSString *)script source:(NSString *)source { _rootNode = [[DoricRootNode alloc] initWithContext:self]; _script = script; _source = source; - _initialParams = [@{@"width": @(LAYOUT_MATCH_PARENT), @"height": @(LAYOUT_MATCH_PARENT)} mutableCopy]; + _initialParams = [@{@"width": @(0), @"height": @(0)} mutableCopy]; [self callEntity:DORIC_ENTITY_CREATE, nil]; } return self; @@ -63,8 +64,10 @@ - (DoricAsyncResult *)callEntity:(NSString *)method withArgumentsArray:(NSArray } - (void)initContextWithWidth:(CGFloat)width height:(CGFloat)height { - [self.initialParams setValue:@(width) forKey:@"width"]; - [self.initialParams setValue:@(height) forKey:@"height"]; + [self.initialParams also:^(NSMutableDictionary *it) { + it[@"width"] = @(width); + it[@"height"] = @(height); + }]; [self callEntity:DORIC_ENTITY_INIT, self.initialParams, nil]; } diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.h b/iOS/Pod/Classes/Shader/DoricGroupNode.h index cd15a6bc..413de989 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.h +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.h @@ -24,15 +24,13 @@ NS_ASSUME_NONNULL_BEGIN -@interface DoricGroupNode : DoricViewNode +@interface DoricGroupNode : DoricViewNode @property(nonatomic, strong) NSMutableDictionary *children; @property(nonatomic, strong) NSMutableArray *indexedChildren; -@property(nonatomic) CGFloat desiredWidth; -@property(nonatomic) CGFloat desiredHeight; -- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig; +- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig; - (P)generateDefaultLayoutParams; @end diff --git a/iOS/Pod/Classes/Shader/DoricGroupNode.m b/iOS/Pod/Classes/Shader/DoricGroupNode.m index 0aefa8dc..7cf1d71e 100644 --- a/iOS/Pod/Classes/Shader/DoricGroupNode.m +++ b/iOS/Pod/Classes/Shader/DoricGroupNode.m @@ -20,6 +20,7 @@ // Created by pengfei.zhou on 2019/7/30. // +#import #import "DoricGroupNode.h" @implementation DoricGroupNode @@ -72,10 +73,10 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop [tobeRemoved addObject:old]; } - LayoutParams *params = node.layoutParams; + DoricLayoutConfig *params = node.layoutConfig; if (params == nil) { params = [self generateDefaultLayoutParams]; - node.layoutParams = params; + node.layoutConfig = params; } [node blend:val[@"props"]]; if (self.indexedChildren.count <= i) { @@ -108,21 +109,35 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop } } -- (LayoutParams *)generateDefaultLayoutParams { - LayoutParams *params = [[LayoutParams alloc] init]; +- (DoricLayoutConfig *)generateDefaultLayoutParams { + DoricLayoutConfig *params = [[DoricLayoutConfig alloc] init]; return params; } -- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig { - LayoutParams *params = child.layoutParams; - if ([params isKindOfClass:MarginLayoutParams.class]) { - MarginLayoutParams *marginParams = (MarginLayoutParams *) params; - NSDictionary *margin = layoutconfig[@"margin"]; +- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig { + DoricLayoutConfig *params = child.layoutConfig; + + [layoutConfig[@"widthSpec"] also:^(NSNumber *it) { + if (it) { + params.widthSpec = (DoricLayoutSpec) [it integerValue]; + } + }]; + + [layoutConfig[@"heightSpec"] also:^(NSNumber *it) { + if (it) { + params.heightSpec = (DoricLayoutSpec) [it integerValue]; + } + }]; + + if ([params isKindOfClass:DoricMarginConfig.class]) { + DoricMarginConfig *marginParams = (DoricMarginConfig *) params; + NSDictionary *margin = layoutConfig[@"margin"]; if (margin) { - marginParams.margin.top = [(NSNumber *) margin[@"top"] floatValue]; - marginParams.margin.left = [(NSNumber *) margin[@"left"] floatValue]; - marginParams.margin.right = [(NSNumber *) margin[@"right"] floatValue]; - marginParams.margin.bottom = [(NSNumber *) margin[@"bottom"] floatValue]; + marginParams.margin = DoricMarginMake( + [(NSNumber *) margin[@"left"] floatValue], + [(NSNumber *) margin[@"top"] floatValue], + [(NSNumber *) margin[@"right"] floatValue], + [(NSNumber *) margin[@"bottom"] floatValue]); } } diff --git a/iOS/Pod/Classes/Shader/DoricHLayoutNode.h b/iOS/Pod/Classes/Shader/DoricHLayoutNode.h index 458546f7..4f6d6086 100644 --- a/iOS/Pod/Classes/Shader/DoricHLayoutNode.h +++ b/iOS/Pod/Classes/Shader/DoricHLayoutNode.h @@ -22,11 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricHLayoutNode : DoricGroupNode -@property(nonatomic) CGFloat space; -@property(nonatomic) DoricGravity gravity; +@interface DoricHLayoutNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricHLayoutNode.m b/iOS/Pod/Classes/Shader/DoricHLayoutNode.m index 26fb4d06..5292ae4d 100644 --- a/iOS/Pod/Classes/Shader/DoricHLayoutNode.m +++ b/iOS/Pod/Classes/Shader/DoricHLayoutNode.m @@ -24,114 +24,42 @@ #import "DoricUtil.h" @implementation DoricHLayoutNode -- (instancetype)init { - if (self = [super init]) { - _space = 0; - _gravity = 0; - } - return self; +- (DoricHLayoutView *)build:(NSDictionary *)props { + return [DoricHLayoutView new]; } -- (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop { +- (void)blendView:(DoricHLayoutView *)view forPropName:(NSString *)name propValue:(id)prop { if ([name isEqualToString:@"gravity"]) { - self.gravity = [(NSNumber *)prop integerValue]; + view.gravity = (DoricGravity) [(NSNumber *) prop integerValue]; } else if ([name isEqualToString:@"space"]) { - self.space = [(NSNumber *)prop floatValue]; + view.space = [(NSNumber *) prop floatValue]; } else { [super blendView:view forPropName:name propValue:prop]; } } -- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig { - [super blendChild:child layoutConfig:layoutconfig]; - if (![child.layoutParams isKindOfClass:VHLayoutParams.class]) { - DoricLog(@"blend HLayout child error,layout params not match"); +- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig { + [super blendChild:child layoutConfig:layoutConfig]; + if (![child.layoutConfig isKindOfClass:DoricLinearConfig.class]) { + DoricLog(@"blend DoricHLayoutView child error,layout params not match"); return; } - VHLayoutParams *params = (VHLayoutParams *)child.layoutParams; - NSDictionary *margin = [layoutconfig objectForKey:@"margin"]; + DoricLinearConfig *params = (DoricLinearConfig *) child.layoutConfig; + NSDictionary *margin = layoutConfig[@"margin"]; if (margin) { - params.margin.top = [(NSNumber *)[margin objectForKey:@"top"] floatValue]; - params.margin.left = [(NSNumber *)[margin objectForKey:@"left"] floatValue]; - params.margin.right = [(NSNumber *)[margin objectForKey:@"right"] floatValue]; - params.margin.bottom = [(NSNumber *)[margin objectForKey:@"bottom"] floatValue]; + params.margin = DoricMarginMake( + [(NSNumber *) margin[@"left"] floatValue], + [(NSNumber *) margin[@"top"] floatValue], + [(NSNumber *) margin[@"right"] floatValue], + [(NSNumber *) margin[@"bottom"] floatValue]); } - NSNumber *alignment = [layoutconfig objectForKey:@"alignment"]; + NSNumber *alignment = layoutConfig[@"alignment"]; if (alignment) { - params.alignment = [alignment integerValue]; + params.alignment = (DoricGravity) [alignment integerValue]; } } -- (VHLayoutParams *)generateDefaultLayoutParams { - return [[VHLayoutParams alloc] init]; +- (DoricLinearConfig *)generateDefaultLayoutParams { + return [[DoricLinearConfig alloc] init]; } - -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - CGFloat maxWidth = 0,maxHeight = 0; - for (DoricViewNode *child in self.indexedChildren) { - [child measureByParent:self]; - CGFloat placeWidth = child.measuredWidth; - CGFloat placeHeight = child.measuredHeight; - maxWidth += placeWidth + self.space; - maxHeight = MAX(maxHeight, placeHeight); - } - - maxWidth -= self.space; - - self.desiredWidth = maxWidth; - self.desiredHeight = maxHeight; - - if (widthSpec == LAYOUT_WRAP_CONTENT) { - self.width = maxWidth; - } - - if (heightSpec == LAYOUT_WRAP_CONTENT) { - self.height = maxHeight; - } -} - -- (void)layoutByParent:(DoricGroupNode *)parent { - if (self.layoutParams.width == LAYOUT_MATCH_PARENT) { - self.width = parent.width; - } - if (self.layoutParams.height == LAYOUT_MATCH_PARENT) { - self.height = parent.height; - } - // layout child - CGFloat xStart = 0; - if ((self.gravity & LEFT) == LEFT) { - xStart = 0; - } else if ((self.gravity & RIGHT) == RIGHT) { - xStart = self.width - self.desiredWidth; - } else if ((self.gravity & CENTER_X) == CENTER_X) { - xStart = (self.width -self.desiredWidth)/2; - } - - for (DoricViewNode *child in self.indexedChildren) { - if (child.layoutParams.width == LAYOUT_MATCH_PARENT) { - child.width = self.width; - } - if (child.layoutParams.height == LAYOUT_MATCH_PARENT) { - child.height = self.height; - } - - if ([child.layoutParams isKindOfClass:VHLayoutParams.class]) { - VHLayoutParams *layoutParams = (VHLayoutParams *)child.layoutParams; - DoricGravity gravity = layoutParams.alignment | self.gravity; - if ((gravity & TOP) == TOP) { - child.top = 0; - } else if ((gravity & BOTTOM) == BOTTOM) { - child.bottom = self.height; - } else if ((gravity & CENTER_Y) == CENTER_Y) { - child.centerY = self.height/2; - } - } - child.left = xStart; - xStart = child.right + self.space; - [child layoutByParent:self]; - } -} - @end diff --git a/iOS/Pod/Classes/Shader/DoricImageNode.m b/iOS/Pod/Classes/Shader/DoricImageNode.m index 28c28195..470d8477 100644 --- a/iOS/Pod/Classes/Shader/DoricImageNode.m +++ b/iOS/Pod/Classes/Shader/DoricImageNode.m @@ -40,12 +40,4 @@ - (void)blendView:(UIImageView *)view forPropName:(NSString *)name propValue:(id [super blendView:view forPropName:name propValue:prop]; } } - -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) { - [self.view sizeToFit]; - } -} @end diff --git a/iOS/Pod/Classes/Shader/DoricLayouts.h b/iOS/Pod/Classes/Shader/DoricLayouts.h new file mode 100644 index 00000000..8b825e6f --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricLayouts.h @@ -0,0 +1,109 @@ +/* + * 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. + */ +// +// Created by pengfei.zhou on 2019/10/23. +// + +#import + + +struct DoricMargin { + CGFloat left; + CGFloat right; + CGFloat top; + CGFloat bottom; +}; +typedef struct DoricMargin DoricMargin; + +DoricMargin DoricMarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom); + +typedef NS_ENUM(NSInteger, DoricLayoutSpec) { + DoricLayoutExact, + DoricLayoutWrapContent, + DoricLayoutAtMost, +}; + +typedef NS_ENUM(NSInteger, DoricGravity) { + SPECIFIED = 1, + START = 1 << 1, + END = 1 << 2, + SHIFT_X = 0, + SHIFT_Y = 4, + LEFT = (START | SPECIFIED) << SHIFT_X, + RIGHT = (END | SPECIFIED) << SHIFT_X, + TOP = (START | SPECIFIED) << SHIFT_Y, + BOTTOM = (END | SPECIFIED) << SHIFT_Y, + CENTER_X = SPECIFIED << SHIFT_X, + CENTER_Y = SPECIFIED << SHIFT_Y, + CENTER = CENTER_X | CENTER_Y, +}; + +@interface DoricLayoutConfig : NSObject +@property(nonatomic, assign) DoricLayoutSpec widthSpec; +@property(nonatomic, assign) DoricLayoutSpec heightSpec; +@property(nonatomic, assign) DoricGravity alignment; + +- (instancetype)init; + +- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height; + +@end + +@interface DoricStackConfig : DoricLayoutConfig +@end + +@interface DoricMarginConfig : DoricLayoutConfig +@property(nonatomic) DoricMargin margin; + +- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin; +@end + +@interface DoricLinearConfig : DoricMarginConfig +@property(nonatomic, assign) NSUInteger weight; +@end + + +@interface DoricLayoutContainer : UIView + +- (T)configForChild:(__kindof UIView *)child; + +- (void)layout; + +- (void)requestLayout; +@end + +@interface DoricStackView : DoricLayoutContainer +@property(nonatomic, assign) DoricGravity gravity; +@end + +@interface DoricLinearView : DoricLayoutContainer +@property(nonatomic, assign) DoricGravity gravity; +@property(nonatomic, assign) CGFloat space; +@end + + +@interface DoricVLayoutView : DoricLinearView +@end + +@interface DoricHLayoutView : DoricLinearView +@end + +@interface UIView (DoricLayoutConfig) +@property(nonatomic, strong) DoricLayoutConfig *layoutConfig; +@property(nonatomic, copy) NSString *tagString; + +- (UIView *)viewWithTagString:(NSString *)tagString; +@end diff --git a/iOS/Pod/Classes/Shader/DoricLayouts.m b/iOS/Pod/Classes/Shader/DoricLayouts.m new file mode 100644 index 00000000..2a6777d6 --- /dev/null +++ b/iOS/Pod/Classes/Shader/DoricLayouts.m @@ -0,0 +1,505 @@ +/* + * 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. + */ +// +// Created by pengfei.zhou on 2019/10/23. +// + +#import "DoricLayouts.h" +#import +#import "UIView+Doric.h" + +DoricMargin DoricMarginMake(CGFloat left, CGFloat top, CGFloat right, CGFloat bottom) { + DoricMargin margin; + margin.left = left; + margin.top = top; + margin.right = right; + margin.bottom = bottom; + return margin; +} + +@implementation DoricLayoutConfig +- (instancetype)init { + if (self = [super init]) { + _widthSpec = DoricLayoutExact; + _heightSpec = DoricLayoutExact; + } + return self; +} + +- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height { + if (self = [super init]) { + _widthSpec = width; + _heightSpec = height; + } + return self; +} +@end + +@implementation DoricMarginConfig +- (instancetype)init { + if (self = [super init]) { + _margin = DoricMarginMake(0, 0, 0, 0); + } + return self; +} + +- (instancetype)initWithWidth:(DoricLayoutSpec)width height:(DoricLayoutSpec)height margin:(DoricMargin)margin { + if (self = [super initWithWidth:width height:height]) { + _margin = margin; + } + return self; +} +@end + +@implementation DoricStackConfig +@end + +@implementation DoricLinearConfig +@end + + +@interface DoricLayoutContainer () +@property(nonatomic, assign) BOOL waitingLayout; +@end + +@implementation DoricLayoutContainer +- (instancetype)init { + if (self = [super init]) { + _waitingLayout = NO; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + _waitingLayout = NO; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + if (self = [super initWithCoder:coder]) { + _waitingLayout = NO; + } + return self; +} + + +- (DoricLayoutConfig *)configForChild:(UIView *)child { + DoricLayoutConfig *config = child.layoutConfig; + if (!config) { + config = [[DoricLayoutConfig alloc] init]; + } + return config; +} + +- (void)requestLayout { + if ([self.superview isKindOfClass:[DoricLinearView class]]) { + [(DoricLinearView *) self.superview requestLayout]; + return; + } + if (self.waitingLayout) { + return; + } + self.waitingLayout = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + self.waitingLayout = NO; + [self sizeToFit]; + [self layout]; + }); +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self requestLayout]; +} + +- (void)layout { + [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView *child, NSUInteger idx, BOOL *stop) { + if ([child isKindOfClass:[DoricLayoutContainer class]]) { + [(DoricLayoutContainer *) child layout]; + } + }]; +} + +@end + + +@interface DoricStackView () + +@property(nonatomic, assign) CGFloat contentWidth; +@property(nonatomic, assign) CGFloat contentHeight; +@end + +@implementation DoricStackView +- (DoricStackConfig *)configForChild:(UIView *)child { + DoricStackConfig *config = (DoricStackConfig *) child.layoutConfig; + if (!config) { + config = [[DoricStackConfig alloc] init]; + } + return config; +} + + +- (void)sizeToFit { + DoricLayoutConfig *config = self.layoutConfig; + self.contentWidth = 0; + self.contentHeight = 0; + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricStackConfig *childConfig = [self configForChild:child]; + if ([child isKindOfClass:[DoricLayoutContainer class]] + || childConfig.widthSpec == DoricLayoutWrapContent + || childConfig.heightSpec == DoricLayoutWrapContent) { + [child sizeToFit]; + } + self.contentWidth = MAX(self.contentWidth, child.width); + self.contentHeight = MAX(self.contentHeight, child.height); + } + if (config.widthSpec == DoricLayoutWrapContent) { + self.width = self.contentWidth; + } else if (config.widthSpec == DoricLayoutAtMost) { + self.width = self.superview.width; + } + if (config.heightSpec == DoricLayoutWrapContent) { + self.height = self.contentHeight; + } else if (config.heightSpec == DoricLayoutAtMost) { + self.height = self.superview.height; + } +} + +- (void)layout { + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricStackConfig *childConfig = [self configForChild:child]; + DoricGravity gravity = childConfig.alignment | self.gravity; + if ((gravity & LEFT) == LEFT) { + child.left = 0; + } else if ((gravity & RIGHT) == RIGHT) { + child.right = self.width; + } else if ((gravity & CENTER_X) == CENTER_X) { + child.centerX = self.width / 2; + } + if ((gravity & TOP) == TOP) { + child.top = 0; + } else if ((gravity & BOTTOM) == BOTTOM) { + child.bottom = self.height; + } else if ((gravity & CENTER_Y) == CENTER_Y) { + child.centerY = self.height / 2; + } + if (childConfig.widthSpec == DoricLayoutAtMost) { + child.width = self.width; + } + if (childConfig.heightSpec == DoricLayoutAtMost) { + child.height = self.height; + } + + if ([child isKindOfClass:[DoricLayoutContainer class]]) { + [(DoricLayoutContainer *) child layout]; + } + } +} +@end + +@interface DoricLinearView () +@property(nonatomic, assign) CGFloat contentWidth; +@property(nonatomic, assign) CGFloat contentHeight; +@property(nonatomic, assign) NSUInteger contentWeight; +@end + +@implementation DoricLinearView +- (DoricLinearConfig *)configForChild:(UIView *)child { + DoricLinearConfig *config = (DoricLinearConfig *) child.layoutConfig; + if (!config) { + config = [[DoricLinearConfig alloc] init]; + } + return config; +} +@end + +@implementation DoricVLayoutView + +- (void)sizeToFit { + DoricLayoutConfig *config = self.layoutConfig; + self.contentWidth = 0; + self.contentHeight = 0; + self.contentWeight = 0; + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + if ([child isKindOfClass:[DoricLayoutContainer class]] + || childConfig.widthSpec == DoricLayoutWrapContent + || childConfig.heightSpec == DoricLayoutWrapContent) { + [child sizeToFit]; + } + self.contentWidth = MAX(self.contentWidth, child.width + childConfig.margin.left + childConfig.margin.right); + self.contentHeight += child.height + self.space + childConfig.margin.top + childConfig.margin.bottom; + self.contentWeight += childConfig.weight; + } + self.contentHeight -= self.space; + if (config.widthSpec == DoricLayoutWrapContent) { + self.width = self.contentWidth; + } else if (config.widthSpec == DoricLayoutAtMost) { + self.width = self.superview.width; + } + if (config.heightSpec == DoricLayoutWrapContent) { + self.height = self.contentHeight; + } else if (config.heightSpec == DoricLayoutAtMost) { + self.height = self.superview.height; + } + if (self.contentWeight) { + CGFloat remain = self.height - self.contentHeight; + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + if (childConfig.weight) { + child.height += remain / self.contentWeight * childConfig.weight; + } + } + self.contentHeight = self.height; + } +} + +- (void)layout { + CGFloat yStart = 0; + if ((self.gravity & TOP) == TOP) { + yStart = 0; + } else if ((self.gravity & BOTTOM) == BOTTOM) { + yStart = self.height - self.contentHeight; + } else if ((self.gravity & CENTER_Y) == CENTER_Y) { + yStart = (self.height - self.contentHeight) / 2; + } + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + DoricGravity gravity = childConfig.alignment | self.gravity; + if ((gravity & LEFT) == LEFT) { + child.left = 0; + } else if ((gravity & RIGHT) == RIGHT) { + child.right = self.width; + } else if ((gravity & CENTER_X) == CENTER_X) { + child.centerX = self.width / 2; + } else { + if (childConfig.margin.left) { + child.left = childConfig.margin.left; + } else if (childConfig.margin.right) { + child.right = self.width - childConfig.margin.right; + } + } + if (childConfig.widthSpec == DoricLayoutAtMost) { + child.width = self.width; + } + if (childConfig.heightSpec == DoricLayoutAtMost) { + child.height = self.height - yStart - childConfig.margin.top - childConfig.margin.bottom - self.space; + } + if (childConfig.margin.top) { + yStart += childConfig.margin.top; + } + child.top = yStart; + yStart = child.bottom + self.space; + if (childConfig.margin.bottom) { + yStart += childConfig.margin.bottom; + } + if ([child isKindOfClass:[DoricLayoutContainer class]]) { + [(DoricLayoutContainer *) child layout]; + } + } +} +@end + +@implementation DoricHLayoutView +- (void)sizeToFit { + DoricLinearConfig *config; + if ([self.superview isKindOfClass:[DoricLinearView class]]) { + config = [(DoricLinearView *) self.superview configForChild:self]; + } else { + config = (DoricLinearConfig *) self.layoutConfig; + if (!config) { + config = [[DoricLinearConfig alloc] init]; + } + } + self.contentWidth = 0; + self.contentHeight = 0; + self.contentWeight = 0; + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + if ([child isKindOfClass:[DoricLayoutContainer class]] + || childConfig.widthSpec == DoricLayoutWrapContent + || childConfig.heightSpec == DoricLayoutWrapContent) { + [child sizeToFit]; + } + self.contentHeight = MAX(self.contentHeight, child.height + childConfig.margin.top + childConfig.margin.bottom); + self.contentWidth += child.width + self.space + childConfig.margin.left + childConfig.margin.right; + self.contentWeight += childConfig.weight; + } + self.contentWidth -= self.space; + if (config.widthSpec == DoricLayoutWrapContent) { + self.width = self.contentWidth; + } else if (config.widthSpec == DoricLayoutAtMost) { + self.width = self.superview.width; + } + if (config.heightSpec == DoricLayoutWrapContent) { + self.height = self.contentHeight; + } else if (config.heightSpec == DoricLayoutAtMost) { + self.height = self.superview.height; + } + if (self.contentWeight) { + CGFloat remain = self.width - self.contentWidth; + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + if (childConfig.weight) { + child.width += remain / self.contentWeight * childConfig.weight; + } + } + self.contentWidth = self.width; + } +} + +- (void)layout { + CGFloat xStart = 0; + if ((self.gravity & LEFT) == LEFT) { + xStart = 0; + } else if ((self.gravity & RIGHT) == RIGHT) { + xStart = self.width - self.contentWidth; + } else if ((self.gravity & CENTER_X) == CENTER_X) { + xStart = (self.width - self.contentWidth) / 2; + } + for (UIView *child in self.subviews) { + if (child.isHidden) { + continue; + } + DoricLinearConfig *childConfig = [self configForChild:child]; + DoricGravity gravity = childConfig.alignment | self.gravity; + if ((gravity & TOP) == TOP) { + child.top = 0; + } else if ((gravity & BOTTOM) == BOTTOM) { + child.bottom = self.height; + } else if ((gravity & CENTER_Y) == CENTER_Y) { + child.centerY = self.height / 2; + } else { + if (childConfig.margin.top) { + child.top = childConfig.margin.top; + } else if (childConfig.margin.bottom) { + child.bottom = self.height - childConfig.margin.bottom; + } + } + + if (childConfig.heightSpec == DoricLayoutAtMost) { + child.height = self.height; + } + if (childConfig.widthSpec == DoricLayoutAtMost) { + child.width = self.width - xStart - childConfig.margin.right - childConfig.margin.left - self.space; + } + + if (childConfig.margin.left) { + xStart += childConfig.margin.left; + } + child.left = xStart; + xStart = child.right + self.space; + if (childConfig.margin.right) { + xStart += childConfig.margin.right; + } + if ([child isKindOfClass:[DoricLayoutContainer class]]) { + [(DoricLayoutContainer *) child layout]; + } + } +} +@end + +static const void *kLayoutConfig = &kLayoutConfig; +static const void *kTagString = &kTagString; + +@implementation UIView (DoricLayoutConfig) +@dynamic layoutConfig; + +- (void)setLayoutConfig:(DoricLayoutConfig *)layoutConfig { + objc_setAssociatedObject(self, kLayoutConfig, layoutConfig, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (DoricLayoutConfig *)layoutConfig { + return objc_getAssociatedObject(self, kLayoutConfig); +} + +- (void)setTagString:(NSString *)tagString { + objc_setAssociatedObject(self, kTagString, tagString, OBJC_ASSOCIATION_COPY_NONATOMIC); + self.tag = [tagString hash]; +} + +- (NSString *)tagString { + return objc_getAssociatedObject(self, kTagString); +} + + +- (UIView *)viewWithTagString:(NSString *)tagString { + // notice the potential hash collision + return [self viewWithTag:[tagString hash]]; +} + +@end + +DoricVLayoutView *vLayout(NSArray <__kindof UIView *> *views) { + DoricVLayoutView *layout = [[DoricVLayoutView alloc] initWithFrame:CGRectZero]; + for (__kindof UIView *uiView in views) { + [layout addSubview:uiView]; + } + layout.layoutConfig = [[DoricLayoutConfig alloc] initWithWidth:DoricLayoutWrapContent height:DoricLayoutWrapContent]; + return layout; +} + +DoricHLayoutView *hLayout(NSArray <__kindof UIView *> *views) { + DoricHLayoutView *layout = [[DoricHLayoutView alloc] initWithFrame:CGRectZero]; + for (__kindof UIView *uiView in views) { + [layout addSubview:uiView]; + } + layout.layoutConfig = [[DoricLayoutConfig alloc] initWithWidth:DoricLayoutWrapContent height:DoricLayoutWrapContent]; + return layout; +} + +DoricVLayoutView *vLayoutWithBlock(NSArray *blocks) { + DoricVLayoutView *layout = [[DoricVLayoutView alloc] initWithFrame:CGRectZero]; + UIView *(^block)(); + for (block in blocks) { + [layout addSubview:block()]; + } + layout.layoutConfig = [[DoricLayoutConfig alloc] initWithWidth:DoricLayoutWrapContent height:DoricLayoutWrapContent]; + return layout; +} + +DoricHLayoutView *hLayoutWithBlock(NSArray *blocks) { + DoricHLayoutView *layout = [[DoricHLayoutView alloc] initWithFrame:CGRectZero]; + UIView *(^block)(); + for (block in blocks) { + [layout addSubview:block()]; + } + layout.layoutConfig = [[DoricLayoutConfig alloc] initWithWidth:DoricLayoutWrapContent height:DoricLayoutWrapContent]; + return layout; +} diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.h b/iOS/Pod/Classes/Shader/DoricRootNode.h index 9fa950b6..58bff00a 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.h +++ b/iOS/Pod/Classes/Shader/DoricRootNode.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @interface DoricRootNode : DoricStackNode -- (void)setupRootView:(UIView *)view; +- (void)setupRootView:(DoricStackView *)view; - (void)render:(NSDictionary *)props; @end diff --git a/iOS/Pod/Classes/Shader/DoricRootNode.m b/iOS/Pod/Classes/Shader/DoricRootNode.m index 6df879d9..ed0ae8db 100644 --- a/iOS/Pod/Classes/Shader/DoricRootNode.m +++ b/iOS/Pod/Classes/Shader/DoricRootNode.m @@ -23,17 +23,15 @@ #import "DoricRootNode.h" @implementation DoricRootNode -- (void)setupRootView:(UIView *)view { +- (void)setupRootView:(DoricStackView *)view { self.view = view; + self.layoutConfig = view.layoutConfig; } - (void)render:(NSDictionary *)props { [self blend:props]; - [self requestLayout]; } - - (void)requestLayout { - [self measureByParent:self]; - [self layoutByParent:self]; + [self.view requestLayout]; } @end diff --git a/iOS/Pod/Classes/Shader/DoricStackNode.h b/iOS/Pod/Classes/Shader/DoricStackNode.h index 71719c10..375cafad 100644 --- a/iOS/Pod/Classes/Shader/DoricStackNode.h +++ b/iOS/Pod/Classes/Shader/DoricStackNode.h @@ -22,10 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricStackNode : DoricGroupNode -@property(nonatomic) DoricGravity gravity; +@interface DoricStackNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricStackNode.m b/iOS/Pod/Classes/Shader/DoricStackNode.m index 50491d08..c260248f 100644 --- a/iOS/Pod/Classes/Shader/DoricStackNode.m +++ b/iOS/Pod/Classes/Shader/DoricStackNode.m @@ -25,93 +25,33 @@ @implementation DoricStackNode -- (instancetype)init { - if (self = [super init]) { - _gravity = 0; - } - return self; +- (DoricStackView *)build:(NSDictionary *)props { + return [DoricStackView new]; } -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - CGFloat maxWidth = 0, maxHeight = 0; - for (DoricViewNode *child in self.indexedChildren) { - [child measureByParent:self]; - CGFloat placeWidth = child.measuredWidth; - CGFloat placeHeight = child.measuredHeight; - maxWidth = MAX(maxWidth, placeWidth); - maxHeight = MAX(maxHeight, placeHeight); - } - self.desiredWidth = maxWidth; - self.desiredHeight = maxHeight; - - if (widthSpec == LAYOUT_WRAP_CONTENT) { - self.width = maxWidth; - } - - if (heightSpec == LAYOUT_WRAP_CONTENT) { - self.height = maxHeight; +- (void)blendView:(DoricStackView *)view forPropName:(NSString *)name propValue:(id)prop { + if ([name isEqualToString:@"gravity"]) { + view.gravity = (DoricGravity) [(NSNumber *) prop integerValue]; + } else { + [super blendView:view forPropName:name propValue:prop]; } } -- (LayoutParams *)generateDefaultLayoutParams { - return [[StackLayoutParams alloc] init]; +- (DoricStackConfig *)generateDefaultLayoutParams { + return [[DoricStackConfig alloc] init]; } -- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig { - [super blendChild:child layoutConfig:layoutconfig]; - if (![child.layoutParams isKindOfClass:StackLayoutParams.class]) { - DoricLog(@"blend Stack child error,layout params not match"); +- (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutConfig { + [super blendChild:child layoutConfig:layoutConfig]; + if (![child.layoutConfig isKindOfClass:DoricStackConfig.class]) { + DoricLog(@"blend DoricHLayoutView child error,layout params not match"); return; } - StackLayoutParams *params = (StackLayoutParams *) child.layoutParams; -// NSDictionary *margin = [layoutconfig objectForKey:@"margin"]; -// if (margin) { -// params.margin.top = [(NSNumber *)[margin objectForKey:@"top"] floatValue]; -// params.margin.left = [(NSNumber *)[margin objectForKey:@"left"] floatValue]; -// params.margin.right = [(NSNumber *)[margin objectForKey:@"right"] floatValue]; -// params.margin.bottom = [(NSNumber *)[margin objectForKey:@"bottom"] floatValue]; -// } - NSNumber *alignment = layoutconfig[@"alignment"]; + DoricStackConfig *params = (DoricStackConfig *) child.layoutConfig; + NSNumber *alignment = layoutConfig[@"alignment"]; if (alignment) { - params.alignment = [alignment integerValue]; + params.alignment = (DoricGravity) [alignment integerValue]; } } -- (void)layoutByParent:(DoricGroupNode *)parent { - for (DoricViewNode *child in self.indexedChildren) { - if (child.layoutParams.width == LAYOUT_MATCH_PARENT) { - child.width = self.width; - } - if (child.layoutParams.height == LAYOUT_MATCH_PARENT) { - child.height = self.height; - } - DoricGravity gravity = self.gravity; - if ([child.layoutParams isKindOfClass:StackLayoutParams.class]) { - StackLayoutParams *layoutParams = (StackLayoutParams *) child.layoutParams; - gravity |= layoutParams.alignment; - } - - if ((gravity & LEFT) == LEFT) { - child.left = self.left; - } - if ((gravity & RIGHT) == RIGHT) { - child.right = self.right; - } - if ((gravity & TOP) == TOP) { - child.top = self.top; - } - if ((gravity & BOTTOM) == BOTTOM) { - child.bottom = self.bottom; - } - if ((gravity & CENTER_X) == CENTER_X) { - child.centerX = self.centerX; - } - if ((gravity & CENTER_Y) == CENTER_Y) { - child.centerY = self.centerY; - } - [child layoutByParent:self]; - } -} @end diff --git a/iOS/Pod/Classes/Shader/DoricTextNode.m b/iOS/Pod/Classes/Shader/DoricTextNode.m index 8c7dbd02..d723c4ad 100644 --- a/iOS/Pod/Classes/Shader/DoricTextNode.m +++ b/iOS/Pod/Classes/Shader/DoricTextNode.m @@ -37,7 +37,7 @@ - (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)pro } else if ([name isEqualToString:@"textColor"]) { view.textColor = DoricColor(prop); } else if ([name isEqualToString:@"textAlignment"]) { - DoricGravity gravity = [(NSNumber *) prop integerValue]; + DoricGravity gravity = (DoricGravity) [(NSNumber *) prop integerValue]; NSTextAlignment alignment = NSTextAlignmentCenter; switch (gravity) { case LEFT: @@ -54,12 +54,4 @@ - (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)pro [super blendView:view forPropName:name propValue:prop]; } } - -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) { - [self.view sizeToFit]; - } -} @end diff --git a/iOS/Pod/Classes/Shader/DoricVLayoutNode.h b/iOS/Pod/Classes/Shader/DoricVLayoutNode.h index 54c15fa3..6024eaa2 100644 --- a/iOS/Pod/Classes/Shader/DoricVLayoutNode.h +++ b/iOS/Pod/Classes/Shader/DoricVLayoutNode.h @@ -22,11 +22,5 @@ #import "DoricGroupNode.h" -NS_ASSUME_NONNULL_BEGIN - -@interface DoricVLayoutNode : DoricGroupNode -@property(nonatomic) CGFloat space; -@property(nonatomic) DoricGravity gravity; +@interface DoricVLayoutNode : DoricGroupNode @end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricVLayoutNode.m b/iOS/Pod/Classes/Shader/DoricVLayoutNode.m index 3ce2cacf..7b586e4c 100644 --- a/iOS/Pod/Classes/Shader/DoricVLayoutNode.m +++ b/iOS/Pod/Classes/Shader/DoricVLayoutNode.m @@ -24,19 +24,16 @@ #import "DoricUtil.h" @implementation DoricVLayoutNode -- (instancetype)init { - if (self = [super init]) { - _space = 0; - _gravity = 0; - } - return self; + +- (DoricVLayoutView *)build:(NSDictionary *)props { + return [DoricVLayoutView new]; } -- (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop { +- (void)blendView:(DoricVLayoutView *)view forPropName:(NSString *)name propValue:(id)prop { if ([name isEqualToString:@"gravity"]) { - self.gravity = [(NSNumber *) prop integerValue]; + view.gravity = (DoricGravity) [(NSNumber *) prop integerValue]; } else if ([name isEqualToString:@"space"]) { - self.space = [(NSNumber *) prop floatValue]; + view.space = [(NSNumber *) prop floatValue]; } else { [super blendView:view forPropName:name propValue:prop]; } @@ -44,92 +41,26 @@ - (void)blendView:(id)view forPropName:(NSString *)name propValue:(id)prop { - (void)blendChild:(DoricViewNode *)child layoutConfig:(NSDictionary *)layoutconfig { [super blendChild:child layoutConfig:layoutconfig]; - if (![child.layoutParams isKindOfClass:VHLayoutParams.class]) { - DoricLog(@"blend VLayout child error,layout params not match"); + if (![child.layoutConfig isKindOfClass:DoricLinearConfig.class]) { + DoricLog(@"blend DoricVLayoutView child error,layout params not match"); return; } - VHLayoutParams *params = (VHLayoutParams *) child.layoutParams; + DoricLinearConfig *params = (DoricLinearConfig *) child.layoutConfig; NSDictionary *margin = layoutconfig[@"margin"]; if (margin) { - params.margin.top = [(NSNumber *) margin[@"top"] floatValue]; - params.margin.left = [(NSNumber *) margin[@"left"] floatValue]; - params.margin.right = [(NSNumber *) margin[@"right"] floatValue]; - params.margin.bottom = [(NSNumber *) margin[@"bottom"] floatValue]; + params.margin = DoricMarginMake( + [(NSNumber *) margin[@"left"] floatValue], + [(NSNumber *) margin[@"top"] floatValue], + [(NSNumber *) margin[@"right"] floatValue], + [(NSNumber *) margin[@"bottom"] floatValue]); } NSNumber *alignment = layoutconfig[@"alignment"]; if (alignment) { - params.alignment = [alignment integerValue]; + params.alignment = (DoricGravity) [alignment integerValue]; } } -- (LayoutParams *)generateDefaultLayoutParams { - return [[VHLayoutParams alloc] init]; -} - -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - CGFloat maxWidth = 0, maxHeight = 0; - for (DoricViewNode *child in self.indexedChildren) { - [child measureByParent:self]; - CGFloat placeWidth = child.measuredWidth; - CGFloat placeHeight = child.measuredHeight; - maxWidth = MAX(maxWidth, placeWidth); - maxHeight += placeHeight + self.space; - } - maxHeight -= self.space; - - self.desiredWidth = maxWidth; - self.desiredHeight = maxHeight; - - if (widthSpec == LAYOUT_WRAP_CONTENT) { - self.width = maxWidth; - } - - if (heightSpec == LAYOUT_WRAP_CONTENT) { - self.height = maxHeight; - } -} - -- (void)layoutByParent:(DoricGroupNode *)parent { - if (self.layoutParams.width == LAYOUT_MATCH_PARENT) { - self.width = parent.width; - } - if (self.layoutParams.height == LAYOUT_MATCH_PARENT) { - self.height = parent.height; - } - // layout child - CGFloat yStart = 0; - if ((self.gravity & TOP) == TOP) { - yStart = 0; - } else if ((self.gravity & BOTTOM) == BOTTOM) { - yStart = self.height - self.desiredHeight; - } else if ((self.gravity & CENTER_Y) == CENTER_Y) { - yStart = (self.height - self.desiredHeight) / 2; - } - - - for (DoricViewNode *child in self.indexedChildren) { - if (child.layoutParams.width == LAYOUT_MATCH_PARENT) { - child.width = self.width; - } - if (child.layoutParams.height == LAYOUT_MATCH_PARENT) { - child.height = self.height; - } - if ([child.layoutParams isKindOfClass:VHLayoutParams.class]) { - VHLayoutParams *layoutParams = (VHLayoutParams *) child.layoutParams; - DoricGravity gravity = layoutParams.alignment | self.gravity; - if ((gravity & LEFT) == LEFT) { - child.left = 0; - } else if ((gravity & RIGHT) == RIGHT) { - child.right = self.width; - } else if ((gravity & CENTER_X) == CENTER_X) { - child.centerX = self.width / 2; - } - } - child.top = yStart; - yStart = child.bottom + self.space; - [child layoutByParent:self]; - } +- (DoricLinearConfig *)generateDefaultLayoutParams { + return [[DoricLinearConfig alloc] init]; } @end diff --git a/iOS/Pod/Classes/Shader/DoricViewContainer.h b/iOS/Pod/Classes/Shader/DoricViewContainer.h deleted file mode 100644 index d15eb2f1..00000000 --- a/iOS/Pod/Classes/Shader/DoricViewContainer.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ -// -// DoricViewContainer.h -// Doric -// -// Created by pengfei.zhou on 2019/7/30. -// - -#import "UIView+Doric.h" - -typedef NS_ENUM(NSInteger, DoricGravity) { - SPECIFIED = 1, - START = 1 << 1, - END = 1 << 2, - SHIFT_X = 0, - SHIFT_Y = 4, - LEFT = (START | SPECIFIED) << SHIFT_X, - RIGHT = (END | SPECIFIED) << SHIFT_X, - TOP = (START | SPECIFIED) << SHIFT_Y, - BOTTOM = (END | SPECIFIED) << SHIFT_Y, - CENTER_X = SPECIFIED << SHIFT_X, - CENTER_Y = SPECIFIED << SHIFT_Y, - CENTER = CENTER_X | CENTER_Y, -}; - -typedef NS_ENUM(NSInteger, DoricLayoutDesc) { - LAYOUT_ABSOLUTE = 0, - LAYOUT_MATCH_PARENT = -1, - LAYOUT_WRAP_CONTENT = -2, -}; - -NS_ASSUME_NONNULL_BEGIN - -@interface DoricRect : NSObject -@property(nonatomic) CGFloat left; -@property(nonatomic) CGFloat right; -@property(nonatomic) CGFloat top; -@property(nonatomic) CGFloat bottom; -@end - - -@interface LayoutParams : NSObject -@property(nonatomic) DoricLayoutDesc width; -@property(nonatomic) DoricLayoutDesc height; -@end - -@interface MarginLayoutParams : LayoutParams -@property(nonatomic, strong) DoricRect *margin; -@end - -@interface StackLayoutParams : LayoutParams -@property(nonatomic) DoricGravity alignment; -@end - -@interface VHLayoutParams : MarginLayoutParams -@property(nonatomic) DoricGravity alignment; -@property(nonatomic) NSInteger weight; -@end - - -@interface UIView (DoricContainer) - -@property(nonatomic, strong) LayoutParams *layoutParams; - -- (void)layout; - -- (void)measure; - -@end - -@interface Stack : UIView -@property(nonatomic) DoricGravity gravity; -@end - -@interface LinearLayout : UIView -@property(nonatomic) DoricGravity gravity; -@property(nonatomic) CGFloat space; -@end - - -@interface VLayout : LinearLayout -@end - -@interface HLayout : LinearLayout -@end - -NS_ASSUME_NONNULL_END diff --git a/iOS/Pod/Classes/Shader/DoricViewContainer.m b/iOS/Pod/Classes/Shader/DoricViewContainer.m deleted file mode 100644 index 8abbcdac..00000000 --- a/iOS/Pod/Classes/Shader/DoricViewContainer.m +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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. - */ -// -// DoricViewContainer.m -// Doric -// -// Created by pengfei.zhou on 2019/7/30. -// - -#import "DoricViewContainer.h" -#import - -@implementation DoricRect -- (instancetype)init { - if (self = [super init]) { - _left = 0; - _right = 0; - _top = 0; - _bottom = 0; - } - return self; -} -@end - -@implementation LayoutParams -- (instancetype)init { - if (self = [super init]) { - _width = LAYOUT_WRAP_CONTENT; - _height = LAYOUT_WRAP_CONTENT; - } - return self; -} -@end - -@implementation MarginLayoutParams -- (instancetype)init { - if (self = [super init]) { - _margin = [[DoricRect alloc] init]; - } - return self; -} -@end - -@implementation StackLayoutParams -- (instancetype)init { - if (self = [super init]) { - _alignment = 0; - } - return self; -} -@end - -@implementation VHLayoutParams -- (instancetype)init { - if (self = [super init]) { - _alignment = 0; - _weight = 0; - } - return self; -} -@end - - -@implementation UIView (DoricContainer) - -- (LayoutParams *)layoutParams { - return objc_getAssociatedObject(self, _cmd); -} - -- (void)setLayoutParams:(LayoutParams *)layoutParams { - objc_setAssociatedObject(self, @selector(layoutParams), layoutParams, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (void)layout { - -} - -- (void)measure { - if (self.layoutParams) { - - } -} -@end - -@implementation Stack -@end - -@implementation LinearLayout -@end - -@implementation VLayout -@end - -@implementation HLayout -@end diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.h b/iOS/Pod/Classes/Shader/DoricViewNode.h index 543f87c0..f7333e42 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.h +++ b/iOS/Pod/Classes/Shader/DoricViewNode.h @@ -21,8 +21,7 @@ // #import "DoricContextHolder.h" -#import "DoricViewContainer.h" - +#import "DoricLayouts.h" #import "UIView+Doric.h" NS_ASSUME_NONNULL_BEGIN @@ -37,24 +36,10 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) NSString *viewId; -@property(nonatomic, strong) LayoutParams *layoutParams; +@property(nonatomic, strong) DoricLayoutConfig *layoutConfig; @property(nonatomic, strong, readonly) NSArray *idList; - -@property(nonatomic) CGFloat x; -@property(nonatomic) CGFloat y; -@property(nonatomic) CGFloat width; -@property(nonatomic) CGFloat height; -@property(nonatomic) CGFloat centerX; -@property(nonatomic) CGFloat centerY; -@property(nonatomic) CGFloat top; -@property(nonatomic) CGFloat left; -@property(nonatomic) CGFloat right; -@property(nonatomic) CGFloat bottom; -@property(nonatomic, readonly) CGFloat measuredWidth; -@property(nonatomic, readonly) CGFloat measuredHeight; - - (V)build:(NSDictionary *)props; - (void)blend:(NSDictionary *)props; @@ -63,10 +48,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)callJSResponse:(NSString *)funcId, ...; -- (void)measureByParent:(DoricGroupNode *)parent; - -- (void)layoutByParent:(DoricGroupNode *)parent; - + (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type; - (void)requestLayout; diff --git a/iOS/Pod/Classes/Shader/DoricViewNode.m b/iOS/Pod/Classes/Shader/DoricViewNode.m index de219bd2..e4d4b214 100644 --- a/iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/iOS/Pod/Classes/Shader/DoricViewNode.m @@ -85,6 +85,7 @@ - (void)blend:(NSDictionary *)props { if (self.view == nil) { self.view = [self build:props]; } + self.view.layoutConfig = self.layoutConfig; for (NSString *key in props) { id value = props[key]; [self blendView:self.view forPropName:key propValue:value]; @@ -94,18 +95,12 @@ - (void)blend:(NSDictionary *)props { - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop { if ([name isEqualToString:@"width"]) { NSNumber *width = (NSNumber *) prop; - if ([width integerValue] < 0) { - self.layoutParams.width = [width integerValue]; - } else { - self.layoutParams.width = LAYOUT_ABSOLUTE; + if ([width floatValue] >= 0) { view.width = [width floatValue]; } } else if ([name isEqualToString:@"height"]) { NSNumber *height = (NSNumber *) prop; - if ([height integerValue] < 0) { - self.layoutParams.height = [height integerValue]; - } else { - self.layoutParams.height = LAYOUT_ABSOLUTE; + if ([height floatValue] >= 0) { view.height = [height floatValue]; } } else if ([name isEqualToString:@"x"]) { @@ -119,14 +114,14 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop [self.parent blendChild:self layoutConfig:prop]; } } else if ([name isEqualToString:@"onClick"]) { - [self.callbackIds setObject:prop forKey:@"onClick"]; + self.callbackIds[@"onClick"] = prop; view.userInteractionEnabled = YES; - UITapGestureRecognizer *tapGesturRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)]; - [view addGestureRecognizer:tapGesturRecognizer]; + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)]; + [view addGestureRecognizer:tapGestureRecognizer]; } else if ([name isEqualToString:@"border"]) { NSDictionary *dic = prop; - CGFloat width = [(NSNumber *) [dic objectForKey:@"width"] floatValue]; - UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]); + CGFloat width = [(NSNumber *) dic[@"width"] floatValue]; + UIColor *color = DoricColor((NSNumber *) dic[@"color"]); view.layer.borderWidth = width; view.layer.borderColor = color.CGColor; } else if ([name isEqualToString:@"corners"]) { @@ -134,10 +129,10 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop view.layer.cornerRadius = [(NSNumber *) prop floatValue]; } else if ([prop isKindOfClass:NSDictionary.class]) { NSDictionary *dic = prop; - 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]; + CGFloat leftTop = [(NSNumber *) dic[@"leftTop"] floatValue]; + CGFloat rightTop = [(NSNumber *) dic[@"rightTop"] floatValue]; + CGFloat rightBottom = [(NSNumber *) dic[@"rightBottom"] floatValue]; + CGFloat leftBottom = [(NSNumber *) dic[@"leftBottom"] floatValue]; CALayer *mask = nil; if (ABS(leftTop - rightTop) > CGFLOAT_MIN || ABS(leftTop - rightBottom) > CGFLOAT_MIN @@ -155,14 +150,14 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop } } else if ([name isEqualToString:@"shadow"]) { NSDictionary *dic = prop; - CGFloat opacity = [(NSNumber *) [dic objectForKey:@"opacity"] floatValue]; + CGFloat opacity = [(NSNumber *) dic[@"opacity"] floatValue]; if (opacity > CGFLOAT_MIN) { view.clipsToBounds = NO; - UIColor *color = DoricColor((NSNumber *) [dic objectForKey:@"color"]); + UIColor *color = DoricColor((NSNumber *) dic[@"color"]); view.layer.shadowColor = color.CGColor; - view.layer.shadowRadius = [(NSNumber *) [dic objectForKey:@"radius"] floatValue]; - view.layer.shadowOffset = CGSizeMake([(NSNumber *) [dic objectForKey:@"offsetX"] floatValue], [(NSNumber *) [dic objectForKey:@"offsetY"] floatValue]); - view.layer.shadowOpacity = opacity; + view.layer.shadowRadius = [(NSNumber *) dic[@"radius"] floatValue]; + view.layer.shadowOffset = CGSizeMake([(NSNumber *) dic[@"offsetX"] floatValue], [(NSNumber *) dic[@"offsetY"] floatValue]); + view.layer.shadowOpacity = (float) opacity; } else { view.clipsToBounds = YES; } @@ -173,35 +168,7 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop } - (void)onClick:(UIView *)view { - [self callJSResponse:[self.callbackIds objectForKey:@"onClick"], nil]; -} - -- (CGFloat)measuredWidth { - if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) { - MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams; - return self.width + marginParams.margin.left + marginParams.margin.right; - } - return self.width; -} - -- (CGFloat)measuredHeight { - if ([self.layoutParams isKindOfClass:MarginLayoutParams.class]) { - MarginLayoutParams *marginParams = (MarginLayoutParams *) self.layoutParams; - return self.height + marginParams.margin.top + marginParams.margin.bottom; - } - return self.height; -} - -- (void)measureByParent:(DoricGroupNode *)parent { - DoricLayoutDesc widthSpec = self.layoutParams.width; - DoricLayoutDesc heightSpec = self.layoutParams.height; - if (widthSpec == LAYOUT_WRAP_CONTENT || heightSpec == LAYOUT_WRAP_CONTENT) { - [self.view sizeToFit]; - } -} - -- (void)layoutByParent:(DoricGroupNode *)parent { - + [self callJSResponse:self.callbackIds[@"onClick"], nil]; } - (NSArray *)idList { @@ -232,87 +199,7 @@ - (void)callJSResponse:(NSString *)funcId, ... { + (DoricViewNode *)create:(DoricContext *)context withType:(NSString *)type { DoricRegistry *registry = context.driver.registry; Class clz = [registry acquireViewNode:type]; - return [[clz alloc] initWithContext:context]; -} - -- (CGFloat)x { - return ((UIView *) self.view).x; -} - -- (CGFloat)y { - return ((UIView *) self.view).y; -} - -- (CGFloat)width { - return ((UIView *) self.view).width; -} - -- (CGFloat)height { - return ((UIView *) self.view).height; -} - -- (CGFloat)top { - return ((UIView *) self.view).top; -} - -- (CGFloat)bottom { - return ((UIView *) self.view).bottom; -} - -- (CGFloat)left { - return ((UIView *) self.view).left; -} - -- (CGFloat)right { - return ((UIView *) self.view).right; -} - -- (CGFloat)centerX { - return ((UIView *) self.view).centerX; -} - -- (CGFloat)centerY { - return ((UIView *) self.view).centerY; -} - -- (void)setX:(CGFloat)x { - ((UIView *) self.view).x = x; -} - -- (void)setY:(CGFloat)y { - ((UIView *) self.view).y = y; -} - -- (void)setWidth:(CGFloat)width { - ((UIView *) self.view).width = width; -} - -- (void)setHeight:(CGFloat)height { - ((UIView *) self.view).height = height; -} - -- (void)setLeft:(CGFloat)left { - ((UIView *) self.view).left = left; -} - -- (void)setRight:(CGFloat)right { - ((UIView *) self.view).right = right; -} - -- (void)setTop:(CGFloat)top { - ((UIView *) self.view).top = top; -} - -- (void)setBottom:(CGFloat)bottom { - ((UIView *) self.view).bottom = bottom; -} - -- (void)setCenterX:(CGFloat)centerX { - ((UIView *) self.view).centerX = centerX; -} - -- (void)setCenterY:(CGFloat)centerY { - ((UIView *) self.view).centerY = centerY; + return [(DoricViewNode *) [clz alloc] initWithContext:context]; } - (void)requestLayout { diff --git a/iOS/Pod/Classes/UIView+Doric.h b/iOS/Pod/Classes/Shader/UIView+Doric.h similarity index 100% rename from iOS/Pod/Classes/UIView+Doric.h rename to iOS/Pod/Classes/Shader/UIView+Doric.h diff --git a/iOS/Pod/Classes/UIView+Doric.m b/iOS/Pod/Classes/Shader/UIView+Doric.m similarity index 100% rename from iOS/Pod/Classes/UIView+Doric.m rename to iOS/Pod/Classes/Shader/UIView+Doric.m diff --git a/iOS/Pod/Classes/Util/DoricExtensions.h b/iOS/Pod/Classes/Util/DoricExtensions.h new file mode 100644 index 00000000..aba09103 --- /dev/null +++ b/iOS/Pod/Classes/Util/DoricExtensions.h @@ -0,0 +1,29 @@ +/* + * 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. + */ +// +// Created by pengfei.zhou on 2019/10/23. +// + +#import + + +@interface NSObject (Doric) +- (id)apply:(id (^)(id it))block; + +- (instancetype)also:(void (^)(id it))block; + +- (void)let:(void (^)(id it))block; +@end diff --git a/iOS/Pod/Classes/Util/DoricExtensions.m b/iOS/Pod/Classes/Util/DoricExtensions.m new file mode 100644 index 00000000..b3b92987 --- /dev/null +++ b/iOS/Pod/Classes/Util/DoricExtensions.m @@ -0,0 +1,35 @@ +/* + * 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. + */ +// +// Created by pengfei.zhou on 2019/10/23. +// + +#import "DoricExtensions.h" + +@implementation NSObject (Doric) +- (id)apply:(id (^)(id it))block { + return block(self); +} + +- (instancetype)also:(void (^)(id it))block { + block(self); + return self; +} + +- (void)let:(void (^)(id it))block { + block(self); +} +@end diff --git a/js-framework/src/ui/panel.ts b/js-framework/src/ui/panel.ts index 6c675f66..e2abef1e 100644 --- a/js-framework/src/ui/panel.ts +++ b/js-framework/src/ui/panel.ts @@ -98,8 +98,8 @@ export abstract class Panel { private retrospectView(ids: string[]): View { return ids.reduce((acc: View, cur) => { - if (acc instanceof Group) { - return acc.children.filter(e => e.viewId === cur)[0] + if (Reflect.has(acc, "subViewById")) { + return Reflect.apply(Reflect.get(acc, "subViewById"), acc, [cur]) } return acc }, this.__root__) diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index 06e36705..b222309e 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -19,9 +19,25 @@ import { uniqueId } from "../util/uniqueId"; import { Gravity } from "../util/gravity"; import { loge } from "../util/log"; -export const MATCH_PARENT = -1 +export enum LayoutSpec { + EXACTLY = 0, + WRAP_CONTENT = 1, + AT_MOST = 2, +} + +export interface LayoutConfig { + widthSpec?: LayoutSpec + heightSpec?: LayoutSpec + margin?: { + left?: number, + right?: number, + top?: number, + bottom?: number, + } + alignment?: Gravity +} + -export const WRAP_CONTENT = -2 export function Property(target: Object, propKey: string) { Reflect.defineMetadata(propKey, true, target) @@ -29,10 +45,10 @@ export function Property(target: Object, propKey: string) { export abstract class View implements Modeling { @Property - width: number = WRAP_CONTENT + width: number = 0 @Property - height: number = WRAP_CONTENT + height: number = 0 @Property x: number = 0 @@ -61,6 +77,26 @@ export abstract class View implements Modeling { @Property viewId = uniqueId('ViewId') + @Property + padding?: { + left?: number, + right?: number, + top?: number, + bottom?: number, + } + + @Property + layoutConfig?: LayoutConfig + + @Property + onClick?: Function + + /** + * Set to reuse native view + */ + @Property + identifier?: string + parent?: Group callbacks: Map = new Map @@ -187,42 +223,28 @@ export abstract class View implements Modeling { toModel() { return this.nativeViewModel } - - @Property - padding?: { - left?: number, - right?: number, - top?: number, - bottom?: number, + let(block: (it: this) => void) { + block(this) } - - @Property - layoutConfig?: Config - - @Property - onClick?: Function -} - -export interface Config { - margin?: { - left?: number, - right?: number, - top?: number, - bottom?: number, + also(block: (it: this) => void) { + block(this) + return this } - alignment?: Gravity } -export interface StackConfig extends Config { +export interface StackConfig extends LayoutConfig { } -export interface LinearConfig extends Config { +export interface LinearConfig extends LayoutConfig { weight?: number } -export abstract class Group extends View { +export interface SuperView { + subViewById(id: string): View | undefined +} +export abstract class Group extends View implements SuperView { @Property readonly children: View[] = new Proxy([], { set: (target, index, value) => { @@ -243,6 +265,14 @@ export abstract class Group extends View { } }) + subViewById(id: string): View | undefined { + for (let view of this.children) { + if (view.viewId === id) { + return view + } + } + return undefined + } addChild(view: View) { this.children.push(view) } @@ -283,6 +313,16 @@ export class Stack extends Group { @Property gravity?: Gravity } + +export class Scroller extends View implements SuperView { + @Property + contentView?: View + + subViewById(id: string): View | undefined { + return this.contentView + } +} + export class Root extends Stack { } @@ -322,31 +362,107 @@ export class Image extends View { imageUrl?: string } -export class List extends View { +export class List extends View implements SuperView { + private cachedViews: Map = new Map -} - -export class Slide extends View { - -} - -export function stack() { - -} - -export function vlayout(providers: Array<() => View>, config: { - width: number - height: number - space?: number -}) { - const vlayout = new VLayout - vlayout.width = config.width - vlayout.height = config.height - if (config.space !== undefined) { - vlayout.space = config.space + subViewById(id: string): View | undefined { + return this.cachedViews.get(id) + } + + @Property + itemCount = 0 + + @Property + renderItem!: (index: number) => View + + + private getItem(itemIdx: number) { + let view = this.cachedViews.get(`${itemIdx}`) + if (view === undefined) { + view = this.renderItem(itemIdx) + this.cachedViews.set(`${itemIdx}`, view) + } + return view + } + + @Property + private renderBunchedItems(items: number[]): View[] { + return items.map(e => this.getItem(e)) } - providers.forEach(e => { - vlayout.addChild(e()) - }) - return vlayout } + +export class SectionList extends View implements SuperView { + private cachedViews: Map = new Map + + subViewById(id: string): View | undefined { + return this.cachedViews.get(id) + } + @Property + sectionRowsCount: number[] = [] + + @Property + renderSectionHeader!: (sectionIdx: number) => View + + @Property + renderItem!: (sectionIdx: number, itemIdx: number) => View + + @Property + sectionHeaderSticky = true + + setupSectionRows(sectionCount: number, numberOfSection: (section: number) => number) { + this.sectionRowsCount = [...Array(sectionCount).keys()].map(e => numberOfSection(e)) + } + + private getItem(sectionIdx: number, itemIdx: number) { + let view = this.cachedViews.get(`${sectionIdx}:${itemIdx}`) + if (view === undefined) { + view = this.renderItem(sectionIdx, itemIdx) + this.cachedViews.set(`${sectionIdx}:${itemIdx}`, view) + } + return view + } + + private getSectionHeader(sectionIdx: number) { + let view = this.cachedViews.get(`${sectionIdx}:`) + if (view === undefined) { + view = this.renderSectionHeader(sectionIdx) + this.cachedViews.set(`${sectionIdx}:`, view) + } + return view + } + + @Property + private renderBunchedItems(items: Array<{ itemIdx: number, sectionIdx: number }>, + headers: number[]): { items: View[], headers: View[] } { + return { + items: items.map(e => this.getItem(e.sectionIdx, e.itemIdx)), + headers: headers.map(e => this.getSectionHeader(e)) + } + } +} + +export class Slide extends View implements SuperView { + @Property + pageCount = 0 + + @Property + renderPage!: (pageIdx: number) => View + + private cachedViews: Map = new Map + subViewById(id: string): View | undefined { + return this.cachedViews.get(id) + } + private getPage(pageIdx: number) { + let view = this.cachedViews.get(`${pageIdx}`) + if (view === undefined) { + view = this.renderPage(pageIdx) + this.cachedViews.set(`${pageIdx}`, view) + } + return view + } + + @Property + private renderBunchedPages(pages: number[]): View[] { + return pages.map(e => this.getPage(e)) + } +} \ No newline at end of file