UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

331 lines • 12.6 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { serializable } from "../../engine/engine_serialization.js"; import { getParam } from "../../engine/engine_utils.js"; import { Behaviour, GameObject } from "../Component.js"; import { Canvas } from "./Canvas.js"; import { RectTransform } from "./RectTransform.js"; const debug = getParam("debuguilayout"); export class Padding { left = 0; right = 0; top = 0; bottom = 0; get vertical() { return this.top + this.bottom; } get horizontal() { return this.left + this.right; } } __decorate([ serializable() ], Padding.prototype, "left", void 0); __decorate([ serializable() ], Padding.prototype, "right", void 0); __decorate([ serializable() ], Padding.prototype, "top", void 0); __decorate([ serializable() ], Padding.prototype, "bottom", void 0); export var TextAnchor; (function (TextAnchor) { TextAnchor[TextAnchor["UpperLeft"] = 0] = "UpperLeft"; TextAnchor[TextAnchor["UpperCenter"] = 1] = "UpperCenter"; TextAnchor[TextAnchor["UpperRight"] = 2] = "UpperRight"; TextAnchor[TextAnchor["MiddleLeft"] = 3] = "MiddleLeft"; TextAnchor[TextAnchor["MiddleCenter"] = 4] = "MiddleCenter"; TextAnchor[TextAnchor["MiddleRight"] = 5] = "MiddleRight"; TextAnchor[TextAnchor["LowerLeft"] = 6] = "LowerLeft"; TextAnchor[TextAnchor["LowerCenter"] = 7] = "LowerCenter"; TextAnchor[TextAnchor["LowerRight"] = 8] = "LowerRight"; TextAnchor[TextAnchor["Custom"] = 9] = "Custom"; })(TextAnchor || (TextAnchor = {})); var Axis; (function (Axis) { Axis["Horizontal"] = "x"; Axis["Vertical"] = "y"; })(Axis || (Axis = {})); export class LayoutGroup extends Behaviour { _rectTransform = null; get rectTransform() { return this._rectTransform; } onParentRectTransformChanged(_comp) { this._needsUpdate = true; } _needsUpdate = false; get isDirty() { return this._needsUpdate; } get isLayoutGroup() { return true; } updateLayout() { if (!this._rectTransform) return; if (debug) console.warn("Layout Update", this.context.time.frame, this.name); this._needsUpdate = false; this.onCalculateLayout(this._rectTransform); } // onBeforeRender(): void { // this.updateLayout(); // } childAlignment = TextAnchor.UpperLeft; reverseArrangement = false; spacing = 0; padding; minWidth = 0; minHeight = 0; flexibleHeight = 0; flexibleWidth = 0; preferredHeight = 0; preferredWidth = 0; start() { this._needsUpdate = true; } onEnable() { if (debug) console.log(this.name, this); this._rectTransform = this.gameObject.getComponent(RectTransform); const canvas = this.gameObject.getComponentInParent(Canvas); if (canvas) { canvas.registerLayoutGroup(this); } this._needsUpdate = true; } onDisable() { const canvas = this.gameObject.getComponentInParent(Canvas); if (canvas) { canvas.unregisterLayoutGroup(this); } } // for animation: set m_Spacing(val) { if (val === this.spacing) return; this._needsUpdate = true; this.spacing = val; } get m_Spacing() { return this.spacing; } } __decorate([ serializable() ], LayoutGroup.prototype, "childAlignment", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "reverseArrangement", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "spacing", void 0); __decorate([ serializable(Padding) ], LayoutGroup.prototype, "padding", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "minWidth", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "minHeight", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "flexibleHeight", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "flexibleWidth", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "preferredHeight", void 0); __decorate([ serializable() ], LayoutGroup.prototype, "preferredWidth", void 0); export class HorizontalOrVerticalLayoutGroup extends LayoutGroup { childControlHeight = true; childControlWidth = true; childForceExpandHeight = false; childForceExpandWidth = false; childScaleHeight = false; childScaleWidth = false; onCalculateLayout(rect) { const axis = this.primaryAxis; const totalWidth = rect.width; let actualWidth = totalWidth; const totalHeight = rect.height; let actualHeight = totalHeight; actualWidth -= this.padding.horizontal; actualHeight -= this.padding.vertical; // if (rect.name === "Title") // console.log(rect.name, "width=" + totalWidth + ", height=" + totalHeight, rect.anchoredPosition.x) const paddingAxis = axis === Axis.Horizontal ? this.padding.horizontal : this.padding.vertical; const isHorizontal = axis === Axis.Horizontal; const isVertical = !isHorizontal; const otherAxis = isHorizontal ? "y" : "x"; const controlSize = isHorizontal ? this.childControlWidth : this.childControlHeight; const controlSizeOtherAxis = isHorizontal ? this.childControlHeight : this.childControlWidth; const forceExpandSize = isHorizontal ? this.childForceExpandWidth : this.childForceExpandHeight; const forceExpandSizeOtherAxis = isHorizontal ? this.childForceExpandHeight : this.childForceExpandWidth; const actualExpandSize = isHorizontal ? actualHeight : actualWidth; const totalSpace = isHorizontal ? totalWidth : totalHeight; // 0 is left/top, 0.5 is middle, 1 is right/bottom const alignmentOnAxis = 0.5 * (isHorizontal ? this.childAlignment % 3 : Math.floor(this.childAlignment / 3)); let start = 0; if (isHorizontal) { start += this.padding.left; } else start += this.padding.top; // Calculate total size of the elements let totalChildSize = 0; let actualRectTransformChildCount = 0; for (let i = 0; i < this.gameObject.children.length; i++) { const ch = this.gameObject.children[i]; const rt = GameObject.getComponent(ch, RectTransform); if (rt?.activeAndEnabled) { actualRectTransformChildCount += 1; if (isHorizontal) { totalChildSize += rt.width; } else { totalChildSize += rt.height; } } } let sizePerChild = 0; const totalSpacing = this.spacing * (actualRectTransformChildCount - 1); if (forceExpandSize || controlSize) { let size = 0; if (isHorizontal) { size = actualWidth -= totalSpacing; } else { size = actualHeight -= totalSpacing; } if (actualRectTransformChildCount > 0) sizePerChild = size / actualRectTransformChildCount; } let leftOffset = 0; leftOffset += this.padding.left; leftOffset -= this.padding.right; if (alignmentOnAxis !== 0) { start = totalSpace - totalChildSize; start *= alignmentOnAxis; start -= totalSpacing * alignmentOnAxis; if (isHorizontal) { start -= this.padding.right * alignmentOnAxis; start += this.padding.left * (1 - alignmentOnAxis); if (start < this.padding.left) { start = this.padding.left; } } else { start -= this.padding.bottom * alignmentOnAxis; start += this.padding.top * (1 - alignmentOnAxis); if (start < this.padding.top) { start = this.padding.top; } } } // Apply layout let k = 1; for (let i = 0; i < this.gameObject.children.length; i++) { const ch = this.gameObject.children[i]; const rt = GameObject.getComponent(ch, RectTransform); if (rt?.activeAndEnabled) { rt.pivot?.set(.5, .5); rt.anchorMin.set(0, 1); rt.anchorMax.set(0, 1); // Horizontal padding const x = totalWidth * .5 + leftOffset * .5; if (rt.anchoredPosition.x !== x) rt.anchoredPosition.x = x; const y = totalHeight * -.5; if (rt.anchoredPosition.y !== y) rt.anchoredPosition.y = y; // Set the size for the secondary axis (e.g. height for a horizontal layout group) if (forceExpandSizeOtherAxis && controlSizeOtherAxis && rt.sizeDelta[otherAxis] !== actualExpandSize) { rt.sizeDelta[otherAxis] = actualExpandSize; } // Set the size for the primary axis (e.g. width for a horizontal layout group) if (forceExpandSize && controlSize && rt.sizeDelta[axis] !== sizePerChild) { rt.sizeDelta[axis] = sizePerChild; } const size = isHorizontal ? rt.width : rt.height; const halfSize = size * .5; start += halfSize; // TODO: this isnt correct yet! if (forceExpandSize) { // this is the center of the cell const preferredStart = sizePerChild * k - sizePerChild * .5; if (preferredStart > start) { start = preferredStart - sizePerChild * .5 + size + this.padding.left; start -= halfSize; } } let value = start; if (axis === Axis.Vertical) value = -value; // Only set the position if it's not already the correct one to avoid triggering the rectTransform dirty event if (rt.anchoredPosition[axis] !== value) { rt.anchoredPosition[axis] = value; } start += halfSize; start += this.spacing; k += 1; } } } } __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childControlHeight", void 0); __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childControlWidth", void 0); __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childForceExpandHeight", void 0); __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childForceExpandWidth", void 0); __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childScaleHeight", void 0); __decorate([ serializable() ], HorizontalOrVerticalLayoutGroup.prototype, "childScaleWidth", void 0); /** * @category User Interface * @group Components */ export class VerticalLayoutGroup extends HorizontalOrVerticalLayoutGroup { get primaryAxis() { return Axis.Vertical; } } /** * @category User Interface * @group Components */ export class HorizontalLayoutGroup extends HorizontalOrVerticalLayoutGroup { get primaryAxis() { return Axis.Horizontal; } } /** * @category User Interface * @group Components */ export class GridLayoutGroup extends LayoutGroup { onCalculateLayout() { } } //# sourceMappingURL=Layout.js.map