immersive2/src/diagram/diagramMenuManager.ts
Michael Mainguy 100c5e612c Move exit XR button to toolbox class
Refactored exit XR button creation from rigplatform to toolbox for better organization and UI cohesion.

- Add setXR() methods to DiagramManager, DiagramMenuManager, and Toolbox to pass WebXRDefaultExperience after initialization
- Create setupXRButton() in Toolbox class that creates button when entering XR
- Position button at bottom-right of toolbox (x: 0.5, y: -0.35, z: 0)
- Use Y-axis rotation (Math.PI) for correct orientation within toolbox coordinate system
- Scale button to 0.2 for appropriate size
- Remove button creation code from rigplatform

Exit button now moves with toolbox and is logically grouped with other UI elements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 06:57:35 -06:00

134 lines
5.4 KiB
TypeScript

import {DiagramEntityType, DiagramEvent, DiagramEventType} from "./types/diagramEntity";
import {AbstractMesh, ActionEvent, Observable, Scene, Vector3, WebXRDefaultExperience, WebXRInputSource} from "@babylonjs/core";
import {InputTextView} from "../information/inputTextView";
import {DefaultScene} from "../defaultScene";
import log from "loglevel";
import {Toolbox} from "../toolbox/toolbox";
import {ClickMenu} from "../menus/clickMenu";
import {DiagramEventObserverMask} from "./types/diagramEventObserverMask";
import {ConnectionPreview} from "../menus/connectionPreview";
import {ScaleMenu2} from "../menus/ScaleMenu2";
import {viewOnly} from "../util/functions/getPath";
import {GroupMenu} from "../menus/groupMenu";
import {ControllerEvent} from "../controllers/types/controllerEvent";
import {ControllerEventType} from "../controllers/types/controllerEventType";
export class DiagramMenuManager {
public readonly toolbox: Toolbox;
public readonly scaleMenu: ScaleMenu2;
private readonly _notifier: Observable<DiagramEvent>;
private readonly _inputTextView: InputTextView;
private _groupMenu: GroupMenu;
private readonly _scene: Scene;
private _logger = log.getLogger('DiagramMenuManager');
private _connectionPreview: ConnectionPreview;
constructor(notifier: Observable<DiagramEvent>, controllerObservable: Observable<ControllerEvent>, readyObservable: Observable<boolean>) {
this._scene = DefaultScene.Scene;
this._notifier = notifier;
this._inputTextView = new InputTextView(controllerObservable);
//this.configMenu = new ConfigMenu(config);
this._inputTextView.onTextObservable.add((evt) => {
const event = {
type: DiagramEventType.MODIFY,
entity: {id: evt.id, text: evt.text, type: DiagramEntityType.ENTITY}
}
this._notifier.notifyObservers(event, DiagramEventObserverMask.FROM_DB);
});
this.toolbox = new Toolbox(readyObservable);
this.scaleMenu = new ScaleMenu2(this._notifier);
if (viewOnly()) {
this.toolbox.handleMesh.setEnabled(false);
//this.scaleMenu.handleMesh.setEnabled(false)
// this.configMenu.handleTransformNode.setEnabled(false);
}
controllerObservable.add((event: ControllerEvent) => {
if (event.type == ControllerEventType.B_BUTTON) {
if (event.value > .8) {
const platform = this._scene.getMeshByName("platform");
if (!platform) {
return;
}
const cameraPos = this._scene.activeCamera.globalPosition;
const localCamera = Vector3.TransformCoordinates(cameraPos, platform.getWorldMatrix());
const toolY = this.toolbox.handleMesh.absolutePosition.y;
if (toolY > (cameraPos.y - .2)) {
this.toolbox.handleMesh.position.y = localCamera.y - .2;
}
const inputY = this._inputTextView.handleMesh.absolutePosition.y;
if (inputY > (cameraPos.y - .2)) {
this._inputTextView.handleMesh.position.y = localCamera.y - .2;
}
const configY = this._inputTextView.handleMesh.absolutePosition.y;
/*if (configY > (cameraPos.y - .2)) {
this.configMenu.handleTransformNode.position.y = localCamera.y - .2;
}*/
}
}
});
}
public get connectionPreview(): ConnectionPreview {
return this._connectionPreview;
}
public connect(mesh: AbstractMesh) {
if (this._connectionPreview) {
this._connectionPreview.connect(mesh);
this._connectionPreview = null;
}
}
public editText(mesh: AbstractMesh) {
this._inputTextView.show(mesh);
}
public createClickMenu(mesh: AbstractMesh, input: WebXRInputSource): ClickMenu {
const clickMenu = new ClickMenu(mesh);
clickMenu.onClickMenuObservable.add((evt: ActionEvent) => {
this._logger.debug(evt);
switch (evt.source.id) {
case "remove":
this.notifyAll({
type: DiagramEventType.REMOVE,
entity: {id: clickMenu.mesh.id, type: DiagramEntityType.ENTITY}
});
break;
case "label":
this.editText(clickMenu.mesh);
break;
case "connect":
this._connectionPreview = new ConnectionPreview(clickMenu.mesh.id, input, evt.additionalData.pickedPoint, this._notifier);
break;
case "size":
this.scaleMenu.show(clickMenu.mesh);
break;
case "group":
this._groupMenu = new GroupMenu(clickMenu.mesh);
break;
case "close":
this.scaleMenu.hide();
break;
}
this._logger.debug(evt);
}, -1, false, this, false);
return clickMenu;
}
private notifyAll(event: DiagramEvent) {
this._notifier.notifyObservers(event, DiagramEventObserverMask.ALL);
}
public setXR(xr: WebXRDefaultExperience): void {
this.toolbox.setXR(xr);
}
}