@fps-games/editor 0.1.1-beta.4 → 0.1.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local-editor-harness.d.ts +6 -2
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +80 -0
- package/dist/local-editor-harness.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +4 -4
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts +2 -0
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/index.js +1 -0
- package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts +3 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js +7 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts +10 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +142 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +82 -14
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +23 -20
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +10 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +195 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +52 -64
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +13 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js +107 -14
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts +6 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +28 -7
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js +5 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.d.ts +4 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.js +100 -9
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-theme.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +24 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts +35 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +376 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +8 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +421 -42
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +2 -2
- package/node_modules/@fps-games/editor-core/package.json +2 -2
- package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +6 -6
|
@@ -2,7 +2,7 @@ import * as LocalEditorPanels from './local-editor-ui-panels';
|
|
|
2
2
|
import { createLocalEditorHierarchyController } from './local-editor-ui-hierarchy-controller';
|
|
3
3
|
import { createLocalEditorContextMenuController } from './local-editor-ui-context-menu';
|
|
4
4
|
import { DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_TRANSFORM_TOOL_DESCRIPTORS, } from '@fps-games/editor-core';
|
|
5
|
-
export { createLocalEditorHierarchyBlankMenu, createLocalEditorHierarchyDeleteShortcutAction, createLocalEditorHierarchyNodeMenu, } from './local-editor-ui-hierarchy-actions';
|
|
5
|
+
export { createLocalEditorHierarchyBlankMenu, createLocalEditorHierarchyCopyShortcutAction, createLocalEditorHierarchyDeleteShortcutAction, createLocalEditorHierarchyDuplicateShortcutAction, createLocalEditorHierarchyNodeMenu, createLocalEditorHierarchyPasteShortcutAction, } from './local-editor-ui-hierarchy-actions';
|
|
6
6
|
export { createLocalEditorHierarchyController, } from './local-editor-ui-hierarchy-controller';
|
|
7
7
|
export { canLocalEditorHierarchyNodeHaveChildren, createLocalEditorHierarchyTreeModel, isLocalEditorHierarchyNodeMovable, } from './local-editor-ui-hierarchy-tree';
|
|
8
8
|
export { createLocalEditorContextMenuController, } from './local-editor-ui-context-menu';
|
|
@@ -10,9 +10,11 @@ import { createLocalEditorWorkbenchInputRouter } from './local-editor-ui-input-r
|
|
|
10
10
|
import { createLocalEditorPanelRegistry } from './local-editor-ui-panel-registry';
|
|
11
11
|
import * as LocalEditorShared from './local-editor-ui-shared';
|
|
12
12
|
import { createShortcutHelpPanel as createLocalEditorShortcutHelpPanel } from './local-editor-ui-shortcuts';
|
|
13
|
-
import { ensureLocalEditorTheme, LOCAL_EDITOR_THEME_CLASS } from './local-editor-ui-theme';
|
|
14
|
-
import { createDefaultLocalEditorWorkbenchLayout, createLocalEditorWorkbench, createSceneHeaderToolbar, createWorkbenchPanelContent, } from './local-editor-ui-workbench';
|
|
13
|
+
import { applyLocalEditorTheme, ensureLocalEditorTheme, LOCAL_EDITOR_THEME_CLASS, normalizeLocalEditorThemeName, } from './local-editor-ui-theme';
|
|
14
|
+
import { createDefaultLocalEditorWorkbenchLayout, createLocalEditorWorkbenchLayoutController, createLocalEditorWorkbench, createSceneHeaderToolbar, createWorkbenchPanelContent, } from './local-editor-ui-workbench';
|
|
15
15
|
export { applyLocalEditorBrowserInspectorControlBinding, createLocalEditorBrowserInspectorControlRegistry, formatLocalEditorBrowserInspectorValue, resolveLocalEditorBrowserInspectorControlRegistration, } from './local-editor-ui-panels';
|
|
16
|
+
export { DEFAULT_LOCAL_EDITOR_THEME, LOCAL_EDITOR_THEME_CLASS, applyLocalEditorTheme, normalizeLocalEditorThemeName, } from './local-editor-ui-theme';
|
|
17
|
+
export { LOCAL_EDITOR_ICON_NAMES, createLocalEditorIcon, isLocalEditorIconName, resolveLocalEditorIconName, } from './local-editor-ui-icons';
|
|
16
18
|
function readInspectorInputValue(input) {
|
|
17
19
|
const control = input.dataset.serializedControl;
|
|
18
20
|
const valueType = input.dataset.serializedValueType;
|
|
@@ -50,6 +52,21 @@ function readInspectorInputValue(input) {
|
|
|
50
52
|
}
|
|
51
53
|
return input.value;
|
|
52
54
|
}
|
|
55
|
+
const TRANSFORM_TOOL_ICONS = {
|
|
56
|
+
select: 'select',
|
|
57
|
+
move: 'move',
|
|
58
|
+
rotate: 'rotate',
|
|
59
|
+
scale: 'scale',
|
|
60
|
+
};
|
|
61
|
+
const TRANSFORM_SPACE_ICONS = {
|
|
62
|
+
world: 'world',
|
|
63
|
+
local: 'local',
|
|
64
|
+
};
|
|
65
|
+
const PLACEMENT_MODE_ICONS = {
|
|
66
|
+
off: 'place-off',
|
|
67
|
+
ground: 'place-ground',
|
|
68
|
+
surface: 'place-surface',
|
|
69
|
+
};
|
|
53
70
|
function readInspectorVectorInputValue(input) {
|
|
54
71
|
const wrapper = input.closest('[data-inspector-vector-control]');
|
|
55
72
|
const values = {};
|
|
@@ -99,11 +116,13 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
99
116
|
const doc = options.document ?? document;
|
|
100
117
|
const root = options.root ?? doc.body;
|
|
101
118
|
const callbacks = options.callbacks ?? {};
|
|
119
|
+
const localTestActionsEnabled = options.localTestActions === true;
|
|
102
120
|
ensureLocalEditorTheme(doc);
|
|
121
|
+
let activeTheme = normalizeLocalEditorThemeName(options.theme);
|
|
103
122
|
const inputRouter = createLocalEditorWorkbenchInputRouter(doc);
|
|
104
123
|
const contextMenu = createLocalEditorContextMenuController(doc, (open) => {
|
|
105
124
|
inputRouter.setContextMenuOpen(open);
|
|
106
|
-
});
|
|
125
|
+
}, activeTheme);
|
|
107
126
|
let currentState = null;
|
|
108
127
|
let inspectorFilter = '';
|
|
109
128
|
const workbenchLayout = createDefaultLocalEditorWorkbenchLayout();
|
|
@@ -125,7 +144,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
125
144
|
'border:1px solid var(--fps-editor-border)',
|
|
126
145
|
'border-radius:3px',
|
|
127
146
|
'background:var(--fps-editor-chrome)',
|
|
128
|
-
'box-shadow:
|
|
147
|
+
'box-shadow:var(--fps-editor-shadow-panel)',
|
|
129
148
|
'font-family:var(--fps-editor-font)',
|
|
130
149
|
'color:var(--fps-editor-text)',
|
|
131
150
|
'font-size:12px',
|
|
@@ -138,7 +157,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
138
157
|
'font-weight:800',
|
|
139
158
|
'white-space:nowrap',
|
|
140
159
|
].join(';');
|
|
141
|
-
const enterEditorButton = LocalEditorShared.createButton(doc, '进入编辑场景');
|
|
160
|
+
const enterEditorButton = LocalEditorShared.createButton(doc, '进入编辑场景', { icon: 'execute' });
|
|
142
161
|
hostChrome.appendChild(hostModeLabel);
|
|
143
162
|
hostChrome.appendChild(enterEditorButton);
|
|
144
163
|
root.appendChild(hostChrome);
|
|
@@ -149,18 +168,50 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
149
168
|
'align-items:center',
|
|
150
169
|
'padding:0 7px',
|
|
151
170
|
'border-radius:999px',
|
|
152
|
-
'background:
|
|
153
|
-
'border:1px solid
|
|
154
|
-
'color
|
|
171
|
+
'background:var(--fps-editor-danger-soft)',
|
|
172
|
+
'border:1px solid var(--fps-editor-danger-border)',
|
|
173
|
+
'color:var(--fps-editor-danger-text)',
|
|
155
174
|
'font-size:11px',
|
|
156
175
|
'font-weight:800',
|
|
157
176
|
].join(';');
|
|
158
177
|
dirtyBadge.textContent = '未保存';
|
|
159
|
-
const saveButton = LocalEditorShared.createButton(doc, '保存场景');
|
|
160
|
-
const saveAndRunButton = LocalEditorShared.createButton(doc, '保存并运行');
|
|
161
|
-
const discardRunButton = LocalEditorShared.createButton(doc, '放弃并运行');
|
|
162
|
-
const
|
|
163
|
-
|
|
178
|
+
const saveButton = LocalEditorShared.createButton(doc, '保存场景', { icon: 'save' });
|
|
179
|
+
const saveAndRunButton = LocalEditorShared.createButton(doc, '保存并运行', { icon: 'execute' });
|
|
180
|
+
const discardRunButton = LocalEditorShared.createButton(doc, '放弃并运行', { icon: 'discard' });
|
|
181
|
+
const localTestButton = LocalEditorShared.createButton(doc, '本地测试', { icon: 'execute' });
|
|
182
|
+
localTestButton.dataset.editorLocalTestToggle = 'true';
|
|
183
|
+
localTestButton.title = '打开本地测试操作';
|
|
184
|
+
const localTestMenu = doc.createElement('div');
|
|
185
|
+
localTestMenu.classList.add(LOCAL_EDITOR_THEME_CLASS);
|
|
186
|
+
localTestMenu.dataset.editorLocalTestMenu = 'true';
|
|
187
|
+
localTestMenu.style.cssText = [
|
|
188
|
+
'position:fixed',
|
|
189
|
+
'z-index:2147483641',
|
|
190
|
+
'display:none',
|
|
191
|
+
'flex-direction:column',
|
|
192
|
+
'gap:6px',
|
|
193
|
+
'min-width:150px',
|
|
194
|
+
'padding:8px',
|
|
195
|
+
'border:1px solid var(--fps-editor-border)',
|
|
196
|
+
'border-radius:3px',
|
|
197
|
+
'background:var(--fps-editor-panel)',
|
|
198
|
+
'box-shadow:var(--fps-editor-shadow-popover)',
|
|
199
|
+
'pointer-events:auto',
|
|
200
|
+
].join(';');
|
|
201
|
+
for (const button of [saveButton, saveAndRunButton, discardRunButton]) {
|
|
202
|
+
button.style.justifyContent = 'flex-start';
|
|
203
|
+
button.style.width = '100%';
|
|
204
|
+
localTestMenu.appendChild(button);
|
|
205
|
+
}
|
|
206
|
+
const localTestGroup = doc.createElement('div');
|
|
207
|
+
localTestGroup.style.cssText = [
|
|
208
|
+
`display:${localTestActionsEnabled ? 'flex' : 'none'}`,
|
|
209
|
+
'align-items:center',
|
|
210
|
+
'gap:4px',
|
|
211
|
+
].join(';');
|
|
212
|
+
localTestGroup.appendChild(localTestButton);
|
|
213
|
+
const undoButton = LocalEditorShared.createButton(doc, '撤销', { icon: 'undo' });
|
|
214
|
+
const redoButton = LocalEditorShared.createButton(doc, '重做', { icon: 'redo' });
|
|
164
215
|
let helpOpen = false;
|
|
165
216
|
const status = doc.createElement('span');
|
|
166
217
|
status.style.cssText = [
|
|
@@ -180,6 +231,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
180
231
|
workbench.leftDock.appendChild(hierarchyPanel);
|
|
181
232
|
workbench.rightDock.appendChild(inspectorPanel);
|
|
182
233
|
root.appendChild(workbench.root);
|
|
234
|
+
const workbenchLayoutController = createLocalEditorWorkbenchLayoutController(doc, workbench);
|
|
183
235
|
const sceneToolOverlay = createSceneHeaderToolbar(doc);
|
|
184
236
|
const sceneTitle = doc.createElement('div');
|
|
185
237
|
sceneTitle.textContent = 'Preview';
|
|
@@ -197,6 +249,14 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
197
249
|
].join(';');
|
|
198
250
|
const sceneQuickActions = doc.createElement('div');
|
|
199
251
|
sceneQuickActions.style.cssText = 'display:flex;align-items:center;gap:4px';
|
|
252
|
+
const sceneUtilityActions = doc.createElement('div');
|
|
253
|
+
sceneUtilityActions.style.cssText = [
|
|
254
|
+
'display:flex',
|
|
255
|
+
'align-items:center',
|
|
256
|
+
'gap:4px',
|
|
257
|
+
'padding-left:8px',
|
|
258
|
+
'border-left:1px solid var(--fps-editor-divider)',
|
|
259
|
+
].join(';');
|
|
200
260
|
const toolGroup = doc.createElement('div');
|
|
201
261
|
toolGroup.style.cssText = [
|
|
202
262
|
'display:flex',
|
|
@@ -209,7 +269,9 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
209
269
|
const transformToolDescriptors = Object.values(DEFAULT_EDITOR_TRANSFORM_TOOL_DESCRIPTORS);
|
|
210
270
|
for (const descriptor of transformToolDescriptors) {
|
|
211
271
|
const shortcut = descriptor.shortcut ? `${descriptor.shortcut} ` : '';
|
|
212
|
-
const button = LocalEditorShared.createButton(doc, `${shortcut}${descriptor.label}
|
|
272
|
+
const button = LocalEditorShared.createButton(doc, `${shortcut}${descriptor.label}`, {
|
|
273
|
+
icon: TRANSFORM_TOOL_ICONS[descriptor.tool],
|
|
274
|
+
});
|
|
213
275
|
button.dataset.transformTool = descriptor.tool;
|
|
214
276
|
button.title = descriptor.handles.length > 0
|
|
215
277
|
? `${descriptor.label} · ${descriptor.handles.map(handle => handle.label).join(' / ')}`
|
|
@@ -226,8 +288,8 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
226
288
|
'border-left:1px solid var(--fps-editor-divider)',
|
|
227
289
|
].join(';');
|
|
228
290
|
const spaceButtons = {
|
|
229
|
-
world: LocalEditorShared.createButton(doc, '世界'),
|
|
230
|
-
local: LocalEditorShared.createButton(doc, '本地'),
|
|
291
|
+
world: LocalEditorShared.createButton(doc, '世界', { icon: TRANSFORM_SPACE_ICONS.world }),
|
|
292
|
+
local: LocalEditorShared.createButton(doc, '本地', { icon: TRANSFORM_SPACE_ICONS.local }),
|
|
231
293
|
};
|
|
232
294
|
for (const [space, button] of Object.entries(spaceButtons)) {
|
|
233
295
|
button.dataset.transformSpace = space;
|
|
@@ -249,7 +311,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
249
311
|
'padding-left:8px',
|
|
250
312
|
'border-left:1px solid var(--fps-editor-divider)',
|
|
251
313
|
].join(';');
|
|
252
|
-
const snapButton = LocalEditorShared.createButton(doc, '吸附');
|
|
314
|
+
const snapButton = LocalEditorShared.createButton(doc, '吸附', { icon: 'snap' });
|
|
253
315
|
snapButton.dataset.transformSnapToggle = 'true';
|
|
254
316
|
snapButton.title = '移动 / 旋转 / 缩放吸附';
|
|
255
317
|
const snapStepInputs = new Map();
|
|
@@ -293,9 +355,9 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
293
355
|
placementLabel.textContent = '放置';
|
|
294
356
|
placementLabel.style.cssText = 'color:var(--fps-editor-muted);font-size:11px;font-weight:900;white-space:nowrap';
|
|
295
357
|
const placementButtons = {
|
|
296
|
-
off: LocalEditorShared.createButton(doc, '关'),
|
|
297
|
-
ground: LocalEditorShared.createButton(doc, '地'),
|
|
298
|
-
surface: LocalEditorShared.createButton(doc, '表'),
|
|
358
|
+
off: LocalEditorShared.createButton(doc, '关', { icon: PLACEMENT_MODE_ICONS.off }),
|
|
359
|
+
ground: LocalEditorShared.createButton(doc, '地', { icon: PLACEMENT_MODE_ICONS.ground }),
|
|
360
|
+
surface: LocalEditorShared.createButton(doc, '表', { icon: PLACEMENT_MODE_ICONS.surface }),
|
|
299
361
|
};
|
|
300
362
|
placementButtons.off.title = '关闭放置模式';
|
|
301
363
|
placementButtons.ground.title = '放置到 XZ 地面';
|
|
@@ -342,7 +404,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
342
404
|
transformActionSelect.appendChild(option);
|
|
343
405
|
}
|
|
344
406
|
transformActionSelect.value = selectedTransformAction;
|
|
345
|
-
const transformActionButton = LocalEditorShared.createButton(doc, '执行');
|
|
407
|
+
const transformActionButton = LocalEditorShared.createButton(doc, '执行', { icon: 'execute' });
|
|
346
408
|
transformActionButton.dataset.transformActionRun = 'true';
|
|
347
409
|
transformActionButton.title = '对当前多选执行 Transform 操作';
|
|
348
410
|
actionGroup.appendChild(transformActionSelect);
|
|
@@ -368,15 +430,17 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
368
430
|
'font-size:11px',
|
|
369
431
|
].join(';');
|
|
370
432
|
sceneMouseHint.textContent = '左键选择 · 空白拖拽框选 · 中键平移 · Alt+左键环绕 · 右键飞行 · 滚轮缩放';
|
|
371
|
-
const
|
|
433
|
+
const themeToggleButton = LocalEditorShared.createButton(doc, '主题', { icon: 'theme' });
|
|
434
|
+
themeToggleButton.dataset.editorThemeToggle = 'true';
|
|
435
|
+
themeToggleButton.style.padding = '5px 7px';
|
|
436
|
+
const sceneHelpButton = LocalEditorShared.createButton(doc, '快捷键', { icon: 'help' });
|
|
372
437
|
sceneHelpButton.style.padding = '5px 7px';
|
|
373
|
-
sceneQuickActions.appendChild(
|
|
374
|
-
sceneQuickActions.appendChild(saveAndRunButton);
|
|
375
|
-
sceneQuickActions.appendChild(discardRunButton);
|
|
438
|
+
sceneQuickActions.appendChild(localTestGroup);
|
|
376
439
|
sceneQuickActions.appendChild(undoButton);
|
|
377
440
|
sceneQuickActions.appendChild(redoButton);
|
|
378
441
|
sceneQuickActions.appendChild(dirtyBadge);
|
|
379
|
-
|
|
442
|
+
sceneUtilityActions.appendChild(themeToggleButton);
|
|
443
|
+
sceneUtilityActions.appendChild(sceneHelpButton);
|
|
380
444
|
const cameraPreviewGroup = doc.createElement('div');
|
|
381
445
|
cameraPreviewGroup.style.cssText = [
|
|
382
446
|
'display:flex',
|
|
@@ -385,12 +449,37 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
385
449
|
'padding-left:8px',
|
|
386
450
|
'border-left:1px solid var(--fps-editor-divider)',
|
|
387
451
|
].join(';');
|
|
388
|
-
const sceneCameraButton = LocalEditorShared.createButton(doc, 'Scene Camera');
|
|
452
|
+
const sceneCameraButton = LocalEditorShared.createButton(doc, 'Scene Camera', { icon: 'camera' });
|
|
389
453
|
sceneCameraButton.dataset.sceneCameraPreviewToggle = 'true';
|
|
390
454
|
sceneCameraButton.title = '从 Main Camera 查看当前场景';
|
|
391
455
|
cameraPreviewGroup.appendChild(sceneCameraButton);
|
|
456
|
+
const toolbarOverflowButton = LocalEditorShared.createButton(doc, '更多', { icon: 'chevron-down' });
|
|
457
|
+
toolbarOverflowButton.dataset.editorToolbarOverflowToggle = 'true';
|
|
458
|
+
toolbarOverflowButton.title = '显示隐藏的工具栏命令';
|
|
459
|
+
toolbarOverflowButton.style.display = 'none';
|
|
460
|
+
const toolbarOverflowMenu = doc.createElement('div');
|
|
461
|
+
toolbarOverflowMenu.classList.add(LOCAL_EDITOR_THEME_CLASS);
|
|
462
|
+
toolbarOverflowMenu.dataset.editorToolbarOverflowMenu = 'true';
|
|
463
|
+
toolbarOverflowMenu.style.cssText = [
|
|
464
|
+
'position:fixed',
|
|
465
|
+
'z-index:2147483641',
|
|
466
|
+
'display:none',
|
|
467
|
+
'flex-direction:column',
|
|
468
|
+
'gap:6px',
|
|
469
|
+
'min-width:220px',
|
|
470
|
+
'max-width:min(420px, calc(100vw - 16px))',
|
|
471
|
+
'max-height:calc(100vh - 72px)',
|
|
472
|
+
'overflow:auto',
|
|
473
|
+
'padding:8px',
|
|
474
|
+
'border:1px solid var(--fps-editor-border)',
|
|
475
|
+
'border-radius:3px',
|
|
476
|
+
'background:var(--fps-editor-panel)',
|
|
477
|
+
'box-shadow:var(--fps-editor-shadow-popover)',
|
|
478
|
+
'pointer-events:auto',
|
|
479
|
+
].join(';');
|
|
392
480
|
sceneToolOverlay.appendChild(sceneTitle);
|
|
393
481
|
sceneToolOverlay.appendChild(sceneQuickActions);
|
|
482
|
+
sceneToolOverlay.appendChild(sceneUtilityActions);
|
|
394
483
|
sceneToolOverlay.appendChild(cameraPreviewGroup);
|
|
395
484
|
sceneToolOverlay.appendChild(toolGroup);
|
|
396
485
|
sceneToolOverlay.appendChild(spaceGroup);
|
|
@@ -401,25 +490,290 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
401
490
|
sceneToolOverlay.appendChild(sceneToolStatus);
|
|
402
491
|
sceneToolOverlay.appendChild(sceneMouseHint);
|
|
403
492
|
sceneToolOverlay.appendChild(status);
|
|
493
|
+
sceneToolOverlay.appendChild(toolbarOverflowButton);
|
|
404
494
|
workbench.sceneHeader.appendChild(sceneToolOverlay);
|
|
495
|
+
root.appendChild(localTestMenu);
|
|
496
|
+
root.appendChild(toolbarOverflowMenu);
|
|
405
497
|
const boxSelectionOverlay = doc.createElement('div');
|
|
498
|
+
boxSelectionOverlay.classList.add(LOCAL_EDITOR_THEME_CLASS);
|
|
406
499
|
boxSelectionOverlay.style.cssText = [
|
|
407
500
|
'position:fixed',
|
|
408
501
|
'z-index:2147483637',
|
|
409
502
|
'display:none',
|
|
410
|
-
'border:1px solid
|
|
411
|
-
'background:
|
|
412
|
-
'box-shadow:
|
|
503
|
+
'border:1px solid var(--fps-editor-drop-target)',
|
|
504
|
+
'background:var(--fps-editor-box-select-bg)',
|
|
505
|
+
'box-shadow:var(--fps-editor-box-select-shadow)',
|
|
413
506
|
'pointer-events:none',
|
|
414
507
|
].join(';');
|
|
415
508
|
root.appendChild(boxSelectionOverlay);
|
|
416
509
|
const shortcutHelpPanel = createLocalEditorShortcutHelpPanel(doc);
|
|
417
510
|
shortcutHelpPanel.classList.add(LOCAL_EDITOR_THEME_CLASS);
|
|
418
511
|
root.appendChild(shortcutHelpPanel);
|
|
512
|
+
function applyThemeToSurfaces() {
|
|
513
|
+
applyLocalEditorTheme(hostChrome, activeTheme);
|
|
514
|
+
applyLocalEditorTheme(workbench.root, activeTheme);
|
|
515
|
+
applyLocalEditorTheme(shortcutHelpPanel, activeTheme);
|
|
516
|
+
applyLocalEditorTheme(boxSelectionOverlay, activeTheme);
|
|
517
|
+
applyLocalEditorTheme(localTestMenu, activeTheme);
|
|
518
|
+
applyLocalEditorTheme(toolbarOverflowMenu, activeTheme);
|
|
519
|
+
contextMenu.setTheme?.(activeTheme);
|
|
520
|
+
}
|
|
521
|
+
function updateThemeToggleButton() {
|
|
522
|
+
const nextTheme = activeTheme === 'dark' ? 'light' : 'dark';
|
|
523
|
+
themeToggleButton.dataset.editorTheme = activeTheme;
|
|
524
|
+
themeToggleButton.setAttribute('aria-pressed', activeTheme === 'light' ? 'true' : 'false');
|
|
525
|
+
themeToggleButton.title = nextTheme === 'light' ? '切换为浅色主题' : '切换为深色主题';
|
|
526
|
+
}
|
|
527
|
+
function setActiveTheme(theme) {
|
|
528
|
+
activeTheme = normalizeLocalEditorThemeName(theme);
|
|
529
|
+
applyThemeToSurfaces();
|
|
530
|
+
updateThemeToggleButton();
|
|
531
|
+
}
|
|
532
|
+
let localTestMenuOpen = false;
|
|
533
|
+
function closeLocalTestMenu() {
|
|
534
|
+
localTestMenuOpen = false;
|
|
535
|
+
localTestMenu.style.display = 'none';
|
|
536
|
+
LocalEditorShared.applyButtonActiveState(localTestButton, false);
|
|
537
|
+
}
|
|
538
|
+
function openLocalTestMenu() {
|
|
539
|
+
if (!localTestActionsEnabled || localTestButton.style.display === 'none')
|
|
540
|
+
return;
|
|
541
|
+
closeToolbarOverflowMenu();
|
|
542
|
+
const win = doc.defaultView;
|
|
543
|
+
const rect = localTestButton.getBoundingClientRect();
|
|
544
|
+
const viewportWidth = win?.innerWidth ?? doc.documentElement.clientWidth;
|
|
545
|
+
localTestMenu.style.top = `${Math.max(8, rect.bottom + 5)}px`;
|
|
546
|
+
localTestMenu.style.right = `${Math.max(8, viewportWidth - rect.right)}px`;
|
|
547
|
+
localTestMenu.style.display = 'flex';
|
|
548
|
+
localTestMenuOpen = true;
|
|
549
|
+
LocalEditorShared.applyButtonActiveState(localTestButton, true);
|
|
550
|
+
}
|
|
551
|
+
const onLocalTestButtonClick = (event) => {
|
|
552
|
+
event.stopPropagation();
|
|
553
|
+
if (localTestMenuOpen)
|
|
554
|
+
closeLocalTestMenu();
|
|
555
|
+
else
|
|
556
|
+
openLocalTestMenu();
|
|
557
|
+
};
|
|
558
|
+
const toolbarOverflowStyleProperties = [
|
|
559
|
+
'alignSelf',
|
|
560
|
+
'borderLeft',
|
|
561
|
+
'display',
|
|
562
|
+
'flex',
|
|
563
|
+
'height',
|
|
564
|
+
'maxWidth',
|
|
565
|
+
'minHeight',
|
|
566
|
+
'minWidth',
|
|
567
|
+
'overflow',
|
|
568
|
+
'paddingLeft',
|
|
569
|
+
'textOverflow',
|
|
570
|
+
'whiteSpace',
|
|
571
|
+
'width',
|
|
572
|
+
];
|
|
573
|
+
const toolbarButtonOriginalPadding = new WeakMap();
|
|
574
|
+
const toolbarOverflowOriginalStyles = new WeakMap();
|
|
575
|
+
const toolbarOrder = [
|
|
576
|
+
sceneTitle,
|
|
577
|
+
sceneQuickActions,
|
|
578
|
+
sceneUtilityActions,
|
|
579
|
+
cameraPreviewGroup,
|
|
580
|
+
toolGroup,
|
|
581
|
+
spaceGroup,
|
|
582
|
+
handleGroup,
|
|
583
|
+
snapGroup,
|
|
584
|
+
placementGroup,
|
|
585
|
+
actionGroup,
|
|
586
|
+
sceneToolStatus,
|
|
587
|
+
sceneMouseHint,
|
|
588
|
+
status,
|
|
589
|
+
toolbarOverflowButton,
|
|
590
|
+
];
|
|
591
|
+
const toolbarOverflowItems = [
|
|
592
|
+
{ id: 'status', element: status, kind: 'text' },
|
|
593
|
+
{ id: 'mouse-hint', element: sceneMouseHint, kind: 'text' },
|
|
594
|
+
{ id: 'tool-status', element: sceneToolStatus, kind: 'text' },
|
|
595
|
+
{ id: 'transform-actions', element: actionGroup, kind: 'group' },
|
|
596
|
+
{ id: 'placement', element: placementGroup, kind: 'group' },
|
|
597
|
+
{ id: 'snap', element: snapGroup, kind: 'group' },
|
|
598
|
+
{ id: 'handles', element: handleGroup, kind: 'group' },
|
|
599
|
+
{ id: 'space', element: spaceGroup, kind: 'group' },
|
|
600
|
+
{ id: 'camera-preview', element: cameraPreviewGroup, kind: 'group' },
|
|
601
|
+
{ id: 'scene-utilities', element: sceneUtilityActions, kind: 'group' },
|
|
602
|
+
];
|
|
603
|
+
let toolbarOverflowOpen = false;
|
|
604
|
+
let toolbarOverflowRaf = null;
|
|
605
|
+
function restoreToolbarOverflowItemStyle(item) {
|
|
606
|
+
const original = toolbarOverflowOriginalStyles.get(item.element);
|
|
607
|
+
if (!original)
|
|
608
|
+
return;
|
|
609
|
+
for (const property of toolbarOverflowStyleProperties) {
|
|
610
|
+
item.element.style[property] = original[property] ?? '';
|
|
611
|
+
}
|
|
612
|
+
toolbarOverflowOriginalStyles.delete(item.element);
|
|
613
|
+
item.element.dataset.editorToolbarOverflowPlacement = 'toolbar';
|
|
614
|
+
}
|
|
615
|
+
function applyToolbarOverflowMenuStyle(item) {
|
|
616
|
+
if (!toolbarOverflowOriginalStyles.has(item.element)) {
|
|
617
|
+
const original = {};
|
|
618
|
+
for (const property of toolbarOverflowStyleProperties) {
|
|
619
|
+
original[property] = item.element.style[property];
|
|
620
|
+
}
|
|
621
|
+
toolbarOverflowOriginalStyles.set(item.element, original);
|
|
622
|
+
}
|
|
623
|
+
item.element.dataset.editorToolbarOverflowPlacement = 'menu';
|
|
624
|
+
item.element.style.alignSelf = 'stretch';
|
|
625
|
+
item.element.style.borderLeft = '0';
|
|
626
|
+
item.element.style.display = item.kind === 'group' ? 'flex' : 'block';
|
|
627
|
+
item.element.style.flex = '0 0 auto';
|
|
628
|
+
item.element.style.height = '';
|
|
629
|
+
item.element.style.maxWidth = 'none';
|
|
630
|
+
item.element.style.minHeight = '0';
|
|
631
|
+
item.element.style.minWidth = '0';
|
|
632
|
+
item.element.style.overflow = item.kind === 'group' ? 'visible' : 'hidden';
|
|
633
|
+
item.element.style.paddingLeft = '0';
|
|
634
|
+
item.element.style.textOverflow = item.kind === 'text' ? 'ellipsis' : '';
|
|
635
|
+
item.element.style.whiteSpace = item.kind === 'text' ? 'nowrap' : '';
|
|
636
|
+
item.element.style.width = '100%';
|
|
637
|
+
}
|
|
638
|
+
function setToolbarButtonCompact(button, compact) {
|
|
639
|
+
const label = button.querySelector('[data-editor-button-label]');
|
|
640
|
+
if (!label)
|
|
641
|
+
return;
|
|
642
|
+
if (!toolbarButtonOriginalPadding.has(button)) {
|
|
643
|
+
toolbarButtonOriginalPadding.set(button, button.style.padding);
|
|
644
|
+
}
|
|
645
|
+
const accessibleLabel = button.dataset.editorToolbarLabel || label.textContent || button.title;
|
|
646
|
+
button.dataset.editorToolbarLabel = accessibleLabel;
|
|
647
|
+
button.setAttribute('aria-label', accessibleLabel);
|
|
648
|
+
if (!button.title)
|
|
649
|
+
button.title = accessibleLabel;
|
|
650
|
+
button.style.padding = compact ? '4px 6px' : toolbarButtonOriginalPadding.get(button) ?? '';
|
|
651
|
+
label.style.display = compact ? 'inline-block' : '';
|
|
652
|
+
label.style.width = compact ? '0' : '';
|
|
653
|
+
label.style.maxWidth = compact ? '0' : '';
|
|
654
|
+
label.style.opacity = compact ? '0' : '';
|
|
655
|
+
label.style.marginLeft = compact ? '-5px' : '';
|
|
656
|
+
}
|
|
657
|
+
function setToolbarCompact(compact) {
|
|
658
|
+
for (const button of Array.from(sceneToolOverlay.querySelectorAll('button'))) {
|
|
659
|
+
setToolbarButtonCompact(button, compact);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function closeToolbarOverflowMenu() {
|
|
663
|
+
toolbarOverflowOpen = false;
|
|
664
|
+
toolbarOverflowMenu.style.display = 'none';
|
|
665
|
+
LocalEditorShared.applyButtonActiveState(toolbarOverflowButton, false);
|
|
666
|
+
}
|
|
667
|
+
function openToolbarOverflowMenu() {
|
|
668
|
+
if (toolbarOverflowMenu.childElementCount === 0)
|
|
669
|
+
return;
|
|
670
|
+
closeLocalTestMenu();
|
|
671
|
+
const win = doc.defaultView;
|
|
672
|
+
const rect = toolbarOverflowButton.getBoundingClientRect();
|
|
673
|
+
const viewportWidth = win?.innerWidth ?? doc.documentElement.clientWidth;
|
|
674
|
+
toolbarOverflowMenu.style.top = `${Math.max(8, rect.bottom + 5)}px`;
|
|
675
|
+
toolbarOverflowMenu.style.right = `${Math.max(8, viewportWidth - rect.right)}px`;
|
|
676
|
+
toolbarOverflowMenu.style.display = 'flex';
|
|
677
|
+
toolbarOverflowOpen = true;
|
|
678
|
+
LocalEditorShared.applyButtonActiveState(toolbarOverflowButton, true);
|
|
679
|
+
}
|
|
680
|
+
function resetToolbarOverflowLayout() {
|
|
681
|
+
setToolbarCompact(false);
|
|
682
|
+
for (const item of toolbarOverflowItems)
|
|
683
|
+
restoreToolbarOverflowItemStyle(item);
|
|
684
|
+
for (const item of toolbarOrder) {
|
|
685
|
+
item.dataset.editorToolbarOverflowed = 'false';
|
|
686
|
+
sceneToolOverlay.appendChild(item);
|
|
687
|
+
}
|
|
688
|
+
LocalEditorShared.clearElement(toolbarOverflowMenu);
|
|
689
|
+
toolbarOverflowButton.style.display = 'none';
|
|
690
|
+
sceneToolOverlay.style.overflowX = 'hidden';
|
|
691
|
+
closeToolbarOverflowMenu();
|
|
692
|
+
}
|
|
693
|
+
function layoutToolbarOverflow() {
|
|
694
|
+
toolbarOverflowRaf = null;
|
|
695
|
+
resetToolbarOverflowLayout();
|
|
696
|
+
if (sceneToolOverlay.style.display === 'none')
|
|
697
|
+
return;
|
|
698
|
+
const availableWidth = sceneToolOverlay.clientWidth;
|
|
699
|
+
if (availableWidth <= 0)
|
|
700
|
+
return;
|
|
701
|
+
const isOverflowing = () => sceneToolOverlay.scrollWidth > availableWidth + 1;
|
|
702
|
+
if (!isOverflowing())
|
|
703
|
+
return;
|
|
704
|
+
toolbarOverflowButton.style.display = 'inline-flex';
|
|
705
|
+
for (const item of toolbarOverflowItems) {
|
|
706
|
+
if (!isOverflowing())
|
|
707
|
+
break;
|
|
708
|
+
if (item.element.style.display === 'none')
|
|
709
|
+
continue;
|
|
710
|
+
item.element.dataset.editorToolbarOverflowed = 'true';
|
|
711
|
+
applyToolbarOverflowMenuStyle(item);
|
|
712
|
+
toolbarOverflowMenu.appendChild(item.element);
|
|
713
|
+
}
|
|
714
|
+
if (toolbarOverflowMenu.childElementCount === 0) {
|
|
715
|
+
toolbarOverflowButton.style.display = 'none';
|
|
716
|
+
}
|
|
717
|
+
if (isOverflowing()) {
|
|
718
|
+
setToolbarCompact(true);
|
|
719
|
+
}
|
|
720
|
+
sceneToolOverlay.style.overflowX = isOverflowing() ? 'auto' : 'hidden';
|
|
721
|
+
}
|
|
722
|
+
function scheduleToolbarOverflowLayout() {
|
|
723
|
+
const win = doc.defaultView;
|
|
724
|
+
if (!win) {
|
|
725
|
+
layoutToolbarOverflow();
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (toolbarOverflowRaf != null)
|
|
729
|
+
win.cancelAnimationFrame(toolbarOverflowRaf);
|
|
730
|
+
toolbarOverflowRaf = win.requestAnimationFrame(layoutToolbarOverflow);
|
|
731
|
+
}
|
|
732
|
+
const onToolbarOverflowButtonClick = (event) => {
|
|
733
|
+
event.stopPropagation();
|
|
734
|
+
if (toolbarOverflowOpen)
|
|
735
|
+
closeToolbarOverflowMenu();
|
|
736
|
+
else
|
|
737
|
+
openToolbarOverflowMenu();
|
|
738
|
+
};
|
|
739
|
+
const onToolbarOverflowPointerDown = (event) => {
|
|
740
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
741
|
+
if (!target)
|
|
742
|
+
return;
|
|
743
|
+
if (target.closest('[data-editor-local-test-menu]') || target.closest('[data-editor-local-test-toggle]'))
|
|
744
|
+
return;
|
|
745
|
+
if (target.closest('[data-editor-toolbar-overflow-menu]') || target.closest('[data-editor-toolbar-overflow-toggle]'))
|
|
746
|
+
return;
|
|
747
|
+
closeLocalTestMenu();
|
|
748
|
+
closeToolbarOverflowMenu();
|
|
749
|
+
};
|
|
750
|
+
const ResizeObserverCtor = doc.defaultView?.ResizeObserver;
|
|
751
|
+
const toolbarResizeObserver = ResizeObserverCtor
|
|
752
|
+
? new ResizeObserverCtor(() => scheduleToolbarOverflowLayout())
|
|
753
|
+
: null;
|
|
754
|
+
toolbarOverflowButton.addEventListener('click', onToolbarOverflowButtonClick);
|
|
755
|
+
localTestButton.addEventListener('click', onLocalTestButtonClick);
|
|
756
|
+
doc.addEventListener('pointerdown', onToolbarOverflowPointerDown);
|
|
757
|
+
toolbarResizeObserver?.observe(sceneToolOverlay);
|
|
758
|
+
doc.defaultView?.addEventListener('resize', scheduleToolbarOverflowLayout);
|
|
759
|
+
applyThemeToSurfaces();
|
|
760
|
+
updateThemeToggleButton();
|
|
419
761
|
enterEditorButton.addEventListener('click', () => callbacks.onEnterEditor?.());
|
|
420
|
-
saveButton.addEventListener('click', () =>
|
|
421
|
-
|
|
422
|
-
|
|
762
|
+
saveButton.addEventListener('click', () => {
|
|
763
|
+
closeLocalTestMenu();
|
|
764
|
+
if (localTestActionsEnabled)
|
|
765
|
+
callbacks.onSaveScene?.();
|
|
766
|
+
});
|
|
767
|
+
saveAndRunButton.addEventListener('click', () => {
|
|
768
|
+
closeLocalTestMenu();
|
|
769
|
+
if (localTestActionsEnabled)
|
|
770
|
+
callbacks.onSaveAndRunGame?.();
|
|
771
|
+
});
|
|
772
|
+
discardRunButton.addEventListener('click', () => {
|
|
773
|
+
closeLocalTestMenu();
|
|
774
|
+
if (localTestActionsEnabled)
|
|
775
|
+
callbacks.onDiscardAndRunGame?.();
|
|
776
|
+
});
|
|
423
777
|
undoButton.addEventListener('click', () => callbacks.onUndo?.());
|
|
424
778
|
redoButton.addEventListener('click', () => callbacks.onRedo?.());
|
|
425
779
|
sceneCameraButton.addEventListener('click', () => {
|
|
@@ -437,6 +791,9 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
437
791
|
sceneHelpButton.addEventListener('click', () => {
|
|
438
792
|
setShortcutHelpOpen(!helpOpen);
|
|
439
793
|
});
|
|
794
|
+
themeToggleButton.addEventListener('click', () => {
|
|
795
|
+
setActiveTheme(activeTheme === 'dark' ? 'light' : 'dark');
|
|
796
|
+
});
|
|
440
797
|
shortcutHelpPanel.addEventListener('click', (event) => {
|
|
441
798
|
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
442
799
|
if (!target?.closest('[data-editor-shortcut-help-close]'))
|
|
@@ -576,7 +933,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
576
933
|
const primaryModifier = event.metaKey || event.ctrlKey;
|
|
577
934
|
const handleDocumentShortcut = inputRouter.shouldHandleDocumentShortcut(event);
|
|
578
935
|
const handleGlobalShortcut = inputRouter.shouldHandleGlobalShortcut(event);
|
|
579
|
-
if (handleDocumentShortcut && primaryModifier && key === 's') {
|
|
936
|
+
if (handleDocumentShortcut && localTestActionsEnabled && primaryModifier && key === 's') {
|
|
580
937
|
event.preventDefault();
|
|
581
938
|
callbacks.onSaveScene?.();
|
|
582
939
|
return;
|
|
@@ -594,8 +951,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
594
951
|
callbacks.onRedo?.();
|
|
595
952
|
return;
|
|
596
953
|
}
|
|
597
|
-
if (handleGlobalShortcut && (
|
|
598
|
-
hierarchyController.handleDeleteShortcut(event);
|
|
954
|
+
if (handleGlobalShortcut && hierarchyController.handleEditShortcut(event)) {
|
|
599
955
|
return;
|
|
600
956
|
}
|
|
601
957
|
if (!handleGlobalShortcut || event.metaKey || event.ctrlKey || event.altKey)
|
|
@@ -719,12 +1075,16 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
719
1075
|
const disabled = state.busy;
|
|
720
1076
|
if (!inEditor || disabled)
|
|
721
1077
|
contextMenu.close();
|
|
722
|
-
hostChrome.style.display = inEditor ? '
|
|
1078
|
+
hostChrome.style.display = !inEditor && localTestActionsEnabled ? 'flex' : 'none';
|
|
723
1079
|
enterEditorButton.disabled = disabled;
|
|
724
1080
|
for (const button of [saveButton, saveAndRunButton, discardRunButton, undoButton, redoButton, sceneHelpButton, sceneCameraButton]) {
|
|
725
|
-
button.style.display = '';
|
|
1081
|
+
button.style.display = 'inline-flex';
|
|
726
1082
|
button.disabled = disabled;
|
|
727
1083
|
}
|
|
1084
|
+
localTestGroup.style.display = inEditor && localTestActionsEnabled ? 'flex' : 'none';
|
|
1085
|
+
localTestButton.disabled = disabled;
|
|
1086
|
+
if (!inEditor || !localTestActionsEnabled)
|
|
1087
|
+
closeLocalTestMenu();
|
|
728
1088
|
if (!inEditor)
|
|
729
1089
|
helpOpen = false;
|
|
730
1090
|
inputRouter.setModalOpen(inEditor && helpOpen);
|
|
@@ -792,11 +1152,11 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
792
1152
|
status.textContent += dragSuffix;
|
|
793
1153
|
status.title = state.statusDetails || status.textContent;
|
|
794
1154
|
status.style.color = state.statusTone === 'error'
|
|
795
|
-
? '
|
|
1155
|
+
? 'var(--fps-editor-danger-strong)'
|
|
796
1156
|
: state.statusTone === 'warning'
|
|
797
|
-
? '
|
|
1157
|
+
? 'var(--fps-editor-warn)'
|
|
798
1158
|
: state.statusTone === 'success'
|
|
799
|
-
? '
|
|
1159
|
+
? 'var(--fps-editor-success)'
|
|
800
1160
|
: 'var(--fps-editor-muted)';
|
|
801
1161
|
sceneToolStatus.textContent = transformTool?.dragPhase === 'dragging'
|
|
802
1162
|
? `正在拖拽 ${transformTool.draggingNodeId ?? '选择对象'}`
|
|
@@ -816,6 +1176,7 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
816
1176
|
else {
|
|
817
1177
|
boxSelectionOverlay.style.display = 'none';
|
|
818
1178
|
}
|
|
1179
|
+
scheduleToolbarOverflowLayout();
|
|
819
1180
|
if (!inEditor)
|
|
820
1181
|
return;
|
|
821
1182
|
const hierarchyDescriptor = panelRegistry.getActivePanel('left');
|
|
@@ -831,12 +1192,30 @@ export function createLocalEditorBrowserUi(options = {}) {
|
|
|
831
1192
|
update(state) {
|
|
832
1193
|
render(state);
|
|
833
1194
|
},
|
|
1195
|
+
setTheme(theme) {
|
|
1196
|
+
setActiveTheme(theme);
|
|
1197
|
+
},
|
|
1198
|
+
getTheme() {
|
|
1199
|
+
return activeTheme;
|
|
1200
|
+
},
|
|
834
1201
|
dispose() {
|
|
1202
|
+
if (toolbarOverflowRaf != null) {
|
|
1203
|
+
doc.defaultView?.cancelAnimationFrame(toolbarOverflowRaf);
|
|
1204
|
+
toolbarOverflowRaf = null;
|
|
1205
|
+
}
|
|
1206
|
+
toolbarResizeObserver?.disconnect();
|
|
1207
|
+
toolbarOverflowButton.removeEventListener('click', onToolbarOverflowButtonClick);
|
|
1208
|
+
localTestButton.removeEventListener('click', onLocalTestButtonClick);
|
|
1209
|
+
doc.removeEventListener('pointerdown', onToolbarOverflowPointerDown);
|
|
1210
|
+
doc.defaultView?.removeEventListener('resize', scheduleToolbarOverflowLayout);
|
|
835
1211
|
hostChrome.remove();
|
|
836
1212
|
workbench.root.remove();
|
|
1213
|
+
localTestMenu.remove();
|
|
1214
|
+
toolbarOverflowMenu.remove();
|
|
837
1215
|
boxSelectionOverlay.remove();
|
|
838
1216
|
shortcutHelpPanel.remove();
|
|
839
1217
|
hierarchyController.dispose();
|
|
1218
|
+
workbenchLayoutController.dispose();
|
|
840
1219
|
contextMenu.dispose();
|
|
841
1220
|
inputRouter.dispose();
|
|
842
1221
|
doc.removeEventListener('keydown', onKeyDown);
|