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.

243 lines
9.6 KiB

3 days ago
(function (global) {
'use strict';
var WorkbenchLayout = global.PWLayout;
if (!WorkbenchLayout) {
console.error('[调试工作台] layout-render 需先加载 layout-core');
return;
}
var PANEL_TITLES = {
hierarchy: '层级',
inspector: '属性',
assets: '资源',
perf: '性能',
console: '控制台',
};
var SPLIT_SIZE = 4;
WorkbenchLayout.prototype._renderNode = function (node) {
if (!node) return null;
if (node.type === 'split') return this._renderSplit(node);
if (node.type === 'game') return this._renderGame(node);
return this._renderTabs(node);
};
WorkbenchLayout.prototype._renderSplit = function (node) {
var self = this;
this._ensureId(node);
var wrap = document.createElement('div');
wrap.className = 'pw-split-node pw-split-dir-' + (node.dir === 'v' ? 'v' : 'h');
wrap.setAttribute('data-node-id', node.id);
this._nodeEls[node.id] = { el: wrap, node: node };
var kids = node.children || [];
var sizes = node.sizes || [];
this._normalizeSizes(sizes, kids.length);
for (var i = 0; i < kids.length; i++) {
if (i > 0) {
(function (index) {
var splitter = document.createElement('div');
splitter.className = 'pw-splitter ' + (node.dir === 'v' ? 'pw-splitter-v' : 'pw-splitter-h');
splitter.addEventListener('pointerdown', function (ev) {
self._beginSplitDrag(ev, node, wrap, index - 1);
});
wrap.appendChild(splitter);
})(i);
}
var childWrap = document.createElement('div');
childWrap.className = 'pw-split-child';
childWrap.style.flex = String(sizes[i]) + ' 1 0';
childWrap.style.minWidth = '0';
childWrap.style.minHeight = '0';
var childEl = this._renderNode(kids[i]);
if (childEl) childWrap.appendChild(childEl);
wrap.appendChild(childWrap);
}
return wrap;
};
WorkbenchLayout.prototype._normalizeSizes = function (sizes, n) {
while (sizes.length < n) sizes.push(1 / n);
sizes.length = n;
var sum = 0;
for (var i = 0; i < n; i++) sum += sizes[i] || 0;
if (sum <= 0) {
for (var j = 0; j < n; j++) sizes[j] = 1 / n;
return;
}
for (var k = 0; k < n; k++) sizes[k] = sizes[k] / sum;
};
WorkbenchLayout.prototype._beginSplitDrag = function (ev, node, wrap, leftIndex) {
ev.preventDefault();
ev.stopPropagation();
var children = [];
for (var i = 0; i < wrap.children.length; i++) {
if (wrap.children[i].classList.contains('pw-split-child')) children.push(wrap.children[i]);
}
if (leftIndex < 0 || leftIndex >= children.length - 1) return;
var a = children[leftIndex];
var b = children[leftIndex + 1];
this._splitDrag = {
node: node,
dir: node.dir,
leftIndex: leftIndex,
startPos: node.dir === 'h' ? ev.clientX : ev.clientY,
a0: node.dir === 'h' ? a.getBoundingClientRect().width : a.getBoundingClientRect().height,
b0: node.dir === 'h' ? b.getBoundingClientRect().width : b.getBoundingClientRect().height,
aEl: a,
bEl: b,
};
var prevCursor = document.body.style.cursor;
document.body.style.cursor = node.dir === 'h' ? 'col-resize' : 'row-resize';
document.body.classList.add('pw-split-dragging');
// iframe 会吞掉 pointermove;拖动期间禁用命中
this._setGameIframePassThrough(true);
var target = ev.currentTarget || ev.target;
if (target && target.setPointerCapture && ev.pointerId != null) {
try { target.setPointerCapture(ev.pointerId); } catch (e) { /* */ }
}
var self = this;
var move = function (e) { self._onSplitMove(e); };
var up = function (e) {
window.removeEventListener('pointermove', move);
window.removeEventListener('pointerup', up);
window.removeEventListener('pointercancel', up);
if (target && target.releasePointerCapture && e && e.pointerId != null) {
try { target.releasePointerCapture(e.pointerId); } catch (err) { /* */ }
}
document.body.style.cursor = prevCursor || '';
document.body.classList.remove('pw-split-dragging');
self._setGameIframePassThrough(false);
self._splitDrag = null;
self._dirty = true;
self._save();
self._syncGameHostGeom();
self._notifyViewResize();
};
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', up);
window.addEventListener('pointercancel', up);
};
/** @param {boolean} on */
WorkbenchLayout.prototype._setGameIframePassThrough = function (on) {
var iframe = document.getElementById('pw-game-iframe');
if (iframe) iframe.style.pointerEvents = on ? 'none' : '';
var host = this.gameHost;
if (host) host.classList.toggle('pw-iframe-passthrough', !!on);
};
WorkbenchLayout.prototype._onSplitMove = function (ev) {
var d = this._splitDrag;
if (!d) return;
var delta = (d.dir === 'h' ? ev.clientX : ev.clientY) - d.startPos;
var total = d.a0 + d.b0;
if (total <= 1) return;
var a1 = Math.max(80, Math.min(total - 80, d.a0 + delta));
var b1 = total - a1;
var ra = a1 / total;
var rb = b1 / total;
var baseA = d.node.sizes[d.leftIndex];
var baseB = d.node.sizes[d.leftIndex + 1];
var pair = baseA + baseB;
d.node.sizes[d.leftIndex] = pair * ra;
d.node.sizes[d.leftIndex + 1] = pair * rb;
d.aEl.style.flex = d.node.sizes[d.leftIndex] + ' 1 0';
d.bEl.style.flex = d.node.sizes[d.leftIndex + 1] + ' 1 0';
this._syncGameHostGeom();
};
WorkbenchLayout.prototype._renderGame = function (node) {
this._ensureId(node);
var leaf = document.createElement('div');
leaf.className = 'pw-leaf pw-leaf-game';
leaf.setAttribute('data-leaf-id', node.id);
leaf.setAttribute('data-node-id', node.id);
this._leafEls[node.id] = { el: leaf, node: node };
this._nodeEls[node.id] = { el: leaf, node: node };
var ph = document.createElement('div');
ph.className = 'pw-game-placeholder';
ph.setAttribute('data-game-placeholder', '1');
leaf.appendChild(ph);
this._gamePlaceholder = ph;
return leaf;
};
WorkbenchLayout.prototype._renderTabs = function (node) {
var self = this;
this._ensureId(node);
var leaf = document.createElement('div');
leaf.className = 'pw-leaf pw-leaf-tabs';
leaf.setAttribute('data-leaf-id', node.id);
leaf.setAttribute('data-node-id', node.id);
this._leafEls[node.id] = { el: leaf, node: node };
this._nodeEls[node.id] = { el: leaf, node: node };
var tabs = document.createElement('div');
tabs.className = 'pw-leaf-tabbar';
tabs.setAttribute('data-leaf-id', node.id);
var body = document.createElement('div');
body.className = 'pw-leaf-body';
body.setAttribute('data-leaf-id', node.id);
var panels = node.panels || [];
if (!panels.length) {
leaf.appendChild(tabs);
leaf.appendChild(body);
return leaf;
}
if (panels.indexOf(node.active) < 0) node.active = panels[0];
for (var i = 0; i < panels.length; i++) {
(function (pid) {
var tab = document.createElement('button');
tab.type = 'button';
tab.className = 'pw-leaf-tab' + (pid === node.active ? ' active' : '');
tab.textContent = PANEL_TITLES[pid] || pid;
tab.setAttribute('data-panel', pid);
tab.draggable = true;
tab.addEventListener('click', function (ev) {
ev.preventDefault();
if (node.active === pid) return;
node.active = pid;
// 仅切换页签内容,禁止 rebuild(会搬移/重排导致 iframe 重载)
self._activateTabLeaf(leaf, tabs, body, node);
self._save();
self._emitVisibility();
});
tab.addEventListener('dragstart', function (ev) {
self._beginDrag(pid, ev);
});
tabs.appendChild(tab);
})(panels[i]);
}
var activeEl = this._panelBodies[node.active];
if (activeEl) body.appendChild(activeEl);
leaf.appendChild(tabs);
leaf.appendChild(body);
return leaf;
};
/**
* 就地切换页签,不重建停靠树。
* @param {HTMLElement} leaf
* @param {HTMLElement} tabs
* @param {HTMLElement} body
* @param {object} node
*/
WorkbenchLayout.prototype._activateTabLeaf = function (leaf, tabs, body, node) {
var buttons = tabs.querySelectorAll('.pw-leaf-tab');
for (var i = 0; i < buttons.length; i++) {
var b = buttons[i];
b.classList.toggle('active', b.getAttribute('data-panel') === node.active);
}
while (body.firstChild) body.removeChild(body.firstChild);
var el = this._panelBodies[node.active];
if (el) body.appendChild(el);
};
})(window);