diff --git a/dist/Counter.js b/dist/Counter.js index 52a1c4f0..96c2f23c 100644 --- a/dist/Counter.js +++ b/dist/Counter.js @@ -21,6 +21,9 @@ class CounterView extends doric.ViewHolder { child1.left = 100 child1.backgroundColor = doric.Color.RED; child1.padding = { left: 50, top: 50 } + child1.layoutConfig = { + alignment: doric.Gravity.Center + } const grandson = new doric.Stack; grandson.width = 100; grandson.height = 100; diff --git a/src/DoricContext.ts b/src/DoricContext.ts index 42f2e018..fb475006 100644 --- a/src/DoricContext.ts +++ b/src/DoricContext.ts @@ -2,7 +2,7 @@ import { jsObtainContext, jsCallEntityMethod } from 'doric/src/runtime/sandbox' import { Panel } from 'doric' import { DoricPlugin } from "./DoricPlugin" import { createContext } from "./DoricDriver" -import { DoricStackViewNode } from './shader/DoricStackViewNode' +import { DoricStackNode } from './shader/DoricStackNode' const doricContexts: Map = new Map let __contextId__ = 0 @@ -17,11 +17,11 @@ export function getDoricContext(contextId: string) { export class DoricContext { contextId = getContextId() pluginInstances: Map = new Map - rootNode: DoricStackViewNode + rootNode: DoricStackNode constructor(content: string) { createContext(this.contextId, content) doricContexts.set(this.contextId, this) - this.rootNode = new DoricStackViewNode(this) + this.rootNode = new DoricStackNode(this) } get panel() { diff --git a/src/DoricRegistry.ts b/src/DoricRegistry.ts index 2780b1a6..c435cac2 100644 --- a/src/DoricRegistry.ts +++ b/src/DoricRegistry.ts @@ -1,8 +1,9 @@ import { DoricPluginClass } from "./DoricPlugin" import { ShaderPlugin } from "./plugins/ShaderPlugin" import { DoricViewNodeClass } from "./shader/DoricViewNode" -import { DoricStackViewNode } from "./shader/DoricStackViewNode" - +import { DoricStackNode } from "./shader/DoricStackNode" +import { DoricVLayoutNode } from './shader/DoricVLayoutNode' +import { DoricHLayoutNode } from './shader/DoricHLayoutNode' const bundles: Map = new Map @@ -37,4 +38,6 @@ export function acquireViewNode(name: string) { registerPlugin('shader', ShaderPlugin) -registerViewNode('Stack', DoricStackViewNode) \ No newline at end of file +registerViewNode('Stack', DoricStackNode) +registerViewNode('VLayout', DoricVLayoutNode) +registerViewNode('HLayout', DoricHLayoutNode) \ No newline at end of file diff --git a/src/shader/DoricHLayoutNode.ts b/src/shader/DoricHLayoutNode.ts new file mode 100644 index 00000000..1f78ea1c --- /dev/null +++ b/src/shader/DoricHLayoutNode.ts @@ -0,0 +1,122 @@ +import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode"; + +export class DoricHLayoutNode extends DoricGroupViewNode { + space = 0 + gravity = 0 + contentSize = { + width: 0, + height: 0, + weight: 0, + } + build() { + return document.createElement('div') + } + blendProps(v: HTMLElement, propName: string, prop: any) { + if (propName === 'space') { + this.space = prop + } else if (propName === 'gravity') { + this.gravity = prop + } else { + super.blendProps(v, propName, prop) + } + } + blend(props: { [index: string]: any }) { + super.blend(props) + this.childNodes.forEach(e => { + e.view.style.position = "absolute" + }) + } + + measureContentSize(targetSize: { width: number, height: number }) { + let width = this.frameWidth + let height = this.frameHeight + let contentSize = { width: 0, height: 0, weight: 0 } + let limitSize = { + width: targetSize.width - this.paddingLeft - this.paddingRight, + height: targetSize.height - this.paddingTop - this.paddingBottom, + } + contentSize = this.childNodes.reduce((prev, current) => { + const size = current.measureContentSize(limitSize) + return { + width: prev.width + size.width + this.space + + current.layoutConfig?.margin?.left || 0 + + current.layoutConfig?.margin?.right || 0, + height: Math.max(prev.height, size.height), + weight: prev.weight + current.layoutConfig?.weight || 0 + } + }, contentSize) + contentSize.width -= this.space + switch (this.layoutConfig.widthSpec) { + case LayoutSpec.AT_MOST: + width = targetSize.width + break + case LayoutSpec.WRAP_CONTENT: + width = contentSize.width + break + default: + break + } + switch (this.layoutConfig.heightSpec) { + case LayoutSpec.AT_MOST: + height = targetSize.height + break + case LayoutSpec.WRAP_CONTENT: + height = contentSize.height + break + default: + break + } + if (contentSize.weight > 0) { + contentSize.width = targetSize.width + } + this.contentSize = contentSize + return { width, height } + } + + layoutSelf(targetSize: FrameSize) { + const { width, height } = this.measureContentSize(targetSize) + this.width = width + this.height = height + let xStart = this.paddingLeft; + if ((this.gravity & LEFT) == LEFT) { + xStart = this.paddingLeft + } else if ((this.gravity & RIGHT) == RIGHT) { + xStart = targetSize.width - this.contentSize.width - this.paddingRight; + } else if ((this.gravity & CENTER_X) == CENTER_X) { + xStart = (targetSize.width - this.contentSize.width - this.paddingLeft - this.paddingRight) / 2 + this.paddingLeft + } + let remain = targetSize.width - this.contentSize.width - this.paddingLeft - this.paddingRight + this.childNodes.forEach(e => { + const childTargetSize = { + width: width - xStart - this.paddingRight, + height: height - this.paddingTop - this.paddingBottom, + } + if (e.layoutConfig?.weight > 0) { + childTargetSize.width += remain / this.contentSize.weight * e.layoutConfig.weight + } + e.layoutSelf(childTargetSize) + let gravity = e.layoutConfig?.alignment | this.gravity + if ((gravity & TOP) === TOP) { + e.y = 0 + } else if ((gravity & BOTTOM) === BOTTOM) { + e.y = height - e.height + this.paddingTop - this.paddingBottom + } else if ((gravity & CENTER_Y) === CENTER_Y) { + e.x = height / 2 - e.height / 2 - this.paddingTop + } else { + if (e.layoutConfig.margin?.left) { + e.x = e.layoutConfig.margin?.left + } else if (e.layoutConfig.margin?.right) { + e.x = width - e.width + this.paddingLeft - this.paddingRight - e.layoutConfig.margin?.right + } + } + if (e.layoutConfig.margin?.left !== undefined) { + xStart += e.layoutConfig.margin.left + } + e.x = xStart - this.paddingLeft + xStart += e.width + this.space + if (e.layoutConfig.margin?.right !== undefined) { + xStart += e.layoutConfig.margin.right + } + }) + } +} \ No newline at end of file diff --git a/src/shader/DoricStackViewNode.ts b/src/shader/DoricStackNode.ts similarity index 98% rename from src/shader/DoricStackViewNode.ts rename to src/shader/DoricStackNode.ts index 8768929a..5a8c674a 100644 --- a/src/shader/DoricStackViewNode.ts +++ b/src/shader/DoricStackNode.ts @@ -1,6 +1,6 @@ import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode"; -export class DoricStackViewNode extends DoricGroupViewNode { +export class DoricStackNode extends DoricGroupViewNode { build() { return document.createElement('div') diff --git a/src/shader/DoricVLayoutNode.ts b/src/shader/DoricVLayoutNode.ts new file mode 100644 index 00000000..7a1a44e8 --- /dev/null +++ b/src/shader/DoricVLayoutNode.ts @@ -0,0 +1,122 @@ +import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode"; + +export class DoricVLayoutNode extends DoricGroupViewNode { + space = 0 + gravity = 0 + contentSize = { + width: 0, + height: 0, + weight: 0, + } + build() { + return document.createElement('div') + } + blendProps(v: HTMLElement, propName: string, prop: any) { + if (propName === 'space') { + this.space = prop + } else if (propName === 'gravity') { + this.gravity = prop + } else { + super.blendProps(v, propName, prop) + } + } + blend(props: { [index: string]: any }) { + super.blend(props) + this.childNodes.forEach(e => { + e.view.style.position = "absolute" + }) + } + + measureContentSize(targetSize: { width: number, height: number }) { + let width = this.frameWidth + let height = this.frameHeight + let contentSize = { width: 0, height: 0, weight: 0 } + let limitSize = { + width: targetSize.width - this.paddingLeft - this.paddingRight, + height: targetSize.height - this.paddingTop - this.paddingBottom, + } + contentSize = this.childNodes.reduce((prev, current) => { + const size = current.measureContentSize(limitSize) + return { + width: Math.max(prev.width, size.width), + height: prev.height + size.height + this.space + + current.layoutConfig?.margin?.top || 0 + + current.layoutConfig?.margin?.bottom || 0, + weight: prev.weight + current.layoutConfig?.weight || 0 + } + }, contentSize) + contentSize.height -= this.space + switch (this.layoutConfig.widthSpec) { + case LayoutSpec.AT_MOST: + width = targetSize.width + break + case LayoutSpec.WRAP_CONTENT: + width = contentSize.width + break + default: + break + } + switch (this.layoutConfig.heightSpec) { + case LayoutSpec.AT_MOST: + height = targetSize.height + break + case LayoutSpec.WRAP_CONTENT: + height = contentSize.height + break + default: + break + } + if (contentSize.weight > 0) { + contentSize.height = targetSize.height + } + this.contentSize = contentSize + return { width, height } + } + + layoutSelf(targetSize: FrameSize) { + const { width, height } = this.measureContentSize(targetSize) + this.width = width + this.height = height + let yStart = this.paddingTop; + if ((this.gravity & TOP) == TOP) { + yStart = this.paddingTop + } else if ((this.gravity & BOTTOM) == BOTTOM) { + yStart = targetSize.height - this.contentSize.height - this.paddingBottom; + } else if ((this.gravity & CENTER_Y) == CENTER_Y) { + yStart = (targetSize.height - this.contentSize.height - this.paddingTop - this.paddingBottom) / 2 + this.paddingTop + } + let remain = targetSize.height - this.contentSize.height - this.paddingTop - this.paddingBottom + this.childNodes.forEach(e => { + const childTargetSize = { + width: width - this.paddingLeft - this.paddingRight, + height: height - yStart - this.paddingBottom, + } + if (e.layoutConfig?.weight > 0) { + childTargetSize.height += remain / this.contentSize.weight * e.layoutConfig.weight + } + e.layoutSelf(childTargetSize) + let gravity = e.layoutConfig?.alignment | this.gravity + if ((gravity & LEFT) === LEFT) { + e.x = 0 + } else if ((gravity & RIGHT) === RIGHT) { + e.x = width - e.width + this.paddingLeft - this.paddingRight + } else if ((gravity & CENTER_X) === CENTER_X) { + e.x = width / 2 - e.width / 2 - this.paddingLeft + } else { + if (e.layoutConfig.margin?.left) { + e.x = e.layoutConfig.margin?.left + } else if (e.layoutConfig.margin?.right) { + e.x = width - e.width + this.paddingLeft - this.paddingRight - e.layoutConfig.margin?.right + } + } + if (e.layoutConfig.margin?.top !== undefined) { + yStart += e.layoutConfig.margin.top + } + e.y = yStart - this.paddingTop + yStart += e.width + this.space + if (e.layoutConfig.margin?.bottom !== undefined) { + yStart += e.layoutConfig.margin.bottom + } + }) + } +} \ No newline at end of file