(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);