diff --git a/doric-android/doric/build.gradle b/doric-android/doric/build.gradle index bcc9f6f6..7608c563 100644 --- a/doric-android/doric/build.gradle +++ b/doric-android/doric/build.gradle @@ -43,7 +43,7 @@ dependencies { api 'com.github.penfeizhou:jsc4a:0.1.0' implementation 'com.squareup.okhttp3:okhttp:3.12.1' implementation('com.github.penfeizhou.android.animation:glide-plugin:2.2.0') { - exclude group:'com.github.bumptech.glide' + exclude group: 'com.github.bumptech.glide' } implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'jp.wasabeef:glide-transformations:4.0.1' @@ -59,6 +59,7 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation 'com.facebook.yoga.android:yoga-layout:1.16.0' } diff --git a/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java b/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java index 629fc5bf..df3a607f 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java @@ -49,6 +49,7 @@ import pub.doric.shader.SwitchNode; import pub.doric.shader.TextNode; import pub.doric.shader.VLayoutNode; import pub.doric.shader.ViewNode; +import pub.doric.shader.flex.FlexNode; import pub.doric.shader.flowlayout.FlowLayoutItemNode; import pub.doric.shader.flowlayout.FlowLayoutNode; import pub.doric.shader.list.ListItemNode; @@ -120,6 +121,7 @@ public class DoricRegistry { this.registerViewNode(NestedSliderNode.class); this.registerViewNode(DraggableNode.class); this.registerViewNode(SwitchNode.class); + this.registerViewNode(FlexNode.class); initRegistry(this); } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/flex/FlexNode.java b/doric-android/doric/src/main/java/pub/doric/shader/flex/FlexNode.java new file mode 100644 index 00000000..bd3ec8f8 --- /dev/null +++ b/doric-android/doric/src/main/java/pub/doric/shader/flex/FlexNode.java @@ -0,0 +1,54 @@ +/* + * 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. + */ +package pub.doric.shader.flex; + +import android.view.ViewGroup; + +import com.facebook.yoga.android.YogaLayout; +import com.github.pengfeizhou.jscore.JSObject; + +import pub.doric.DoricContext; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.shader.GroupNode; +import pub.doric.shader.ViewNode; + +/** + * @Description: FlexBox Node + * @Author: pengfei.zhou + * @CreateDate: 2020-04-09 + */ +@DoricPlugin(name = "FlexLayout") +public class FlexNode extends GroupNode { + public FlexNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + protected YogaLayout build() { + return new YogaLayout(getContext()); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new YogaLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT); + } + + @Override + protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) { + super.blendSubLayoutConfig(viewNode, jsObject); + } +} diff --git a/doric-iOS/Pod/Classes/DoricRegistry.m b/doric-iOS/Pod/Classes/DoricRegistry.m index cc926181..0da4645f 100644 --- a/doric-iOS/Pod/Classes/DoricRegistry.m +++ b/doric-iOS/Pod/Classes/DoricRegistry.m @@ -52,6 +52,7 @@ #import "DoricCoordinatorPlugin.h" #import "DoricSwitchNode.h" #import "DoricNotchPlugin.h" +#import "DoricFlexNode.h" @interface DoricLibraries : NSObject @property(nonatomic, strong) NSMutableSet *libraries; @@ -139,6 +140,7 @@ - (void)innerRegister { [self registerViewNode:DoricInputNode.class withName:@"Input"]; [self registerViewNode:DoricDraggableNode.class withName:@"Draggable"]; [self registerViewNode:DoricSwitchNode.class withName:@"Switch"]; + [self registerViewNode:DoricFlexNode.class withName:@"FlexLayout"]; } - (void)registerJSBundle:(NSString *)bundle withName:(NSString *)name { diff --git a/doric-iOS/Pod/Classes/Shader/DoricFlexNode.h b/doric-iOS/Pod/Classes/Shader/DoricFlexNode.h new file mode 100644 index 00000000..3e8d29c3 --- /dev/null +++ b/doric-iOS/Pod/Classes/Shader/DoricFlexNode.h @@ -0,0 +1,25 @@ +/* + * 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 2020/4/9. +// + +#import +#import "DoricGroupNode.h" + +@interface DoricFlexNode : DoricGroupNode +- (void)blendSubNode:(DoricViewNode *)subNode flexConfig:(NSDictionary *)flexConfig; +@end \ No newline at end of file diff --git a/doric-iOS/Pod/Classes/Shader/DoricFlexNode.m b/doric-iOS/Pod/Classes/Shader/DoricFlexNode.m new file mode 100644 index 00000000..a9a2cb24 --- /dev/null +++ b/doric-iOS/Pod/Classes/Shader/DoricFlexNode.m @@ -0,0 +1,119 @@ +/* + * 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 2020/4/9. +// + +#import +#import "DoricFlexNode.h" +#import "DoricExtensions.h" +#import "YGLayout.h" + +@interface DoricFlexView : UIView +@end + +@implementation DoricFlexView +- (CGSize)sizeThatFits:(CGSize)size { + return [self.yoga calculateLayoutWithSize:size]; +} +@end + +@implementation DoricFlexNode +- (UIView *)build { + return [[DoricFlexView new] also:^(DoricFlexView *it) { + it.yoga.isEnabled = YES; + }]; +} + +- (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop { + if ([name isEqualToString:@"flexConfig"]) { + if ([prop isKindOfClass:[NSDictionary class]]) { + [((DoricFlexNode *) self.superNode) blendSubNode:self flexConfig:prop]; + } + } else { + [super blendView:view forPropName:name propValue:prop]; + } +} + +- (void)blendSubNode:(DoricViewNode *)subNode flexConfig:(NSDictionary *)flexConfig { + +} + +- (void)blendYoga:(YGLayout *)yoga from:(NSDictionary *)flexConfig { + [flexConfig enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + [self blendYoga:yoga name:key value:obj]; + }]; +} + +- (void)blendYoga:(YGLayout *)yoga name:(NSString *)name value:(id)value { + if ([name isEqualToString:@"direction"]) { + yoga.direction = (YGDirection) [(NSNumber *) value integerValue]; + } else if ([name isEqualToString:@"flexDirection"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + } else if ([name isEqualToString:@"justifyContent"]) { + yoga.justifyContent = (YGJustify) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"alignContent"]) { + yoga.alignContent = (YGAlign) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + }else if ([name isEqualToString:@"justifyContent"]) { + yoga.flexDirection = (YGFlexDirection) [(NSNumber *) value integerValue]; + } + +} + + +- (void)requestLayout { + [super requestLayout]; + [self.view.yoga applyLayoutPreservingOrigin:NO]; +} +@end \ No newline at end of file diff --git a/doric-iOS/Pod/Classes/Shader/DoricViewNode.m b/doric-iOS/Pod/Classes/Shader/DoricViewNode.m index d715412a..b2616b55 100644 --- a/doric-iOS/Pod/Classes/Shader/DoricViewNode.m +++ b/doric-iOS/Pod/Classes/Shader/DoricViewNode.m @@ -29,6 +29,7 @@ #import "DoricSuperNode.h" #import "DoricExtensions.h" #import "DoricPromise.h" +#import "DoricFlexNode.h" void DoricAddEllipticArcPath(CGMutablePathRef path, CGPoint origin, @@ -264,6 +265,11 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop } else if ([name isEqualToString:@"hidden"]) { self.view.hidden = [prop boolValue]; self.view.doricLayout.disabled = [prop boolValue]; + } else if ([name isEqualToString:@"flexConfig"]) { + if ([prop isKindOfClass:[NSDictionary class]] + && [self.superNode isKindOfClass:[DoricFlexNode class]]) { + [((DoricFlexNode *) self.superNode) blendSubNode:self flexConfig:prop]; + } } else { DoricLog(@"Blend View error for View Type :%@, prop is %@", self.class, name); } diff --git a/doric-js/lib/src/widget/flexlayout.d.ts b/doric-js/lib/src/widget/flexlayout.d.ts new file mode 100644 index 00000000..0b06133e --- /dev/null +++ b/doric-js/lib/src/widget/flexlayout.d.ts @@ -0,0 +1,23 @@ +import { IView, Group } from "../ui/view"; +import { Modeling } from "../util/types"; +declare enum ValueType { + Point = 0, + Percent = 1, + Auto = 2 +} +export declare class FlexValue implements Modeling { + type: ValueType; + value: number; + static Auto: FlexValue; + static percent(v: number): FlexValue; + static point(v: number): FlexValue; + toModel(): { + type: ValueType; + value: number; + }; +} +export interface IFlex extends IView { +} +export declare class FlexLayout extends Group implements IFlex { +} +export {}; diff --git a/doric-js/lib/src/widget/flexlayout.js b/doric-js/lib/src/widget/flexlayout.js new file mode 100644 index 00000000..7244a879 --- /dev/null +++ b/doric-js/lib/src/widget/flexlayout.js @@ -0,0 +1,49 @@ +/* + * 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. + */ +import { Group } from "../ui/view"; +var ValueType; +(function (ValueType) { + ValueType[ValueType["Point"] = 0] = "Point"; + ValueType[ValueType["Percent"] = 1] = "Percent"; + ValueType[ValueType["Auto"] = 2] = "Auto"; +})(ValueType || (ValueType = {})); +export class FlexValue { + constructor() { + this.type = ValueType.Auto; + this.value = 0; + } + static percent(v) { + const ret = new FlexValue; + ret.type = ValueType.Percent; + ret.value = v; + return ret; + } + static point(v) { + const ret = new FlexValue; + ret.type = ValueType.Point; + ret.value = v; + return ret; + } + toModel() { + return { + type: this.type, + value: this.value, + }; + } +} +FlexValue.Auto = new FlexValue; +export class FlexLayout extends Group { +} diff --git a/doric-js/src/ui/view.es5.ts b/doric-js/src/ui/view.es5.ts index accec06b..9e5255c8 100644 --- a/doric-js/src/ui/view.es5.ts +++ b/doric-js/src/ui/view.es5.ts @@ -20,6 +20,7 @@ import { loge } from "../util/log"; import { BridgeContext } from "../runtime/global"; import { LayoutConfig } from '../util/layoutconfig' import { IAnimation } from "./animation"; +import { FlexConfig } from "../util/flexbox"; export function Property(target: View, propKey: string) { Object.defineProperty(target, propKey, { @@ -79,6 +80,11 @@ export interface IView { */ rotation?: number /**----------transform----------*/ + + /** + * Only affected when its superview or itself is FlexLayout. + */ + flexConfig?: FlexConfig } export type NativeViewModel = { @@ -343,6 +349,9 @@ export abstract class View implements Modeling, IView { rotation?: number /**----------transform----------*/ + @Property + flexConfig?: FlexConfig + doAnimation(context: BridgeContext, animation: IAnimation) { return this.nativeChannel(context, "doAnimation")(animation.toModel()).then((args) => { for (let key in args) { diff --git a/doric-js/src/ui/view.ts b/doric-js/src/ui/view.ts index 65578050..4b9ed82f 100644 --- a/doric-js/src/ui/view.ts +++ b/doric-js/src/ui/view.ts @@ -20,6 +20,7 @@ import { loge } from "../util/log"; import { BridgeContext } from "../runtime/global"; import { LayoutConfig } from '../util/layoutconfig' import { IAnimation } from "./animation"; +import { FlexConfig } from "../util/flexbox"; export function Property(target: Object, propKey: string) { Reflect.defineMetadata(propKey, true, target) @@ -68,6 +69,11 @@ export interface IView { */ rotation?: number /**----------transform----------*/ + + /** + * Only affected when its superview or itself is FlexLayout. + */ + flexConfig?: FlexConfig } export type NativeViewModel = { @@ -338,6 +344,9 @@ export abstract class View implements Modeling, IView { rotation?: number /**----------transform----------*/ + @Property + flexConfig?: FlexConfig + doAnimation(context: BridgeContext, animation: IAnimation) { return this.nativeChannel(context, "doAnimation")(animation.toModel()).then((args) => { for (let key in args) { diff --git a/doric-js/src/util/flexbox.ts b/doric-js/src/util/flexbox.ts new file mode 100644 index 00000000..b20fb2db --- /dev/null +++ b/doric-js/src/util/flexbox.ts @@ -0,0 +1,168 @@ +/* + * 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. + */ +import { Modeling } from "./types"; + +enum ValueType { + Point = 0, + Percent = 1, + Auto = 2, +} + +export class FlexValue implements Modeling { + + type = ValueType.Auto + value = 0 + + static Auto = new FlexValue + + static percent(v: number) { + const ret = new FlexValue + ret.type = ValueType.Percent + ret.value = v + return ret + } + + static point(v: number) { + const ret = new FlexValue + ret.type = ValueType.Point + ret.value = v + return ret + } + + toModel() { + return { + type: this.type, + value: this.value, + } + } +} + +export enum FlexDirection { + COLUMN = 0, + COLUMN_REVERSE = 1, + ROW = 2, + ROW_REVERSE = 3, +} + +export enum Align { + AUTO = 0, + FLEX_START = 1, + CENTER = 2, + FLEX_END = 3, + STRETCH = 4, + BASELINE = 5, + SPACE_BETWEEN = 6, + SPACE_AROUND = 7, +} + +export enum Justify { + FLEX_START = 0, + CENTER = 1, + FLEX_END = 2, + SPACE_BETWEEN = 3, + SPACE_AROUND = 4, + SPACE_EVENLY = 5, +} + +export enum Direction { + INHERIT = 0, + LTR = 1, + RTL = 2, +} + +export enum PositionType { + RELATIVE = 0, + ABSOLUTE = 1, +} +export enum Wrap { + NO_WRAP = 0, + WRAP = 1, + WRAP_REVERSE = 2, +} +export enum OverFlow { + VISIBLE = 0, + HIDDEN = 1, + SCROLL = 2, +} + +export enum Display { + FLEX = 0, + NONE = 1, +} + +export interface FlexConfig { + direction?: Direction + flexDirection?: FlexDirection + justifyContent?: Justify + alignContent?: Align + alignItems?: Align + alignSelf?: Align + positionType?: PositionType + flexWrap?: Wrap + overFlow?: OverFlow + display?: Display + flex?: number + flexGrow?: number + flexShrink?: number + flexBasis?: FlexValue + + marginLeft?: FlexValue + marginRight?: FlexValue + marginTop?: FlexValue + marginBottom?: FlexValue + marginStart?: FlexValue + marginEnd?: FlexValue + marginHorizontal?: FlexValue + marginVertical?: FlexValue + margin?: FlexValue + + paddingLeft?: FlexValue + paddingRight?: FlexValue + paddingTop?: FlexValue + paddingBottom?: FlexValue + paddingStart?: FlexValue + paddingEnd?: FlexValue + paddingHorizontal?: FlexValue + paddingVertical?: FlexValue + padding?: FlexValue + + + borderWidthLeft?: FlexValue + borderWidthRight?: FlexValue + borderWidthTop?: FlexValue + borderWidthBottom?: FlexValue + borderWidthStart?: FlexValue + borderWidthEnd?: FlexValue + borderWidth?: FlexValue + + left?: FlexValue + right?: FlexValue + top?: FlexValue + bottom?: FlexValue + start?: FlexValue + end?: FlexValue + + width?: FlexValue + height?: FlexValue + + minWidth?: FlexValue + minHeight?: FlexValue + + maxWidth?: FlexValue + maxHeight?: FlexValue + + aspectRatio?: number +} \ No newline at end of file diff --git a/doric-js/src/widget/layouts.ts b/doric-js/src/widget/layouts.ts index aa077b6e..6b301690 100644 --- a/doric-js/src/widget/layouts.ts +++ b/doric-js/src/widget/layouts.ts @@ -91,4 +91,21 @@ export function vlayout(views: View[], config?: IVLayout) { } } return ret +} + + + +export class FlexLayout extends Group { +} + +export function flexlayout(views: View[], config: IView) { + const ret = new FlexLayout + for (let v of views) { + ret.addChild(v) + } + if (config) { + for (let key in config) { + Reflect.set(ret, key, Reflect.get(config, key, config), ret) + } + } } \ No newline at end of file