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

(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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function escapeAttr(s) {
return escapeHtml(s).replace(/"/g, '&quot;');
}
global.PWInspectorPanel = InspectorPanel;
})(window);