Start typechecking more runtime objects

This commit is contained in:
Jordan Eldredge 2019-09-19 07:03:31 -07:00
parent 7652381d46
commit 2d7db715c2
8 changed files with 100 additions and 76 deletions

View file

@ -9,9 +9,9 @@ class AnimatedLayer extends Layer {
constructor(node, parent, annotations, store) {
super(node, parent, annotations, store);
this._setAttributeDefaults(this.attributes);
this._convertAttributeTypes(this.attributes);
this._initializeStartAndEnd(this.attributes);
this._setAttributeDefaults();
this._convertAttributeTypes();
this._initializeStartAndEnd();
this._playing = this.attributes.autoplay;
this._frameNum = this.attributes.start || 0;
@ -20,7 +20,8 @@ class AnimatedLayer extends Layer {
this._setupAnimationLoop();
}
_setAttributeDefaults(attributes: Object): void {
_setAttributeDefaults(): void {
const { attributes } = this;
if (attributes.autoplay == null) {
attributes.autoplay = "0";
}
@ -32,7 +33,8 @@ class AnimatedLayer extends Layer {
}
}
_convertAttributeTypes(attributes: Object): void {
_convertAttributeTypes(): void {
const { attributes } = this;
if (attributes.autoplay != null) {
attributes.autoplay = !!Number(attributes.autoplay);
}
@ -50,7 +52,8 @@ class AnimatedLayer extends Layer {
}
}
_initializeStartAndEnd(attributes: Object): void {
_initializeStartAndEnd(): void {
const { attributes } = this;
if (attributes.start != null && attributes.end != null) {
return;
}

View file

@ -27,7 +27,7 @@ class Group extends GuiObject {
return;
}
oncreateobject(newobj): void {
oncreateobject(newobj: GuiObject): void {
this.js_trigger("onCreateObject", newobj);
}

View file

@ -4,45 +4,71 @@ import {
findParentNodeOfType,
unimplementedWarning,
} from "../utils";
import * as MakiSelectors from "../MakiSelectors";
import { ModernStore, XmlNode } from "../types";
type TargetParams = {
alpha?: number;
x?: number;
y?: number;
w?: number;
h?: number;
};
type TransitionParams = {
alpha?: { start: number; end: number };
x?: { start: number; end: number };
y?: { start: number; end: number };
w?: { start: number; end: number };
h?: { start: number; end: number };
};
type AnimationPropKeys = "alpha" | "x" | "y" | "w" | "h";
const ANIMATION_PROP_KEYS: AnimationPropKeys[] = ["alpha", "x", "y", "w", "h"];
class GuiObject extends MakiObject {
visible: boolean;
_targetAnimationSpeed: number;
_startParams: Object;
_targetParams: Object;
_transitionParams: Object;
_targetParams: TargetParams;
_transitionParams: TransitionParams;
_targetAnimationStartTime: number;
_targetAnimationCancelID: number;
constructor(node, parent, annotations, store) {
_targetAnimationCancelID: number | null;
constructor(
node: XmlNode,
parent: MakiObject,
annotations: Object = {},
store: ModernStore
) {
super(node, parent, annotations, store);
this._setAttributeDefaults(this.attributes);
this._convertAttributeTypes(this.attributes);
this._setAttributeDefaults();
this._convertAttributeTypes();
this.visible = true;
this._startParams = {};
this._targetAnimationSpeed = 0;
this._targetParams = {};
this._transitionParams = {};
this._selectorCache = new Map();
this._targetAnimationStartTime = 0;
this._targetAnimationCancelID = null;
}
_setAttributeDefaults(attributes) {
if (attributes.alpha == null) {
attributes.alpha = "255";
_setAttributeDefaults() {
if (this.attributes.alpha == null) {
this.attributes.alpha = "255";
}
if (attributes.x == null) {
attributes.x = "0";
if (this.attributes.x == null) {
this.attributes.x = "0";
}
if (attributes.y == null) {
attributes.y = "0";
if (this.attributes.y == null) {
this.attributes.y = "0";
}
if (attributes.ghost == null) {
attributes.ghost = "0";
if (this.attributes.ghost == null) {
this.attributes.ghost = "0";
}
}
_convertAttributeTypes(attributes) {
_convertAttributeTypes() {
const { attributes } = this;
if (attributes.alpha != null) {
attributes.alpha = Number(attributes.alpha);
}
@ -57,24 +83,6 @@ class GuiObject extends MakiObject {
}
}
_useUidSelector(selector) {
// TODO: use memoize for this
if (!this._selectorCache.has(selector)) {
this._selectorCache.set(selector, selector(this._uid));
}
return this._selectorCache.get(selector)(this._store.getState());
}
_compareToUidSelector(value, selector) {
const selectorValue = this._useUidSelector(selector);
if (selectorValue !== value) {
console.error(
`Maki state ${value} is out of sync with tree state ${selectorValue}`
);
}
return value;
}
/**
* getclassname()
*
@ -86,10 +94,11 @@ class GuiObject extends MakiObject {
}
findobject(id: string) {
return findDescendantByTypeAndId(this, null, id);
const self: MakiObject = this;
return findDescendantByTypeAndId(self, null, id);
}
init(newRoot): void {
init(newRoot: MakiObject): void {
this.parent = newRoot;
newRoot.js_addChild(this);
}
@ -107,8 +116,10 @@ class GuiObject extends MakiObject {
return this.parent;
}
getparentlayout() {
return findParentNodeOfType(this, new Set(["layout"]));
// TODO: Make this return a `Layout` once `Layout` is typed.
getparentlayout(): MakiObject | null {
const self: MakiObject = this;
return findParentNodeOfType(self, new Set(["layout"]));
}
show(): void {
@ -122,10 +133,7 @@ class GuiObject extends MakiObject {
}
gettop(): number {
return this._compareToUidSelector(
Number(this.attributes.y) || 0,
MakiSelectors.getTop
);
return Number(this.attributes.y) || 0;
}
getleft(): number {
@ -270,17 +278,17 @@ class GuiObject extends MakiObject {
(currentTime - this._targetAnimationStartTime) /
this._targetAnimationSpeed;
if (progress > 1) {
this._startParams = {};
this._targetParams = {};
this.ontargetreached();
return;
}
["alpha", "x", "y", "w", "h"].forEach(attr => {
if (this._transitionParams[attr]) {
this.attributes[attr] =
this._targetParams[attr] * progress +
this._startParams[attr] * (1 - progress);
ANIMATION_PROP_KEYS.forEach(attr => {
const transition = this._transitionParams[attr];
if (transition == null) {
return;
}
const { start, end } = transition;
this.attributes[attr] = end * progress + start * (1 - progress);
});
this.js_trigger("js_update");
this._targetAnimationLoop();
@ -290,14 +298,18 @@ class GuiObject extends MakiObject {
gototarget(): void {
this._transitionParams = {};
["alpha", "x", "y", "w", "h"].forEach(attr => {
if (this._targetParams[attr] != null) {
this._transitionParams[attr] = true;
this._startParams[attr] =
this.attributes[attr] != null
? this.attributes[attr]
: this._targetParams[attr];
ANIMATION_PROP_KEYS.forEach(attr => {
const target = this._targetParams[attr];
if (target == null) {
return;
}
const attribute = this.attributes[attr];
this._transitionParams[attr] = {
start: attribute != null ? Number(attribute) : target,
end: target,
};
});
this._targetAnimationStartTime = window.performance.now();
@ -309,9 +321,10 @@ class GuiObject extends MakiObject {
}
canceltarget(): void {
window.cancelAnimationFrame(this._targetAnimationCancelID);
if (this._targetAnimationCancelID != null) {
window.cancelAnimationFrame(this._targetAnimationCancelID);
}
this._targetAnimationCancelID = null;
this._startParams = {};
this._targetParams = {};
}
@ -339,12 +352,12 @@ class GuiObject extends MakiObject {
return;
}
bringabove(guiobj) {
bringabove(guiobj: GuiObject) {
unimplementedWarning("bringabove");
return;
}
bringbelow(guiobj) {
bringbelow(guiobj: GuiObject) {
unimplementedWarning("bringbelow");
return;
}
@ -529,7 +542,7 @@ class GuiObject extends MakiObject {
y: number,
p1: number,
p2: number,
source
source: GuiObject
): number {
unimplementedWarning("onaction");
this.js_trigger("onAction", action, param, x, y, p1, p2, source);

View file

@ -1,5 +1,6 @@
import Group from "./Group";
import { findParentNodeOfType, unimplementedWarning } from "../utils";
import MakiObject from "./MakiObject";
class Layout extends Group {
/**
@ -13,7 +14,8 @@ class Layout extends Group {
}
getcontainer() {
return findParentNodeOfType(this, new Set(["container"]));
const self: MakiObject = this;
return findParentNodeOfType(self, new Set(["container"]));
}
ondock(): void {

View file

@ -7,7 +7,10 @@ class MakiObject {
name: string;
_uid: number;
_store: ModernStore;
attributes: Object;
// TODO: This should really just be `string | undefined` and we should handle
// type conversion differently. Having one type that holds both the pre and
// post type coerced values is too confusing.
attributes: { [key: string]: string | number | boolean | undefined };
parent: MakiObject;
_emitter: Emitter;
children: MakiObject[];

View file

@ -3,7 +3,7 @@ export type MakiTree = any;
export type XmlNode = {
children: XmlNode[];
attributes: { id?: string; file?: string };
attributes: { [key: string]: string | undefined };
name: string;
uid: number; // TODO: They don't start with these. Do we need types for each stage?
};

View file

@ -243,7 +243,7 @@ export function findParentOrCurrentNodeOfType<
export function findDescendantByTypeAndId<
T extends { children: T[]; name: string; attributes?: { id?: string } }
>(node: T, type: string, id: string): T | null {
>(node: T, type: string | null, id: string): T | null {
if (node.children.length === 0) {
return null;
}

View file

@ -18,7 +18,10 @@
"js/**/*.tsx",
"modern/src/utils.ts",
"modern/src/Emitter.ts",
"modern/src/runtime/MakiObject.ts"
"modern/src/runtime/MakiObject.ts",
"modern/src/runtime/GuiObject.ts",
"modern/src/runtime/Layout.ts",
"modern/src/runtime/Group.ts"
],
"exclude": ["node_modules", "**/*.spec.ts", "demo/built"]
}