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