diff --git a/src/ui/UIContext.ts b/src/ui/UIContext.ts index 36b18c9..b0b24be 100644 --- a/src/ui/UIContext.ts +++ b/src/ui/UIContext.ts @@ -5,6 +5,7 @@ import DrawLayerType from './DrawLayerType'; import FactoryRegistry from './components/FactoryRegistry'; import FontString from './components/simple/FontString'; import Frame from './components/simple/Frame'; +import LayoutFrame from './components/abstract/LayoutFrame'; import Renderer from './rendering/Renderer'; import UIRoot from './components/UIRoot'; import ScriptingContext from './scripting/ScriptingContext'; @@ -44,7 +45,7 @@ class UIContext { for (const { template } of templates) { // TODO: Does this bit require lock/release of templates? if (template && !template.locked) { - parentName = node.attributes.get('parent'); + parentName = template.node.attributes.get('parent'); } } } @@ -187,7 +188,7 @@ class UIContext { } } else { this.createFrame(child, null, status); - // TODO: Re-layout + LayoutFrame.resizePending(); } } } diff --git a/src/ui/components/UIRoot.ts b/src/ui/components/UIRoot.ts index 8f6e810..f381943 100644 --- a/src/ui/components/UIRoot.ts +++ b/src/ui/components/UIRoot.ts @@ -109,6 +109,17 @@ class UIRoot extends LayoutFrame { strata.batchDirty = 1; } + notifyFrameMovedOrResized(frame: Frame) { + const strata = this.strata[frame.strataType]; + strata.levelsDirty = 1; + + if (this.layout.frame) { + this.raiseFrame(this.layout.frame, false); + } + + // TODO: Focus check + } + onLayerUpdate(elapsedSecs: number) { // TODO: Clean-up destroyed frames diff --git a/src/ui/components/abstract/FramePoint.ts b/src/ui/components/abstract/FramePoint.ts index f1bc47b..38405fd 100644 --- a/src/ui/components/abstract/FramePoint.ts +++ b/src/ui/components/abstract/FramePoint.ts @@ -72,8 +72,18 @@ class FramePoint { return rect; } - setRelative(_relative: LayoutFrame, _relativePoint: Type, _offsetX: number, _offsetY: number) { - // TODO: Implement + markUnused() { + this.type = Type.TOPLEFT - 1; + this.offset.setElements(0.0, 0.0); + // this.relative = null; + this.flags = this.flags & 0x2 ? 0x2 | 0x4 | 0x8 : 0x8; + } + + setRelative(relative: LayoutFrame, relativePoint: Type, offsetX: number, offsetY: number) { + this.type = relativePoint; + this.offset.setElements(offsetX, offsetY); + this.relative = relative; + this.flags = this.flags & 0x2 ? 0x2 | 0x4 : 0x0; } x(scale: number) { diff --git a/src/ui/components/abstract/LayoutFrame.ts b/src/ui/components/abstract/LayoutFrame.ts index 8038222..b497004 100644 --- a/src/ui/components/abstract/LayoutFrame.ts +++ b/src/ui/components/abstract/LayoutFrame.ts @@ -1,4 +1,3 @@ -import ScriptRegion from './ScriptRegion'; import XMLNode from '../../XMLNode'; import { stringToFramePointType } from '../../utils'; import { @@ -81,7 +80,7 @@ class LayoutFrame { }; this.resizeList = LinkedList.using('link'); - this.resizeCounter = NaN; + this.resizeCounter = 0; this.points = [ null, null, null, @@ -329,10 +328,21 @@ class LayoutFrame { return true; } + clearAllPoints() { + this.freePoints(); + } + freePoints() { + let i = 0; for (const point of this.points) { - // TODO: Implementation - console.error('freeing point', point); + if (point && !(point.flags & 0x8)) { + if (point.relative) { + point.relative.unregisterResize(this, 1 << i); + } + + point.markUnused(); + } + ++i; } } @@ -357,6 +367,10 @@ class LayoutFrame { return FramePoint.UNDEFINED; } + getLayoutFrameByName(_name: string): LayoutFrame | null { + return null; + } + getRect() { if (!(this.layoutFlags & 0x1)) { return undefined; @@ -367,6 +381,11 @@ class LayoutFrame { return rect; } + isResizeDependency(_dependentFrame: LayoutFrame) { + // TODO: Implementation + return false; + } + loadXML(node: XMLNode, status: Status) { const size = node.getChildByName('Size'); if (size) { @@ -413,8 +432,7 @@ class LayoutFrame { let relative = layoutParent; if (relativeValue) { - const fqname = this.fullyQualifyName(relativeValue)!; - relative = ScriptRegion.getObjectByName(fqname); + relative = this.getLayoutFrameByName(relativeValue); if (!relative) { status.warning(`could not find relative frame: ${relativeValue}`); continue; @@ -554,6 +572,10 @@ class LayoutFrame { } } + setLayoutScale(_scale: number, _force: boolean) { + // TODO: Implementation + } + setPoint(pointType: FramePointType, relative: LayoutFrame | null, relativePointType: FramePointType, offsetX = 0, offsetY = 0, resize = false) { if (!relative || !relative.canBeAnchorFor(this)) { return; diff --git a/src/ui/components/abstract/ScriptObject.ts b/src/ui/components/abstract/ScriptObject.ts index f768db8..3432534 100644 --- a/src/ui/components/abstract/ScriptObject.ts +++ b/src/ui/components/abstract/ScriptObject.ts @@ -17,10 +17,6 @@ class ScriptObject extends FrameScriptObject { this._name = null; } - get displayName() { - return this.name || ''; - } - get name() { return this._name; } diff --git a/src/ui/components/abstract/ScriptRegion.ts b/src/ui/components/abstract/ScriptRegion.ts index 58fa87a..c0988f5 100644 --- a/src/ui/components/abstract/ScriptRegion.ts +++ b/src/ui/components/abstract/ScriptRegion.ts @@ -34,10 +34,15 @@ class ScriptRegion extends multipleClasses(ScriptObject, LayoutFrame) { } get layoutParent(): LayoutFrame { - if (this.width === 0.0 || !this.parent) { + if (!this._parent || this._parent.layoutScale === 0.0) { return UIRoot.instance; } - return this.parent; + return this._parent; + } + + getLayoutFrameByName(name: string): LayoutFrame | null { + const fqname = this.fullyQualifyName(name); + return ScriptRegion.getObjectByName(fqname); } loadXML(node: XMLNode, status: Status) { diff --git a/src/ui/components/simple/Frame.ts b/src/ui/components/simple/Frame.ts index bd95463..85d7a9d 100644 --- a/src/ui/components/simple/Frame.ts +++ b/src/ui/components/simple/Frame.ts @@ -16,7 +16,7 @@ import { Status, stringToBoolean, } from '../../../utils'; -import { Rect } from '../../../math'; +import { EPSILON1, Rect, areClose } from '../../../math'; import { stringToDrawLayerType, stringToFrameStrataType, @@ -53,6 +53,7 @@ class Frame extends ScriptRegion { visible: boolean; strataType: FrameStrataType; level: number; + frameScale: number; layersEnabled: EnumRecord; backdrop: Backdrop | null; @@ -77,6 +78,7 @@ class Frame extends ScriptRegion { this.visible = false; this.strataType = FrameStrataType.MEDIUM; this.level = 0; + this.frameScale = 1.0; this.layersEnabled = [ true, @@ -210,11 +212,13 @@ class Frame extends ScriptRegion { if (parent) { this.setFrameStrataType(parent.strataType); this.setFrameLevel(parent.level + 1, true); + this.updateScale(false); // TODO: Alpha and scrolling adjustments } else { this.setFrameStrataType(FrameStrataType.MEDIUM); this.setFrameLevel(0, true); + this.updateScale(false); // TODO: Alpha and scrolling adjustments } @@ -222,7 +226,7 @@ class Frame extends ScriptRegion { if (parent) { // TODO: Parent attachment protection const node = new FrameNode(this); - parent.children.linkToHead(node); + parent.children.linkToTail(node); } if (this.shown) { @@ -482,7 +486,7 @@ class Frame extends ScriptRegion { return false; } - if (this.parent && !this.parent.visible) { + if (this._parent && !this._parent.visible) { return false; } @@ -604,11 +608,23 @@ class Frame extends ScriptRegion { // TODO: Set hit rect - if (this.backdrop) { - this.backdrop.update(this.rect); + if (!areClose(rect.minX, this.rect.maxX) || !areClose(rect.minY, this.rect.maxY)) { + if (this.backdrop) { + this.backdrop.update(this.rect); + } + + const ratio = 1.0 / this.layoutScale; + const width = ratio * (this.rect.maxX - this.rect.minX); + const height = ratio * (this.rect.maxY - this.rect.minY); + + this.onFrameSizeChangedRange(width, height); } - // TODO: Remaining implementation + UIRoot.instance.notifyFrameMovedOrResized(this); + } + + onFrameSizeChangedRange(_width: number, _height: number) { + // TODO } onLayerShow() { @@ -648,6 +664,29 @@ class Frame extends ScriptRegion { this.runScript('OnShow'); } } + + updateScale(force: boolean) { + let scale = this.frameScale; + if (this.parent) { + scale *= this.parent.layoutScale; + } + + if ((!force && areClose(scale, this.layoutScale, EPSILON1)) || scale === 0.0) { + return false; + } + + this.setLayoutScale(scale, force); + + for (const region of this.regions) { + region.setLayoutScale(scale, force); + } + + for (const child of this.children) { + child.frame.updateScale(false); + } + + return true; + } } export default Frame; diff --git a/src/ui/components/simple/Texture.ts b/src/ui/components/simple/Texture.ts index 9359763..2cc4b10 100644 --- a/src/ui/components/simple/Texture.ts +++ b/src/ui/components/simple/Texture.ts @@ -73,9 +73,9 @@ class Texture extends Region { } get width() { - const layoutwidth = super.width; - if (layoutwidth !== 0.0) { - return layoutwidth; + const layoutWidth = super.width; + if (layoutWidth !== 0.0) { + return layoutWidth; } if (this.texture && this.texture.isLoaded) { @@ -253,7 +253,20 @@ class Texture extends Region { } postLoadXML(_node: XMLNode) { - // TODO + if (this._parent) { + let i = 0; + for (const point of this.points) { + if (point && !(point.flags & 0x8)) { + break; + } + + if (i + 1 === this.points.length) { + this.setAllPoints(this._parent, true); + break; + } + ++i; + } + } } onFrameSizeChanged(rect: Rect) { diff --git a/src/ui/scripting/FrameScriptObject.ts b/src/ui/scripting/FrameScriptObject.ts index 49aca39..2fae107 100644 --- a/src/ui/scripting/FrameScriptObject.ts +++ b/src/ui/scripting/FrameScriptObject.ts @@ -41,10 +41,18 @@ class FrameScriptObject { ); } + get displayName() { + return this.name || ''; + } + get isLuaRegistered() { return this.luaRef !== null; } + get name(): string | null { + return null; + } + register(name: string | null = null) { const L = ScriptingContext.instance.state;