瀏覽代碼

Merge pull request #8012 from belfortk/make-node-ports-editable

Make node ports editable
David Catuhe 5 年之前
父節點
當前提交
e7b586eaef

File diff suppressed because it is too large
+ 1241 - 1237
dist/preview release/babylon.module.d.ts


+ 1 - 0
dist/preview release/what's new.md

@@ -26,6 +26,7 @@
 
 - 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)
+- Can now edit Node port names ([belfortk](https://github.com/belfortk)
 
 ### Inspector
 

+ 24 - 7
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -17,6 +17,7 @@ import { TextLineComponent } from '../../sharedComponents/textLineComponent';
 import { Engine } from 'babylonjs/Engines/engine';
 import { FramePropertyTabComponent } from '../../diagram/properties/framePropertyComponent';
 import { FrameNodePortPropertyTabComponent } from '../../diagram/properties/frameNodePortPropertyComponent';
+import { NodePortPropertyTabComponent } from '../../diagram/properties/nodePortPropertyComponent';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes';
 import { Color3LineComponent } from '../../sharedComponents/color3LineComponent';
@@ -28,6 +29,7 @@ import { Vector4LineComponent } from '../../sharedComponents/vector4LineComponen
 import { Observer } from 'babylonjs/Misc/observable';
 import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
 import { FrameNodePort } from '../../diagram/frameNodePort';
+import { NodePort } from '../../diagram/nodePort';
 import { isFramePortData } from '../../diagram/graphCanvas';
 require("./propertyTab.scss");
 
@@ -35,25 +37,34 @@ interface IPropertyTabComponentProps {
     globalState: GlobalState;
 }
 
-export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, { currentNode: Nullable<GraphNode>, currentFrame: Nullable<GraphFrame>, currentFrameNodePort: Nullable<FrameNodePort> }> {
+interface IPropertyTabComponentState { 
+    currentNode: Nullable<GraphNode>, 
+    currentFrame: Nullable<GraphFrame>, 
+    currentFrameNodePort: Nullable<FrameNodePort>,
+    currentNodePort: Nullable<NodePort>
+ }
+
+export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, IPropertyTabComponentState> {
     private _onBuiltObserver: Nullable<Observer<void>>;
 
     constructor(props: IPropertyTabComponentProps) {
         super(props)
 
-        this.state = { currentNode: null, currentFrame: null, currentFrameNodePort: null };
+        this.state = { currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: null };
     }
 
     componentDidMount() {
         this.props.globalState.onSelectionChangedObservable.add(selection => {
             if (selection instanceof GraphNode) {
-                this.setState({ currentNode: selection, currentFrame: null, currentFrameNodePort: null });
+                this.setState({ currentNode: selection, currentFrame: null, currentFrameNodePort: null, currentNodePort: null });
             } else if (selection instanceof GraphFrame) {
-                this.setState({ currentNode: null, currentFrame: selection, currentFrameNodePort: null });
+                this.setState({ currentNode: null, currentFrame: selection, currentFrameNodePort: null, currentNodePort: null });
             } else if(isFramePortData(selection)) {
-                this.setState({ currentNode: null, currentFrame: selection.frame, currentFrameNodePort: selection.port });
+                this.setState({ currentNode: null, currentFrame: selection.frame, currentFrameNodePort: selection.port, currentNodePort: null });
+            } else if (selection instanceof NodePort && selection.hasLabel()) {
+                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: selection})
             } else {
-                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null });
+                this.setState({ currentNode: null, currentFrame: null, currentFrameNodePort: null, currentNodePort: null });
             }
         });
 
@@ -240,7 +251,7 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                             NODE MATERIAL EDITOR
                         </div>
                     </div>
-                    {this.state.currentNode.renderProperties()}
+                    {this.state.currentNode?.renderProperties() || this.state.currentNodePort?.node.renderProperties()}
                 </div>
             );
         }
@@ -251,6 +262,12 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
             );
         }
 
