498 lines
17 KiB
JavaScript
498 lines
17 KiB
JavaScript
import Reconciler from 'react-reconciler';
|
||
import * as React from 'react';
|
||
import { isChanged } from './Util.mjs';
|
||
globalThis.React = React;
|
||
const TESTING = false;
|
||
class DisplayObjectElem {
|
||
_elem = null;
|
||
get elem() {
|
||
if (!this._elem)
|
||
throw new Error('invalid elem in ' + (this.name || 'unknown elem'));
|
||
return this._elem;
|
||
}
|
||
_container = null;
|
||
get mounted() {
|
||
return !!this._container;
|
||
}
|
||
visible = true;
|
||
mount(container) {
|
||
this._container = container;
|
||
this.updateElem('*');
|
||
}
|
||
;
|
||
unmount() {
|
||
this._container = null;
|
||
this._elem = null;
|
||
this[Symbol.dispose]();
|
||
}
|
||
[Symbol.dispose]() {
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return newProps.visible !== void 0 && this.visible !== newProps.visible;
|
||
}
|
||
update(props) {
|
||
const changed = this.updateProps(props);
|
||
if (this._elem)
|
||
this.updateElem(changed);
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = [];
|
||
if (newProps.visible !== void 0 && this.visible !== newProps.visible) {
|
||
this.visible = newProps.visible;
|
||
changed.push('visible');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
if (isChanged(changed, 'visible'))
|
||
this.elem.m_visible = this.visible;
|
||
}
|
||
}
|
||
class SpriteElem extends DisplayObjectElem {
|
||
_parent = null;
|
||
get parent() {
|
||
if (!this._parent)
|
||
throw new Error('parent is not setted');
|
||
return this._parent;
|
||
}
|
||
get elem() { return super.elem; }
|
||
interactive = true;
|
||
removeFromParent() {
|
||
if (!this._parent)
|
||
return;
|
||
if ( /*this.mounted && */this._container) {
|
||
this.unmount();
|
||
}
|
||
this._parent.children.splice(this._parent.children.indexOf(this), 1);
|
||
this._parent = null;
|
||
}
|
||
setParent(parent) {
|
||
if (this._parent) {
|
||
if (this._parent == parent)
|
||
return;
|
||
this.removeFromParent();
|
||
}
|
||
this._parent = parent;
|
||
parent.children.push(this);
|
||
if (parent.mounted)
|
||
this.mount(parent._container);
|
||
}
|
||
children = [];
|
||
mount(container) {
|
||
this._container = container;
|
||
super.mount(container);
|
||
if (this.children.length) {
|
||
this.children.forEach(child => {
|
||
child.mount(container);
|
||
});
|
||
}
|
||
}
|
||
;
|
||
unmount() {
|
||
super.unmount();
|
||
if (this.children.length) {
|
||
this.children.forEach(child => {
|
||
child.unmount();
|
||
});
|
||
}
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return super.shouldUpdate(newProps) ||
|
||
(newProps.interactive !== void 0 && this.interactive !== newProps.interactive);
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = super.updateProps(newProps);
|
||
if (newProps.interactive !== void 0 && this.interactive !== newProps.interactive) {
|
||
this.interactive = newProps.interactive;
|
||
changed.push('interactive');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
super.updateElem(changed);
|
||
if (isChanged(changed, 'interactive'))
|
||
this.elem.m_interactive = this.interactive;
|
||
}
|
||
}
|
||
class MovieClipElem extends SpriteElem {
|
||
get elem() { return super.elem; }
|
||
get parent() { return super.parent; }
|
||
name = '';
|
||
gotoAndStop = '';
|
||
constructor(name) {
|
||
super();
|
||
this.name = name;
|
||
}
|
||
mount(container) {
|
||
if (!this.parent)
|
||
throw new Error('cannot mount without parent for MovieClipElem');
|
||
if (!this._elem) {
|
||
this._elem = this.parent.elem.getMovieClipByName(this.name);
|
||
}
|
||
if (!this._elem)
|
||
throw new Error(`elem ${this.name} not found`);
|
||
super.mount(container);
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return super.shouldUpdate(newProps) ||
|
||
(newProps.gotoAndStop !== void 0 && this.gotoAndStop !== newProps.gotoAndStop);
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = super.updateProps(newProps);
|
||
if (newProps.gotoAndStop !== void 0 && this.gotoAndStop !== newProps.gotoAndStop) {
|
||
this.gotoAndStop = newProps.gotoAndStop;
|
||
changed.push('gotoAndStop');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
super.updateElem(changed);
|
||
if (isChanged(changed, 'gotoAndStop') && this.gotoAndStop) {
|
||
this.elem.gotoAndStop(this.gotoAndStop);
|
||
}
|
||
}
|
||
}
|
||
class TextFieldElem extends SpriteElem {
|
||
scaleMultilineTextForLongestWord = false;
|
||
scaleIfNecessary = false;
|
||
autoAdjustText = false;
|
||
name = '';
|
||
text = '';
|
||
align = 0;
|
||
get elem() { return super.elem; }
|
||
get parent() { return super.parent; }
|
||
constructor(name) {
|
||
super();
|
||
this.name = name;
|
||
}
|
||
mount(container) {
|
||
if (!this.parent)
|
||
throw new Error('cannot mount without parent for TextFieldElem');
|
||
if (!this._elem) {
|
||
this._elem = this.parent.elem.getTextFieldByName(this.name);
|
||
}
|
||
super.mount(container);
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return super.shouldUpdate(newProps) ||
|
||
(newProps.scaleIfNecessary !== void 0 && this.scaleIfNecessary !== newProps.scaleIfNecessary) ||
|
||
(newProps.scaleMultilineTextForLongestWord !== void 0 && this.scaleMultilineTextForLongestWord !== newProps.scaleMultilineTextForLongestWord) ||
|
||
(newProps.autoAdjustText != void 0 && this.autoAdjustText !== newProps.autoAdjustText) ||
|
||
(newProps.text !== void 0 && this.text !== newProps.text) ||
|
||
(newProps.align !== void 0 && this.align !== newProps.align);
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = super.updateProps(newProps);
|
||
if (newProps.scaleIfNecessary !== void 0 && this.scaleIfNecessary !== newProps.scaleIfNecessary) {
|
||
this.scaleIfNecessary = newProps.scaleIfNecessary !== false;
|
||
changed.push('scaleIfNecessary');
|
||
}
|
||
if (newProps.scaleMultilineTextForLongestWord !== void 0 && this.scaleMultilineTextForLongestWord !== newProps.scaleMultilineTextForLongestWord) {
|
||
this.scaleMultilineTextForLongestWord = newProps.scaleMultilineTextForLongestWord !== false;
|
||
changed.push('scaleMultilineTextForLongestWord');
|
||
}
|
||
if (newProps.autoAdjustText != void 0 && this.autoAdjustText !== newProps.autoAdjustText) {
|
||
this.autoAdjustText = newProps.autoAdjustText !== false;
|
||
changed.push('autoAdjustText');
|
||
}
|
||
if (newProps.text !== void 0 && this.text !== newProps.text) {
|
||
this.text = newProps.text;
|
||
changed.push('text');
|
||
}
|
||
if (newProps.align !== void 0 && this.align !== newProps.align) {
|
||
this.align = newProps.align;
|
||
changed.push('align');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
super.updateElem(changed);
|
||
if (changed != '*' && isChanged(changed, 'align')) {
|
||
this.elem.setAlign(this.align);
|
||
}
|
||
if (isChanged(changed, 'text', 'scaleIfNeccessary')) {
|
||
if (this.scaleIfNecessary) {
|
||
SC.MovieClipHelper.setTextAndScaleIfNecessary(this.elem, this.text, false, false);
|
||
}
|
||
else {
|
||
this.elem.setText(this.text);
|
||
}
|
||
}
|
||
if (isChanged(changed, 'scaleMultilineTextForLongestWord')) {
|
||
if (this.scaleMultilineTextForLongestWord) {
|
||
SC.MovieClipHelper.scaleMultilineTextForLongestWord(this.elem, true, false);
|
||
}
|
||
else {
|
||
// ?
|
||
}
|
||
}
|
||
if (isChanged(changed, 'autoAdjustText')) {
|
||
if (this.autoAdjustText) {
|
||
SC.MovieClipHelper.autoAdjustText(this.elem, false, true, true);
|
||
}
|
||
else {
|
||
// ?
|
||
}
|
||
}
|
||
}
|
||
}
|
||
class GameButtonElem extends MovieClipElem {
|
||
button = null;
|
||
autoAdjustChildTexts = false;
|
||
enabled = true;
|
||
buttonListener = null;
|
||
buttonListenerSetted = false;
|
||
mount(container) {
|
||
this.button = container.addGameButton(this.name, true);
|
||
this._elem = this.button.getMovieClip();
|
||
super.mount(container);
|
||
}
|
||
unmount() {
|
||
super.unmount();
|
||
if (this.buttonListenerSetted)
|
||
removeButtonListener(this.name);
|
||
}
|
||
buttonListenCallback() {
|
||
this.buttonListener && this.elem && this.buttonListener(this.elem);
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return super.shouldUpdate(newProps) || this.enabled != newProps.enabled;
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = super.updateProps(newProps);
|
||
if (newProps.autoAdjustChildTexts !== void 0 && this.autoAdjustChildTexts !== newProps.autoAdjustChildTexts) {
|
||
this.autoAdjustChildTexts = newProps.autoAdjustChildTexts !== false;
|
||
changed.push('autoAdjustChildTexts');
|
||
}
|
||
if (newProps.enabled !== void 0 && this.enabled !== newProps.enabled) {
|
||
this.enabled = newProps.enabled !== false;
|
||
changed.push('enabled');
|
||
}
|
||
if (newProps.buttonListener !== void 0 && this.buttonListener !== newProps.buttonListener) {
|
||
this.buttonListener = newProps.buttonListener;
|
||
changed.push('buttonListener');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
super.updateElem(changed);
|
||
if (isChanged(changed, 'autoAdjustChildTexts')) {
|
||
SC.MovieClipHelper.autoAdjustChildTexts(this.button, false, true, true);
|
||
}
|
||
if (!this.button)
|
||
return;
|
||
if (isChanged(changed, 'enabled')) {
|
||
this.button.setEnabled(this.enabled);
|
||
}
|
||
if (isChanged(changed, 'buttonListener') && this.buttonListener) {
|
||
if (!this.buttonListenerSetted)
|
||
addButtonListener(this.button, this.name, this.buttonListenCallback.bind(this));
|
||
}
|
||
if (isChanged(changed, 'visible'))
|
||
this.button.m_visible = this.visible;
|
||
if (isChanged(changed, 'interactive'))
|
||
this.button.m_interactive = this.interactive;
|
||
}
|
||
[Symbol.dispose]() {
|
||
if (!this.button)
|
||
return;
|
||
this.button[Symbol.dispose]();
|
||
this.button = null;
|
||
}
|
||
}
|
||
class SelectableButtonElem extends MovieClipElem {
|
||
enabled = false;
|
||
onToggle = null;
|
||
button = null;
|
||
mount(container) {
|
||
if (!this.parent)
|
||
throw new Error('cannot mount without parent for SelectableButtonElem');
|
||
this.button = new SC.MagicSelectableButton();
|
||
if (!this.button)
|
||
throw new Error(`SelectableButton create failed`);
|
||
this.button.setToggleCallback(this.onToggleCallback.bind(this));
|
||
super.mount(container);
|
||
this.button.setMovieClip(this._elem, true);
|
||
this.parent.elem.addChild(this.button);
|
||
this.button.getMovieClip().setText("text_on", SC.StringTable.getString("TID_SETTINGS_ON"));
|
||
this.button.getMovieClip().setText("text_off", SC.StringTable.getString("TID_SETTINGS_OFF"));
|
||
}
|
||
[Symbol.dispose]() {
|
||
if (!this.button)
|
||
return;
|
||
this.button[Symbol.dispose]();
|
||
this.button = null;
|
||
}
|
||
onToggleCallback() {
|
||
this.onToggle && this.button && this.onToggle(this.button);
|
||
}
|
||
shouldUpdate(newProps) {
|
||
return super.shouldUpdate(newProps) ||
|
||
(newProps.enabled !== void 0 && this.enabled !== newProps.enabled) ||
|
||
(newProps.onToggle !== void 0 && this.onToggle !== newProps.onToggle);
|
||
}
|
||
updateProps(newProps) {
|
||
const changed = super.updateProps(newProps);
|
||
if (newProps.enabled !== void 0 && this.enabled !== newProps.enabled) {
|
||
this.enabled = newProps.enabled !== false;
|
||
changed.push('enabled');
|
||
}
|
||
if (newProps.onToggle !== void 0 && this.onToggle !== newProps.onToggle) {
|
||
this.onToggle = newProps.onToggle;
|
||
changed.push('onToggle');
|
||
}
|
||
return changed;
|
||
}
|
||
updateElem(changed) {
|
||
super.updateElem(changed);
|
||
if (!this.button)
|
||
return;
|
||
if (isChanged(changed, 'enabled'))
|
||
this.button.setSelected(this.enabled);
|
||
}
|
||
}
|
||
class Root extends SpriteElem {
|
||
mount(container) {
|
||
this._elem = container.getMovieClip();
|
||
super.mount(container);
|
||
}
|
||
}
|
||
const HostConfig = {
|
||
getRootHostContext() {
|
||
// TESTING && console.log('getRootHostContext');
|
||
return {};
|
||
},
|
||
//CanvasPanel()的parentHostContext是getRootHostContext返回的值
|
||
getChildHostContext(parentHostContext) {
|
||
// TESTING && console.log('getChildHostContext', parentHostContext);
|
||
return parentHostContext; //no use, share one
|
||
},
|
||
appendInitialChild(parent, child) {
|
||
TESTING && console.log('appendInitialChild', parent, child);
|
||
HostConfig.appendChild(parent, child);
|
||
},
|
||
appendChildToContainer(container, child) {
|
||
TESTING && console.log('appendChildToContainer');
|
||
try {
|
||
child.mount(container);
|
||
}
|
||
catch (e) {
|
||
console.error('error while mounting');
|
||
console.error(e);
|
||
}
|
||
},
|
||
appendChild(parent, child) {
|
||
TESTING && console.log("appendChild");
|
||
child.setParent(parent);
|
||
},
|
||
createInstance(type, props, container) {
|
||
TESTING && console.log('createInstance', type);
|
||
try {
|
||
let ret = null;
|
||
switch (type) {
|
||
case 'root':
|
||
ret = new Root();
|
||
break;
|
||
case "movieClip":
|
||
ret = new MovieClipElem(props.name);
|
||
break;
|
||
case "selectableButton":
|
||
ret = new SelectableButtonElem(props.name);
|
||
break;
|
||
case "gameButton":
|
||
ret = new GameButtonElem(props.name);
|
||
break;
|
||
case "textField":
|
||
ret = new TextFieldElem(props.name);
|
||
break;
|
||
}
|
||
if (!ret)
|
||
throw new Error(`createInstance failed for ${type}`);
|
||
ret.update(props);
|
||
return ret;
|
||
}
|
||
catch (e) {
|
||
console.warn(e);
|
||
}
|
||
},
|
||
createTextInstance(text) {
|
||
TESTING && console.log('createTextInstance', text);
|
||
return null;
|
||
},
|
||
finalizeInitialChildren(arg0, type, props, children, arg4) {
|
||
// console.log('finalizeInitialChildren', [...arguments].map(JSON.stringify));
|
||
return false;
|
||
},
|
||
getPublicInstance(instance) {
|
||
TESTING && console.warn('getPublicInstance', instance);
|
||
return instance;
|
||
},
|
||
now: Date.now,
|
||
prepareForCommit(arg0) {
|
||
TESTING && console.log('prepareForCommit', arg0);
|
||
},
|
||
resetAfterCommit(container) {
|
||
return;
|
||
},
|
||
resetTextContent() {
|
||
TESTING && console.error('resetTextContent not implemented!');
|
||
},
|
||
shouldSetTextContent(type, props) {
|
||
// console.log('shouldSetTextContent', type, JSON.stringify(props));
|
||
return false;
|
||
},
|
||
commitTextUpdate(textInstance, oldText, newText) {
|
||
return;
|
||
},
|
||
//return false表示不更新,真值将会出现到commitUpdate的updatePayload里头
|
||
prepareUpdate(instance, type, oldProps, newProps) {
|
||
TESTING && console.log('prepareUpdate', instance, type, oldProps, newProps);
|
||
return instance.shouldUpdate(newProps);
|
||
},
|
||
commitUpdate(instance, updatePayload, type, oldProps, newProps) {
|
||
TESTING && console.log('commitUpdate', instance, updatePayload, type, oldProps, newProps);
|
||
instance.update(newProps);
|
||
},
|
||
removeChildFromContainer(container, child) {
|
||
TESTING && console.log('removeChildFromContainer', container, child);
|
||
child.unmount();
|
||
},
|
||
removeChild(parent, child) {
|
||
TESTING && console.log('removeChild');
|
||
child.removeFromParent();
|
||
},
|
||
clearContainer(container) {
|
||
TESTING && console.log('clearContainer', container);
|
||
},
|
||
detachDeletedInstance(instance) {
|
||
},
|
||
//useSyncScheduling: true,
|
||
supportsMutation: true,
|
||
isPrimaryRenderer: true,
|
||
supportsPersistence: false,
|
||
supportsHydration: false,
|
||
shouldDeprioritizeSubtree: undefined,
|
||
setTimeout: undefined,
|
||
clearTimeout: undefined,
|
||
cancelDeferredCallback: undefined,
|
||
noTimeout: undefined,
|
||
scheduleDeferredCallback: undefined,
|
||
};
|
||
const ReactRendererTitan = Reconciler(HostConfig);
|
||
export default ReactRendererTitan;
|
||
// temp
|
||
const listenerMap = new Map();
|
||
function addButtonListener(button, name, callback) {
|
||
name = name[0].toUpperCase() + name.slice(1);
|
||
console.log('addButtonListener', name);
|
||
const listener = new SC.TitanScriptButtonListener('Settings_' + name, (button) => {
|
||
console.log(name + " clicked");
|
||
callback(button);
|
||
});
|
||
SC.TitanUtil.addButtonListener(button, listener);
|
||
listenerMap.set(name, listener);
|
||
return name;
|
||
}
|
||
function removeButtonListener(name) {
|
||
listenerMap.delete(name);
|
||
}
|