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.
253 lines
11 KiB
253 lines
11 KiB
(function (global) {
|
|
'use strict';
|
|
|
|
/**
|
|
* 属性面板(壳侧):通过 Bridge 读写节点属性;标签优先使用 displayName。
|
|
* @constructor
|
|
*/
|
|
function InspectorPanel() {
|
|
this.el = null;
|
|
this.body = null;
|
|
this._bridge = null;
|
|
this._uuid = '';
|
|
this._props = null;
|
|
}
|
|
|
|
InspectorPanel.prototype.setBridge = function (bridge) {
|
|
this._bridge = bridge;
|
|
};
|
|
|
|
InspectorPanel.prototype.mount = function () {
|
|
var root = document.createElement('div');
|
|
root.className = 'pw-panel-inner pw-inspector';
|
|
root.style.height = '100%';
|
|
root.style.display = 'flex';
|
|
root.style.flexDirection = 'column';
|
|
root.innerHTML =
|
|
'<div class="pw-panel-body pw-inspector-body" id="pw-inspector-body">' +
|
|
'<div class="pw-inspector-empty">未选中节点</div></div>';
|
|
this.el = root;
|
|
this.body = root.querySelector('#pw-inspector-body');
|
|
return root;
|
|
};
|
|
|
|
InspectorPanel.prototype.loadUuid = function (uuid) {
|
|
var self = this;
|
|
this._uuid = uuid || '';
|
|
if (!this._uuid || !this._bridge) {
|
|
this._props = null;
|
|
if (this.body) this.body.innerHTML = '<div class="pw-inspector-empty">未选中节点</div>';
|
|
return;
|
|
}
|
|
this._bridge.call('node.get', { uuid: this._uuid }).then(function (props) {
|
|
self._props = props;
|
|
self.render();
|
|
}).catch(function () {
|
|
if (self.body) self.body.innerHTML = '<div class="pw-inspector-empty">读取失败</div>';
|
|
});
|
|
};
|
|
|
|
InspectorPanel.prototype.render = function () {
|
|
var body = this.body;
|
|
var p = this._props;
|
|
if (!body) return;
|
|
if (!p) {
|
|
body.innerHTML = '<div class="pw-inspector-empty">未选中节点</div>';
|
|
return;
|
|
}
|
|
var html = [];
|
|
html.push('<section class="pw-insp-section open">');
|
|
html.push('<header class="pw-insp-sec-head" data-toggle="1"><span class="pw-insp-caret"></span><span>节点</span></header>');
|
|
html.push('<div class="pw-insp-sec-body">');
|
|
html.push(rowText('名称', 'name', p.name));
|
|
html.push(rowCheck('激活', 'active', !!p.active));
|
|
html.push(rowNum('Layer', 'layer', p.layer));
|
|
html.push(rowVec3('位置', ['px', 'py', 'pz'], [p.pos.x, p.pos.y, p.pos.z]));
|
|
html.push(rowVec3('旋转', ['rx', 'ry', 'rz'], [p.euler.x, p.euler.y, p.euler.z]));
|
|
html.push(rowVec3('缩放', ['sx', 'sy', 'sz'], [p.scale.x, p.scale.y, p.scale.z]));
|
|
html.push('</div></section>');
|
|
|
|
var comps = p.comps || [];
|
|
for (var i = 0; i < comps.length; i++) {
|
|
var c = comps[i];
|
|
html.push('<section class="pw-insp-section open">');
|
|
html.push('<header class="pw-insp-sec-head" data-toggle="1"><span class="pw-insp-caret"></span><span>' +
|
|
escapeHtml(c.name) + '</span></header>');
|
|
html.push('<div class="pw-insp-sec-body">');
|
|
var fields = c.fields || [];
|
|
if (!fields.length) html.push('<div class="pw-insp-muted">无可编辑公开字段</div>');
|
|
for (var f = 0; f < fields.length; f++) {
|
|
html.push(renderField(c.index, fields[f]));
|
|
}
|
|
html.push('</div></section>');
|
|
}
|
|
|
|
body.innerHTML = html.join('');
|
|
this._bindFields();
|
|
this._bindSections();
|
|
};
|
|
|
|
/**
|
|
* @param {number} compIndex
|
|
* @param {object} field
|
|
* @returns {string}
|
|
*/
|
|
function renderField(compIndex, field) {
|
|
var label = field.label || field.key;
|
|
var fid = 'c' + compIndex + '_' + field.key;
|
|
var type = field.type;
|
|
if (field.readOnly || type === 'ref') {
|
|
return rowRef(label, field.value);
|
|
}
|
|
if (type === 'boolean') return rowCheck(label, fid, !!field.value);
|
|
if (type === 'number') return rowNum(label, fid, field.value);
|
|
if (type === 'string') return rowText(label, fid, field.value);
|
|
if (type === 'enum') return rowEnum(label, fid, field.value, field.enumOptions || []);
|
|
if (type === 'vec2') {
|
|
return rowVec2(label, [fid + '_x', fid + '_y'], [field.value.x, field.value.y], ['X', 'Y']);
|
|
}
|
|
if (type === 'vec3') {
|
|
return rowVec3(label, [fid + '_x', fid + '_y', fid + '_z'], [field.value.x, field.value.y, field.value.z]);
|
|
}
|
|
if (type === 'size') {
|
|
return rowVec2(label, [fid + '_w', fid + '_h'], [field.value.width, field.value.height], ['W', 'H']);
|
|
}
|
|
if (type === 'color') return rowColor(label, fid, field.value);
|
|
return rowText(label, fid, field.value == null ? '' : String(field.value));
|
|
}
|
|
|
|
InspectorPanel.prototype._bindSections = function () {
|
|
var heads = this.body.querySelectorAll('.pw-insp-sec-head[data-toggle]');
|
|
for (var i = 0; i < heads.length; i++) {
|
|
heads[i].addEventListener('click', function (ev) {
|
|
var sec = ev.currentTarget.parentNode;
|
|
if (sec) sec.classList.toggle('open');
|
|
});
|
|
}
|
|
};
|
|
|
|
InspectorPanel.prototype._bindFields = function () {
|
|
var self = this;
|
|
var body = this.body;
|
|
if (!body || !this._bridge || !this._uuid) return;
|
|
var inputs = body.querySelectorAll('[data-fid]');
|
|
for (var i = 0; i < inputs.length; i++) {
|
|
(function (el) {
|
|
var run = function () {
|
|
var fid = el.getAttribute('data-fid');
|
|
var value;
|
|
if (el.tagName === 'SELECT') value = parseEnumValue(el);
|
|
else if (el.type === 'checkbox') value = !!el.checked;
|
|
else if (el.type === 'color') value = el.value;
|
|
else if (el.type === 'number') value = parseFloat(el.value);
|
|
else value = el.value;
|
|
self._bridge.call('node.set', { uuid: self._uuid, fid: fid, value: value });
|
|
if (el.getAttribute('data-color-pair')) {
|
|
var siblings = el.parentNode ? el.parentNode.querySelectorAll('[data-fid="' + fid + '"]') : [];
|
|
for (var si = 0; si < siblings.length; si++) {
|
|
if (siblings[si] === el) continue;
|
|
var nv = String(value);
|
|
if (nv.charAt(0) !== '#' && siblings[si].type === 'color') nv = '#' + nv;
|
|
if (siblings[si].type === 'color') siblings[si].value = nv.slice(0, 7);
|
|
else siblings[si].value = nv;
|
|
}
|
|
}
|
|
};
|
|
el.addEventListener('change', run);
|
|
if (el.type !== 'checkbox' && el.tagName !== 'SELECT') el.addEventListener('input', run);
|
|
})(inputs[i]);
|
|
}
|
|
};
|
|
|
|
function parseEnumValue(el) {
|
|
var opt = el.options[el.selectedIndex];
|
|
if (!opt) return el.value;
|
|
var raw = opt.getAttribute('data-num');
|
|
if (raw != null && raw !== '') return Number(raw);
|
|
return el.value;
|
|
}
|
|
|
|
function labelHtml(label) {
|
|
var text = label == null ? '' : String(label);
|
|
return '<label class="pw-insp-label" title="' + escapeAttr(text) + '">' + escapeHtml(text) + '</label>';
|
|
}
|
|
function rowText(label, id, value) {
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<input class="pw-insp-input" data-fid="' + id + '" type="text" value="' +
|
|
escapeAttr(value == null ? '' : String(value)) + '" /></div>';
|
|
}
|
|
function rowNum(label, id, value) {
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<input class="pw-insp-input pw-insp-num" data-fid="' + id +
|
|
'" type="number" step="any" value="' + (value != null ? value : 0) + '" /></div>';
|
|
}
|
|
function rowCheck(label, id, value) {
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<label class="pw-insp-check"><input data-fid="' + id + '" type="checkbox"' +
|
|
(value ? ' checked' : '') + ' /><span></span></label></div>';
|
|
}
|
|
function rowEnum(label, id, value, options) {
|
|
var opts = [];
|
|
for (var i = 0; i < options.length; i++) {
|
|
var o = options[i];
|
|
var selected = o.value === value || String(o.value) === String(value);
|
|
opts.push('<option value="' + escapeAttr(String(o.value)) + '" data-num="' +
|
|
escapeAttr(String(o.value)) + '"' + (selected ? ' selected' : '') + '>' +
|
|
escapeHtml(o.name) + '</option>');
|
|
}
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<select class="pw-insp-input pw-insp-select" data-fid="' + id + '">' + opts.join('') + '</select></div>';
|
|
}
|
|
function rowRef(label, value) {
|
|
var v = value || {};
|
|
var text = (v.kind ? v.kind + ' · ' : '') + (v.name || '(空)');
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<div class="pw-insp-ref" title="' + escapeAttr(v.uuid || text) + '">' +
|
|
'<span class="pw-insp-ref-kind">' + escapeHtml(v.kind || 'Object') + '</span>' +
|
|
'<span class="pw-insp-ref-name">' + escapeHtml(v.name || '(空)') + '</span></div></div>';
|
|
}
|
|
function rowColor(label, id, value) {
|
|
var r = (value && value.r) || 0;
|
|
var g = (value && value.g) || 0;
|
|
var b = (value && value.b) || 0;
|
|
var hex = '#' + toHex2(r) + toHex2(g) + toHex2(b);
|
|
return '<div class="pw-insp-row">' + labelHtml(label) +
|
|
'<div class="pw-insp-color">' +
|
|
'<input class="pw-insp-color-picker" data-fid="' + id + '_hex" data-color-pair="1" type="color" value="' + hex + '" />' +
|
|
'<input class="pw-insp-input pw-insp-color-hex" data-fid="' + id + '_hex" data-color-pair="1" type="text" value="' + hex + '" />' +
|
|
'</div></div>';
|
|
}
|
|
function rowVec3(label, ids, values) {
|
|
return '<div class="pw-insp-row pw-insp-vec">' + labelHtml(label) +
|
|
'<div class="pw-insp-xyz">' +
|
|
axis('X', ids[0], values[0]) +
|
|
axis('Y', ids[1], values[1]) +
|
|
axis('Z', ids[2], values[2]) +
|
|
'</div></div>';
|
|
}
|
|
function rowVec2(label, ids, values, axisNames) {
|
|
return '<div class="pw-insp-row pw-insp-vec">' + labelHtml(label) +
|
|
'<div class="pw-insp-xyz">' +
|
|
axis(axisNames[0], ids[0], values[0]) +
|
|
axis(axisNames[1], ids[1], values[1]) +
|
|
'</div></div>';
|
|
}
|
|
function axis(name, id, value) {
|
|
return '<div class="pw-insp-axis"><span class="pw-insp-axis-tag">' + name +
|
|
'</span><input class="pw-insp-input pw-insp-num" data-fid="' + id +
|
|
'" type="number" step="any" value="' + (value != null ? value : 0) + '" /></div>';
|
|
}
|
|
function toHex2(n) {
|
|
var s = Math.max(0, Math.min(255, n | 0)).toString(16);
|
|
return s.length < 2 ? '0' + s : s;
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
function escapeAttr(s) {
|
|
return escapeHtml(s).replace(/"/g, '"');
|
|
}
|
|
|
|
global.PWInspectorPanel = InspectorPanel;
|
|
})(window);
|
|
|