(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 = '
' + '
未选中节点
'; 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 = '
未选中节点
'; 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 = '
读取失败
'; }); }; InspectorPanel.prototype.render = function () { var body = this.body; var p = this._props; if (!body) return; if (!p) { body.innerHTML = '
未选中节点
'; return; } var html = []; html.push('
'); html.push('
节点
'); html.push('
'); 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('
'); var comps = p.comps || []; for (var i = 0; i < comps.length; i++) { var c = comps[i]; html.push('
'); html.push('
' + '' + '' + '' + escapeHtml(c.name) + '' + '⋯
'); html.push('
'); var fields = c.fields || []; if (isWidgetComp(c.name)) { html.push(renderWidget(c.index, fields)); } else { for (var n = 0; n < fields.length; n++) { var fd = fields[n]; if (fd && String(fd.key).indexOf('editor') === 0) continue; html.push(renderField(c.index, fd)); } if (!fields.length) html.push('
无可编辑公开字段
'); } html.push('
'); } 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)); } function isWidgetComp(name) { return /Widget/i.test(String(name || '')); } /** * cc.Widget —— 对齐官方编辑器: * 对齐示意图 + Horizontal/Vertical Alignment 分段 + Target + Align Mode + 边距 */ function renderWidget(compIndex, fields) { var get = function (key) { for (var i = 0; i < fields.length; i++) { if (fields[i].key === key) return fields[i]; } return null; }; var flag = function (key) { var f = get(key); return !!(f && f.value); }; var hLeft = flag('isAlignLeft'); var hCenter = flag('isAlignHorizontalCenter'); var hRight = flag('isAlignRight'); var vTop = flag('isAlignTop'); var vCenter = flag('isAlignVerticalCenter'); var vBottom = flag('isAlignBottom'); var hMode = 'none'; if (hLeft && hRight) hMode = 'stretch'; else if (hCenter && hLeft) hMode = 'left-center'; else if (hCenter && hRight) hMode = 'right-center'; else if (hCenter) hMode = 'center'; else if (hLeft) hMode = 'left'; else if (hRight) hMode = 'right'; var vMode = 'none'; if (vTop && vBottom) vMode = 'stretch'; else if (vCenter && vTop) vMode = 'top-center'; else if (vCenter && vBottom) vMode = 'bottom-center'; else if (vCenter) vMode = 'center'; else if (vTop) vMode = 'top'; else if (vBottom) vMode = 'bottom'; var html = []; // 对齐示意图(官方 Widget 预览) html.push(''); html.push('
' + '
' + segBtn(compIndex, 'none', 'NONE', hMode === 'none') + segBtn(compIndex, 'left', '⇤', hMode === 'left') + segBtn(compIndex, 'left-center', '◧', hMode === 'left-center') + segBtn(compIndex, 'right-center', '◨', hMode === 'right-center') + segBtn(compIndex, 'right', '⇥', hMode === 'right') + segBtn(compIndex, 'stretch', '⇹', hMode === 'stretch') + '
'); html.push('
' + '
' + segBtn(compIndex, 'none', 'NONE', vMode === 'none') + segBtn(compIndex, 'top', '⤒', vMode === 'top') + segBtn(compIndex, 'top-center', '⬓', vMode === 'top-center') + segBtn(compIndex, 'bottom-center', '⬒', vMode === 'bottom-center') + segBtn(compIndex, 'bottom', '⤓', vMode === 'bottom') + segBtn(compIndex, 'stretch', '⇳', vMode === 'stretch') + '
'); var target = get('target'); html.push(rowRef('Target', target ? target.value : null)); var alignMode = get('alignMode'); if (alignMode) html.push(renderField(compIndex, alignMode)); // 边距 var edgeKeys = ['top', 'bottom', 'left', 'right']; for (var e = 0; e < edgeKeys.length; e++) { var ek = edgeKeys[e]; var ef = get(ek); if (ef) html.push(renderField(compIndex, ef)); } return html.join(''); } function segBtn(compIndex, mode, text, active) { return ''; } 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; // Widget 对齐分段 var segs = body.querySelectorAll('.pw-seg-btn[data-widget-mode]'); for (var s = 0; s < segs.length; s++) { (function (btn) { btn.addEventListener('click', function () { var mode = btn.getAttribute('data-widget-mode') || 'none'; var group = btn.parentNode; var isH = group && group.getAttribute('data-widget-h'); var compIndex = btn.getAttribute('data-widget-comp') || '0'; var flags = {}; if (isH) { flags.isAlignLeft = mode === 'left' || mode === 'left-center' || mode === 'stretch'; flags.isAlignHorizontalCenter = mode === 'left-center' || mode === 'center' || mode === 'right-center'; flags.isAlignRight = mode === 'right' || mode === 'right-center' || mode === 'stretch'; } else { flags.isAlignTop = mode === 'top' || mode === 'top-center' || mode === 'stretch'; flags.isAlignVerticalCenter = mode === 'top-center' || mode === 'center' || mode === 'bottom-center'; flags.isAlignBottom = mode === 'bottom' || mode === 'bottom-center' || mode === 'stretch'; } var keys = Object.keys(flags); for (var k = 0; k < keys.length; k++) { self._bridge.call('node.set', { uuid: self._uuid, fid: 'c' + compIndex + '_' + keys[k], value: flags[keys[k]], }); } // UI 更新 if (group) { var btns = group.querySelectorAll('.pw-seg-btn'); for (var b = 0; b < btns.length; b++) { btns[b].classList.toggle('active', btns[b] === btn); } } }); })(segs[s]); } 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 === 'BUTTON' && el.getAttribute('data-align')) { var on = !el.classList.contains('active'); el.classList.toggle('active', on); self._bridge.call('node.set', { uuid: self._uuid, fid: fid, value: !!on }); return; } if (el.tagName === 'SELECT') value = parseEnumValue(el); else if (el.type === 'checkbox') { value = !!el.checked; var lab = el.closest ? el.closest('.pw-insp-check') : null; if (lab) lab.classList.toggle('on', value); } 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.tagName !== 'BUTTON') { 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 ''; } function rowText(label, id, value) { return '
' + labelHtml(label) + '
'; } function rowNum(label, id, value) { return '
' + labelHtml(label) + '
'; } function rowCheck(label, id, value) { return '
' + labelHtml(label) + '
'; } 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(''); } return '
' + labelHtml(label) + '
'; } function rowRef(label, value) { var v = value || {}; var text = (v.kind ? v.kind + ' · ' : '') + (v.name || '(空)'); return '
' + labelHtml(label) + '
' + '' + escapeHtml(v.kind || 'Object') + '' + '' + escapeHtml(v.name || '(空)') + '
'; } 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 '
' + labelHtml(label) + '
' + '' + '' + '
'; } function rowVec3(label, ids, values) { return '
' + labelHtml(label) + '
' + axis('X', ids[0], values[0]) + axis('Y', ids[1], values[1]) + axis('Z', ids[2], values[2]) + '
'; } function rowVec2(label, ids, values, axisNames) { return '
' + labelHtml(label) + '
' + axis(axisNames[0], ids[0], values[0]) + axis(axisNames[1], ids[1], values[1]) + '
'; } function axis(name, id, value) { return '
' + name + '
'; } 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, '>'); } function escapeAttr(s) { return escapeHtml(s).replace(/"/g, '"'); } global.PWInspectorPanel = InspectorPanel; })(window);