+        if (this.state.currentNodePort) {
+            return (
+                <NodePortPropertyTabComponent globalState={this.props.globalState} nodePort={this.state.currentNodePort}/>
+            );
+        }
+
         if (this.state.currentFrame) {
             return (
                 <FramePropertyTabComponent globalState={this.props.globalState} frame={this.state.currentFrame}/>

+ 1 - 12
nodeEditor/src/diagram/frameNodePort.ts

@@ -9,7 +9,6 @@ import { NodeMaterialConnectionPoint } from 'babylonjs/Materials/Node/nodeMateri
 import { FramePortData, isFramePortData } from './graphCanvas';
 
 export class FrameNodePort extends NodePort {
-    private _portLabel: Element;
     private _parentFrameId: number;
     private _isInput: boolean;
     private _framePortPosition: FramePortPosition
@@ -28,25 +27,16 @@ export class FrameNodePort extends NodePort {
         return this._isInput;
     }
 
-    public get portLabel() {
-        return this._portLabel.innerHTML;
-    }
-
     public get framePortId() {
         return this._framePortId;
     }
 
-    public set portLabel(newLabel: string) {
-        this._portLabel.innerHTML = newLabel;
-    }
-
     public get framePortPosition() {
         return this._framePortPosition;
     }
 
     public set framePortPosition(position: FramePortPosition) {
         this._framePortPosition = position;
-        console.log(this.onFramePortPositionChangedObservable.observers);
         this.onFramePortPositionChangedObservable.notifyObservers(this);
     }
 
@@ -54,7 +44,6 @@ export class FrameNodePort extends NodePort {
         super(portContainer, connectionPoint,node, globalState);
 
         this._parentFrameId = parentFrameId;
-        this._portLabel = portContainer.children[0];
         this._isInput = isInput;
         this._framePortId = framePortId;
 
@@ -83,7 +72,7 @@ export class FrameNodePort extends NodePort {
         if (!displayManager || displayManager.shouldDisplayPortLabels(block)) {
             let portLabel = root.ownerDocument!.createElement("div");
             portLabel.classList.add("port-label");
-            portLabel.innerHTML = connectionPoint.name;        
+            portLabel.innerHTML = connectionPoint.displayName || connectionPoint.name;        
             portContainer.appendChild(portLabel);
         }
 

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

@@ -198,6 +198,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                     } else {                    
                         this._selectedNodes = [selection];
                     }
+                } else if(selection instanceof NodePort && !selection.hasLabel()){ // if node port is uneditable, select graphNode instead
+                    props.globalState.onSelectionChangedObservable.notifyObservers(selection.node)
                 } else if(selection instanceof NodePort){
                     this._selectedNodes = [];
                     this._selectedFrame = null;
@@ -625,6 +627,8 @@ export class GraphCanvasComponent extends React.Component<IGraphCanvasComponentP
                         }
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(data);
                     }
+                } else if(this._candidateLink.portA instanceof NodePort){
+                    this.props.globalState.onSelectionChangedObservable.notifyObservers(this._candidateLink.portA );
                 }
             }
             this._candidateLink.dispose();

+ 9 - 0
nodeEditor/src/diagram/graphNode.ts

