Browse Source

fixing frame port bugs

Kyle Belfort 5 năm trước cách đây
mục cha
commit
e0b99e9283

+ 8 - 5
dist/preview release/what's new.md

@@ -8,7 +8,7 @@
 
 ### General
 
-- Refactored React refs from old string API to React.createRef() API ([Kyle Belfort](https://github.com/belfortk)
+- Refactored React refs from old string API to React.createRef() API ([belfortk](https://github.com/belfortk)
 
 - Scale on one axis for `BoundingBoxGizmo` ([cedricguillemet](https://github.com/cedricguillemet))
 
@@ -22,8 +22,8 @@
 
 ### NME
 
-- Frames are now resizable from the corners ([Kyle Belfort](https://github.com/belfortk)
-- Can now rename and re-order frame inputs and outputs ([Kyle Belfort](https://github.com/belfortk)
+- Frames are now resizable from the corners ([belfortk](https://github.com/belfortk)
+- Can now rename and re-order frame inputs and outputs ([belfortk](https://github.com/belfortk)
 
 ### Inspector
 
@@ -89,7 +89,7 @@
 - Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72)
 - Fix picking issue in the Solid Particle System when MultiMaterial is enabled ([jerome](https://github.com/jbousquie))
 - `QuadraticErrorSimplification` was not exported ([RaananW](https://github.com/Raananw)
-- Fix NME Frames bug where collapsing and moving a frame removed the nodes inside ([Kyle Belfort](https://github.com/belfortk)
+- Fix NME Frames bug where collapsing and moving a frame removed the nodes inside ([belfortk](https://github.com/belfortk)
 - Fix moving / disappearing controls when freezing/unfreezing the ScrollViewer ([Popov72](https://github.com/Popov72)
 - Fix: when using instances, master mesh (if displayed) does not have correct instance buffer values ([Popov72](https://github.com/Popov72)
 - Exit XR will only trigger only if state is IN_XR ([RaananW](https://github.com/RaananW))
@@ -116,7 +116,10 @@
 - Fixed error when downloading async createScene function in playground ([#7926](https://github.com/BabylonJS/Babylon.js/issues/7926)) ([RaananW](https://github.com/RaananW))
 - Fix issue where ThinEngine.prototype.createDynamicEngine is undefined when using VideoTexture with es6 packages ([rvadhavk](https://github.com/rvadhavk))
 - Fix [issue](https://forum.babylonjs.com/t/virtualjoystick-needs-to-set-style-touch-action-none-explicitly/9562) that canvas for `VirtualJoystick` does not have `touch-action: "none"` set by default ([joergplewe](https://github.com/joergplewe))
-- Fix [issue](https://github.com/BabylonJS/Babylon.js/issues/7943) that prevented user from re-loading custom meshes
+- Fix [issue](https://github.com/BabylonJS/Babylon.js/issues/7943) that prevented user from re-loading custom meshes([belfortk](https://github.com/belfortk))
+- Fix bug in NME where collapsed frames didn't redraw output links to outside nodes([belfortk](https://github.com/belfortk))
+- Fix bug in NME where links were not redrawn after moving frame port ([belfortk](https://github.com/belfortk))
+- Fix bugs in NME that were causing inconsistent behavior displaying Move Node Up and Down buttons on frame ports([belfortk](https://github.com/belfortk))
 
 ## Breaking changes
 

+ 9 - 8
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -26,8 +26,9 @@ import { Vector2LineComponent } from '../../sharedComponents/vector2LineComponen
 import { Vector3LineComponent } from '../../sharedComponents/vector3LineComponent';
 import { Vector4LineComponent } from '../../sharedComponents/vector4LineComponent';
 import { Observer } from 'babylonjs/Misc/observable';
-import { FrameNodePort } from '../../diagram/frameNodePort';
 import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
+import { FrameNodePort } from '../../diagram/frameNodePort';
+import { isFramePortData } from '../../diagram/graphCanvas';
 require("./propertyTab.scss");
 
 interface IPropertyTabComponentProps {
@@ -49,8 +50,8 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                 this.setState({ currentNode: selection, currentFrame: null, currentFrameNodePort: null });
             } else if (selection instanceof GraphFrame) {
                 this.setState({ currentNode: null, currentFrame: selection, currentFrameNodePort: null });
-            } else if(selection instanceof FrameNodePort) {
-                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: selection });
+            } else if(isFramePortData(selection)) {
+                this.setState({ currentNode: null, currentFrame: selection.frame, currentFrameNodePort: selection.port });
             } else {
                 this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null });
             }
@@ -243,16 +244,16 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                 </div>
             );
         }
-
-        if (this.state.currentFrame) {
+        
+        if (this.state.currentFrameNodePort && this.state.currentFrame) {
             return (
-                <FramePropertyTabComponent globalState={this.props.globalState} frame={this.state.currentFrame}/>
+                <FrameNodePortPropertyTabComponent globalState={this.props.globalState} frame={this.state.currentFrame} frameNodePort={this.state.currentFrameNodePort}/>
             );
         }
 
-        if (this.state.currentFrameNodePort) {
+        if (this.state.currentFrame) {
             return (
-                <FrameNodePortPropertyTabComponent globalState={this.props.globalState} frameNodePort={this.state.currentFrameNodePort}/>
+                <FramePropertyTabComponent globalState={this.props.globalState} frame={this.state.currentFrame}/>
             );
         }
 

+ 13 - 15
nodeEditor/src/diagram/frameNodePort.ts

@@ -6,22 +6,18 @@ import { IDisplayManager } from './display/displayManager';
 import { Observable } from 'babylonjs/Misc/observable';
 import { Nullable } from 'babylonjs/types';
 import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
+import { FramePortData, isFramePortData } from './graphCanvas';
 
 export class FrameNodePort extends NodePort {
-    private _onFramePortMoveUpObservable = new Observable<FrameNodePort>();
-    private _onFramePortMoveDownObservable = new Observable<FrameNodePort>();
-    private _onFramePortPositionChangedObservable = new Observable<FramePortPosition>();
     private _portLabel: Element;
+    private _parentFrameId: number;
     private _isInput: boolean;
     private _framePortPosition: FramePortPosition
-    private _framePortId: Nullable<number>;
+    private _framePortId: number;
+    private _onFramePortPositionChangedObservable = new Observable<FrameNodePort>();
 
-    public get onFramePortMoveUpObservable() {
-        return this._onFramePortMoveUpObservable;
-    }
-
-    public get onFramePortMoveDownObservable() {
-        return this._onFramePortMoveDownObservable;
+    public get parentFrameId () {
+        return this._parentFrameId;
     }
 
     public get onFramePortPositionChangedObservable() {
@@ -50,18 +46,20 @@ export class FrameNodePort extends NodePort {
 
     public set framePortPosition(position: FramePortPosition) {
         this._framePortPosition = position;
-        this.onFramePortPositionChangedObservable.notifyObservers(position);
+        console.log(this.onFramePortPositionChangedObservable.observers);
+        this.onFramePortPositionChangedObservable.notifyObservers(this);
     }
 
-    public constructor(portContainer: HTMLElement, public connectionPoint: NodeMaterialConnectionPoint, public node: GraphNode, globalState: GlobalState, isInput: boolean, framePortId: number) {
+    public constructor(portContainer: HTMLElement, public connectionPoint: NodeMaterialConnectionPoint, public node: GraphNode, globalState: GlobalState, isInput: boolean, framePortId: number, parentFrameId: number) {
         super(portContainer, connectionPoint,node, globalState);
 
+        this._parentFrameId = parentFrameId;
         this._portLabel = portContainer.children[0];
         this._isInput = isInput;
         this._framePortId = framePortId;
 
         this._onSelectionChangedObserver = this._globalState.onSelectionChangedObservable.add((selection) => {
-            if (selection === this) {
+            if (isFramePortData(selection) && (selection as FramePortData).port === this) {
                 this._img.classList.add("selected");
             } else {
                 this._img.classList.remove("selected");
@@ -72,7 +70,7 @@ export class FrameNodePort extends NodePort {
     }
 
     public static CreateFrameNodePortElement(connectionPoint: NodeMaterialConnectionPoint, node: GraphNode, root: HTMLElement, 
-        displayManager: Nullable<IDisplayManager>, globalState: GlobalState, isInput: boolean, framePortId: number) {
+        displayManager: Nullable<IDisplayManager>, globalState: GlobalState, isInput: boolean, framePortId: number, parentFrameId: number) {
         let portContainer = root.ownerDocument!.createElement("div");
         let block = connectionPoint.ownerBlock;
 
@@ -89,7 +87,7 @@ export class FrameNodePort extends NodePort {
             portContainer.appendChild(portLabel);
         }
 
-        return new FrameNodePort(portContainer, connectionPoint, node, globalState, isInput, framePortId);
+        return new FrameNodePort(portContainer, connectionPoint, node, globalState, isInput, framePortId, parentFrameId);
     }
 
 } 

+ 28 - 4
nodeEditor/src/diagram/graphCanvas.tsx

@@ -22,6 +22,18 @@ export interface IGraphCanvasComponentProps {
     globalState: GlobalState
 }
 
+export type FramePortData = {
+    frame: GraphFrame,
+    port: FrameNodePort
+}
+
+export const isFramePortData = (variableToCheck: any): variableToCheck is FramePortData => {
+    if (variableToCheck) {
+        return (variableToCheck as FramePortData).port !== undefined;
+    }
+    else return false;
+}
+
 export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentProps> {
     private readonly MinZoom = 0.1;
     private readonly MaxZoom = 4;
@@ -186,12 +198,16 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                     } else {                    
                         this._selectedNodes = [selection];
                     }
-                }
-                else {
+                } else if(selection instanceof NodePort){
                     this._selectedNodes = [];
                     this._selectedFrame = null;
                     this._selectedLink = null;
                     this._selectedPort = selection;
+                } else {
+                    this._selectedNodes = [];
+                    this._selectedFrame = null;
+                    this._selectedLink = null;
+                    this._selectedPort = selection.port;
                 }
             }
         });
@@ -599,8 +615,16 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                 this.processCandidatePort();          
                 this.props.globalState.onCandidateLinkMoved.notifyObservers(null);
             } else { // is a click event on NodePort
-                if(this._candidateLink.portA instanceof FrameNodePort) { //only on Frame Ports
-                    this.props.globalState.onSelectionChangedObservable.notifyObservers(this._candidateLink.portA);
+                if(this._candidateLink.portA instanceof FrameNodePort) { //only on Frame Node Ports
+                    const port = this._candidateLink.portA;
+                    const frame = this.frames.find((frame: GraphFrame) => frame.id === port.parentFrameId);
+                    if (frame) {
+                        const data: FramePortData = {
+                            frame,
+                            port
+                        }
+                        this.props.globalState.onSelectionChangedObservable.notifyObservers(data);
+                    }
                 }
             }
             this._candidateLink.dispose();

+ 83 - 69
nodeEditor/src/diagram/graphFrame.ts

@@ -1,5 +1,5 @@
 import { GraphNode } from './graphNode';
-import { GraphCanvasComponent } from './graphCanvas';
+import { GraphCanvasComponent, FramePortData } from './graphCanvas';
 import { Nullable } from 'babylonjs/types';
 import { Observer, Observable } from 'babylonjs/Misc/observable';
 import { NodeLink } from './nodeLink';
@@ -51,7 +51,7 @@ export class GraphFrame {
     private _ownerCanvas: GraphCanvasComponent;
     private _mouseStartPointX: Nullable<number> = null;
     private _mouseStartPointY: Nullable<number> = null;
-    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphNode | NodeLink | GraphFrame | NodePort>>>;
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
     private _onGraphNodeRemovalObserver: Nullable<Observer<GraphNode>>; 
     private _isCollapsed = false;
     private _frameInPorts: FrameNodePort[] = [];
@@ -80,18 +80,11 @@ export class GraphFrame {
     }
 
     private _createInputPort(port: NodePort, node: GraphNode) {
-        let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._inputPortContainer, null, this._ownerCanvas.globalState, true, GraphFrame._FramePortCounter++)
+        let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._inputPortContainer, null, this._ownerCanvas.globalState, true, GraphFrame._FramePortCounter++, this.id);
         this._frameInPorts.push(localPort);
 
         port.delegatedPort = localPort;
         this._controlledPorts.push(port);
-        localPort.onFramePortMoveUpObservable.add((nodePort: FrameNodePort) => {
-                this.moveFramePortUp(nodePort);
-        });
-
-        localPort.onFramePortMoveDownObservable.add((nodePort: FrameNodePort) => {
-                this.moveFramePortDown(nodePort);
-        })
     }
 
     public set isCollapsed(value: boolean) {
@@ -120,38 +113,25 @@ export class GraphFrame {
 
                                 if (!portAdded) {
                                     portAdded = true;
-                                    localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeB!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++);
+                                    localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, link.nodeB!, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
                                     this._frameOutPorts.push(localPort);
 
-                                    localPort.onFramePortMoveUpObservable.add((nodePort: FrameNodePort) => {
-                                            this.moveFramePortUp(nodePort);
-                                    });
-                            
-                                    localPort.onFramePortMoveDownObservable.add((nodePort: FrameNodePort) => {
-                                            this.moveFramePortDown(nodePort);
-                                    })
+                                    link.isVisible = true;
+
                                 } else {
                                     localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
                                 }
 
                                 port.delegatedPort = localPort;
                                 this._controlledPorts.push(port);
-                                link.isVisible = false;
                             }
                         }
                     } else {
-                        let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++)
+                        let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
                         this._frameOutPorts.push(localPort);
                         port.delegatedPort = localPort;
                         this._controlledPorts.push(port);
 
-                        localPort.onFramePortMoveUpObservable.add((nodePort: FrameNodePort) => {
-                                this.moveFramePortUp(nodePort);
-                        });
-                
-                        localPort.onFramePortMoveDownObservable.add((nodePort: FrameNodePort) => {
-                                this.moveFramePortDown(nodePort);
-                        });
                     }
                 }
 
@@ -169,25 +149,37 @@ export class GraphFrame {
                 }
             }
 
-            for(let i = 0; i < this._frameInPorts.length; i++) {
-                const port = this._frameInPorts[i];
-                if(i === 0){
-                    port.framePortPosition = FramePortPosition.Top;
-                } else if(i === this._frameInPorts.length -1){
-                    port.framePortPosition = FramePortPosition.Bottom;
-                } else {
-                    port.framePortPosition = FramePortPosition.Middle;
+            // mark FrameInPorts with position
+            if(this._frameInPorts.length == 2){
+                this._frameInPorts[0].framePortPosition = FramePortPosition.Top;
+                this._frameInPorts[1].framePortPosition = FramePortPosition.Bottom;
+            } else {
+                for(let i = 0; i < this._frameInPorts.length; i++) {
+                    const port = this._frameInPorts[i];
+                    if(i === 0){
+                        port.framePortPosition = FramePortPosition.Top;
+                    } else if(i === this._frameInPorts.length -1){
+                        port.framePortPosition = FramePortPosition.Bottom;
+                    } else {
+                        port.framePortPosition = FramePortPosition.Middle;
+                    }
                 }
             }
 
-            for(let i = 0; i < this._frameOutPorts.length; i++) {
-                const port = this._frameOutPorts[i];
-                if(i === 0){
-                    port.framePortPosition = FramePortPosition.Top
-                } else if(i === this._frameInPorts.length -1){
-                    port.framePortPosition = FramePortPosition.Bottom
-                } else {
-                    port.framePortPosition = FramePortPosition.Middle
+            // mark FrameOutPorts with position
+            if(this._frameOutPorts.length == 2){
+                this._frameOutPorts[0].framePortPosition = FramePortPosition.Top;
+                this._frameOutPorts[1].framePortPosition = FramePortPosition.Bottom;
+            } else {
+                for(let i = 0; i < this._frameOutPorts.length; i++) {
+                    const port = this._frameOutPorts[i];
+                    if(i === 0){
+                        port.framePortPosition = FramePortPosition.Top
+                    } else if(i === this._frameInPorts.length -1){
+                        port.framePortPosition = FramePortPosition.Bottom
+                    } else {
+                        port.framePortPosition = FramePortPosition.Middle
+                    }
                 }
             }
 
@@ -617,24 +609,25 @@ export class GraphFrame {
         evt.stopPropagation();
     }
 
-    private moveFramePortUp(nodePort: FrameNodePort){
+    public moveFramePortUp(nodePort: FrameNodePort){
+        let elementsArray: ChildNode[];
         if(nodePort.isInput) {
             if(this._inputPortContainer.children.length < 2) {
                 return;
             }
-            const elementsArray = Array.from(this._inputPortContainer.childNodes);
-            this._movePortUp(elementsArray, nodePort);
+            elementsArray = Array.from(this._inputPortContainer.childNodes);
+            this._movePortUp(elementsArray, nodePort, this._frameInPorts);
         } else {
             if(this._outputPortContainer.children.length < 2) {
                 return;
             }
-            const elementsArray = Array.from(this._outputPortContainer.childNodes);
-            this._movePortUp(elementsArray, nodePort);
+            elementsArray = Array.from(this._outputPortContainer.childNodes);
+            this._movePortUp(elementsArray, nodePort, this._frameOutPorts);
         }
-
+        this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
     }
 
-    private _movePortUp(elementsArray: ChildNode[], nodePort: FrameNodePort) {
+    private _movePortUp(elementsArray: ChildNode[], nodePort: FrameNodePort, framePortList: FrameNodePort[]) {
         // update UI
         const indexInElementArray = (elementsArray as HTMLElement[]).findIndex(elem => elem.dataset.framePortId === `${nodePort.framePortId}`)
         if(indexInElementArray === 0){
@@ -645,34 +638,47 @@ export class GraphFrame {
         firstPortElement.parentElement?.insertBefore(secondPortElement, firstPortElement);
 
         // update Frame Port Container
-        const indexInContainer = this._frameInPorts.findIndex(framePort => framePort === nodePort);
-        [this._frameInPorts[indexInContainer -1], this._frameInPorts[indexInContainer]] = [this._frameInPorts[indexInContainer], this._frameInPorts[indexInContainer -1]]; // swap idicies
+        const indexInContainer = framePortList.findIndex(framePort => framePort === nodePort);
+        [framePortList[indexInContainer -1], framePortList[indexInContainer]] = [framePortList[indexInContainer], framePortList[indexInContainer -1]]; // swap idicies
         
-        // notify nodePort if it is now at Top (indexInElementArray === 1)
-        if (indexInElementArray === 1) {
-            nodePort.onFramePortPositionChangedObservable.notifyObservers(FramePortPosition.Top);
+        //special case framePortList.length == 2
+        if(framePortList.length == 2) {
+            framePortList[1].framePortPosition = FramePortPosition.Bottom;
+            framePortList[0].framePortPosition = FramePortPosition.Top;
         } else {
-            nodePort.onFramePortPositionChangedObservable.notifyObservers(FramePortPosition.Middle);
+            // notify nodePort if it is now at Top (indexInElementArray === 1)
+            if (indexInElementArray === 1) {
+                framePortList[1].framePortPosition = FramePortPosition.Middle;
+                framePortList[0].framePortPosition = FramePortPosition.Top;
+            } else if(indexInContainer === elementsArray.length-1) {
+                framePortList[framePortList.length -1].framePortPosition = FramePortPosition.Bottom;
+                framePortList[framePortList.length -2].framePortPosition = FramePortPosition.Middle;
+            } else {
+                nodePort.framePortPosition = FramePortPosition.Middle;
+            }
         }
     }
     
-    private moveFramePortDown(nodePort: FrameNodePort){
+    public moveFramePortDown(nodePort: FrameNodePort){
+        let elementsArray: ChildNode[];
         if(nodePort.isInput) {
             if(this._inputPortContainer.children.length < 2) {
                 return;
             }
-            const elementsArray = Array.from(this._inputPortContainer.childNodes);
-            this._movePortDown(elementsArray, nodePort);
+            elementsArray = Array.from(this._inputPortContainer.childNodes);
+            this._movePortDown(elementsArray, nodePort, this._frameInPorts);
         } else {
             if(this._outputPortContainer.children.length < 2) {
                 return;
             }
-            const elementsArray = Array.from(this._outputPortContainer.childNodes);
-            this._movePortDown(elementsArray, nodePort);
+            elementsArray = Array.from(this._outputPortContainer.childNodes);
+            this._movePortDown(elementsArray, nodePort, this._frameOutPorts);
         }
+
+        this.ports.forEach((framePort: FrameNodePort) => framePort.node._refreshLinks());
     }
 
-    private _movePortDown(elementsArray: ChildNode[], nodePort: FrameNodePort) {
+    private _movePortDown(elementsArray: ChildNode[], nodePort: FrameNodePort, framePortList: FrameNodePort[]) {
         // update UI
         const indexInElementArray = (elementsArray as HTMLElement[]).findIndex(elem => elem.dataset.framePortId === `${nodePort.framePortId}`)
         if(indexInElementArray === elementsArray.length -1){
@@ -683,16 +689,24 @@ export class GraphFrame {
         firstPort.parentElement?.insertBefore(secondPort, firstPort);
 
         // update Frame Port Container
-        const indexInContainer = this._frameInPorts.findIndex(framePort => framePort === nodePort);
-        [this._frameInPorts[indexInContainer], this._frameInPorts[indexInContainer + 1]] = [this._frameInPorts[indexInContainer + 1], this._frameInPorts[indexInContainer]]; // swap idicies
+        const indexInContainer = framePortList.findIndex(framePort => framePort === nodePort);
+        [framePortList[indexInContainer], framePortList[indexInContainer + 1]] = [framePortList[indexInContainer + 1], framePortList[indexInContainer]]; // swap idicies
 
         // notify nodePort if it is now at bottom (indexInContainer === elementsArray.length-2)
-        if(indexInContainer === elementsArray.length-2) {
-            nodePort.onFramePortPositionChangedObservable.notifyObservers(FramePortPosition.Bottom);
+        if(framePortList.length == 2) {
+            framePortList[0].framePortPosition = FramePortPosition.Top;
+            framePortList[1].framePortPosition = FramePortPosition.Bottom;
         } else {
-            nodePort.onFramePortPositionChangedObservable.notifyObservers(FramePortPosition.Middle);
-        }
-
+            if(indexInContainer === elementsArray.length-2) {
+                framePortList[elementsArray.length-2].framePortPosition = FramePortPosition.Middle;
+                framePortList[elementsArray.length-1].framePortPosition = FramePortPosition.Bottom;
+            } else if(indexInContainer === 0){
+                framePortList[0].framePortPosition = FramePortPosition.Top;
+                framePortList[1].framePortPosition = FramePortPosition.Middle;
+            } else {
+                nodePort.framePortPosition = FramePortPosition.Middle;
+            }
+         }
     }
 
     private initResizing = (evt: PointerEvent) => {

+ 2 - 2
nodeEditor/src/diagram/graphNode.ts

@@ -3,7 +3,7 @@ import { GlobalState } from '../globalState';
 import { Nullable } from 'babylonjs/types';
 import { Observer } from 'babylonjs/Misc/observable';
 import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMaterialBlockConnectionPoint';
-import { GraphCanvasComponent } from './graphCanvas';
+import { GraphCanvasComponent, FramePortData } from './graphCanvas';
 import { PropertyLedger } from './propertyLedger';
 import * as React from 'react';
 import { GenericPropertyTabComponent } from './properties/genericNodePropertyComponent';
@@ -31,7 +31,7 @@ export class GraphNode {
     private _mouseStartPointX: Nullable<number> = null;
     private _mouseStartPointY: Nullable<number> = null    
     private _globalState: GlobalState;
-    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphNode | NodeLink | GraphFrame | NodePort>>>;   
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;  
     private _onSelectionBoxMovedObserver: Nullable<Observer<ClientRect | DOMRect>>;  
     private _onFrameCreatedObserver: Nullable<Observer<GraphFrame>>; 
     private _onUpdateRequiredObserver: Nullable<Observer<void>>;  

+ 2 - 2
nodeEditor/src/diagram/nodeLink.ts

@@ -1,4 +1,4 @@
-import { GraphCanvasComponent } from './graphCanvas';
+import { GraphCanvasComponent, FramePortData } from './graphCanvas';
 import { GraphNode } from './graphNode';
 import { NodePort } from './nodePort';
 import { Nullable } from 'babylonjs/types';
@@ -14,7 +14,7 @@ export class NodeLink {
     private _nodeB?: GraphNode;
     private _path: SVGPathElement;
     private _selectionPath: SVGPathElement;
-    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphNode | NodeLink | GraphFrame | NodePort>>>;
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;  
     private _isVisible = true;
 
     public onDisposedObservable = new Observable<NodeLink>();

+ 2 - 1
nodeEditor/src/diagram/nodePort.ts

@@ -10,13 +10,14 @@ import { GraphNode } from './graphNode';
 import { NodeLink } from './nodeLink';
 import { GraphFrame } from './graphFrame';
 import { FrameNodePort } from './frameNodePort';
+import { FramePortData } from './graphCanvas';
 
 export class NodePort {
     protected _element: HTMLDivElement;
     protected _img: HTMLImageElement;
     protected _globalState: GlobalState;
     protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
-    protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphNode | NodeLink | GraphFrame | NodePort | FrameNodePort>>>;  
+    protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;  
     
     public delegatedPort: Nullable<FrameNodePort> = null;
 

+ 45 - 26
nodeEditor/src/diagram/properties/frameNodePortPropertyComponent.tsx

@@ -4,56 +4,75 @@ import { LineContainerComponent } from '../../sharedComponents/lineContainerComp
 import { GlobalState } from '../../globalState';
 import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
 import { ButtonLineComponent } from '../../sharedComponents/buttonLineComponent';
-import { FramePortPosition } from '../graphFrame';
+import { FramePortPosition, GraphFrame } from '../graphFrame';
 import { Nullable } from 'babylonjs/types';
 import { Observer } from 'babylonjs/Misc/observable';
 import { FrameNodePort } from '../frameNodePort';
+import { NodePort } from '../nodePort';
+import { GraphNode } from '../graphNode';
+import { NodeLink } from '../nodeLink';
+import { FramePortData, isFramePortData } from '../graphCanvas';
 
 export interface IFrameNodePortPropertyTabComponentProps {
     globalState: GlobalState
     frameNodePort: FrameNodePort;
+    frame: GraphFrame;
 }
 
-export class FrameNodePortPropertyTabComponent extends React.Component<IFrameNodePortPropertyTabComponentProps, {framePortPosition: FramePortPosition}> {    
-    private _onFramePortPositionChangedObserver: Nullable<Observer<FramePortPosition>>;
+export class FrameNodePortPropertyTabComponent extends React.Component<IFrameNodePortPropertyTabComponentProps, { port: FrameNodePort }> {
+    private _onFramePortPositionChangedObserver: Nullable<Observer<FrameNodePort>>;
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | NodePort | GraphNode | NodeLink | FramePortData>>>;
 
     constructor(props: IFrameNodePortPropertyTabComponentProps) {
         super(props);
         this.state = {
-            framePortPosition: this.props.frameNodePort.framePortPosition
-        };
+            port: this.props.frameNodePort
+        }
 
-        this._onFramePortPositionChangedObserver = this.props.frameNodePort.onFramePortPositionChangedObservable.add((position) => {
-            this.setState({framePortPosition: position})
+        const _this = this;
+        this._onSelectionChangedObserver = this.props.globalState.onSelectionChangedObservable.add((selection) => {
+            if (isFramePortData(selection)) {
+                selection.port.onFramePortPositionChangedObservable.clear();
+                _this._onFramePortPositionChangedObserver = selection.port.onFramePortPositionChangedObservable.add((port: FrameNodePort) => {
+                    _this.setState({ port: port });
+                });
+
+                _this.setState({ port: selection.port });
+            }
+        });
+
+        this._onFramePortPositionChangedObserver = this.props.frameNodePort.onFramePortPositionChangedObservable.add((port: FrameNodePort) => {
+            _this.setState({ port: port });
         });
     }
 
     componentWillUnmount() {
-        this.props.frameNodePort.onFramePortPositionChangedObservable.remove(this._onFramePortPositionChangedObserver)
+        this.props.frameNodePort.onFramePortPositionChangedObservable.remove(this._onFramePortPositionChangedObserver);
+        this.props.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
     }
 
-    render() {      
+    render() {
         return (
             <div id="propertyTab">
-            <div id="header">
-                <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
-                <div id="title">
-                    NODE MATERIAL EDITOR
+                <div id="header">
+                    <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
+                    <div id="title">
+                        NODE MATERIAL EDITOR
+                </div>
+                </div>
+                <div>
+                    <LineContainerComponent title="GENERAL">
+                        <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portLabel" target={this.props.frameNodePort} />
+                        {this.props.frameNodePort.framePortPosition !== FramePortPosition.Top && <ButtonLineComponent label="Move Port Up" onClick={() => {
+                            this.props.frame.moveFramePortUp(this.props.frameNodePort);
+                        }} />}
+
+                        {this.props.frameNodePort.framePortPosition !== FramePortPosition.Bottom && <ButtonLineComponent label="Move Port Down" onClick={() => {
+                            this.props.frame.moveFramePortDown(this.props.frameNodePort);
+                        }} />}
+                    </LineContainerComponent>
                 </div>
             </div>
-            <div>
-                <LineContainerComponent title="GENERAL">
-                    <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portLabel" target={this.props.frameNodePort}/>
-                     {this.state.framePortPosition !== FramePortPosition.Top && <ButtonLineComponent label="Move Node Up" onClick={() => {
-                                this.props.frameNodePort.onFramePortMoveUpObservable.notifyObservers(this.props.frameNodePort);
-                            }} />}
-
-                    {this.state.framePortPosition !== FramePortPosition.Bottom && <ButtonLineComponent label="Move Node Down" onClick={() => {
-                                this.props.frameNodePort.onFramePortMoveDownObservable.notifyObservers(this.props.frameNodePort);
-                            }} />}
-                </LineContainerComponent>
-            </div>
-        </div>
         );
     }
 }

+ 2 - 1
nodeEditor/src/globalState.ts

@@ -12,13 +12,14 @@ import { NodePort } from './diagram/nodePort';
 import { NodeLink } from './diagram/nodeLink';
 import { GraphFrame } from './diagram/graphFrame';
 import { FrameNodePort } from './diagram/frameNodePort';
+import { FramePortData } from './diagram/graphCanvas';
 
 export class GlobalState {
     nodeMaterial: NodeMaterial;
     hostElement: HTMLElement;
     hostDocument: HTMLDocument;
     hostWindow: Window;
-    onSelectionChangedObservable = new Observable<Nullable<GraphNode | NodeLink | GraphFrame | NodePort>>();
+    onSelectionChangedObservable = new Observable<Nullable<GraphNode | NodeLink | GraphFrame | NodePort | FramePortData>>();
     onRebuildRequiredObservable = new Observable<void>();
     onBuiltObservable = new Observable<void>();
     onResetRequiredObservable = new Observable<void>();