(function (global) { 'use strict'; var WorkbenchLayout = global.PWLayout; if (!WorkbenchLayout) { console.error('[调试工作台] layout-tree 需先加载 layout-core'); return; } WorkbenchLayout.prototype._snapshotGroupColumns = function (group, excludePanelId) { var cols = []; for (var i = 0; i < group.length; i++) { var node = group[i]; if (this._isGameNode(node)) { cols.push({ kind: 'game' }); continue; } var panels = this._collectPanelIds(node); var kept = []; for (var j = 0; j < panels.length; j++) { if (panels[j] !== excludePanelId) kept.push(panels[j]); } if (!kept.length) continue; cols.push({ kind: 'panels', panels: kept }); } return cols; }; WorkbenchLayout.prototype._collectPanelIds = function (node) { var out = []; this._walkPanelIds(node, out); return out; }; WorkbenchLayout.prototype._walkPanelIds = function (node, out) { if (!node) return; if (node.type === 'tabs') { var panels = node.panels || []; for (var i = 0; i < panels.length; i++) out.push(panels[i]); return; } if (node.type === 'split') { for (var j = 0; j < node.children.length; j++) this._walkPanelIds(node.children[j], out); } }; /** 按快照在 cleanup 后的树中找回同级列节点 */ WorkbenchLayout.prototype._resolveColumnsFromSnapshot = function (cols) { var leaves = []; for (var i = 0; i < cols.length; i++) { if (cols[i].kind === 'game') { var g = this._findNodeById(this._tree, 'leaf_game'); if (g) leaves.push(g); continue; } var panels = cols[i].panels || []; var found = null; for (var p = 0; p < panels.length; p++) { found = this._findTabsWith(this._tree, panels[p]); if (found) break; } if (found && leaves.indexOf(found) < 0) leaves.push(found); } return this._liftToSiblingBundle(leaves); }; WorkbenchLayout.prototype._pathNodes = function (target) { var path = []; var walk = function (node, acc) { if (node === target) { path = acc.concat([node]); return true; } if (node && node.type === 'split') { for (var i = 0; i < node.children.length; i++) { if (walk(node.children[i], acc.concat([node]))) return true; } } return false; }; walk(this._tree, []); return path; }; WorkbenchLayout.prototype._containsNode = function (root, target) { if (root === target) return true; if (!root || root.type !== 'split') return false; for (var i = 0; i < root.children.length; i++) { if (this._containsNode(root.children[i], target)) return true; } return false; }; WorkbenchLayout.prototype._directChildContaining = function (parent, target) { if (!parent || parent.type !== 'split') return null; for (var i = 0; i < parent.children.length; i++) { if (this._containsNode(parent.children[i], target)) return parent.children[i]; } return null; }; /** 将多个叶子提升为同一父 split 下的直接子节点(保持顺序) */ WorkbenchLayout.prototype._liftToSiblingBundle = function (leaves) { if (!leaves || !leaves.length) return []; if (leaves.length === 1) return leaves; var paths = []; for (var i = 0; i < leaves.length; i++) paths.push(this._pathNodes(leaves[i])); var parent = null; var depth = 0; while (true) { var ref = paths[0][depth]; if (!ref) break; var same = true; for (var a = 1; a < paths.length; a++) { if (paths[a][depth] !== ref) { same = false; break; } } if (!same) break; parent = ref; depth++; } if (!parent || parent.type !== 'split') return leaves; var bundle = []; var seen = {}; for (var L = 0; L < leaves.length; L++) { var child = this._directChildContaining(parent, leaves[L]); if (child && child.id && !seen[child.id]) { seen[child.id] = true; bundle.push(child); } } bundle.sort(function (a, b) { return parent.children.indexOf(a) - parent.children.indexOf(b); }); return bundle.length ? bundle : leaves; }; /** * 将一组兄弟节点包成整体,再在指定边沿外侧新建区域。 * 例:h[层级,属性,游戏] 底边 → h[ v[h[层级,属性], 控制台], 游戏] */ WorkbenchLayout.prototype._splitBesideGroup = function (nodes, edge, panelId) { if (!nodes || !nodes.length) return; var first = nodes[0]; var parentInfo = this._findParent(this._tree, first); if (!parentInfo || !parentInfo.parent) { this._splitBeside(first, edge, panelId); return; } var parent = parentInfo.parent; var start = parentInfo.index; // 校验 nodes 仍是 parent 中连续片段(允许 id 匹配) for (var i = 0; i < nodes.length; i++) { var child = parent.children[start + i]; if (!child || (child !== nodes[i] && child.id !== nodes[i].id)) { this._splitBeside(first, edge, panelId); return; } nodes[i] = child; } var end = start + nodes.length - 1; var newLeaf = this._tabsNode([panelId]); var dir = (edge === 'left' || edge === 'right') ? 'h' : 'v'; var innerDir = parent.dir; var groupNode; if (nodes.length === 1) { groupNode = nodes[0]; } else { var groupSizes = []; for (var s = start; s <= end; s++) { groupSizes.push(parent.sizes[s] != null ? parent.sizes[s] : 1); } this._normalizeSizes(groupSizes, groupSizes.length); groupNode = { type: 'split', id: 'split_' + (this._leafSeq++), dir: innerDir, sizes: groupSizes, children: nodes.slice(), }; } var wrapped = { type: 'split', id: 'split_' + (this._leafSeq++), dir: dir, sizes: [0.64, 0.36], children: (edge === 'left' || edge === 'top') ? [newLeaf, groupNode] : [groupNode, newLeaf], }; var newChildren = []; var newSizes = []; for (var c = 0; c < parent.children.length; c++) { if (c === start) { newChildren.push(wrapped); var sum = 0; for (var k = start; k <= end; k++) sum += parent.sizes[k] != null ? parent.sizes[k] : 0; newSizes.push(sum > 0 ? sum : 1); c = end; continue; } if (c < start || c > end) { newChildren.push(parent.children[c]); newSizes.push(parent.sizes[c] != null ? parent.sizes[c] : 1); } } parent.children = newChildren; parent.sizes = newSizes; this._normalizeSizes(parent.sizes, parent.children.length); }; WorkbenchLayout.prototype._splitBeside = function (targetNode, edge, panelId) { this._ensureId(targetNode); var parentInfo = this._findParent(this._tree, targetNode); var newLeaf = this._tabsNode([panelId]); var dir = (edge === 'left' || edge === 'right') ? 'h' : 'v'; var first = (edge === 'left' || edge === 'top') ? newLeaf : targetNode; var second = (edge === 'left' || edge === 'top') ? targetNode : newLeaf; if (!parentInfo) { this._tree = { type: 'split', id: 'split_' + (this._leafSeq++), dir: dir, sizes: [0.5, 0.5], children: [first, second], }; return; } var parent = parentInfo.parent; var index = parentInfo.index; this._ensureId(parent); // 同方向则插入为兄弟;否则把整个目标(可为 split 整行/整列)包进新 split if (parent.type === 'split' && parent.dir === dir) { var insertAt = (edge === 'left' || edge === 'top') ? index : index + 1; parent.children.splice(insertAt, 0, newLeaf); var n = parent.children.length; var sizes = []; for (var i = 0; i < n; i++) sizes.push(1 / n); parent.sizes = sizes; } else { var split = { type: 'split', id: 'split_' + (this._leafSeq++), dir: dir, sizes: [0.5, 0.5], children: [first, second], }; parent.children[index] = split; } }; WorkbenchLayout.prototype._removePanel = function (panelId) { this._removePanelFrom(this._tree, panelId); this._tree = this._cleanup(this._tree); }; WorkbenchLayout.prototype._removePanelFrom = function (node, panelId) { if (!node) return false; if (node.type === 'tabs') { var idx = (node.panels || []).indexOf(panelId); if (idx >= 0) { node.panels.splice(idx, 1); if (node.active === panelId) node.active = node.panels[0] || ''; return true; } return false; } if (node.type === 'split') { for (var i = 0; i < node.children.length; i++) { if (this._removePanelFrom(node.children[i], panelId)) return true; } } return false; }; WorkbenchLayout.prototype._cleanup = function (node) { if (!node) return null; if (node.type === 'game') return node; if (node.type === 'tabs') { if (!node.panels || !node.panels.length) return null; return node; } if (node.type === 'split') { var kids = []; var sizes = []; for (var i = 0; i < node.children.length; i++) { var c = this._cleanup(node.children[i]); if (c) { kids.push(c); sizes.push(node.sizes && node.sizes[i] != null ? node.sizes[i] : 1); } } if (!kids.length) return null; if (kids.length === 1) return kids[0]; // 合并同方向嵌套 var flatKids = []; var flatSizes = []; for (var j = 0; j < kids.length; j++) { if (kids[j].type === 'split' && kids[j].dir === node.dir) { var sub = kids[j]; var part = sizes[j]; var subSum = 0; for (var s = 0; s < sub.sizes.length; s++) subSum += sub.sizes[s]; for (var k = 0; k < sub.children.length; k++) { flatKids.push(sub.children[k]); flatSizes.push(part * (sub.sizes[k] / (subSum || 1))); } } else { flatKids.push(kids[j]); flatSizes.push(sizes[j]); } } this._normalizeSizes(flatSizes, flatKids.length); // 必须保留 id:按 id 停靠(尤其是整树左右新建)依赖稳定标识 return { type: 'split', id: node.id || ('split_' + (this._leafSeq++)), dir: node.dir, sizes: flatSizes, children: flatKids, }; } return node; }; WorkbenchLayout.prototype._findNodeById = function (node, id) { if (!node) return null; if (node.id === id) return node; if (node.type === 'split') { for (var i = 0; i < node.children.length; i++) { var f = this._findNodeById(node.children[i], id); if (f) return f; } } return null; }; WorkbenchLayout.prototype._findParent = function (node, child, parent, index) { if (!node) return null; if (node === child) return parent ? { parent: parent, index: index } : null; if (node.type === 'split') { for (var i = 0; i < node.children.length; i++) { var f = this._findParent(node.children[i], child, node, i); if (f) return f; } } return null; }; WorkbenchLayout.prototype._findTabsWith = function (node, panelId) { if (!node) return null; if (node.type === 'tabs' && (node.panels || []).indexOf(panelId) >= 0) return node; if (node.type === 'split') { for (var i = 0; i < node.children.length; i++) { var f = this._findTabsWith(node.children[i], panelId); if (f) return f; } } return null; }; })(window);