diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/DoricRegistry.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/DoricRegistry.java index 9310c3d8..882efcab 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/DoricRegistry.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/DoricRegistry.java @@ -3,10 +3,12 @@ package com.github.penfeizhou.doric; import android.text.TextUtils; import com.github.penfeizhou.doric.plugin.ShaderPlugin; +import com.github.penfeizhou.doric.shader.HLayoutNode; import com.github.penfeizhou.doric.shader.ImageNode; import com.github.penfeizhou.doric.shader.RootNode; import com.github.penfeizhou.doric.shader.StackNode; import com.github.penfeizhou.doric.shader.TextNode; +import com.github.penfeizhou.doric.shader.VLayoutNode; import com.github.penfeizhou.doric.shader.ViewNode; import com.github.penfeizhou.doric.utils.DoricMetaInfo; import com.github.penfeizhou.doric.plugin.DoricJavaPlugin; @@ -47,6 +49,8 @@ public class DoricRegistry { this.registerViewNode(TextNode.class); this.registerViewNode(ImageNode.class); this.registerViewNode(StackNode.class); + this.registerViewNode(VLayoutNode.class); + this.registerViewNode(HLayoutNode.class); initRegistry(this); } diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/HLayoutNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/HLayoutNode.java new file mode 100644 index 00000000..b5ae2cbe --- /dev/null +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/HLayoutNode.java @@ -0,0 +1,26 @@ +package com.github.penfeizhou.doric.shader; + +import android.widget.LinearLayout; + +import com.github.penfeizhou.doric.DoricContext; +import com.github.penfeizhou.doric.extension.bridge.DoricPlugin; +import com.github.pengfeizhou.jscore.JSObject; + +/** + * @Description: com.github.penfeizhou.doric.shader + * @Author: pengfei.zhou + * @CreateDate: 2019-07-23 + */ +@DoricPlugin(name = "HLayout") +public class HLayoutNode extends LinearNode { + public HLayoutNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + public LinearLayout build(JSObject jsObject) { + LinearLayout linearLayout = super.build(jsObject); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + return linearLayout; + } +} diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java new file mode 100644 index 00000000..35fb7a77 --- /dev/null +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java @@ -0,0 +1,54 @@ +package com.github.penfeizhou.doric.shader; + +import android.graphics.drawable.ShapeDrawable; +import android.widget.LinearLayout; + +import com.github.penfeizhou.doric.DoricContext; +import com.github.penfeizhou.doric.utils.DoricUtils; +import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; + +/** + * @Description: com.github.penfeizhou.doric.shader + * @Author: pengfei.zhou + * @CreateDate: 2019-07-23 + */ +public class LinearNode extends GroupNode { + public LinearNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + public LinearLayout build(JSObject jsObject) { + return new LinearLayout(getContext()); + } + + @Override + protected void blend(LinearLayout view, String name, JSValue prop) { + switch (name) { + case "space": + ShapeDrawable shapeDrawable; + if (view.getDividerDrawable() == null) { + shapeDrawable = new ShapeDrawable(); + shapeDrawable.setAlpha(0); + view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE); + } else { + shapeDrawable = (ShapeDrawable) view.getDividerDrawable(); + view.setDividerDrawable(null); + } + if (view.getOrientation() == LinearLayout.VERTICAL) { + shapeDrawable.setIntrinsicHeight(DoricUtils.dp2px(prop.asNumber().toFloat())); + } else { + shapeDrawable.setIntrinsicWidth(DoricUtils.dp2px(prop.asNumber().toFloat())); + } + view.setDividerDrawable(shapeDrawable); + break; + case "gravity": + view.setGravity(prop.asNumber().toInt()); + break; + default: + super.blend(view, name, prop); + break; + } + } +} diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/StackNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/StackNode.java index 08a9592b..5d3274ab 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/StackNode.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/StackNode.java @@ -5,6 +5,7 @@ import android.widget.FrameLayout; import com.github.penfeizhou.doric.DoricContext; import com.github.penfeizhou.doric.extension.bridge.DoricPlugin; import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; /** * @Description: com.github.penfeizhou.doric.widget @@ -21,4 +22,15 @@ public class StackNode extends GroupNode { public FrameLayout build(JSObject jsObject) { return new FrameLayout(getContext()); } + + @Override + protected void blend(FrameLayout view, String name, JSValue prop) { + switch (name) { + case "gravity": + view.setForegroundGravity(prop.asNumber().toInt()); + break; + default: + super.blend(view, name, prop); + } + } } diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/VLayoutNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/VLayoutNode.java new file mode 100644 index 00000000..bb5d8704 --- /dev/null +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/VLayoutNode.java @@ -0,0 +1,27 @@ +package com.github.penfeizhou.doric.shader; + +import android.widget.LinearLayout; + +import com.github.penfeizhou.doric.DoricContext; +import com.github.penfeizhou.doric.extension.bridge.DoricPlugin; +import com.github.pengfeizhou.jscore.JSObject; + +/** + * @Description: com.github.penfeizhou.doric.shader + * @Author: pengfei.zhou + * @CreateDate: 2019-07-23 + */ +@DoricPlugin(name = "VLayout") +public class VLayoutNode extends LinearNode { + public VLayoutNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + public LinearLayout build(JSObject jsObject) { + LinearLayout linearLayout = super.build(jsObject); + linearLayout.setOrientation(LinearLayout.VERTICAL); + return linearLayout; + } + +} diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/ViewNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/ViewNode.java index 74d3b1c0..45904cb2 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/ViewNode.java @@ -58,10 +58,18 @@ public abstract class ViewNode extends DoricComponent { protected void blend(T view, String name, JSValue prop) { switch (name) { case "width": - view.getLayoutParams().width = DoricUtils.dp2px(prop.asNumber().toFloat()); + if (prop.asNumber().toInt() < 0) { + view.getLayoutParams().width = prop.asNumber().toInt(); + } else { + view.getLayoutParams().width = DoricUtils.dp2px(prop.asNumber().toFloat()); + } break; case "height": - view.getLayoutParams().height = DoricUtils.dp2px(prop.asNumber().toFloat()); + if (prop.asNumber().toInt() < 0) { + view.getLayoutParams().height = prop.asNumber().toInt(); + } else { + view.getLayoutParams().height = DoricUtils.dp2px(prop.asNumber().toFloat()); + } break; case "x": if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { diff --git a/js-framework/demo.ts b/js-framework/demo.ts index 9c6f7caa..d3255c76 100644 --- a/js-framework/demo.ts +++ b/js-framework/demo.ts @@ -1,35 +1,52 @@ -import { NativeCall, Text, Alignment, Color, VLayout, Panel, log, logw, loge } from "./index" -import { Group } from "./src/ui/view"; - +import { Gravity, Mutable, NativeCall, Text, Color, VLayout, Panel, log, logw, loge, Group, Stack, } from "./index" @Entry export class MyPage extends Panel { - state = { - count: 0 - } build(rootView: Group): void { + const state = Mutable.of(1) const numberView = new Text numberView.width = 100 numberView.height = 200 numberView.top = 50 - numberView.text = this.state.count.toString() + state.bind((v) => { + numberView.text = v.toString() + }) numberView.textSize = 40 numberView.centerX = rootView.width / 2 rootView.addChild(numberView) const click = new Text - click.width = click.height = 100 click.textSize = 20 click.text = '点击计数' click.onClick = () => { - this.state.count++ - numberView.text = this.state.count.toString() + state.set(state.get() + 1) } - click.centerX = rootView.width / 2 click.top = numberView.bottom + 20 rootView.addChild(click) - rootView.bgColor = Color.safeParse('#00ff00') + + const vlayout = new VLayout + vlayout.width = this.getRootView().width + vlayout.height = 500 + + vlayout.top = 50 + vlayout.centerX = this.getRootView().width / 2 + vlayout.space = 0 + vlayout.gravity = (new Gravity()).bottom() + const v = [1, 2, 3,].map(e => { + const stack = new Stack + stack.width = stack.height = 50 + stack.bgColor = Color.safeParse('#00ff00') + vlayout.addChild(stack) + stack.onClick = () => { + loge('stack:onClick') + if (vlayout.space !== undefined) { + loge('change space') + vlayout.space += 10 + } + } + }) + rootView.addChild(vlayout) } @NativeCall diff --git a/js-framework/index.ts b/js-framework/index.ts index b75927fd..d469f866 100644 --- a/js-framework/index.ts +++ b/js-framework/index.ts @@ -2,4 +2,6 @@ export * from "./src/ui/view" export * from "./src/ui/panel" export * from "./src/util/color" export * from './src/util/log' +export * from './src/util/types' +export * from './src/util/gravity' export * from './src/runtime/global' \ No newline at end of file diff --git a/js-framework/src/ui/state.ts b/js-framework/src/ui/state.ts index f2497413..3b5be2fd 100644 --- a/js-framework/src/ui/state.ts +++ b/js-framework/src/ui/state.ts @@ -5,6 +5,8 @@ function from(obj: Object) { } }) } + + class Wrapper { val: any constructor(val: any) { diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index 7019bb29..11e12799 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -1,18 +1,23 @@ import { Color, GradientColor } from "../util/color" import { Modeling, Model, obj2Model } from "../util/types"; import { uniqueId } from "../util/uniqueId"; +import { Gravity } from "../util/gravity"; import { loge } from "../util/log"; +export const MATCH_PARENT = -1 + +export const WRAP_CONTENT = -2 + export function Property(target: Object, propKey: string) { Reflect.defineMetadata(propKey, true, target) } export abstract class View implements Modeling { @Property - width: number = 0 + width: number = WRAP_CONTENT @Property - height: number = 0 + height: number = WRAP_CONTENT @Property x: number = 0 @@ -146,9 +151,11 @@ export abstract class View implements Modeling { } } } + isDirty() { return Reflect.ownKeys(this.__dirty_props__).length !== 0 } + responseCallback(id: string, ...args: any) { const f = this.id2Callback(id) if (f instanceof Function) { @@ -176,20 +183,9 @@ export abstract class View implements Modeling { @Property config?: Config -} -export enum Alignment { - center = 0, - start, - end, -} - -export enum Gravity { - center = 0, - left, - right, - top, - bottom, + @Property + onClick?: Function } export interface Config { @@ -199,14 +195,14 @@ export interface Config { top?: number, bottom?: number, } - alignment?: Alignment + alignment?: Gravity } export interface StackConfig extends Config { } -export interface LayoutConfig extends Config { +export interface LinearConfig extends Config { weight?: number } @@ -257,7 +253,7 @@ export abstract class Group extends View { export class Stack extends Group { @Property - gravity?: number + gravity?: Gravity } export class Root extends Stack { @@ -267,7 +263,7 @@ class LinearLayout extends Group { space?: number @Property - gravity?: number + gravity?: Gravity } export class VLayout extends LinearLayout { @@ -288,9 +284,6 @@ export class Text extends View { @Property maxLines?: number - - @Property - onClick?: Function } export class Image extends View { @@ -305,3 +298,24 @@ export class List extends View { 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 + } + providers.forEach(e => { + vlayout.addChild(e()) + }) + return vlayout +} diff --git a/js-framework/src/util/gravity.ts b/js-framework/src/util/gravity.ts new file mode 100644 index 00000000..78464b81 --- /dev/null +++ b/js-framework/src/util/gravity.ts @@ -0,0 +1,64 @@ +import { Modeling } from "./types"; + +const SPECIFIED = 1 +const START = 1 << 1 +const END = 1 << 2 + +const SHIFT_X = 0 +const SHIFT_Y = 4 + +export const LEFT = (START | SPECIFIED) << SHIFT_X +export const RIGHT = (END | SPECIFIED) << SHIFT_X + +export const TOP = (START | SPECIFIED) << SHIFT_Y +export const BOTTOM = (END | SPECIFIED) << SHIFT_Y + +export const CENTER_X = SPECIFIED << SHIFT_X +export const CENTER_Y = SPECIFIED << SHIFT_Y + +export const CENTER = CENTER_X | CENTER_Y + +export class Gravity implements Modeling { + val = 0 + + left() { + this.val |= LEFT + return this + } + + right() { + this.val |= RIGHT + return this + } + + top() { + this.val |= TOP + return this + } + + bottom() { + this.val |= BOTTOM + return this + } + + center() { + this.val |= CENTER + return this + } + + centerX() { + this.val |= CENTER_X + return this + } + + centerY() { + this.val |= CENTER_Y + return this + } + + + toModel() { + return this.val + } + +} \ No newline at end of file diff --git a/js-framework/src/util/types.ts b/js-framework/src/util/types.ts index ad81451e..b42a90bb 100644 --- a/js-framework/src/util/types.ts +++ b/js-framework/src/util/types.ts @@ -15,4 +15,36 @@ export function obj2Model(obj: Model): Model { } type _M = string | number | boolean | Modeling | { [index: string]: Model | undefined } -export type Model = _M | Array<_M> \ No newline at end of file +export type Model = _M | Array<_M> + +export type Binder = (v: T) => void + +export class Mutable{ + private val: T + + private binders: Set> = new Set + + get = () => { + return this.val + } + + set = (v: T) => { + this.val = v + this.binders.forEach(e => { + Reflect.apply(e, undefined, [this.val]) + }) + } + + private constructor(v: T) { + this.val = v + } + + bind(binder: Binder) { + this.binders.add(binder) + Reflect.apply(binder, undefined, [this.val]) + } + + static of(v: E) { + return new Mutable(v) + } +} \ No newline at end of file diff --git a/js-framework/src/util/uniqueId.ts b/js-framework/src/util/uniqueId.ts index 372510f9..ecf66b00 100644 --- a/js-framework/src/util/uniqueId.ts +++ b/js-framework/src/util/uniqueId.ts @@ -1,5 +1,4 @@ -let __uniqueId__: number = 0 - +let __uniqueId__ = 0 export function uniqueId(prefix: string) { return `__${prefix}_${__uniqueId__++}__`; } \ No newline at end of file