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.
375 lines
15 KiB
375 lines
15 KiB
|
4 days ago
|
(function (global) {
|
||
|
|
'use strict';
|
||
|
|
var WorkbenchLayout = global.PWLayout;
|
||
|
|
if (!WorkbenchLayout) {
|
||
|
|
console.error('[调试工作台] layout-float 需先加载 layout-core');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var PANEL_TITLES = {
|
||
|
|
hierarchy: '层级',
|
||
|
|
inspector: '属性',
|
||
|
|
assets: '资源',
|
||
|
|
perf: '性能',
|
||
|
|
console: '控制台',
|
||
|
|
};
|
||
|
|
var PANEL_MEM_KEY = 'preview-workbench-panel-mem-v1';
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype.togglePanel = function (panelId) {
|
||
|
|
if (this.mode === 'compact') {
|
||
|
|
if (this.activeDrawer === panelId) this.closeDrawer();
|
||
|
|
else this.openDrawer(panelId);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (this._floats[panelId]) {
|
||
|
|
this._rememberFloat(panelId);
|
||
|
|
this._closeFloat(panelId, true);
|
||
|
|
this._savePanelMemory();
|
||
|
|
this._emitVisibility();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var existing = this._findTabsWith(this._tree, panelId);
|
||
|
|
if (existing) {
|
||
|
|
if (existing.panels.length === 1 && existing.active === panelId) {
|
||
|
|
this._rememberPanelFromTree(panelId);
|
||
|
|
this._removePanel(panelId);
|
||
|
|
this._tree = this._cleanup(this._tree) || this._presetTree('minimal');
|
||
|
|
this._dirty = true;
|
||
|
|
this.rebuild();
|
||
|
|
this._save();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
existing.active = panelId;
|
||
|
|
this.rebuild();
|
||
|
|
this._save();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// 打开:优先记忆位置;无记忆则悬浮,禁止强行改当前停靠树
|
||
|
|
this._openPanelByMemory(panelId);
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._openPanelByMemory = function (panelId) {
|
||
|
|
var mem = this._panelMemory[panelId];
|
||
|
|
if (mem && mem.mode === 'tabs' && mem.mates && mem.mates.length) {
|
||
|
|
for (var i = 0; i < mem.mates.length; i++) {
|
||
|
|
var host = this._findTabsWith(this._tree, mem.mates[i]);
|
||
|
|
if (host) {
|
||
|
|
if (host.panels.indexOf(panelId) < 0) host.panels.push(panelId);
|
||
|
|
host.active = panelId;
|
||
|
|
this._dirty = true;
|
||
|
|
this.rebuild();
|
||
|
|
this._save();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (mem && mem.mode === 'dock' && mem.anchors && mem.anchors.length) {
|
||
|
|
var leaves = [];
|
||
|
|
for (var a = 0; a < mem.anchors.length; a++) {
|
||
|
|
var leaf = this._findTabsWith(this._tree, mem.anchors[a]);
|
||
|
|
if (leaf && leaves.indexOf(leaf) < 0) leaves.push(leaf);
|
||
|
|
}
|
||
|
|
var bundle = this._liftToSiblingBundle(leaves);
|
||
|
|
if (bundle.length) {
|
||
|
|
if (bundle.length === 1) this._splitBeside(bundle[0], mem.edge || 'bottom', panelId);
|
||
|
|
else this._splitBesideGroup(bundle, mem.edge || 'bottom', panelId);
|
||
|
|
this._dirty = true;
|
||
|
|
this._tree = this._cleanup(this._tree) || this._tree;
|
||
|
|
this.rebuild();
|
||
|
|
this._rememberPanelFromTree(panelId);
|
||
|
|
this._save();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this._openFloat(panelId, mem && mem.float ? mem.float : null);
|
||
|
|
this._savePanelMemory();
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._rememberPanelFromTree = function (panelId) {
|
||
|
|
var leaf = this._findTabsWith(this._tree, panelId);
|
||
|
|
if (!leaf) return;
|
||
|
|
if ((leaf.panels || []).length > 1) {
|
||
|
|
var mates = [];
|
||
|
|
for (var i = 0; i < leaf.panels.length; i++) {
|
||
|
|
if (leaf.panels[i] !== panelId) mates.push(leaf.panels[i]);
|
||
|
|
}
|
||
|
|
this._panelMemory[panelId] = { mode: 'tabs', mates: mates };
|
||
|
|
this._savePanelMemory();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var parentInfo = this._findParent(this._tree, leaf);
|
||
|
|
var floatRect = null;
|
||
|
|
var info = this._leafEls[leaf.id];
|
||
|
|
if (info && info.el) {
|
||
|
|
var r = info.el.getBoundingClientRect();
|
||
|
|
var rootRect = this.root.getBoundingClientRect();
|
||
|
|
floatRect = {
|
||
|
|
x: Math.max(8, r.left - rootRect.left),
|
||
|
|
y: Math.max(40, r.top - rootRect.top),
|
||
|
|
w: Math.max(240, r.width),
|
||
|
|
h: Math.max(160, r.height),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
if (!parentInfo || !parentInfo.parent) {
|
||
|
|
this._panelMemory[panelId] = { mode: 'float', float: floatRect };
|
||
|
|
this._savePanelMemory();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var parent = parentInfo.parent;
|
||
|
|
var idx = parentInfo.index;
|
||
|
|
var neighbor = idx > 0 ? parent.children[idx - 1] : parent.children[idx + 1];
|
||
|
|
var edge = 'bottom';
|
||
|
|
if (parent.dir === 'h') edge = idx > 0 ? 'right' : 'left';
|
||
|
|
else edge = idx > 0 ? 'bottom' : 'top';
|
||
|
|
var anchors = neighbor ? this._collectPanelIds(neighbor) : [];
|
||
|
|
this._panelMemory[panelId] = {
|
||
|
|
mode: anchors.length ? 'dock' : 'float',
|
||
|
|
anchors: anchors,
|
||
|
|
edge: edge,
|
||
|
|
float: floatRect,
|
||
|
|
};
|
||
|
|
this._savePanelMemory();
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._ensureFloatLayer = function () {
|
||
|
|
if (this._floatLayer) return;
|
||
|
|
var layer = document.createElement('div');
|
||
|
|
layer.className = 'pw-float-layer';
|
||
|
|
layer.id = 'pw-float-layer';
|
||
|
|
this.root.appendChild(layer);
|
||
|
|
this._floatLayer = layer;
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._openFloat = function (panelId, rect) {
|
||
|
|
this._ensureFloatLayer();
|
||
|
|
if (this._floats[panelId]) return;
|
||
|
|
var body = this._panelBodies[panelId];
|
||
|
|
if (!body) return;
|
||
|
|
var win = document.createElement('div');
|
||
|
|
win.className = 'pw-float-win';
|
||
|
|
win.setAttribute('data-float-panel', panelId);
|
||
|
|
var x = (rect && rect.x != null) ? rect.x : 72 + Object.keys(this._floats).length * 24;
|
||
|
|
var y = (rect && rect.y != null) ? rect.y : 72 + Object.keys(this._floats).length * 24;
|
||
|
|
var w = (rect && rect.w != null) ? rect.w : 360;
|
||
|
|
var h = (rect && rect.h != null) ? rect.h : 320;
|
||
|
|
win.style.left = x + 'px';
|
||
|
|
win.style.top = y + 'px';
|
||
|
|
win.style.width = w + 'px';
|
||
|
|
win.style.height = h + 'px';
|
||
|
|
|
||
|
|
var head = document.createElement('div');
|
||
|
|
head.className = 'pw-float-head';
|
||
|
|
head.innerHTML =
|
||
|
|
'<span class="pw-float-title"></span>' +
|
||
|
|
'<span class="pw-float-hint">拖标题栏移动 / 停靠</span>' +
|
||
|
|
'<button type="button" class="pw-btn pw-float-close">关闭</button>';
|
||
|
|
head.querySelector('.pw-float-title').textContent = PANEL_TITLES[panelId] || panelId;
|
||
|
|
var content = document.createElement('div');
|
||
|
|
content.className = 'pw-float-body';
|
||
|
|
content.appendChild(body);
|
||
|
|
win.appendChild(head);
|
||
|
|
win.appendChild(content);
|
||
|
|
this._floatLayer.appendChild(win);
|
||
|
|
|
||
|
|
var self = this;
|
||
|
|
head.querySelector('.pw-float-close').addEventListener('click', function () {
|
||
|
|
self._rememberFloat(panelId);
|
||
|
|
self._closeFloat(panelId, true);
|
||
|
|
self._savePanelMemory();
|
||
|
|
});
|
||
|
|
// 仅标题栏可拖:移动窗口;拖到停靠区松手即停靠(禁止拖面板内容)
|
||
|
|
head.addEventListener('pointerdown', function (ev) {
|
||
|
|
if (ev.target && ev.target.closest && ev.target.closest('.pw-float-close')) return;
|
||
|
|
var f = self._floats[panelId];
|
||
|
|
if (!f) return;
|
||
|
|
ev.preventDefault();
|
||
|
|
if (head.setPointerCapture) head.setPointerCapture(ev.pointerId);
|
||
|
|
self._setGameIframePassThrough(true);
|
||
|
|
win.classList.add('pw-float-dragging');
|
||
|
|
self._floatDrag = {
|
||
|
|
id: panelId,
|
||
|
|
ox: ev.clientX,
|
||
|
|
oy: ev.clientY,
|
||
|
|
x: f.x,
|
||
|
|
y: f.y,
|
||
|
|
moved: false,
|
||
|
|
};
|
||
|
|
window.addEventListener('pointermove', self._onFloatMove);
|
||
|
|
window.addEventListener('pointerup', self._onFloatUp);
|
||
|
|
});
|
||
|
|
|
||
|
|
this._floats[panelId] = { x: x, y: y, w: w, h: h, el: win };
|
||
|
|
this._panelMemory[panelId] = { mode: 'float', float: { x: x, y: y, w: w, h: h } };
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._onFloatMove = function (ev) {
|
||
|
|
var d = this._floatDrag;
|
||
|
|
if (!d || !this._floats[d.id]) return;
|
||
|
|
var nx = d.x + (ev.clientX - d.ox);
|
||
|
|
var ny = d.y + (ev.clientY - d.oy);
|
||
|
|
var f = this._floats[d.id];
|
||
|
|
var rootRect = this.root.getBoundingClientRect();
|
||
|
|
f.x = Math.max(0, Math.min(rootRect.width - 80, nx));
|
||
|
|
f.y = Math.max(28, Math.min(rootRect.height - 40, ny));
|
||
|
|
f.el.style.left = f.x + 'px';
|
||
|
|
f.el.style.top = f.y + 'px';
|
||
|
|
d.moved = true;
|
||
|
|
this._probeFloatDockHover(ev.clientX, ev.clientY, d.id);
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._onFloatUp = function (ev) {
|
||
|
|
var d = this._floatDrag;
|
||
|
|
this._floatDrag = null;
|
||
|
|
window.removeEventListener('pointermove', this._onFloatMove);
|
||
|
|
window.removeEventListener('pointerup', this._onFloatUp);
|
||
|
|
this._setGameIframePassThrough(false);
|
||
|
|
if (!d) return;
|
||
|
|
var win = this._floats[d.id] && this._floats[d.id].el;
|
||
|
|
if (win) win.classList.remove('pw-float-dragging');
|
||
|
|
// 使用拖动过程中最后一次悬停结果,避免松手瞬间坐标抖动导致重探测失败
|
||
|
|
var targetId = this._hoverTargetId;
|
||
|
|
var edge = this._hoverEdge;
|
||
|
|
var group = this._hoverGroup;
|
||
|
|
var groupCols = this._hoverGroupCols;
|
||
|
|
var docked = false;
|
||
|
|
if (d.moved && edge && (targetId || (groupCols && groupCols.length) || (group && group.length))) {
|
||
|
|
this._applyDrop(d.id, targetId, edge, group, groupCols);
|
||
|
|
docked = !this._floats[d.id];
|
||
|
|
}
|
||
|
|
this._clearDragVisual();
|
||
|
|
this._dragPanel = null;
|
||
|
|
this._hoverTargetId = null;
|
||
|
|
this._hoverEdge = null;
|
||
|
|
this._hoverGroup = null;
|
||
|
|
this._hoverGroupCols = null;
|
||
|
|
if (!docked) {
|
||
|
|
this._rememberFloat(d.id);
|
||
|
|
this._savePanelMemory();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/** 悬浮窗移动时:按坐标几何命中 leaf,刷新停靠指示 */
|
||
|
|
WorkbenchLayout.prototype._probeFloatDockHover = function (cx, cy, panelId) {
|
||
|
|
this._dragPanel = panelId;
|
||
|
|
var info = this._findLeafAtClientPoint(cx, cy);
|
||
|
|
// 外缘停靠:即使点不在任何 leaf 上(例如右侧空白),仍允许
|
||
|
|
if (!info) {
|
||
|
|
var ws = this.workspace ? this.workspace.getBoundingClientRect() : null;
|
||
|
|
if (ws && ws.width > 40) {
|
||
|
|
var margin = Math.min(48, ws.width * 0.08);
|
||
|
|
if (cx >= ws.right - margin || cx <= ws.left + margin) {
|
||
|
|
var side = cx >= ws.right - margin ? 'right' : 'left';
|
||
|
|
var rootEdge = this._resolveWorkspaceSideDrop(side);
|
||
|
|
if (rootEdge) {
|
||
|
|
this._hoverTargetId = '__workspace__';
|
||
|
|
this._hoverEdge = rootEdge.edge;
|
||
|
|
this._hoverGroup = null;
|
||
|
|
this._hoverGroupCols = null;
|
||
|
|
this._paintDockHover(rootEdge, null);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this._clearDragVisual();
|
||
|
|
this._hoverTargetId = null;
|
||
|
|
this._hoverEdge = null;
|
||
|
|
this._hoverGroup = null;
|
||
|
|
this._hoverGroupCols = null;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this._updateDockHoverFromPoint(cx, cy, panelId, info, null);
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._dockFloatingPanel = function (panelId, cx, cy) {
|
||
|
|
this._probeFloatDockHover(cx, cy, panelId);
|
||
|
|
var targetId = this._hoverTargetId;
|
||
|
|
var edge = this._hoverEdge;
|
||
|
|
var group = this._hoverGroup;
|
||
|
|
var groupCols = this._hoverGroupCols;
|
||
|
|
if (!edge) return false;
|
||
|
|
if (!targetId && !(groupCols && groupCols.length) && !(group && group.length)) return false;
|
||
|
|
this._applyDrop(panelId, targetId, edge, group, groupCols);
|
||
|
|
return !this._floats[panelId];
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._rememberFloat = function (panelId) {
|
||
|
|
var f = this._floats[panelId];
|
||
|
|
if (!f) return;
|
||
|
|
this._panelMemory[panelId] = {
|
||
|
|
mode: 'float',
|
||
|
|
float: { x: f.x, y: f.y, w: f.w, h: f.h },
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._closeFloat = function (panelId, keepMemory) {
|
||
|
|
var f = this._floats[panelId];
|
||
|
|
if (!f) return;
|
||
|
|
if (f.el && f.el.parentNode) f.el.parentNode.removeChild(f.el);
|
||
|
|
delete this._floats[panelId];
|
||
|
|
if (!keepMemory) return;
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._restoreFloats = function () {
|
||
|
|
if (this._workbenchOff) return;
|
||
|
|
for (var id in this._panelMemory) {
|
||
|
|
if (!Object.prototype.hasOwnProperty.call(this._panelMemory, id)) continue;
|
||
|
|
var mem = this._panelMemory[id];
|
||
|
|
if (mem && mem.mode === 'float' && !this._findTabsWith(this._tree, id)) {
|
||
|
|
this._openFloat(id, mem.float || null);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._savePanelMemory = function () {
|
||
|
|
try {
|
||
|
|
localStorage.setItem(PANEL_MEM_KEY, JSON.stringify(this._panelMemory));
|
||
|
|
} catch (e) { /* */ }
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._loadPanelMemory = function () {
|
||
|
|
try {
|
||
|
|
var raw = localStorage.getItem(PANEL_MEM_KEY);
|
||
|
|
if (!raw) return;
|
||
|
|
var data = JSON.parse(raw);
|
||
|
|
if (data && typeof data === 'object') this._panelMemory = data;
|
||
|
|
} catch (e) {
|
||
|
|
this._panelMemory = {};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype._findFirstTabs = function (node) {
|
||
|
|
if (!node) return null;
|
||
|
|
if (node.type === 'tabs') return node;
|
||
|
|
if (node.type === 'split') {
|
||
|
|
for (var i = 0; i < node.children.length; i++) {
|
||
|
|
var f = this._findFirstTabs(node.children[i]);
|
||
|
|
if (f) return f;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype.openDrawer = function (panelId) {
|
||
|
|
var drawer = document.getElementById('pw-drawer');
|
||
|
|
var body = document.getElementById('pw-drawer-body');
|
||
|
|
var title = document.getElementById('pw-drawer-title');
|
||
|
|
var el = this._panelBodies[panelId];
|
||
|
|
if (!drawer || !body || !el) return;
|
||
|
|
body.innerHTML = '';
|
||
|
|
body.appendChild(el);
|
||
|
|
if (title) title.textContent = PANEL_TITLES[panelId] || panelId;
|
||
|
|
drawer.hidden = false;
|
||
|
|
this.activeDrawer = panelId;
|
||
|
|
this._emitVisibility();
|
||
|
|
};
|
||
|
|
|
||
|
|
WorkbenchLayout.prototype.closeDrawer = function () {
|
||
|
|
var drawer = document.getElementById('pw-drawer');
|
||
|
|
if (drawer) drawer.hidden = true;
|
||
|
|
var id = this.activeDrawer;
|
||
|
|
this.activeDrawer = '';
|
||
|
|
if (id) this.rebuild();
|
||
|
|
else this._emitVisibility();
|
||
|
|
};
|
||
|
|
|
||
|
|
})(window);
|