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