@@ -51,6 +51,7 @@ export class GraphNode {
             this._visual.classList.add("hidden");
         } else {
             this._visual.classList.remove("hidden");
+            this._upateNodePortNames();
         }
 
         for (var link of this._links) {
@@ -60,6 +61,14 @@ export class GraphNode {
         this._refreshLinks();
     }
 
+    private _upateNodePortNames(){
+        for (var port of this._inputPorts.concat(this._outputPorts)) {
+            if(port.hasLabel()){
+                port.portName = port.connectionPoint.displayName || port.connectionPoint.name;
+            }
+        }
+    }
+
     public get outputPorts() {
         return this._outputPorts;
     }

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

@@ -16,6 +16,7 @@ export class NodePort {
     protected _element: HTMLDivElement;
     protected _img: HTMLImageElement;
     protected _globalState: GlobalState;
+    protected _portLabelElement: Element;
     protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
     protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;  
     
@@ -29,6 +30,21 @@ export class NodePort {
         return this._element;
     }
 
+    public get portName(){
+        return this.connectionPoint.displayName || this.connectionPoint.name;
+    }
+
+    public set portName(newName: string){
+        if(this._portLabelElement) {
+            this.connectionPoint.displayName = newName;
+            this._portLabelElement.innerHTML = newName;
+        }
+    }
+
+    public hasLabel(){
+        return !!this._portLabelElement;
+    }
+
     public refresh() {
         this._element.style.background = BlockTools.GetColorFromConnectionNodeType(this.connectionPoint.type);
         switch (this.connectionPoint.type) {
@@ -62,6 +78,11 @@ export class NodePort {
         this._img = portContainer.ownerDocument!.createElement("img");
         this._element.appendChild(this._img );
 
+        // determine if node name is editable
+        if (portContainer.children[0].className === 'port-label') {
+            this._portLabelElement = portContainer.children[0];
+        }
+
         (this._element as any).port = this;
 
         // Drag support
@@ -79,6 +100,14 @@ export class NodePort {
             this._globalState.onCandidatePortSelectedObservable.notifyObservers(this);
         });
 
+        this._onSelectionChangedObserver = this._globalState.onSelectionChangedObservable.add((selection) => {
+            if (selection === this) {
+                this._img.classList.add("selected");
+            } else {
+                this._img.classList.remove("selected");
+            }
+        });
+
         this.refresh();
     }
 
@@ -102,7 +131,7 @@ export class NodePort {
         if (!displayManager || displayManager.shouldDisplayPortLabels(block)) {
             let portLabel = root.ownerDocument!.createElement("div");
             portLabel.classList.add("port-label");
-            portLabel.innerHTML = connectionPoint.name;        
+            portLabel.innerHTML = connectionPoint.displayName || connectionPoint.name;        
             portContainer.appendChild(portLabel);
         }
     

+ 1 - 1
nodeEditor/src/diagram/properties/frameNodePortPropertyComponent.tsx

@@ -62,7 +62,7 @@ export class FrameNodePortPropertyTabComponent extends React.Component<IFrameNod
                 </div>
                 <div>
                     <LineContainerComponent title="GENERAL">
-                        <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portLabel" target={this.props.frameNodePort} />
+                        <TextInputLineComponent globalState={this.props.globalState} label="Port Name" propertyName="portName" target={this.props.frameNodePort} />
                         {this.props.frameNodePort.framePortPosition !== FramePortPosition.Top && <ButtonLineComponent label="Move Port Up" onClick={() => {
                             this.props.frame.moveFramePortUp(this.props.frameNodePort);
                         }} />}

+ 47 - 0
nodeEditor/src/diagram/properties/nodePortPropertyComponent.tsx

@@ -0,0 +1,47 @@
+
+import * as React from "react";
+import { LineContainerComponent } from '../../sharedComponents/lineContainerComponent';
+import { GlobalState } from '../../globalState';
+import { TextInputLineComponent } from '../../sharedComponents/textInputLineComponent';
+import {  GraphFrame } from '../graphFrame';
+import { Nullable } from 'babylonjs/types';
+import { Observer } from 'babylonjs/Misc/observable';
+import { NodePort } from '../nodePort';
+import { GraphNode } from '../graphNode';
+import { NodeLink } from '../nodeLink';
+import { FramePortData } from '../graphCanvas';
+
+export interface IFrameNodePortPropertyTabComponentProps {
+    globalState: GlobalState
+    nodePort: NodePort;
+}
+
+export class NodePortPropertyTabComponent extends React.Component<IFrameNodePortPropertyTabComponentProps> {
+    private _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | NodePort | GraphNode | NodeLink | FramePortData>>>;
+
+    constructor(props: IFrameNodePortPropertyTabComponentProps) {
+        super(props);
+    }
+
+    componentWillUnmount() {
+        this.props.globalState.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
+    }
+
+    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>
+                </div>
+                <div>
+                    <LineContainerComponent title="GENERAL">
+                        {this.props.nodePort.hasLabel() && <TextInputLineComponent globalState={this.props.globalState} label="Port Label" propertyName="portName" target={this.props.nodePort} />}
+                    </LineContainerComponent>
+                </div>
+            </div>
+        );
+    }
+}

+ 25 - 0
src/Materials/Node/nodeMaterialBlock.ts

@@ -656,11 +656,16 @@ export class NodeMaterialBlock {
         serializationObject.comments = this.comments;
 
         serializationObject.inputs = [];
+        serializationObject.outputs = [];
 
         for (var input of this.inputs) {
             serializationObject.inputs.push(input.serialize());
         }
 
+        for (var output of this.outputs) {
+            serializationObject.outputs.push(output.serialize(false));
+        }
+
         return serializationObject;
     }
 
@@ -668,6 +673,26 @@ export class NodeMaterialBlock {
     public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
         this.name = serializationObject.name;
         this.comments = serializationObject.comments;
+        this._deserializePortDisplayNames(serializationObject);
+    }
+
+    private _deserializePortDisplayNames(serializationObject: any) {
+        const serializedInputs = serializationObject.inputs;
+        const serializedOutputs = serializationObject.outputs;
+        if (serializedInputs) {
+            serializedInputs.forEach((port: any, i: number) => {
+                if (port.displayName) {
+                    this.inputs[i].displayName = port.displayName;
+                }
+            });
+        }
+        if (serializedOutputs) {
+            serializedOutputs.forEach((port: any, i: number) => {
+                if (port.displayName) {
+                    this.outputs[i].displayName = port.displayName;
+                }
+            });
+        }
     }
 
     /**

+ 9 - 2
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -134,6 +134,11 @@ export class NodeMaterialConnectionPoint {
     public name: string;
 
     /**
+     * Gets or sets the connection point name
+     */
+    public displayName: string;
+
+    /**
      * Gets or sets a boolean indicating that this connection point can be omitted
      */
     public isOptional: boolean;
@@ -426,14 +431,16 @@ export class NodeMaterialConnectionPoint {
 
     /**
      * Serializes this point in a JSON representation
+     * @param isInput defines if the connection point is an input (default is true)
      * @returns the serialized point object
      */
-    public serialize(): any {
+    public serialize(isInput = true): any {
         let serializationObject: any = {};
 
         serializationObject.name = this.name;
+        serializationObject.displayName = this.displayName;
 
-        if (this.connectedPoint) {
+        if (isInput && this.connectedPoint) {
             serializationObject.inputName = this.name;
             serializationObject.targetBlockId = this.connectedPoint.ownerBlock.uniqueId;
             serializationObject.targetConnectionName = this.connectedPoint.name;