Browse Source

implement NME preview area in pop out window

removing extraneous imports
Kyle Belfort 5 years ago
parent
commit
4e9ed4d1fb

+ 2 - 2
inspector/src/inspector.ts

@@ -66,7 +66,7 @@ export class Inspector {
                     targetDoc.head!.appendChild(newLinkEl);
                 }
             } catch (e) {
-                console.log(e)
+                console.log(e);
             }
 
         }
@@ -221,7 +221,7 @@ export class Inspector {
         if (this._EmbedHost) {
             this._OpenedPane++;
             const embedHostElement = React.createElement(EmbedHostComponent, {
-                globalState: this._GlobalState, scene: scene,                
+                globalState: this._GlobalState, scene: scene,
                 extensibilityGroups: options.explorerExtensibility,
                 noExpand: !options.enablePopup,
                 noClose: !options.enableClose,

+ 15 - 2
nodeEditor/src/components/preview/previewMeshControlComponent.tsx

@@ -2,16 +2,20 @@
 import * as React from "react";
 import { GlobalState } from '../../globalState';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faCircle, faRing, faCube, faHockeyPuck, faSquareFull, faPlus, faDotCircle } from '@fortawesome/free-solid-svg-icons';
+import { faCircle, faRing, faCube, faHockeyPuck, faSquareFull, faPlus, faDotCircle, faWindowRestore } from '@fortawesome/free-solid-svg-icons';
 import { PreviewMeshType } from './previewMeshType';
 import { DataStorage } from '../../dataStorage';
 
 interface IPreviewMeshControlComponent {
     globalState: GlobalState;
+    togglePreviewAreaComponent: () => void;
 }
 
 export class PreviewMeshControlComponent extends React.Component<IPreviewMeshControlComponent> {
 
+    // private _EmbedHost: Nullable<HTMLElement>;
+
+
     changeMeshType(newOne: PreviewMeshType) {
         if (this.props.globalState.previewMeshType === newOne) {
             return;
@@ -38,6 +42,10 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
         (document.getElementById("file-picker")! as HTMLInputElement).value = "";
     }
 
+    onPopUp() {
+        this.props.togglePreviewAreaComponent();
+    }
+
     render() {
         return (
             <div id="preview-mesh-bar">
@@ -70,13 +78,18 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
                     title="Preview with a shader ball"  
                     onClick={() => this.changeMeshType(PreviewMeshType.ShaderBall)} className={"button" + (this.props.globalState.previewMeshType === PreviewMeshType.ShaderBall ? " selected" : "")}>
                     <FontAwesomeIcon icon={faDotCircle} />
-                </div>                           
+                </div>   
                 <div className={"button align"} title="Preview with a custom mesh" >
                     <label htmlFor="file-picker" id="file-picker-label">
                         <FontAwesomeIcon icon={faPlus} />
                     </label>
                     <input ref="file-picker" id="file-picker" type="file" onChange={evt => this.useCustomMesh(evt)} accept=".gltf, .glb, .babylon, .obj"/>
                 </div>
+                <div
+                    title="Open preview in new window" id="preview-new-window"
+                    onClick={() => this.onPopUp()} className="button">
+                    <FontAwesomeIcon icon={faWindowRestore} />
+                </div>                          
             </div>
         );
 

+ 215 - 3
nodeEditor/src/graphEditor.tsx

@@ -20,6 +20,8 @@ import { SerializationTools } from './serializationTools';
 import { GraphCanvasComponent } from './diagram/graphCanvas';
 import { GraphNode } from './diagram/graphNode';
 import { GraphFrame } from './diagram/graphFrame';
+import * as ReactDOM from 'react-dom';
+import { IInspectorOptions } from 'babylonjs';
 
 require("./main.scss");
 
@@ -27,7 +29,19 @@ interface IGraphEditorProps {
     globalState: GlobalState;
 }
 
-export class GraphEditor extends React.Component<IGraphEditorProps> {
+type State = {
+    showPreviewPopUp: boolean;
+};
+
+interface IInternalPreviewAreaOptions extends IInspectorOptions {
+    popup: boolean;
+    original: boolean;
+    explorerWidth?: string;
+    inspectorWidth?: string;
+    embedHostWidth?: string;
+}
+
+export class GraphEditor extends React.Component<IGraphEditorProps, State> {
     private readonly NodeWidth = 100;
     private _graphCanvas: GraphCanvasComponent;
 
@@ -46,6 +60,14 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
     private _mouseLocationY = 0;
     private _onWidgetKeyUpPointer: any;
 
+    private _PreviewHost: Nullable<HTMLElement>;
+    private _popUpWindow: Window;
+
+
+    public readonly state: State = {
+        showPreviewPopUp: false
+    };
+
     /**
      * Creates a node and recursivly creates its parent nodes from it's input
      * @param nodeMaterialBlock 
@@ -122,6 +144,10 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
     constructor(props: IGraphEditorProps) {
         super(props);
 
+        this.state = {
+            showPreviewPopUp: false
+        };
+
         this.props.globalState.onRebuildRequiredObservable.add(() => {
             if (this.props.globalState.nodeMaterial) {
                 this.buildMaterial();
@@ -511,6 +537,192 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
         this.forceUpdate();
     }
 
+    handlePopUp = () => {
+        this.setState({
+            showPreviewPopUp : true
+        });
+        this.createPopUp();
+        window.addEventListener('beforeunload', this.handleClosingPopUp);
+    }
+
+    handleClosingPopUp = () => {
+        this._previewManager.dispose();
+        this._popUpWindow.close();
+        this.setState({
+            showPreviewPopUp: false
+        }, () => this.initiatePreviewArea()
+        );
+    }
+
+    initiatePreviewArea = (canvas: HTMLCanvasElement = this.props.globalState.hostDocument.getElementById("preview-canvas") as HTMLCanvasElement) => {
+        this._previewManager =  new PreviewManager(canvas, this.props.globalState);
+    }
+
+    createPopUp = () => {
+        const userOptions = {
+            original: true,
+            popup: false,
+            overlay: false,
+            embedMode: false,
+            enableClose: true,
+            handleResize: true,
+            enablePopup: true,
+
+        };
+        const options = {
+            embedHostWidth: "100%",
+            popup: true,
+            ...userOptions
+        };
+        const popUpWindow = this.createPopupWindow("PREVIEW AREA", "_PreviewHostWindow");
+        if (popUpWindow) {
+            popUpWindow.addEventListener('beforeunload',  this.handleClosingPopUp);
+            const parentControl = popUpWindow.document.getElementById('node-editor-graph-root');
+            this.createPreviewMeshControlHost(options, parentControl);
+            this.createPreviewHost(options, parentControl);
+            if (parentControl) {
+                this.fixPopUpStyles(parentControl.ownerDocument!);
+                this.initiatePreviewArea(parentControl.ownerDocument!.getElementById("preview-canvas") as HTMLCanvasElement);
+            }
+        }
+    }
+
+    createPopupWindow = (title: string, windowVariableName: string, width = 500, height = 500): Window | null => {
+        const windowCreationOptionsList = {
+            width: width,
+            height: height,
+            top: (window.innerHeight - width) / 2 + window.screenY,
+            left: (window.innerWidth - height) / 2 + window.screenX
+        };
+
+        var windowCreationOptions = Object.keys(windowCreationOptionsList)
+            .map(
+                (key) => key + '=' + (windowCreationOptionsList as any)[key]
+            )
+            .join(',');
+
+        const popupWindow = window.open("", title, windowCreationOptions);
+        if (!popupWindow) {
+            return null;
+        }
+
+        const parentDocument = popupWindow.document;
+
+        parentDocument.title = title;
+        parentDocument.body.style.width = "100%";
+        parentDocument.body.style.height = "100%";
+        parentDocument.body.style.margin = "0";
+        parentDocument.body.style.padding = "0";
+
+        let parentControl = parentDocument.createElement("div");
+        parentControl.style.width = "100%";
+        parentControl.style.height = "100%";
+        parentControl.style.margin = "0";
+        parentControl.style.padding = "0";
+        parentControl.style.display = "block";
+        parentControl.style.gridTemplateRows = "unset";
+        parentControl.id = 'node-editor-graph-root';
+        parentControl.className = 'right-panel';
+
+        popupWindow.document.body.appendChild(parentControl);
+
+        this.copyStyles(window.document, parentDocument);
+
+        (this as any)[windowVariableName] = popupWindow;
+
+        this._popUpWindow = popupWindow;
+
+        return popupWindow;
+    }
+
+    copyStyles = (sourceDoc: HTMLDocument, targetDoc: HTMLDocument) => {
+        const styleContainer = [];
+        for (var index = 0; index < sourceDoc.styleSheets.length; index++) {
+            var styleSheet: any = sourceDoc.styleSheets[index];
+            try {
+                if (styleSheet.href) { // for <link> elements loading CSS from a URL
+                    const newLinkEl = sourceDoc.createElement('link');
+
+                    newLinkEl.rel = 'stylesheet';
+                    newLinkEl.href = styleSheet.href;
+                    targetDoc.head!.appendChild(newLinkEl);
+                    styleContainer.push(newLinkEl);
+                }
+                else if (styleSheet.cssRules) { // for <style> elements
+                    const newStyleEl = sourceDoc.createElement('style');
+
+                    for (var cssRule of styleSheet.cssRules) {
+                        if (cssRule.selectorText !== '.right-panel #preview-config-bar .button') { // skip css grid layout rules
+                            newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText));
+                        }
+                    }
+
+                    targetDoc.head!.appendChild(newStyleEl);
+                    styleContainer.push(newStyleEl);
+                } 
+            } catch (e) {
+                console.log(e);
+            }
+        }
+    }
+
+    createPreviewMeshControlHost = (options: IInternalPreviewAreaOptions, parentControl: Nullable<HTMLElement>) => {
+        // Prepare the preview control host
+        if (parentControl) {
+            const host = parentControl.ownerDocument!.createElement("div");
+
+            host.id = "PreviewMeshControl-host";
+            host.style.width = options.embedHostWidth || "auto";
+            host.style.display = "block";
+            host.style.height = "30px";
+
+            parentControl.appendChild(host);
+            const PreviewMeshControlComponentHost = React.createElement(PreviewMeshControlComponent, {
+                globalState: this.props.globalState,
+                togglePreviewAreaComponent: this.handlePopUp
+            });
+            ReactDOM.render(PreviewMeshControlComponentHost, host);
+        }
+    }
+
+    createPreviewHost = (options: IInternalPreviewAreaOptions, parentControl: Nullable<HTMLElement>) => {
+        // Prepare the preview host
+        if (parentControl) {
+            const host = parentControl.ownerDocument!.createElement("div");
+
+            host.id = "PreviewAreaComponent-host";
+            host.style.width = options.embedHostWidth || "auto";
+            host.style.display = "block";
+
+            parentControl.appendChild(host);
+
+            this._PreviewHost = host;
+
+            if (!options.overlay) {
+                this._PreviewHost.style.position = "relative";
+            }
+        }
+
+        if (this._PreviewHost) {
+            const PreviewAreaComponentHost = React.createElement(PreviewAreaComponent, {
+                globalState: this.props.globalState,
+                width: 200
+            });
+            ReactDOM.render(PreviewAreaComponentHost, this._PreviewHost);
+        }
+    }
+
+    fixPopUpStyles = (document: Document) => {
+        const previewContainer = document.getElementById("preview");
+        if (previewContainer) {
+            previewContainer.style.height = "calc(100% - 60px)";
+        }
+        const newWindowButton = document.getElementById('preview-new-window');
+        if (newWindowButton) {
+            newWindowButton.style.display = 'none';
+        }
+    }
+
     render() {
         return (
             <Portal globalState={this.props.globalState}>
@@ -559,8 +771,8 @@ export class GraphEditor extends React.Component<IGraphEditorProps> {
                     {/* Property tab */}
                     <div className="right-panel">
                         <PropertyTabComponent globalState={this.props.globalState} />
-                        <PreviewMeshControlComponent globalState={this.props.globalState} />
-                        <PreviewAreaComponent globalState={this.props.globalState} width={this._rightWidth}/>
+                        {!this.state.showPreviewPopUp ? <PreviewMeshControlComponent globalState={this.props.globalState} togglePreviewAreaComponent={this.handlePopUp} /> : null }
+                        {!this.state.showPreviewPopUp ? <PreviewAreaComponent globalState={this.props.globalState} width={this._rightWidth} /> : null}
                     </div>
 
                     <LogComponent globalState={this.props.globalState} />