Enhance editor store with viewport layout state management

This commit is contained in:
2026-04-03 01:36:16 +02:00
parent a6f8e43c9f
commit d32129edfc

View File

@@ -22,11 +22,16 @@ import {
type ViewportTransientState type ViewportTransientState
} from "../viewport-three/viewport-transient-state"; } from "../viewport-three/viewport-transient-state";
import { import {
areViewportPanelCameraStatesEqual,
cloneViewportLayoutState,
cloneViewportPanelCameraState,
createDefaultViewportLayoutState, createDefaultViewportLayoutState,
type ViewportDisplayMode, type ViewportDisplayMode,
type ViewportLayoutState,
type ViewportLayoutMode, type ViewportLayoutMode,
type ViewportPanelId, type ViewportPanelId,
type ViewportPanelState, type ViewportPanelState,
type ViewportPanelCameraState,
type ViewportQuadSplit type ViewportQuadSplit
} from "../viewport-three/viewport-layout"; } from "../viewport-three/viewport-layout";
@@ -47,6 +52,7 @@ export interface EditorStoreState {
interface EditorStoreOptions { interface EditorStoreOptions {
initialDocument?: SceneDocument; initialDocument?: SceneDocument;
initialViewportLayoutState?: ViewportLayoutState;
storage?: KeyValueStorage | null; storage?: KeyValueStorage | null;
storageKey?: string; storageKey?: string;
} }
@@ -60,10 +66,10 @@ export class EditorStore {
private document: SceneDocument; private document: SceneDocument;
private selection: EditorSelection = { kind: "none" }; private selection: EditorSelection = { kind: "none" };
private toolMode: ToolMode = "select"; private toolMode: ToolMode = "select";
private viewportLayoutMode: ViewportLayoutMode = "single"; private viewportLayoutMode: ViewportLayoutMode;
private activeViewportPanelId: ViewportPanelId = "topLeft"; private activeViewportPanelId: ViewportPanelId;
private viewportPanels = createDefaultViewportLayoutState().panels; private viewportPanels: Record<ViewportPanelId, ViewportPanelState>;
private viewportQuadSplit = createDefaultViewportLayoutState().viewportQuadSplit; private viewportQuadSplit: ViewportQuadSplit;
private viewportTransientState = createDefaultViewportTransientState(); private viewportTransientState = createDefaultViewportTransientState();
private previousEditingToolMode: Exclude<ToolMode, "play"> = "select"; private previousEditingToolMode: Exclude<ToolMode, "play"> = "select";
private readonly history = new CommandHistory(); private readonly history = new CommandHistory();
@@ -89,7 +95,13 @@ export class EditorStore {
}; };
constructor(options: EditorStoreOptions = {}) { constructor(options: EditorStoreOptions = {}) {
const initialViewportLayoutState = cloneViewportLayoutState(options.initialViewportLayoutState ?? createDefaultViewportLayoutState());
this.document = options.initialDocument ?? createEmptySceneDocument(); this.document = options.initialDocument ?? createEmptySceneDocument();
this.viewportLayoutMode = initialViewportLayoutState.layoutMode;
this.activeViewportPanelId = initialViewportLayoutState.activePanelId;
this.viewportPanels = initialViewportLayoutState.panels;
this.viewportQuadSplit = initialViewportLayoutState.viewportQuadSplit;
this.storage = options.storage ?? null; this.storage = options.storage ?? null;
this.storageKey = options.storageKey ?? DEFAULT_SCENE_DRAFT_STORAGE_KEY; this.storageKey = options.storageKey ?? DEFAULT_SCENE_DRAFT_STORAGE_KEY;
this.snapshot = this.createSnapshot(); this.snapshot = this.createSnapshot();
@@ -171,6 +183,21 @@ export class EditorStore {
this.emit(); this.emit();
} }
setViewportPanelCameraState(panelId: ViewportPanelId, cameraState: ViewportPanelCameraState) {
if (areViewportPanelCameraStatesEqual(this.viewportPanels[panelId].cameraState, cameraState)) {
return;
}
this.viewportPanels = {
...this.viewportPanels,
[panelId]: {
...this.viewportPanels[panelId],
cameraState: cloneViewportPanelCameraState(cameraState)
}
};
this.emit();
}
setViewportQuadSplit(viewportQuadSplit: ViewportQuadSplit) { setViewportQuadSplit(viewportQuadSplit: ViewportQuadSplit) {
if (this.viewportQuadSplit.x === viewportQuadSplit.x && this.viewportQuadSplit.y === viewportQuadSplit.y) { if (this.viewportQuadSplit.x === viewportQuadSplit.x && this.viewportQuadSplit.y === viewportQuadSplit.y) {
return; return;
@@ -298,7 +325,7 @@ export class EditorStore {
}; };
} }
return saveSceneDocumentDraft(this.storage, this.document, this.storageKey); return saveSceneDocumentDraft(this.storage, this.document, this.createViewportLayoutState(), this.storageKey);
} }
loadDraft(): EditorDraftLoadResult { loadDraft(): EditorDraftLoadResult {
@@ -316,6 +343,11 @@ export class EditorStore {
} }
this.replaceDocument(draftResult.document); this.replaceDocument(draftResult.document);
if (draftResult.viewportLayoutState !== null) {
this.applyViewportLayoutState(draftResult.viewportLayoutState);
}
return draftResult; return draftResult;
} }
@@ -337,6 +369,24 @@ export class EditorStore {
} }
} }
private createViewportLayoutState(): ViewportLayoutState {
return cloneViewportLayoutState({
layoutMode: this.viewportLayoutMode,
activePanelId: this.activeViewportPanelId,
panels: this.viewportPanels,
viewportQuadSplit: this.viewportQuadSplit
});
}
private applyViewportLayoutState(viewportLayoutState: ViewportLayoutState) {
const nextViewportLayoutState = cloneViewportLayoutState(viewportLayoutState);
this.viewportLayoutMode = nextViewportLayoutState.layoutMode;
this.activeViewportPanelId = nextViewportLayoutState.activePanelId;
this.viewportPanels = nextViewportLayoutState.panels;
this.viewportQuadSplit = nextViewportLayoutState.viewportQuadSplit;
}
private createSnapshot(): EditorStoreState { private createSnapshot(): EditorStoreState {
return { return {
document: this.document, document: this.document,