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.
 
 
 
 

363 lines
13 KiB

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