(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 =
'' +
'拖标题栏移动 / 停靠' +
'';
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);