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.
211 lines
8.7 KiB
211 lines
8.7 KiB
(function (global) {
|
|
'use strict';
|
|
|
|
var BUILTIN = {
|
|
UITransform: 1, Canvas: 1, Widget: 1, Camera: 1, Sprite: 1, Label: 1,
|
|
RichText: 1, Button: 1, Layout: 1, ScrollView: 1, Mask: 1, Graphics: 1,
|
|
Animation: 1, AudioSource: 1, UIOpacity: 1, BlockInputEvents: 1,
|
|
SafeArea: 1, EditBox: 1, Toggle: 1, PageView: 1, Slider: 1,
|
|
ProgressBar: 1, Skeleton: 1, ParticleSystem2D: 1, MeshRenderer: 1,
|
|
Light: 1, DirectionalLight: 1, SkinnedMeshRenderer: 1, RigidBody2D: 1,
|
|
BoxCollider2D: 1, CircleCollider2D: 1, PolygonCollider2D: 1,
|
|
TiledMap: 1, TiledLayer: 1, VideoPlayer: 1, WebView: 1,
|
|
SubContextView: 1, Mobiledirector: 1, RenderRoot2D: 1,
|
|
};
|
|
|
|
/**
|
|
* 层级面板(壳侧):仅渲染 Agent 序列化树,不碰 cc。
|
|
* @constructor
|
|
*/
|
|
function HierarchyPanel() {
|
|
this.el = null;
|
|
this.treeEl = null;
|
|
this._bridge = null;
|
|
this._filter = '';
|
|
this._expanded = {};
|
|
this._defaultInited = false;
|
|
this._tree = null;
|
|
this._selectedUuid = '';
|
|
}
|
|
|
|
HierarchyPanel.prototype.setBridge = function (bridge) {
|
|
this._bridge = bridge;
|
|
};
|
|
|
|
HierarchyPanel.prototype.setSelectedUuid = function (uuid) {
|
|
this._selectedUuid = uuid || '';
|
|
this._render();
|
|
};
|
|
|
|
HierarchyPanel.prototype.applyTree = function (data) {
|
|
this._tree = data;
|
|
if (data && data.selected) this._selectedUuid = data.selected;
|
|
if (data && data.root) {
|
|
var key = data.root.uuid || 'scene';
|
|
if (!this._defaultInited) {
|
|
this._expanded[key] = true;
|
|
this._defaultInited = true;
|
|
}
|
|
}
|
|
this._render();
|
|
};
|
|
|
|
HierarchyPanel.prototype.mount = function () {
|
|
var self = this;
|
|
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 class="pw-panel-tools">' +
|
|
'<input id="pw-hier-filter" class="pw-input" placeholder="搜索节点" style="width:90px" />' +
|
|
'<button type="button" id="pw-hier-collapse" class="pw-btn" title="全部折叠">折叠</button>' +
|
|
'<button type="button" id="pw-hier-refresh" class="pw-btn">刷新</button>' +
|
|
'</div></div><div class="pw-panel-body pw-hier-tree" id="pw-hier-tree"></div>';
|
|
this.el = root;
|
|
this.treeEl = root.querySelector('#pw-hier-tree');
|
|
root.querySelector('#pw-hier-filter').addEventListener('input', function (ev) {
|
|
self._filter = (ev.target.value || '').toLowerCase();
|
|
self._render();
|
|
});
|
|
root.querySelector('#pw-hier-refresh').addEventListener('click', function () {
|
|
if (!self._bridge) return;
|
|
self._bridge.call('hier.getTree').then(function (t) { self.applyTree(t); });
|
|
});
|
|
root.querySelector('#pw-hier-collapse').addEventListener('click', function () {
|
|
self._expanded = {};
|
|
self._defaultInited = false;
|
|
self._render();
|
|
});
|
|
return root;
|
|
};
|
|
|
|
HierarchyPanel.prototype._render = function () {
|
|
if (!this.treeEl) return;
|
|
if (!this._tree || !this._tree.root) {
|
|
this.treeEl.innerHTML = '<div class="pw-muted">等待场景…</div>';
|
|
return;
|
|
}
|
|
var scrollTop = this.treeEl.scrollTop;
|
|
var html = [];
|
|
this._renderNode(this._tree.root, 0, html, true, true);
|
|
this.treeEl.innerHTML = html.join('') || '<div class="pw-muted">空</div>';
|
|
this.treeEl.scrollTop = scrollTop;
|
|
this._bindTreeEvents();
|
|
};
|
|
|
|
HierarchyPanel.prototype._bindTreeEvents = function () {
|
|
var self = this;
|
|
var twists = this.treeEl.querySelectorAll('.pw-tree-twist:not(.empty)');
|
|
for (var i = 0; i < twists.length; i++) {
|
|
twists[i].addEventListener('click', function (ev) {
|
|
ev.stopPropagation();
|
|
var key = ev.currentTarget.getAttribute('data-key');
|
|
self._expanded[key] = !self._expanded[key];
|
|
self._render();
|
|
});
|
|
}
|
|
var items = this.treeEl.querySelectorAll('.pw-tree-item');
|
|
for (var j = 0; j < items.length; j++) {
|
|
items[j].addEventListener('click', function (ev) {
|
|
if (ev.target.classList.contains('pw-tree-twist')) return;
|
|
var uuid = ev.currentTarget.getAttribute('data-uuid');
|
|
self._selectedUuid = uuid || '';
|
|
if (self._bridge) self._bridge.call('hier.select', { uuid: uuid });
|
|
self._render();
|
|
});
|
|
items[j].addEventListener('dblclick', function (ev) {
|
|
ev.preventDefault();
|
|
var key = ev.currentTarget.getAttribute('data-key');
|
|
if (!key) return;
|
|
self._expanded[key] = !self._expanded[key];
|
|
self._render();
|
|
});
|
|
}
|
|
};
|
|
|
|
HierarchyPanel.prototype._renderNode = function (node, depth, out, forceShow, isRoot) {
|
|
if (!node) return;
|
|
var name = node.name || '(未命名)';
|
|
var key = node.uuid || name;
|
|
var children = node.children || [];
|
|
var hasChildren = children.length > 0;
|
|
var filterHit = !this._filter || name.toLowerCase().indexOf(this._filter) >= 0;
|
|
var childHit = this._filter ? this._childMatch(node) : false;
|
|
if (this._filter && !filterHit && !childHit && !forceShow) return;
|
|
|
|
var expanded = !!this._expanded[key];
|
|
if (this._filter && childHit) expanded = true;
|
|
|
|
var info = this._classify(node, !!isRoot);
|
|
var cls = 'pw-tree-item';
|
|
if (this._selectedUuid && this._selectedUuid === node.uuid) cls += ' selected';
|
|
if (node.active === false) cls += ' inactive';
|
|
var pad = 4 + depth * 14;
|
|
var twist = hasChildren
|
|
? '<button type="button" class="pw-tree-twist" data-key="' + escapeAttr(key) + '">' + (expanded ? '▼' : '▶') + '</button>'
|
|
: '<button type="button" class="pw-tree-twist empty">▶</button>';
|
|
var ts = info.hasScript ? '<span class="pw-tree-ts">TS</span>' : '';
|
|
out.push(
|
|
'<div class="' + cls + '" data-uuid="' + escapeAttr(node.uuid || '') + '" data-key="' + escapeAttr(key) +
|
|
'" style="padding-left:' + pad + 'px" title="' + escapeAttr(info.comps.join(', ')) + '">' +
|
|
twist +
|
|
'<span class="pw-tree-icon ' + info.icon + '"></span>' +
|
|
'<span class="pw-tree-name kind-' + info.kind + '">' + escapeHtml(name) + '</span>' +
|
|
ts +
|
|
'</div>'
|
|
);
|
|
|
|
if (!hasChildren || !expanded) return;
|
|
for (var i = 0; i < children.length; i++) {
|
|
this._renderNode(children[i], depth + 1, out, false, false);
|
|
}
|
|
};
|
|
|
|
HierarchyPanel.prototype._classify = function (node, isRoot) {
|
|
var names = node.comps || [];
|
|
var hasScript = false;
|
|
for (var i = 0; i < names.length; i++) {
|
|
var n = names[i];
|
|
if (n && !BUILTIN[n] && n.indexOf('cc.') !== 0) hasScript = true;
|
|
}
|
|
var icon = 'node';
|
|
var kind = 'node';
|
|
if (isRoot) { icon = 'scene'; kind = 'scene'; }
|
|
else if (hasName(names, 'Camera')) { icon = 'camera'; kind = 'camera'; }
|
|
else if (hasName(names, 'Canvas')) { icon = 'canvas'; kind = 'canvas'; }
|
|
else if (hasName(names, 'Label') || hasName(names, 'RichText')) { icon = 'label'; kind = 'label'; }
|
|
else if (hasName(names, 'Sprite')) { icon = 'sprite'; kind = 'sprite'; }
|
|
else if (hasScript) { icon = 'script'; kind = 'script'; }
|
|
else if (hasName(names, 'UITransform')) { icon = 'ui'; kind = 'ui'; }
|
|
return { icon: icon, kind: kind, hasScript: hasScript, comps: names };
|
|
};
|
|
|
|
HierarchyPanel.prototype._childMatch = function (node) {
|
|
var children = node.children;
|
|
if (!children) return false;
|
|
for (var i = 0; i < children.length; i++) {
|
|
var n = children[i];
|
|
if ((n.name || '').toLowerCase().indexOf(this._filter) >= 0) return true;
|
|
if (this._childMatch(n)) return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
function hasName(list, name) {
|
|
for (var i = 0; i < list.length; i++) if (list[i] === name) return true;
|
|
return false;
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
function escapeAttr(s) {
|
|
return escapeHtml(s).replace(/"/g, '"');
|
|
}
|
|
|
|
global.PWHierarchyPanel = HierarchyPanel;
|
|
})(window);
|
|
|