@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
JavaScript
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