You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
317 lines
12 KiB
317 lines
12 KiB
(function (global) {
|
|
'use strict';
|
|
|
|
/**
|
|
* Runtime property inspector with live edits.
|
|
* @constructor
|
|
*/
|
|
function InspectorPanel() {
|
|
this.el = null;
|
|
this.body = null;
|
|
this._cc = null;
|
|
this._pick = null;
|
|
this._node = null;
|
|
}
|
|
|
|
/** @param {*} pick */
|
|
InspectorPanel.prototype.setPick = function (pick) {
|
|
var self = this;
|
|
this._pick = pick;
|
|
if (pick) {
|
|
pick.onSelect(function (node) {
|
|
self._node = node;
|
|
self.render();
|
|
});
|
|
}
|
|
};
|
|
|
|
/** @param {*} cc */
|
|
InspectorPanel.prototype.bindEngine = function (cc) {
|
|
this._cc = cc;
|
|
};
|
|
|
|
InspectorPanel.prototype.mount = function () {
|
|
var root = document.createElement('div');
|
|
root.className = 'pw-panel-inner';
|
|
root.style.height = '100%';
|
|
root.style.display = 'flex';
|
|
root.style.flexDirection = 'column';
|
|
root.innerHTML =
|
|
'<div class="pw-panel-head"><span class="pw-panel-title">属性</span></div>' +
|
|
'<div class="pw-panel-body" id="pw-inspector-body"><div class="pw-muted">未选中节点</div></div>';
|
|
this.el = root;
|
|
this.body = root.querySelector('#pw-inspector-body');
|
|
return root;
|
|
};
|
|
|
|
InspectorPanel.prototype.render = function () {
|
|
var body = this.body;
|
|
var node = this._node;
|
|
var cc = this._cc;
|
|
if (!body) return;
|
|
if (!node || !node.isValid) {
|
|
body.innerHTML = '<div class="pw-muted">未选中节点</div>';
|
|
return;
|
|
}
|
|
var p = node.getPosition ? node.getPosition() : node.position;
|
|
var e = node.eulerAngles || (node.getRotation ? { x: 0, y: 0, z: 0 } : { x: 0, y: 0, z: 0 });
|
|
if (node.eulerAngles) e = node.eulerAngles;
|
|
var s = node.getScale ? node.getScale() : node.scale;
|
|
var html = [];
|
|
html.push('<div class="pw-comp-block"><h5>节点</h5><div class="pw-field-grid">');
|
|
html.push(fieldText('名称', 'name', node.name));
|
|
html.push(fieldCheck('激活', 'active', !!node.active));
|
|
html.push(fieldNum('层级', 'layer', node.layer));
|
|
html.push(fieldNum('位置 X', 'px', p.x));
|
|
html.push(fieldNum('位置 Y', 'py', p.y));
|
|
html.push(fieldNum('位置 Z', 'pz', p.z));
|
|
html.push(fieldNum('旋转 X', 'rx', e.x));
|
|
html.push(fieldNum('旋转 Y', 'ry', e.y));
|
|
html.push(fieldNum('旋转 Z', 'rz', e.z));
|
|
html.push(fieldNum('缩放 X', 'sx', s.x));
|
|
html.push(fieldNum('缩放 Y', 'sy', s.y));
|
|
html.push(fieldNum('缩放 Z', 'sz', s.z));
|
|
html.push('</div></div>');
|
|
|
|
var uit = node.getComponent && cc && node.getComponent(cc.UITransform);
|
|
if (uit) {
|
|
html.push('<div class="pw-comp-block"><h5>UITransform</h5><div class="pw-field-grid">');
|
|
html.push(fieldNum('宽度', 'uiw', uit.width));
|
|
html.push(fieldNum('高度', 'uih', uit.height));
|
|
html.push(fieldNum('锚点 X', 'uax', uit.anchorX));
|
|
html.push(fieldNum('锚点 Y', 'uay', uit.anchorY));
|
|
html.push('</div></div>');
|
|
}
|
|
|
|
if (node.components) {
|
|
for (var i = 0; i < node.components.length; i++) {
|
|
var comp = node.components[i];
|
|
if (!comp || !comp.constructor) continue;
|
|
var cname = comp.constructor.name;
|
|
if (cname === 'UITransform') continue;
|
|
html.push('<div class="pw-comp-block"><h5>' + escapeHtml(cname) + '</h5><div class="pw-field-grid">');
|
|
var keys = Object.keys(comp);
|
|
var shown = 0;
|
|
for (var k = 0; k < keys.length && shown < 24; k++) {
|
|
var key = keys[k];
|
|
if (!key || key.charAt(0) === '_' || key === 'node' || key === 'uuid') continue;
|
|
var val = comp[key];
|
|
var t = typeof val;
|
|
if (t === 'number') {
|
|
html.push(fieldNum(key, 'c' + i + '_' + key, val));
|
|
shown++;
|
|
} else if (t === 'boolean') {
|
|
html.push(fieldCheck(key, 'c' + i + '_' + key, val));
|
|
shown++;
|
|
} else if (t === 'string') {
|
|
html.push(fieldText(key, 'c' + i + '_' + key, val));
|
|
shown++;
|
|
}
|
|
}
|
|
if (!shown) html.push('<span class="pw-muted">无可编辑公开字段</span>');
|
|
html.push('</div></div>');
|
|
}
|
|
}
|
|
|
|
body.innerHTML = html.join('');
|
|
this._bindFields(node, uit);
|
|
};
|
|
|
|
InspectorPanel.prototype._bindFields = function (node, uit) {
|
|
var self = this;
|
|
var body = this.body;
|
|
var cc = this._cc;
|
|
|
|
function byFid(id) {
|
|
return body.querySelector('[data-fid="' + id.replace(/"/g, '') + '"]');
|
|
}
|
|
function num(id) {
|
|
var el = byFid(id);
|
|
return el ? parseFloat(el.value) : NaN;
|
|
}
|
|
function bind(id, apply) {
|
|
var el = byFid(id);
|
|
if (!el) return;
|
|
var run = function () {
|
|
try {
|
|
apply(el);
|
|
} catch (e) {
|
|
console.warn('[调试工作台] 设置属性失败', id, e);
|
|
}
|
|
};
|
|
el.addEventListener('change', run);
|
|
el.addEventListener('input', run);
|
|
}
|
|
|
|
function readPos() {
|
|
return node.getPosition ? node.getPosition() : node.position;
|
|
}
|
|
function writePos(x, y, z) {
|
|
if (typeof node.setPosition === 'function') {
|
|
node.setPosition(x, y, z);
|
|
return;
|
|
}
|
|
if (cc && cc.Vec3) node.position = new cc.Vec3(x, y, z);
|
|
}
|
|
function readScale() {
|
|
return node.getScale ? node.getScale() : node.scale;
|
|
}
|
|
function writeScale(x, y, z) {
|
|
if (typeof node.setScale === 'function') {
|
|
node.setScale(x, y, z);
|
|
return;
|
|
}
|
|
if (cc && cc.Vec3) node.scale = new cc.Vec3(x, y, z);
|
|
}
|
|
function writeEuler(x, y, z) {
|
|
if (typeof node.setRotationFromEuler === 'function') {
|
|
node.setRotationFromEuler(x, y, z);
|
|
return;
|
|
}
|
|
if (node.eulerAngles) {
|
|
node.eulerAngles = cc && cc.Vec3 ? new cc.Vec3(x, y, z) : { x: x, y: y, z: z };
|
|
}
|
|
}
|
|
|
|
bind('name', function (el) { node.name = el.value; });
|
|
bind('active', function (el) { node.active = !!el.checked; });
|
|
bind('layer', function () {
|
|
var v = num('layer');
|
|
if (!isNaN(v)) node.layer = v | 0;
|
|
});
|
|
bind('px', function () {
|
|
var v = num('px');
|
|
if (isNaN(v)) return;
|
|
var p = readPos();
|
|
writePos(v, p.y, p.z);
|
|
});
|
|
bind('py', function () {
|
|
var v = num('py');
|
|
if (isNaN(v)) return;
|
|
var p = readPos();
|
|
writePos(p.x, v, p.z);
|
|
});
|
|
bind('pz', function () {
|
|
var v = num('pz');
|
|
if (isNaN(v)) return;
|
|
var p = readPos();
|
|
writePos(p.x, p.y, v);
|
|
});
|
|
bind('rx', function () {
|
|
var v = num('rx');
|
|
if (isNaN(v)) return;
|
|
var e = node.eulerAngles;
|
|
writeEuler(v, e.y, e.z);
|
|
});
|
|
bind('ry', function () {
|
|
var v = num('ry');
|
|
if (isNaN(v)) return;
|
|
var e = node.eulerAngles;
|
|
writeEuler(e.x, v, e.z);
|
|
});
|
|
bind('rz', function () {
|
|
var v = num('rz');
|
|
if (isNaN(v)) return;
|
|
var e = node.eulerAngles;
|
|
writeEuler(e.x, e.y, v);
|
|
});
|
|
bind('sx', function () {
|
|
var v = num('sx');
|
|
if (isNaN(v)) return;
|
|
var s = readScale();
|
|
writeScale(v, s.y, s.z);
|
|
});
|
|
bind('sy', function () {
|
|
var v = num('sy');
|
|
if (isNaN(v)) return;
|
|
var s = readScale();
|
|
writeScale(s.x, v, s.z);
|
|
});
|
|
bind('sz', function () {
|
|
var v = num('sz');
|
|
if (isNaN(v)) return;
|
|
var s = readScale();
|
|
writeScale(s.x, s.y, v);
|
|
});
|
|
|
|
if (uit) {
|
|
bind('uiw', function () {
|
|
var w = num('uiw');
|
|
var h = num('uih');
|
|
if (isNaN(w)) return;
|
|
if (typeof uit.setContentSize === 'function') uit.setContentSize(w, isNaN(h) ? uit.height : h);
|
|
else uit.width = w;
|
|
});
|
|
bind('uih', function () {
|
|
var w = num('uiw');
|
|
var h = num('uih');
|
|
if (isNaN(h)) return;
|
|
if (typeof uit.setContentSize === 'function') uit.setContentSize(isNaN(w) ? uit.width : w, h);
|
|
else uit.height = h;
|
|
});
|
|
bind('uax', function () {
|
|
var v = num('uax');
|
|
if (isNaN(v)) return;
|
|
if (typeof uit.setAnchorPoint === 'function') uit.setAnchorPoint(v, uit.anchorY);
|
|
else uit.anchorX = v;
|
|
});
|
|
bind('uay', function () {
|
|
var v = num('uay');
|
|
if (isNaN(v)) return;
|
|
if (typeof uit.setAnchorPoint === 'function') uit.setAnchorPoint(uit.anchorX, v);
|
|
else uit.anchorY = v;
|
|
});
|
|
}
|
|
|
|
if (node.components) {
|
|
for (var i = 0; i < node.components.length; i++) {
|
|
var comp = node.components[i];
|
|
if (!comp) continue;
|
|
var keys = Object.keys(comp);
|
|
for (var k = 0; k < keys.length; k++) {
|
|
var key = keys[k];
|
|
var id = 'c' + i + '_' + key;
|
|
var el = byFid(id);
|
|
if (!el) continue;
|
|
(function (component, prop, input) {
|
|
var apply = function () {
|
|
try {
|
|
var cur = component[prop];
|
|
if (typeof cur === 'number') {
|
|
var n = parseFloat(input.value);
|
|
if (!isNaN(n)) component[prop] = n;
|
|
} else if (typeof cur === 'boolean') {
|
|
component[prop] = !!input.checked;
|
|
} else if (typeof cur === 'string') {
|
|
component[prop] = input.value;
|
|
}
|
|
} catch (err) {
|
|
console.warn('[调试工作台] 组件属性失败', prop, err);
|
|
}
|
|
};
|
|
input.addEventListener('change', apply);
|
|
input.addEventListener('input', apply);
|
|
})(comp, key, el);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function fieldText(label, id, value) {
|
|
return '<label>' + escapeHtml(label) + '</label><input class="pw-input" data-fid="' + escapeAttr(id) + '" value="' + escapeAttr(value) + '" />';
|
|
}
|
|
function fieldNum(label, id, value) {
|
|
return '<label>' + escapeHtml(label) + '</label><input class="pw-input" type="number" step="any" data-fid="' + escapeAttr(id) + '" value="' + (value != null ? value : 0) + '" />';
|
|
}
|
|
function fieldCheck(label, id, value) {
|
|
return '<label>' + escapeHtml(label) + '</label><input type="checkbox" data-fid="' + escapeAttr(id) + '" ' + (value ? 'checked' : '') + ' />';
|
|
}
|
|
function escapeHtml(s) {
|
|
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
function escapeAttr(s) {
|
|
return escapeHtml(s).replace(/"/g, '"');
|
|
}
|
|
|
|
global.PWInspectorPanel = InspectorPanel;
|
|
})(window);
|
|
|