diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 03c2cb5..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-root = true
-
-[*.cs]
-indent_style = space
-indent_size = 4
-end_of_line = crlf
-insert_final_newline = false
-trim_trailing_whitespace = true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..aec9b6d
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,38 @@
+name: build
+
+on:
+ pull_request:
+ branches:
+ - '*'
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - os: windows
+ build-target: Android
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ clean: false
+ lfs: true
+
+ # Installs the Unity Editor based on your project version text file
+ # sets -> env.UNITY_EDITOR_PATH
+ # sets -> env.UNITY_PROJECT_PATH
+ # https://github.com/XRTK/unity-setup
+ - uses: xrtk/unity-setup@v7.2
+ with:
+ build-targets: ${{ matrix.build-target }}
+
+ - name: Unity Build (${{ matrix.build-target }})
+ uses: RageAgainstThePixel/unity-build@v5
+ with:
+ build-target: ${{ matrix.build-target }}
+ publish-artifacts: false
diff --git a/.github/workflows/upm-subtree-split.yml b/.github/workflows/upm-subtree-split.yml
new file mode 100644
index 0000000..af28306
--- /dev/null
+++ b/.github/workflows/upm-subtree-split.yml
@@ -0,0 +1,35 @@
+name: upm-subtree-split
+
+on:
+ push:
+ branches:
+ - main
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ upm-subtree-split:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ token: ${{ secrets.CI_TOKEN }}
+ fetch-depth: 0
+
+ - name: upm subtree split
+ run: |
+ # upm subtree split
+ git config user.name github-actions
+ git config user.email github-actions@github.com
+ git fetch --all --tags
+ $packageDir = Get-Item -Path "**/Packages/com.*" | Select-Object -ExpandProperty FullName
+ $packageDir = $packageDir.replace('${{ github.workspace }}/','')
+ Write-Host $packageDir
+ git subtree split --prefix="$packageDir" -b upm
+ git checkout upm
+ git fetch origin upm
+ git rebase origin/upm --reapply-cherry-picks
+ git push origin upm --force-with-lease --tags --set-upstream --verbose
+ working-directory: ${{ github.workspace }}
+ shell: pwsh
diff --git a/.gitignore b/.gitignore
index c75a01e..d7bb963 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,30 +1,84 @@
-/[Ll]ibrary/
-/[Tt]emp/
-/[Oo]bj/
-/[Bb]uild/
+# ============ #
+# System Files #
+# ============ #
+.DS_Store
+._*
-# Autogenerated VS/MD solution and project files
-*.csproj
-*.unityproj
-*.sln
-*.suo
-*.tmp
-*.user
+# =============== #
+# Unity generated #
+# =============== #
+[Aa]pp/
+[Aa]pp.meta
+[Bb]in/
+[Bb]uilds/
+[Bb]uild/
+[Ll]ibrary/
+[Ll]ogs/
+[Oo]bj/
+[Tt]emp/
+UserSettings/
+UWP/
+WindowsStoreApp/
+UnityGenerated/
+UnityPackageManager/
+.out/
+.gradle/
+project.json
+project.lock.json
+*.package
+TextMesh Pro.meta
+TextMesh Pro/
+UIElementsSchema/
+*packages-lock.json
+
+# ============ #
+# Certificates #
+# ============ #
+*.cert
+*.privkey
+*.pfx
+*.pfx.meta
+
+# ===================================== #
+# Visual Studio / MonoDevelop generated #
+# ===================================== #
+.vs/
+ExportedObj/
+obj/
+*.svd
*.userprefs
+/*.csproj
+*.csproj
*.pidb
-*.booproj
+*.suo
+/*.sln
+*.sln
+*.user
+*.unityproj
+*.ipch
+*.opensdf
+*.sdf
+*.tlog
+*.log
+*.idb
+*.opendb
+*.vsconfig
-# Unity3D generated meta files
-*.pidb.meta
+# ============================ #
+# Visual Studio Code Generated #
+# ============================ #
+.vscode/
-# Unity3D Generated File On Crash Reports
-sysinfo.txt
+# ========================= #
+# Jetbrains Rider Generated #
+# ========================= #
+.idea/
+_ReSharper.Caches
-/Examples/
-
-.git.meta
-.gitignore.meta
-.gitattributes.meta
-
-# OS X only:
-.DS_Store
\ No newline at end of file
+# ===================== #
+# Project Specific List #
+# ===================== #
+--Version/
+artifacts/
+StreamingAssets/
+StreamingAssets.meta
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 10d780a..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,40 +0,0 @@
-## Contributing to xNode
-💙Thank you for taking the time to contribute💙
-
-If you haven't already, join our [Discord channel](https://discord.gg/qgPrHv4)!
-
-## Pull Requests
-Try to keep your pull requests relevant, neat, and manageable. If you are adding multiple features, split them into separate PRs.
-These are the main points to follow:
-
-1) Use formatting which is consistent with the rest of xNode base (see below)
-2) Keep _one feature_ per PR (see below)
-3) xNode aims to be compatible with C# 4.x, do not use new language features
-4) Avoid including irellevant whitespace or formatting changes
-5) Comment your code
-6) Spell check your code / comments
-7) Use concrete types, not *var*
-8) Use english language
-
-## New features
-xNode aims to be simple and extendible, not trying to fix all of Unity's shortcomings.
-
-Approved changes might be rejected if bundled with rejected changes, so keep PRs as separate as possible.
-
-If your feature aims to cover something not related to editing nodes, it generally won't be accepted. If in doubt, ask on the Discord channel.
-
-## Coding conventions
-Using consistent formatting is key to having a clean git history. Skim through the code and you'll get the hang of it quickly.
-* Methods, Types and properties PascalCase
-* Variables camelCase
-* Public methods XML commented. Params described if not obvious
-* Explicit usage of brackets when doing multiple math operations on the same line
-
-## Formatting
-I use VSCode with the C# FixFormat extension and the following setting overrides:
-```json
-"csharpfixformat.style.spaces.beforeParenthesis": false,
-"csharpfixformat.style.indent.regionIgnored": true
-```
-* Open braces on same line as condition
-* 4 spaces for indentation.
diff --git a/Scripts/Editor/NodeEditorAssetModProcessor.cs b/Scripts/Editor/NodeEditorAssetModProcessor.cs
deleted file mode 100644
index f4b14a2..0000000
--- a/Scripts/Editor/NodeEditorAssetModProcessor.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using UnityEditor;
-using UnityEngine;
-using System.IO;
-
-namespace XNodeEditor {
- /// Deals with modified assets
- class NodeEditorAssetModProcessor : UnityEditor.AssetModificationProcessor {
-
- /// Automatically delete Node sub-assets before deleting their script.
- /// This is important to do, because you can't delete null sub assets.
- /// For another workaround, see: https://gitlab.com/RotaryHeart-UnityShare/subassetmissingscriptdelete
- private static AssetDeleteResult OnWillDeleteAsset (string path, RemoveAssetOptions options) {
- // Skip processing anything without the .cs extension
- if (Path.GetExtension(path) != ".cs") return AssetDeleteResult.DidNotDelete;
-
- // Get the object that is requested for deletion
- UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath (path);
-
- // If we aren't deleting a script, return
- if (!(obj is UnityEditor.MonoScript)) return AssetDeleteResult.DidNotDelete;
-
- // Check script type. Return if deleting a non-node script
- UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
- System.Type scriptType = script.GetClass ();
- if (scriptType == null || (scriptType != typeof (XNode.Node) && !scriptType.IsSubclassOf (typeof (XNode.Node)))) return AssetDeleteResult.DidNotDelete;
-
- // Find all ScriptableObjects using this script
- string[] guids = AssetDatabase.FindAssets ("t:" + scriptType);
- for (int i = 0; i < guids.Length; i++) {
- string assetpath = AssetDatabase.GUIDToAssetPath (guids[i]);
- Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath (assetpath);
- for (int k = 0; k < objs.Length; k++) {
- XNode.Node node = objs[k] as XNode.Node;
- if (node.GetType () == scriptType) {
- if (node != null && node.graph != null) {
- // Delete the node and notify the user
- Debug.LogWarning (node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
- node.graph.RemoveNode (node);
- }
- }
- }
- }
- // We didn't actually delete the script. Tell the internal system to carry on with normal deletion procedure
- return AssetDeleteResult.DidNotDelete;
- }
-
- /// Automatically re-add loose node assets to the Graph node list
- [InitializeOnLoadMethod]
- private static void OnReloadEditor () {
- // Find all NodeGraph assets
- string[] guids = AssetDatabase.FindAssets ("t:" + typeof (XNode.NodeGraph));
- for (int i = 0; i < guids.Length; i++) {
- string assetpath = AssetDatabase.GUIDToAssetPath (guids[i]);
- XNode.NodeGraph graph = AssetDatabase.LoadAssetAtPath (assetpath, typeof (XNode.NodeGraph)) as XNode.NodeGraph;
- graph.nodes.RemoveAll(x => x == null); //Remove null items
- Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath (assetpath);
- // Ensure that all sub node assets are present in the graph node list
- for (int u = 0; u < objs.Length; u++) {
- // Ignore null sub assets
- if (objs[u] == null) continue;
- if (!graph.nodes.Contains (objs[u] as XNode.Node)) graph.nodes.Add(objs[u] as XNode.Node);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeGraphImporter.cs b/Scripts/Editor/NodeGraphImporter.cs
deleted file mode 100644
index 3faf54f..0000000
--- a/Scripts/Editor/NodeGraphImporter.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using UnityEditor;
-using UnityEditor.Experimental.AssetImporters;
-using UnityEngine;
-using XNode;
-
-namespace XNodeEditor {
- /// Deals with modified assets
- class NodeGraphImporter : AssetPostprocessor {
- private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) {
- foreach (string path in importedAssets) {
- // Skip processing anything without the .asset extension
- if (Path.GetExtension(path) != ".asset") continue;
-
- // Get the object that is requested for deletion
- NodeGraph graph = AssetDatabase.LoadAssetAtPath(path);
- if (graph == null) continue;
-
- // Get attributes
- Type graphType = graph.GetType();
- NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll(
- graphType.GetCustomAttributes(typeof(NodeGraph.RequireNodeAttribute), true), x => x as NodeGraph.RequireNodeAttribute);
-
- Vector2 position = Vector2.zero;
- foreach (NodeGraph.RequireNodeAttribute attrib in attribs) {
- if (attrib.type0 != null) AddRequired(graph, attrib.type0, ref position);
- if (attrib.type1 != null) AddRequired(graph, attrib.type1, ref position);
- if (attrib.type2 != null) AddRequired(graph, attrib.type2, ref position);
- }
- }
- }
-
- private static void AddRequired(NodeGraph graph, Type type, ref Vector2 position) {
- if (!graph.nodes.Any(x => x.GetType() == type)) {
- XNode.Node node = graph.AddNode(type);
- node.position = position;
- position.x += 200;
- if (node.name == null || node.name.Trim() == "") node.name = NodeEditorUtilities.NodeDefaultName(type);
- if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(graph))) AssetDatabase.AddObjectToAsset(node, graph);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/package.json b/package.json
deleted file mode 100644
index 9c1ec7d..0000000
--- a/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "com.github.siccity.xnode",
- "description": "xNode provides a set of APIs and an editor interface for creating and editing custom node graphs.",
- "version": "1.8.0",
- "unity": "2018.1",
- "displayName": "xNode"
-}
diff --git a/Scripts.meta b/xNode/Assets/Scenes.meta
similarity index 57%
rename from Scripts.meta
rename to xNode/Assets/Scenes.meta
index ab712b6..cdcb446 100644
--- a/Scripts.meta
+++ b/xNode/Assets/Scenes.meta
@@ -1,9 +1,8 @@
fileFormatVersion: 2
-guid: 657b15cb3ec32a24ca80faebf094d0f4
+guid: 999581c3922a8f942959bcb0a0fc7d18
folderAsset: yes
-timeCreated: 1505418321
-licenseType: Free
DefaultImporter:
+ externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
diff --git a/xNode/Assets/Scenes/SampleScene.unity b/xNode/Assets/Scenes/SampleScene.unity
new file mode 100644
index 0000000..2221b04
--- /dev/null
+++ b/xNode/Assets/Scenes/SampleScene.unity
@@ -0,0 +1,267 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 705507994}
+ m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 12
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 500
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 500
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 2
+ m_PVRDenoiserTypeDirect: 0
+ m_PVRDenoiserTypeIndirect: 0
+ m_PVRDenoiserTypeAO: 0
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 0
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &705507993
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 705507995}
+ - component: {fileID: 705507994}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &705507994
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 705507993}
+ m_Enabled: 1
+ serializedVersion: 8
+ m_Type: 1
+ m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+ m_Intensity: 1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_Lightmapping: 1
+ m_LightShadowCasterMode: 0
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 705507993}
+ m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 3, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1 &963194225
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 963194228}
+ - component: {fileID: 963194227}
+ - component: {fileID: 963194226}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_GateFitMode: 2
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInternal: {fileID: 0}
+ m_GameObject: {fileID: 963194225}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 1, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/CONTRIBUTING.md.meta b/xNode/Assets/Scenes/SampleScene.unity.meta
similarity index 62%
rename from CONTRIBUTING.md.meta
rename to xNode/Assets/Scenes/SampleScene.unity.meta
index 5d7c128..952bd1e 100644
--- a/CONTRIBUTING.md.meta
+++ b/xNode/Assets/Scenes/SampleScene.unity.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: bc1db8b29c76d44648c9c86c2dfade6d
-TextScriptImporter:
+guid: 9fc0d4010bbf28b4594072e72b8655ab
+DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
diff --git a/xNode/Packages/com.pillow.xnode/.editorconfig b/xNode/Packages/com.pillow.xnode/.editorconfig
new file mode 100644
index 0000000..c485248
--- /dev/null
+++ b/xNode/Packages/com.pillow.xnode/.editorconfig
@@ -0,0 +1,75 @@
+# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options
+
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = true
+trim_trailing_whitespace = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_open_brace = all
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:error
+
+# Code-block preferences
+csharp_prefer_braces = true:error
+
+# Use language keywords for types
+dotnet_style_predefined_type_for_member_access = true
+dotnet_style_predefined_type_for_locals_parameters_members = true
+
+# Code Style
+csharp_style_var_when_type_is_apparent = true
+
+#### Resharper/Rider Rules ####
+# https://www.jetbrains.com/help/resharper/EditorConfig_Properties.html
+
+resharper_csharp_force_attribute_style=separate
+csharp_place_field_attribute_on_same_line=false
+csharp_place_accessorholder_attribute_on_same_line=false
+csharp_trailing_comma_in_multiline_lists=false
+csharp_trailing_comma_in_singleline_lists=false
+csharp_keep_existing_attribute_arrangement=false
+csharp_blank_lines_around_region=1
+csharp_blank_lines_inside_region=1
+csharp_keep_blank_lines_in_code=false
+csharp_remove_blank_lines_near_braces_in_code=true
+csharp_blank_lines_before_control_transfer_statements=1
+csharp_blank_lines_after_control_transfer_statements=1
+csharp_blank_lines_before_block_statements=1
+csharp_blank_lines_after_block_statements=1
+csharp_blank_lines_before_multiline_statements=1
+csharp_blank_lines_after_multiline_statements=1
+csharp_blank_lines_around_block_case_section=0
+csharp_blank_lines_around_multiline_case_section=0
+csharp_blank_lines_before_case=0
+csharp_blank_lines_after_case=0
+resharper_unity_duplicate_event_function_highlighting=error
+resharper_unity_duplicate_shortcut_highlighting=error
+resharper_unity_expected_component_highlighting=error
+resharper_unity_explicit_tag_comparison_highlighting=error
+resharper_unity_incorrect_mono_behaviour_instantiation_highlighting=error
+resharper_unity_incorrect_scriptable_object_instantiation_highlighting=error
+resharper_unity_no_null_coalescing_highlighting=error
+resharper_unity_performance_critical_code_camera_main_highlighting=error
+resharper_unity_performance_critical_code_invocation_highlighting=warning
+resharper_unity_performance_critical_code_null_comparison_highlighting=warning
+resharper_unity_possible_misapplication_of_attribute_to_multiple_fields_highlighting=error
+resharper_unity_redundant_attribute_on_target_highlighting=error
+resharper_unity_redundant_formerly_serialized_as_attribute_highlighting=error
+resharper_unity_unresolved_component_or_scriptable_object_highlighting=error
+resharper_use_name_of_instead_of_type_of_highlighting=error
+resharper_wrong_public_modifier_specification_highlighting=error
diff --git a/xNode/Packages/com.pillow.xnode/.github/workflows/publish.yml b/xNode/Packages/com.pillow.xnode/.github/workflows/publish.yml
new file mode 100644
index 0000000..ae4a4a2
--- /dev/null
+++ b/xNode/Packages/com.pillow.xnode/.github/workflows/publish.yml
@@ -0,0 +1,31 @@
+name: publish
+
+on:
+ push:
+ branches:
+ - upm
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ token: ${{ secrets.CI_TOKEN }}
+ ref: upm
+ clean: true
+ lfs: true
+
+ - uses: xrtk/upm-release@development
+ with:
+ upm-username: 'pillow-build-bot'
+ upm-email: 'hello@pillow.social'
+ upm-server-address: 'http://upm.pillow.social:4873'
+ upm-auth-token: '${{ secrets.UPM_AUTH_TOKEN }}'
+ github-username: 'TogetherXR'
+ github-pat: '${{ secrets.CI_TOKEN }}'
+ github-token: '${{ secrets.GITHUB_TOKEN }}'
+ package-root: '${{ github.workspace }}'
diff --git a/xNode/Packages/com.pillow.xnode/.npmignore b/xNode/Packages/com.pillow.xnode/.npmignore
new file mode 100644
index 0000000..f231e53
--- /dev/null
+++ b/xNode/Packages/com.pillow.xnode/.npmignore
@@ -0,0 +1,2 @@
+.npmrc
+.github/
\ No newline at end of file
diff --git a/xNode/Packages/com.pillow.xnode/.npmrc b/xNode/Packages/com.pillow.xnode/.npmrc
new file mode 100644
index 0000000..a49e127
--- /dev/null
+++ b/xNode/Packages/com.pillow.xnode/.npmrc
@@ -0,0 +1 @@
+strict-ssl=false
diff --git a/LICENSE.md b/xNode/Packages/com.pillow.xnode/LICENSE.md
similarity index 100%
rename from LICENSE.md
rename to xNode/Packages/com.pillow.xnode/LICENSE.md
diff --git a/LICENSE.md.meta b/xNode/Packages/com.pillow.xnode/LICENSE.md.meta
similarity index 100%
rename from LICENSE.md.meta
rename to xNode/Packages/com.pillow.xnode/LICENSE.md.meta
diff --git a/README.md.meta b/xNode/Packages/com.pillow.xnode/Runtime.meta
similarity index 57%
rename from README.md.meta
rename to xNode/Packages/com.pillow.xnode/Runtime.meta
index dd3ed6f..8f44005 100644
--- a/README.md.meta
+++ b/xNode/Packages/com.pillow.xnode/Runtime.meta
@@ -1,6 +1,7 @@
fileFormatVersion: 2
-guid: 243efae3a6b7941ad8f8e54dcf38ce8c
-TextScriptImporter:
+guid: 809e2193de010ae4b9fa476efbe41cd4
+folderAsset: yes
+DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
diff --git a/Scripts/Attributes.meta b/xNode/Packages/com.pillow.xnode/Runtime/Attributes.meta
similarity index 100%
rename from Scripts/Attributes.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Attributes.meta
diff --git a/Scripts/Attributes/NodeEnum.cs b/xNode/Packages/com.pillow.xnode/Runtime/Attributes/NodeEnum.cs
similarity index 100%
rename from Scripts/Attributes/NodeEnum.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Attributes/NodeEnum.cs
diff --git a/Scripts/Attributes/NodeEnum.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Attributes/NodeEnum.cs.meta
similarity index 100%
rename from Scripts/Attributes/NodeEnum.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Attributes/NodeEnum.cs.meta
diff --git a/Scripts/Attributes/PortTypeOverrideAttribute.cs b/xNode/Packages/com.pillow.xnode/Runtime/Attributes/PortTypeOverrideAttribute.cs
similarity index 97%
rename from Scripts/Attributes/PortTypeOverrideAttribute.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Attributes/PortTypeOverrideAttribute.cs
index 316ca2d..9f890f0 100644
--- a/Scripts/Attributes/PortTypeOverrideAttribute.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Attributes/PortTypeOverrideAttribute.cs
@@ -1,12 +1,12 @@
-using System;
-/// Overrides the ValueType of the Port, to have a ValueType different from the type of its serializable field
-/// Especially useful in Dynamic Port Lists to create Value-Port Pairs with different type.
-[AttributeUsage(AttributeTargets.Field)]
-public class PortTypeOverrideAttribute : Attribute {
- public Type type;
- /// Overrides the ValueType of the Port
- /// ValueType of the Port
- public PortTypeOverrideAttribute(Type type) {
- this.type = type;
- }
-}
+using System;
+/// Overrides the ValueType of the Port, to have a ValueType different from the type of its serializable field
+/// Especially useful in Dynamic Port Lists to create Value-Port Pairs with different type.
+[AttributeUsage(AttributeTargets.Field)]
+public class PortTypeOverrideAttribute : Attribute {
+ public Type type;
+ /// Overrides the ValueType of the Port
+ /// ValueType of the Port
+ public PortTypeOverrideAttribute(Type type) {
+ this.type = type;
+ }
+}
diff --git a/Scripts/Attributes/PortTypeOverrideAttribute.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Attributes/PortTypeOverrideAttribute.cs.meta
similarity index 100%
rename from Scripts/Attributes/PortTypeOverrideAttribute.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Attributes/PortTypeOverrideAttribute.cs.meta
diff --git a/Scripts/Editor.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor.meta
similarity index 100%
rename from Scripts/Editor.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor.meta
diff --git a/Scripts/Editor/AdvancedGenericMenu.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/AdvancedGenericMenu.cs
similarity index 93%
rename from Scripts/Editor/AdvancedGenericMenu.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/AdvancedGenericMenu.cs
index a22fa83..c6e674b 100644
--- a/Scripts/Editor/AdvancedGenericMenu.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/AdvancedGenericMenu.cs
@@ -51,9 +51,13 @@ namespace XNodeEditor
public void Run()
{
if ( func2 != null )
+ {
func2( userData );
+ }
else if ( func != null )
+ {
func();
+ }
}
}
@@ -62,33 +66,41 @@ namespace XNodeEditor
private AdvancedGenericMenuItem FindOrCreateItem( string name, AdvancedGenericMenuItem currentRoot = null )
{
if ( string.IsNullOrWhiteSpace( name ) )
+ {
return null;
+ }
AdvancedGenericMenuItem item = null;
- string[] paths = name.Split( '/' );
+ var paths = name.Split( '/' );
if ( currentRoot == null )
{
item = items.FirstOrDefault( x => x != null && x.name == paths[0] );
if ( item == null )
+ {
items.Add( item = new AdvancedGenericMenuItem( paths[0] ) );
+ }
}
else
{
item = currentRoot.children.OfType().FirstOrDefault( x => x.name == paths[0] );
if ( item == null )
+ {
currentRoot.AddChild( item = new AdvancedGenericMenuItem( paths[0] ) );
+ }
}
if ( paths.Length > 1 )
+ {
return FindOrCreateItem( string.Join( "/", paths, 1, paths.Length - 1 ), item );
+ }
return item;
}
private AdvancedGenericMenuItem FindParent( string name )
{
- string[] paths = name.Split( '/' );
+ var paths = name.Split( '/' );
return FindOrCreateItem( string.Join( "/", paths, 0, paths.Length - 1 ) );
}
@@ -169,9 +181,13 @@ namespace XNodeEditor
{
var parent = string.IsNullOrWhiteSpace( path ) ? null : FindParent( path );
if ( parent == null )
+ {
items.Add( null );
+ }
else
+ {
parent.AddSeparator();
+ }
}
//
@@ -195,9 +211,13 @@ namespace XNodeEditor
foreach ( var m in items )
{
if ( m == null )
+ {
root.AddSeparator();
+ }
else
+ {
root.AddChild( m );
+ }
}
return root;
@@ -206,8 +226,10 @@ namespace XNodeEditor
protected override void ItemSelected( AdvancedDropdownItem item )
{
if ( item is AdvancedGenericMenuItem gmItem )
+ {
gmItem.Run();
+ }
}
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/Editor/AdvancedGenericMenu.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/AdvancedGenericMenu.cs.meta
similarity index 100%
rename from Scripts/Editor/AdvancedGenericMenu.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/AdvancedGenericMenu.cs.meta
diff --git a/Scripts/Editor/Drawers.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers.meta
similarity index 100%
rename from Scripts/Editor/Drawers.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers.meta
diff --git a/Scripts/Editor/Drawers/NodeEnumDrawer.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/NodeEnumDrawer.cs
similarity index 86%
rename from Scripts/Editor/Drawers/NodeEnumDrawer.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/NodeEnumDrawer.cs
index 8aa748c..cdb9a9e 100644
--- a/Scripts/Editor/Drawers/NodeEnumDrawer.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/NodeEnumDrawer.cs
@@ -27,8 +27,11 @@ namespace XNodeEditor {
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
// Get current enum name
- string enumName = "";
- if (property.enumValueIndex >= 0 && property.enumValueIndex < property.enumDisplayNames.Length) enumName = property.enumDisplayNames[property.enumValueIndex];
+ var enumName = "";
+ if (property.enumValueIndex >= 0 && property.enumValueIndex < property.enumDisplayNames.Length)
+ {
+ enumName = property.enumDisplayNames[property.enumValueIndex];
+ }
#if UNITY_2017_1_OR_NEWER
// Display dropdown
@@ -49,16 +52,16 @@ namespace XNodeEditor {
public static void ShowContextMenuAtMouse(SerializedProperty property) {
// Initialize menu
- GenericMenu menu = new GenericMenu();
+ var menu = new GenericMenu();
// Add all enum display names to menu
- for (int i = 0; i < property.enumDisplayNames.Length; i++) {
- int index = i;
+ for (var i = 0; i < property.enumDisplayNames.Length; i++) {
+ var index = i;
menu.AddItem(new GUIContent(property.enumDisplayNames[i]), false, () => SetEnum(property, index));
}
// Display at cursor position
- Rect r = new Rect(Event.current.mousePosition, new Vector2(0, 0));
+ var r = new Rect(Event.current.mousePosition, new Vector2(0, 0));
menu.DropDown(r);
}
@@ -68,4 +71,4 @@ namespace XNodeEditor {
property.serializedObject.Update();
}
}
-}
\ No newline at end of file
+}
diff --git a/Scripts/Editor/Drawers/NodeEnumDrawer.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/NodeEnumDrawer.cs.meta
similarity index 100%
rename from Scripts/Editor/Drawers/NodeEnumDrawer.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/NodeEnumDrawer.cs.meta
diff --git a/Scripts/Editor/Drawers/Odin.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin.meta
similarity index 100%
rename from Scripts/Editor/Drawers/Odin.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin.meta
diff --git a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs
diff --git a/Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InNodeEditorAttributeProcessor.cs.meta
diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InputAttributeDrawer.cs
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InputAttributeDrawer.cs
diff --git a/Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/InputAttributeDrawer.cs.meta
diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/OutputAttributeDrawer.cs
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/OutputAttributeDrawer.cs
diff --git a/Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta
similarity index 100%
rename from Scripts/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Drawers/Odin/OutputAttributeDrawer.cs.meta
diff --git a/Scripts/Editor/GraphAndNodeEditor.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphAndNodeEditor.cs
similarity index 92%
rename from Scripts/Editor/GraphAndNodeEditor.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphAndNodeEditor.cs
index 6859855..28154cb 100644
--- a/Scripts/Editor/GraphAndNodeEditor.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphAndNodeEditor.cs
@@ -57,8 +57,8 @@ namespace XNodeEditor {
serializedObject.Update();
if (GUILayout.Button("Edit graph", GUILayout.Height(40))) {
- SerializedProperty graphProp = serializedObject.FindProperty("graph");
- NodeEditorWindow w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph);
+ var graphProp = serializedObject.FindProperty("graph");
+ var w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph);
w.Home(); // Focus selected node
}
@@ -72,4 +72,4 @@ namespace XNodeEditor {
}
}
#endif
-}
\ No newline at end of file
+}
diff --git a/Scripts/Editor/GraphAndNodeEditor.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphAndNodeEditor.cs.meta
similarity index 100%
rename from Scripts/Editor/GraphAndNodeEditor.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphAndNodeEditor.cs.meta
diff --git a/Scripts/Editor/GraphRenameFixAssetProcessor.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphRenameFixAssetProcessor.cs
similarity index 92%
rename from Scripts/Editor/GraphRenameFixAssetProcessor.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphRenameFixAssetProcessor.cs
index 264e8b1..dc65f77 100644
--- a/Scripts/Editor/GraphRenameFixAssetProcessor.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphRenameFixAssetProcessor.cs
@@ -16,8 +16,8 @@ namespace XNodeEditor {
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths) {
- for (int i = 0; i < movedAssets.Length; i++) {
- Node nodeAsset = AssetDatabase.LoadMainAssetAtPath(movedAssets[i]) as Node;
+ for (var i = 0; i < movedAssets.Length; i++) {
+ var nodeAsset = AssetDatabase.LoadMainAssetAtPath(movedAssets[i]) as Node;
// If the renamed asset is a node graph, but the v2 AssetDatabase has swapped a sub-asset node to be its
// main asset, reset the node graph to be the main asset and rename the node asset back to its default
@@ -32,4 +32,4 @@ namespace XNodeEditor {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphRenameFixAssetProcessor.cs.meta
similarity index 100%
rename from Scripts/Editor/GraphRenameFixAssetProcessor.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/GraphRenameFixAssetProcessor.cs.meta
diff --git a/Scripts/Editor/Internal.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal.meta
similarity index 100%
rename from Scripts/Editor/Internal.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal.meta
diff --git a/Scripts/Editor/Internal/RerouteReference.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal/RerouteReference.cs
similarity index 100%
rename from Scripts/Editor/Internal/RerouteReference.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal/RerouteReference.cs
diff --git a/Scripts/Editor/Internal/RerouteReference.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal/RerouteReference.cs.meta
similarity index 100%
rename from Scripts/Editor/Internal/RerouteReference.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/Internal/RerouteReference.cs.meta
diff --git a/Scripts/Editor/NodeEditor.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditor.cs
similarity index 81%
rename from Scripts/Editor/NodeEditor.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditor.cs
index 8522fc0..aeeb286 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditor.cs
@@ -1,187 +1,217 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using UnityEditor;
-using UnityEngine;
-#if ODIN_INSPECTOR
-using Sirenix.OdinInspector.Editor;
-using Sirenix.Utilities;
-using Sirenix.Utilities.Editor;
-#endif
-#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
-using GenericMenu = XNodeEditor.AdvancedGenericMenu;
-#endif
-
-namespace XNodeEditor {
- /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes.
- [CustomNodeEditor(typeof(XNode.Node))]
- public class NodeEditor : XNodeEditor.Internal.NodeEditorBase {
-
- /// Fires every whenever a node was modified through the editor
- public static Action onUpdateNode;
- public readonly static Dictionary portPositions = new Dictionary();
-
-#if ODIN_INSPECTOR
- protected internal static bool inNodeEditor = false;
-#endif
-
- public virtual void OnHeaderGUI() {
- GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
- }
-
- /// Draws standard field editors for all public fields
- public virtual void OnBodyGUI() {
-#if ODIN_INSPECTOR
- inNodeEditor = true;
-#endif
-
- // Unity specifically requires this to save/update any serial object.
- // serializedObject.Update(); must go at the start of an inspector gui, and
- // serializedObject.ApplyModifiedProperties(); goes at the end.
- serializedObject.Update();
- string[] excludes = { "m_Script", "graph", "position", "ports" };
-
-#if ODIN_INSPECTOR
- try
- {
-#if ODIN_INSPECTOR_3
- objectTree.BeginDraw( true );
-#else
- InspectorUtilities.BeginDrawPropertyTree(objectTree, true);
-#endif
- }
- catch ( ArgumentNullException )
- {
-#if ODIN_INSPECTOR_3
- objectTree.EndDraw();
-#else
- InspectorUtilities.EndDrawPropertyTree(objectTree);
-#endif
- NodeEditor.DestroyEditor(this.target);
- return;
- }
-
- GUIHelper.PushLabelWidth( 84 );
- objectTree.Draw( true );
-#if ODIN_INSPECTOR_3
- objectTree.EndDraw();
-#else
- InspectorUtilities.EndDrawPropertyTree(objectTree);
-#endif
- GUIHelper.PopLabelWidth();
-#else
-
- // Iterate through serialized properties and draw them like the Inspector (But with ports)
- SerializedProperty iterator = serializedObject.GetIterator();
- bool enterChildren = true;
- while (iterator.NextVisible(enterChildren)) {
- enterChildren = false;
- if (excludes.Contains(iterator.name)) continue;
- NodeEditorGUILayout.PropertyField(iterator, true);
- }
-#endif
-
- // Iterate through dynamic ports and draw them in the order in which they are serialized
- foreach (XNode.NodePort dynamicPort in target.DynamicPorts) {
- if (NodeEditorGUILayout.IsDynamicPortListPort(dynamicPort)) continue;
- NodeEditorGUILayout.PortField(dynamicPort);
- }
-
- serializedObject.ApplyModifiedProperties();
-
-#if ODIN_INSPECTOR
- // Call repaint so that the graph window elements respond properly to layout changes coming from Odin
- if (GUIHelper.RepaintRequested) {
- GUIHelper.ClearRepaintRequest();
- window.Repaint();
- }
-#endif
-
-#if ODIN_INSPECTOR
- inNodeEditor = false;
-#endif
- }
-
- public virtual int GetWidth() {
- Type type = target.GetType();
- int width;
- if (type.TryGetAttributeWidth(out width)) return width;
- else return 208;
- }
-
- /// Returns color for target node
- public virtual Color GetTint() {
- // Try get color from [NodeTint] attribute
- Type type = target.GetType();
- Color color;
- if (type.TryGetAttributeTint(out color)) return color;
- // Return default color (grey)
- else return NodeEditorPreferences.GetSettings().tintColor;
- }
-
- public virtual GUIStyle GetBodyStyle() {
- return NodeEditorResources.styles.nodeBody;
- }
-
- public virtual GUIStyle GetBodyHighlightStyle() {
- return NodeEditorResources.styles.nodeHighlight;
- }
-
- /// Override to display custom node header tooltips
- public virtual string GetHeaderTooltip() {
- return null;
- }
-
- /// Add items for the context menu when right-clicking this node. Override to add custom menu items.
- public virtual void AddContextMenuItems(GenericMenu menu) {
- bool canRemove = true;
- // Actions if only one node is selected
- if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
- XNode.Node node = Selection.activeObject as XNode.Node;
- menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node));
- menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedNode);
-
- canRemove = NodeGraphEditor.GetEditor(node.graph, NodeEditorWindow.current).CanRemove(node);
- }
-
- // Add actions to any number of selected nodes
- menu.AddItem(new GUIContent("Copy"), false, NodeEditorWindow.current.CopySelectedNodes);
- menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes);
-
- if (canRemove) menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes);
- else menu.AddItem(new GUIContent("Remove"), false, null);
-
- // Custom sctions if only one node is selected
- if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
- XNode.Node node = Selection.activeObject as XNode.Node;
- menu.AddCustomContextMenuItems(node);
- }
- }
-
- /// Rename the node asset. This will trigger a reimport of the node.
- public void Rename(string newName) {
- if (newName == null || newName.Trim() == "") newName = NodeEditorUtilities.NodeDefaultName(target.GetType());
- target.name = newName;
- OnRename();
- AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
- }
-
- /// Called after this node's name has changed.
- public virtual void OnRename() { }
-
- [AttributeUsage(AttributeTargets.Class)]
- public class CustomNodeEditorAttribute : Attribute,
- XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib {
- private Type inspectedType;
- /// Tells a NodeEditor which Node type it is an editor for
- /// Type that this editor can edit
- public CustomNodeEditorAttribute(Type inspectedType) {
- this.inspectedType = inspectedType;
- }
-
- public Type GetInspectedType() {
- return inspectedType;
- }
- }
- }
-}
\ No newline at end of file
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+#if ODIN_INSPECTOR
+using Sirenix.OdinInspector.Editor;
+using Sirenix.Utilities;
+using Sirenix.Utilities.Editor;
+#endif
+#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
+using GenericMenu = XNodeEditor.AdvancedGenericMenu;
+#endif
+
+namespace XNodeEditor {
+ /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes.
+ [CustomNodeEditor(typeof(XNode.Node))]
+ public class NodeEditor : XNodeEditor.Internal.NodeEditorBase {
+
+ /// Fires every whenever a node was modified through the editor
+ public static Action onUpdateNode;
+ public readonly static Dictionary portPositions = new Dictionary();
+
+#if ODIN_INSPECTOR
+ protected internal static bool inNodeEditor = false;
+#endif
+
+ public virtual void OnHeaderGUI() {
+ GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
+ }
+
+ /// Draws standard field editors for all public fields
+ public virtual void OnBodyGUI() {
+#if ODIN_INSPECTOR
+ inNodeEditor = true;
+#endif
+
+ // Unity specifically requires this to save/update any serial object.
+ // serializedObject.Update(); must go at the start of an inspector gui, and
+ // serializedObject.ApplyModifiedProperties(); goes at the end.
+ serializedObject.Update();
+ string[] excludes = { "m_Script", "graph", "position", "ports" };
+
+#if ODIN_INSPECTOR
+ try
+ {
+#if ODIN_INSPECTOR_3
+ objectTree.BeginDraw( true );
+#else
+ InspectorUtilities.BeginDrawPropertyTree(objectTree, true);
+#endif
+ }
+ catch ( ArgumentNullException )
+ {
+#if ODIN_INSPECTOR_3
+ objectTree.EndDraw();
+#else
+ InspectorUtilities.EndDrawPropertyTree(objectTree);
+#endif
+ NodeEditor.DestroyEditor(this.target);
+ return;
+ }
+
+ GUIHelper.PushLabelWidth( 84 );
+ objectTree.Draw( true );
+#if ODIN_INSPECTOR_3
+ objectTree.EndDraw();
+#else
+ InspectorUtilities.EndDrawPropertyTree(objectTree);
+#endif
+ GUIHelper.PopLabelWidth();
+#else
+
+ // Iterate through serialized properties and draw them like the Inspector (But with ports)
+ var iterator = serializedObject.GetIterator();
+ var enterChildren = true;
+ while (iterator.NextVisible(enterChildren)) {
+ enterChildren = false;
+ if (excludes.Contains(iterator.name))
+ {
+ continue;
+ }
+
+ NodeEditorGUILayout.PropertyField(iterator, true);
+ }
+#endif
+
+ // Iterate through dynamic ports and draw them in the order in which they are serialized
+ foreach (var dynamicPort in target.DynamicPorts) {
+ if (NodeEditorGUILayout.IsDynamicPortListPort(dynamicPort))
+ {
+ continue;
+ }
+
+ NodeEditorGUILayout.PortField(dynamicPort);
+ }
+
+ serializedObject.ApplyModifiedProperties();
+
+#if ODIN_INSPECTOR
+ // Call repaint so that the graph window elements respond properly to layout changes coming from Odin
+ if (GUIHelper.RepaintRequested) {
+ GUIHelper.ClearRepaintRequest();
+ window.Repaint();
+ }
+#endif
+
+#if ODIN_INSPECTOR
+ inNodeEditor = false;
+#endif
+ }
+
+ public virtual int GetWidth() {
+ var type = target.GetType();
+ int width;
+ if (type.TryGetAttributeWidth(out width))
+ {
+ return width;
+ }
+ else
+ {
+ return 208;
+ }
+ }
+
+ /// Returns color for target node
+ public virtual Color GetTint() {
+ // Try get color from [NodeTint] attribute
+ var type = target.GetType();
+ Color color;
+ if (type.TryGetAttributeTint(out color))
+ {
+ return color;
+ }
+ // Return default color (grey)
+ else
+ {
+ return NodeEditorPreferences.GetSettings().tintColor;
+ }
+ }
+
+ public virtual GUIStyle GetBodyStyle() {
+ return NodeEditorResources.styles.nodeBody;
+ }
+
+ public virtual GUIStyle GetBodyHighlightStyle() {
+ return NodeEditorResources.styles.nodeHighlight;
+ }
+
+ /// Override to display custom node header tooltips
+ public virtual string GetHeaderTooltip() {
+ return null;
+ }
+
+ /// Add items for the context menu when right-clicking this node. Override to add custom menu items.
+ public virtual void AddContextMenuItems(GenericMenu menu) {
+ var canRemove = true;
+ // Actions if only one node is selected
+ if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
+ var node = Selection.activeObject as XNode.Node;
+ menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node));
+ menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedNode);
+
+ canRemove = NodeGraphEditor.GetEditor(node.graph, NodeEditorWindow.current).CanRemove(node);
+ }
+
+ // Add actions to any number of selected nodes
+ menu.AddItem(new GUIContent("Copy"), false, NodeEditorWindow.current.CopySelectedNodes);
+ menu.AddItem(new GUIContent("Duplicate"), false, NodeEditorWindow.current.DuplicateSelectedNodes);
+
+ if (canRemove)
+ {
+ menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes);
+ }
+ else
+ {
+ menu.AddItem(new GUIContent("Remove"), false, null);
+ }
+
+ // Custom sctions if only one node is selected
+ if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
+ var node = Selection.activeObject as XNode.Node;
+ menu.AddCustomContextMenuItems(node);
+ }
+ }
+
+ /// Rename the node asset. This will trigger a reimport of the node.
+ public void Rename(string newName) {
+ if (newName == null || newName.Trim() == "")
+ {
+ newName = NodeEditorUtilities.NodeDefaultName(target.GetType());
+ }
+
+ target.name = newName;
+ OnRename();
+ AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
+ }
+
+ /// Called after this node's name has changed.
+ public virtual void OnRename() { }
+
+ [AttributeUsage(AttributeTargets.Class)]
+ public class CustomNodeEditorAttribute : Attribute,
+ XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib {
+ private Type inspectedType;
+ /// Tells a NodeEditor which Node type it is an editor for
+ /// Type that this editor can edit
+ public CustomNodeEditorAttribute(Type inspectedType) {
+ this.inspectedType = inspectedType;
+ }
+
+ public Type GetInspectedType() {
+ return inspectedType;
+ }
+ }
+ }
+}
diff --git a/Scripts/Editor/NodeEditor.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditor.cs.meta
similarity index 100%
rename from Scripts/Editor/NodeEditor.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditor.cs.meta
diff --git a/Scripts/Editor/NodeEditorAction.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAction.cs
similarity index 68%
rename from Scripts/Editor/NodeEditorAction.cs
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAction.cs
index a9147f2..fb80486 100644
--- a/Scripts/Editor/NodeEditorAction.cs
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAction.cs
@@ -1,566 +1,700 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using UnityEditor;
-using UnityEngine;
-using XNodeEditor.Internal;
-#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
-using GenericMenu = XNodeEditor.AdvancedGenericMenu;
-#endif
-
-namespace XNodeEditor {
- public partial class NodeEditorWindow {
- public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid }
- public static NodeActivity currentActivity = NodeActivity.Idle;
- public static bool isPanning { get; private set; }
- public static Vector2[] dragOffset;
-
- public static XNode.Node[] copyBuffer = null;
-
- public bool IsDraggingPort { get { return draggedOutput != null; } }
- public bool IsHoveringPort { get { return hoveredPort != null; } }
- public bool IsHoveringNode { get { return hoveredNode != null; } }
- public bool IsHoveringReroute { get { return hoveredReroute.port != null; } }
-
- /// Return the dragged port or null if not exist
- public XNode.NodePort DraggedOutputPort { get { XNode.NodePort result = draggedOutput; return result; } }
- /// Return the Hovered port or null if not exist
- public XNode.NodePort HoveredPort { get { XNode.NodePort result = hoveredPort; return result; } }
- /// Return the Hovered node or null if not exist
- public XNode.Node HoveredNode { get { XNode.Node result = hoveredNode; return result; } }
-
- private XNode.Node hoveredNode = null;
- [NonSerialized] public XNode.NodePort hoveredPort = null;
- [NonSerialized] private XNode.NodePort draggedOutput = null;
- [NonSerialized] private XNode.NodePort draggedOutputTarget = null;
- [NonSerialized] private XNode.NodePort autoConnectOutput = null;
- [NonSerialized] private List draggedOutputReroutes = new List();
-
- private RerouteReference hoveredReroute = new RerouteReference();
- public List selectedReroutes = new List();
- private Vector2 dragBoxStart;
- private UnityEngine.Object[] preBoxSelection;
- private RerouteReference[] preBoxSelectionReroute;
- private Rect selectionBox;
- private bool isDoubleClick = false;
- private Vector2 lastMousePosition;
- private float dragThreshold = 1f;
-
- public void Controls() {
- wantsMouseMove = true;
- Event e = Event.current;
- switch (e.type) {
- case EventType.DragUpdated:
- case EventType.DragPerform:
- DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
- if (e.type == EventType.DragPerform) {
- DragAndDrop.AcceptDrag();
- graphEditor.OnDropObjects(DragAndDrop.objectReferences);
- }
- break;
- case EventType.MouseMove:
- //Keyboard commands will not get correct mouse position from Event
- lastMousePosition = e.mousePosition;
- break;
- case EventType.ScrollWheel:
- float oldZoom = zoom;
- if (e.delta.y > 0) zoom += 0.1f * zoom;
- else zoom -= 0.1f * zoom;
- if (NodeEditorPreferences.GetSettings().zoomToMouse) panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset);
- break;
- case EventType.MouseDrag:
- if (e.button == 0) {
- if (IsDraggingPort) {
- // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously
- if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) {
- draggedOutputTarget = hoveredPort;
- } else {
- draggedOutputTarget = null;
- }
- Repaint();
- } else if (currentActivity == NodeActivity.HoldNode) {
- RecalculateDragOffsets(e);
- currentActivity = NodeActivity.DragNode;
- Repaint();
- }
- if (currentActivity == NodeActivity.DragNode) {
- // Holding ctrl inverts grid snap
- bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap;
- if (e.control) gridSnap = !gridSnap;
-
- Vector2 mousePos = WindowToGridPosition(e.mousePosition);
- // Move selected nodes with offset
- for (int i = 0; i < Selection.objects.Length; i++) {
- if (Selection.objects[i] is XNode.Node) {
- XNode.Node node = Selection.objects[i] as XNode.Node;
- Undo.RecordObject(node, "Moved Node");
- Vector2 initial = node.position;
- node.position = mousePos + dragOffset[i];
- if (gridSnap) {
- node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8;
- node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8;
- }
-
- // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame.
- Vector2 offset = node.position - initial;
- if (offset.sqrMagnitude > 0) {
- foreach (XNode.NodePort output in node.Outputs) {
- Rect rect;
- if (portConnectionPoints.TryGetValue(output, out rect)) {
- rect.position += offset;
- portConnectionPoints[output] = rect;
- }
- }
-
- foreach (XNode.NodePort input in node.Inputs) {
- Rect rect;
- if (portConnectionPoints.TryGetValue(input, out rect)) {
- rect.position += offset;
- portConnectionPoints[input] = rect;
- }
- }
- }
- }
- }
- // Move selected reroutes with offset
- for (int i = 0; i < selectedReroutes.Count; i++) {
- Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i];
- if (gridSnap) {
- pos.x = (Mathf.Round(pos.x / 16) * 16);
- pos.y = (Mathf.Round(pos.y / 16) * 16);
- }
- selectedReroutes[i].SetPoint(pos);
- }
- Repaint();
- } else if (currentActivity == NodeActivity.HoldGrid) {
- currentActivity = NodeActivity.DragGrid;
- preBoxSelection = Selection.objects;
- preBoxSelectionReroute = selectedReroutes.ToArray();
- dragBoxStart = WindowToGridPosition(e.mousePosition);
- Repaint();
- } else if (currentActivity == NodeActivity.DragGrid) {
- Vector2 boxStartPos = GridToWindowPosition(dragBoxStart);
- Vector2 boxSize = e.mousePosition - boxStartPos;
- if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); }
- if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); }
- selectionBox = new Rect(boxStartPos, boxSize);
- Repaint();
- }
- } else if (e.button == 1 || e.button == 2) {
- //check drag threshold for larger screens
- if (e.delta.magnitude > dragThreshold) {
- panOffset += e.delta * zoom;
- isPanning = true;
- }
- }
- break;
- case EventType.MouseDown:
- Repaint();
- if (e.button == 0) {
- draggedOutputReroutes.Clear();
-
- if (IsHoveringPort) {
- if (hoveredPort.IsOutput) {
- draggedOutput = hoveredPort;
- autoConnectOutput = hoveredPort;
- } else {
- hoveredPort.VerifyConnections();
- autoConnectOutput = null;
- if (hoveredPort.IsConnected) {
- XNode.Node node = hoveredPort.node;
- XNode.NodePort output = hoveredPort.Connection;
- int outputConnectionIndex = output.GetConnectionIndex(hoveredPort);
- draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex);
- hoveredPort.Disconnect(output);
- draggedOutput = output;
- draggedOutputTarget = hoveredPort;
- if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
- }
- }
- } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
- // If mousedown on node header, select or deselect
- if (!Selection.Contains(hoveredNode)) {
- SelectNode(hoveredNode, e.control || e.shift);
- if (!e.control && !e.shift) selectedReroutes.Clear();
- } else if (e.control || e.shift) DeselectNode(hoveredNode);
-
- // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown.
- isDoubleClick = (e.clickCount == 2);
-
- e.Use();
- currentActivity = NodeActivity.HoldNode;
- } else if (IsHoveringReroute) {
- // If reroute isn't selected
- if (!selectedReroutes.Contains(hoveredReroute)) {
- // Add it
- if (e.control || e.shift) selectedReroutes.Add(hoveredReroute);
- // Select it
- else {
- selectedReroutes = new List() { hoveredReroute };
- Selection.activeObject = null;
- }
-
- }
- // Deselect
- else if (e.control || e.shift) selectedReroutes.Remove(hoveredReroute);
- e.Use();
- currentActivity = NodeActivity.HoldNode;
- }
- // If mousedown on grid background, deselect all
- else if (!IsHoveringNode) {
- currentActivity = NodeActivity.HoldGrid;
- if (!e.control && !e.shift) {
- selectedReroutes.Clear();
- Selection.activeObject = null;
- }
- }
- }
- break;
- case EventType.MouseUp:
- if (e.button == 0) {
- //Port drag release
- if (IsDraggingPort) {
- // If connection is valid, save it
- if (draggedOutputTarget != null && graphEditor.CanConnect(draggedOutput, draggedOutputTarget)) {
- XNode.Node node = draggedOutputTarget.node;
- if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
-
- // ConnectionIndex can be -1 if the connection is removed instantly after creation
- int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
- if (connectionIndex != -1) {
- draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
- if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
- EditorUtility.SetDirty(graph);
- }
- }
- // Open context menu for auto-connection if there is no target node
- else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) {
- GenericMenu menu = new GenericMenu();
- graphEditor.AddContextMenuItems(menu, draggedOutput.ValueType);
- menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
- }
- //Release dragged connection
- draggedOutput = null;
- draggedOutputTarget = null;
- EditorUtility.SetDirty(graph);
- if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
- } else if (currentActivity == NodeActivity.DragNode) {
- IEnumerable nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node);
- foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node);
- if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
- } else if (!IsHoveringNode) {
- // If click outside node, release field focus
- if (!isPanning) {
- EditorGUI.FocusTextInControl(null);
- EditorGUIUtility.editingTextField = false;
- }
- if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
- }
-
- // If click node header, select it.
- if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) {
- selectedReroutes.Clear();
- SelectNode(hoveredNode, false);
-
- // Double click to center node
- if (isDoubleClick) {
- Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) ? nodeSizes[hoveredNode] / 2 : Vector2.zero;
- panOffset = -hoveredNode.position - nodeDimension;
- }
- }
-
- // If click reroute, select it.
- if (IsHoveringReroute && !(e.control || e.shift)) {
- selectedReroutes = new List() { hoveredReroute };
- Selection.activeObject = null;
- }
-
- Repaint();
- currentActivity = NodeActivity.Idle;
- } else if (e.button == 1 || e.button == 2) {
- if (!isPanning) {
- if (IsDraggingPort) {
- draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
- } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) {
- selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
- selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1);
- } else if (IsHoveringReroute) {
- ShowRerouteContextMenu(hoveredReroute);
- } else if (IsHoveringPort) {
- ShowPortContextMenu(hoveredPort);
- } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
- if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false);
- autoConnectOutput = null;
- GenericMenu menu = new GenericMenu();
- NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu);
- menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
- e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places.
- } else if (!IsHoveringNode) {
- autoConnectOutput = null;
- GenericMenu menu = new GenericMenu();
- graphEditor.AddContextMenuItems(menu);
- menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
- }
- }
- isPanning = false;
- }
- // Reset DoubleClick
- isDoubleClick = false;
- break;
- case EventType.KeyDown:
- if (EditorGUIUtility.editingTextField || GUIUtility.keyboardControl != 0) break;
- else if (e.keyCode == KeyCode.F) Home();
- if (NodeEditorUtilities.IsMac()) {
- if (e.keyCode == KeyCode.Return) RenameSelectedNode();
- } else {
- if (e.keyCode == KeyCode.F2) RenameSelectedNode();
- }
- if (e.keyCode == KeyCode.A) {
- if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) {
- foreach (XNode.Node node in graph.nodes) {
- DeselectNode(node);
- }
- } else {
- foreach (XNode.Node node in graph.nodes) {
- SelectNode(node, true);
- }
- }
- Repaint();
- }
- break;
- case EventType.ValidateCommand:
- case EventType.ExecuteCommand:
- if (e.commandName == "SoftDelete") {
- if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes();
- e.Use();
- } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") {
- if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes();
- e.Use();
- } else if (e.commandName == "Duplicate") {
- if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes();
- e.Use();
- } else if (e.commandName == "Copy") {
- if (!EditorGUIUtility.editingTextField) {
- if (e.type == EventType.ExecuteCommand) CopySelectedNodes();
- e.Use();
- }
- } else if (e.commandName == "Paste") {
- if (!EditorGUIUtility.editingTextField) {
- if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition));
- e.Use();
- }
- }
- Repaint();
- break;
- case EventType.Ignore:
- // If release mouse outside window
- if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) {
- Repaint();
- currentActivity = NodeActivity.Idle;
- }
- break;
- }
- }
-
- private void RecalculateDragOffsets(Event current) {
- dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count];
- // Selected nodes
- for (int i = 0; i < Selection.objects.Length; i++) {
- if (Selection.objects[i] is XNode.Node) {
- XNode.Node node = Selection.objects[i] as XNode.Node;
- dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition);
- }
- }
-
- // Selected reroutes
- for (int i = 0; i < selectedReroutes.Count; i++) {
- dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition);
- }
- }
-
- /// Puts all selected nodes in focus. If no nodes are present, resets view and zoom to to origin
- public void Home() {
- var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList();
- if (nodes.Count > 0) {
- Vector2 minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
- Vector2 maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y)));
- panOffset = -(minPos + (maxPos - minPos) / 2f);
- } else {
- zoom = 2;
- panOffset = Vector2.zero;
- }
- }
-
- /// Remove nodes in the graph in Selection.objects
- public void RemoveSelectedNodes() {
- // We need to delete reroutes starting at the highest point index to avoid shifting indices
- selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList();
- for (int i = 0; i < selectedReroutes.Count; i++) {
- selectedReroutes[i].RemovePoint();
- }
- selectedReroutes.Clear();
- foreach (UnityEngine.Object item in Selection.objects) {
- if (item is XNode.Node) {
- XNode.Node node = item as XNode.Node;
- graphEditor.RemoveNode(node);
- }
- }
- }
-
- /// Initiate a rename on the currently selected node
- public void RenameSelectedNode() {
- if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
- XNode.Node node = Selection.activeObject as XNode.Node;
- Vector2 size;
- if (nodeSizes.TryGetValue(node, out size)) {
- RenamePopup.Show(Selection.activeObject, size.x);
- } else {
- RenamePopup.Show(Selection.activeObject);
- }
- }
- }
-
- /// Draw this node on top of other nodes by placing it last in the graph.nodes list
- public void MoveNodeToTop(XNode.Node node) {
- int index;
- while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) {
- graph.nodes[index] = graph.nodes[index + 1];
- graph.nodes[index + 1] = node;
- }
- }
-
- /// Duplicate selected nodes and select the duplicates
- public void DuplicateSelectedNodes() {
- // Get selected nodes which are part of this graph
- XNode.Node[] selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray();
- if (selectedNodes == null || selectedNodes.Length == 0) return;
- // Get top left node position
- Vector2 topLeftNode = selectedNodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
- InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30));
- }
-
- public void CopySelectedNodes() {
- copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray();
- }
-
- public void PasteNodes(Vector2 pos) {
- InsertDuplicateNodes(copyBuffer, pos);
- }
-
- private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) {
- if (nodes == null || nodes.Length == 0) return;
-
- // Get top-left node
- Vector2 topLeftNode = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
- Vector2 offset = topLeft - topLeftNode;
-
- UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length];
- Dictionary substitutes = new Dictionary();
- for (int i = 0; i < nodes.Length; i++) {
- XNode.Node srcNode = nodes[i];
- if (srcNode == null) continue;
-
- // Check if user is allowed to add more of given node type
- XNode.Node.DisallowMultipleNodesAttribute disallowAttrib;
- Type nodeType = srcNode.GetType();
- if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) {
- int typeCount = graph.nodes.Count(x => x.GetType() == nodeType);
- if (typeCount >= disallowAttrib.max) continue;
- }
-
- XNode.Node newNode = graphEditor.CopyNode(srcNode);
- substitutes.Add(srcNode, newNode);
- newNode.position = srcNode.position + offset;
- newNodes[i] = newNode;
- }
-
- // Walk through the selected nodes again, recreate connections, using the new nodes
- for (int i = 0; i < nodes.Length; i++) {
- XNode.Node srcNode = nodes[i];
- if (srcNode == null) continue;
- foreach (XNode.NodePort port in srcNode.Ports) {
- for (int c = 0; c < port.ConnectionCount; c++) {
- XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c);
- XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c);
-
- XNode.Node newNodeIn, newNodeOut;
- if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) {
- newNodeIn.UpdatePorts();
- newNodeOut.UpdatePorts();
- inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
- outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
- }
- if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort);
- }
- }
- }
- EditorUtility.SetDirty(graph);
- // Select the new nodes
- Selection.objects = newNodes;
- }
-
- /// Draw a connection as we are dragging it
- public void DrawDraggedConnection() {
- if (IsDraggingPort) {
- Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null);
- float thickness = graphEditor.GetNoodleThickness(draggedOutput, null);
- NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null);
- NoodleStroke stroke = graphEditor.GetNoodleStroke(draggedOutput, null);
-
- Rect fromRect;
- if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return;
- List gridPoints = new List();
- gridPoints.Add(fromRect.center);
- for (int i = 0; i < draggedOutputReroutes.Count; i++) {
- gridPoints.Add(draggedOutputReroutes[i]);
- }
- if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center);
- else gridPoints.Add(WindowToGridPosition(Event.current.mousePosition));
-
- DrawNoodle(gradient, path, stroke, thickness, gridPoints);
-
- GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(draggedOutput);
- Color bgcol = Color.black;
- Color frcol = gradient.colorKeys[0].color;
- bgcol.a = 0.6f;
- frcol.a = 0.6f;
-
- // Loop through reroute points again and draw the points
- for (int i = 0; i < draggedOutputReroutes.Count; i++) {
- // Draw reroute point at position
- Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16));
- rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8);
- rect = GridToWindowRect(rect);
-
- NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol, portStyle.normal.background, portStyle.active.background);
- }
- }
- }
-
- bool IsHoveringTitle(XNode.Node node) {
- Vector2 mousePos = Event.current.mousePosition;
- //Get node position
- Vector2 nodePos = GridToWindowPosition(node.position);
- float width;
- Vector2 size;
- if (nodeSizes.TryGetValue(node, out size)) width = size.x;
- else width = 200;
- Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
- return windowRect.Contains(mousePos);
- }
-
- /// Attempt to connect dragged output to target node
- public void AutoConnect(XNode.Node node) {
- if (autoConnectOutput == null) return;
-
- // Find compatible input port
- XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && graphEditor.CanConnect(autoConnectOutput, x));
- if (inputPort != null) autoConnectOutput.Connect(inputPort);
-
- // Save changes
- EditorUtility.SetDirty(graph);
- if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
- autoConnectOutput = null;
- }
- }
-}
\ No newline at end of file
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using XNodeEditor.Internal;
+#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
+using GenericMenu = XNodeEditor.AdvancedGenericMenu;
+#endif
+
+namespace XNodeEditor {
+ public partial class NodeEditorWindow {
+ public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid }
+ public static NodeActivity currentActivity = NodeActivity.Idle;
+ public static bool isPanning { get; private set; }
+ public static Vector2[] dragOffset;
+
+ public static XNode.Node[] copyBuffer = null;
+
+ public bool IsDraggingPort { get { return draggedOutput != null; } }
+ public bool IsHoveringPort { get { return hoveredPort != null; } }
+ public bool IsHoveringNode { get { return hoveredNode != null; } }
+ public bool IsHoveringReroute { get { return hoveredReroute.port != null; } }
+
+ /// Return the dragged port or null if not exist
+ public XNode.NodePort DraggedOutputPort { get { var result = draggedOutput; return result; } }
+ /// Return the Hovered port or null if not exist
+ public XNode.NodePort HoveredPort { get { var result = hoveredPort; return result; } }
+ /// Return the Hovered node or null if not exist
+ public XNode.Node HoveredNode { get { var result = hoveredNode; return result; } }
+
+ private XNode.Node hoveredNode = null;
+ [NonSerialized] public XNode.NodePort hoveredPort = null;
+ [NonSerialized] private XNode.NodePort draggedOutput = null;
+ [NonSerialized] private XNode.NodePort draggedOutputTarget = null;
+ [NonSerialized] private XNode.NodePort autoConnectOutput = null;
+ [NonSerialized] private List draggedOutputReroutes = new List();
+
+ private RerouteReference hoveredReroute = new RerouteReference();
+ public List selectedReroutes = new List();
+ private Vector2 dragBoxStart;
+ private UnityEngine.Object[] preBoxSelection;
+ private RerouteReference[] preBoxSelectionReroute;
+ private Rect selectionBox;
+ private bool isDoubleClick = false;
+ private Vector2 lastMousePosition;
+ private float dragThreshold = 1f;
+
+ public void Controls() {
+ wantsMouseMove = true;
+ var e = Event.current;
+ switch (e.type) {
+ case EventType.DragUpdated:
+ case EventType.DragPerform:
+ DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
+ if (e.type == EventType.DragPerform) {
+ DragAndDrop.AcceptDrag();
+ graphEditor.OnDropObjects(DragAndDrop.objectReferences);
+ }
+ break;
+ case EventType.MouseMove:
+ //Keyboard commands will not get correct mouse position from Event
+ lastMousePosition = e.mousePosition;
+ break;
+ case EventType.ScrollWheel:
+ var oldZoom = zoom;
+ if (e.delta.y > 0)
+ {
+ zoom += 0.1f * zoom;
+ }
+ else
+ {
+ zoom -= 0.1f * zoom;
+ }
+
+ if (NodeEditorPreferences.GetSettings().zoomToMouse)
+ {
+ panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset);
+ }
+
+ break;
+ case EventType.MouseDrag:
+ if (e.button == 0) {
+ if (IsDraggingPort) {
+ // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously
+ if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) {
+ draggedOutputTarget = hoveredPort;
+ } else {
+ draggedOutputTarget = null;
+ }
+ Repaint();
+ } else if (currentActivity == NodeActivity.HoldNode) {
+ RecalculateDragOffsets(e);
+ currentActivity = NodeActivity.DragNode;
+ Repaint();
+ }
+ if (currentActivity == NodeActivity.DragNode) {
+ // Holding ctrl inverts grid snap
+ var gridSnap = NodeEditorPreferences.GetSettings().gridSnap;
+ if (e.control)
+ {
+ gridSnap = !gridSnap;
+ }
+
+ var mousePos = WindowToGridPosition(e.mousePosition);
+ // Move selected nodes with offset
+ for (var i = 0; i < Selection.objects.Length; i++) {
+ if (Selection.objects[i] is XNode.Node) {
+ var node = Selection.objects[i] as XNode.Node;
+ Undo.RecordObject(node, "Moved Node");
+ var initial = node.position;
+ node.position = mousePos + dragOffset[i];
+ if (gridSnap) {
+ node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8;
+ node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8;
+ }
+
+ // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame.
+ var offset = node.position - initial;
+ if (offset.sqrMagnitude > 0) {
+ foreach (var output in node.Outputs) {
+ Rect rect;
+ if (portConnectionPoints.TryGetValue(output, out rect)) {
+ rect.position += offset;
+ portConnectionPoints[output] = rect;
+ }
+ }
+
+ foreach (var input in node.Inputs) {
+ Rect rect;
+ if (portConnectionPoints.TryGetValue(input, out rect)) {
+ rect.position += offset;
+ portConnectionPoints[input] = rect;
+ }
+ }
+ }
+ }
+ }
+ // Move selected reroutes with offset
+ for (var i = 0; i < selectedReroutes.Count; i++) {
+ var pos = mousePos + dragOffset[Selection.objects.Length + i];
+ if (gridSnap) {
+ pos.x = (Mathf.Round(pos.x / 16) * 16);
+ pos.y = (Mathf.Round(pos.y / 16) * 16);
+ }
+ selectedReroutes[i].SetPoint(pos);
+ }
+ Repaint();
+ } else if (currentActivity == NodeActivity.HoldGrid) {
+ currentActivity = NodeActivity.DragGrid;
+ preBoxSelection = Selection.objects;
+ preBoxSelectionReroute = selectedReroutes.ToArray();
+ dragBoxStart = WindowToGridPosition(e.mousePosition);
+ Repaint();
+ } else if (currentActivity == NodeActivity.DragGrid) {
+ var boxStartPos = GridToWindowPosition(dragBoxStart);
+ var boxSize = e.mousePosition - boxStartPos;
+ if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); }
+ if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); }
+ selectionBox = new Rect(boxStartPos, boxSize);
+ Repaint();
+ }
+ } else if (e.button == 1 || e.button == 2) {
+ //check drag threshold for larger screens
+ if (e.delta.magnitude > dragThreshold) {
+ panOffset += e.delta * zoom;
+ isPanning = true;
+ }
+ }
+ break;
+ case EventType.MouseDown:
+ Repaint();
+ if (e.button == 0) {
+ draggedOutputReroutes.Clear();
+
+ if (IsHoveringPort) {
+ if (hoveredPort.IsOutput) {
+ draggedOutput = hoveredPort;
+ autoConnectOutput = hoveredPort;
+ } else {
+ hoveredPort.VerifyConnections();
+ autoConnectOutput = null;
+ if (hoveredPort.IsConnected) {
+ var node = hoveredPort.node;
+ var output = hoveredPort.Connection;
+ var outputConnectionIndex = output.GetConnectionIndex(hoveredPort);
+ draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex);
+ hoveredPort.Disconnect(output);
+ draggedOutput = output;
+ draggedOutputTarget = hoveredPort;
+ if (NodeEditor.onUpdateNode != null)
+ {
+ NodeEditor.onUpdateNode(node);
+ }
+ }
+ }
+ } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
+ // If mousedown on node header, select or deselect
+ if (!Selection.Contains(hoveredNode)) {
+ SelectNode(hoveredNode, e.control || e.shift);
+ if (!e.control && !e.shift)
+ {
+ selectedReroutes.Clear();
+ }
+ } else if (e.control || e.shift)
+ {
+ DeselectNode(hoveredNode);
+ }
+
+ // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown.
+ isDoubleClick = (e.clickCount == 2);
+
+ e.Use();
+ currentActivity = NodeActivity.HoldNode;
+ } else if (IsHoveringReroute) {
+ // If reroute isn't selected
+ if (!selectedReroutes.Contains(hoveredReroute)) {
+ // Add it
+ if (e.control || e.shift)
+ {
+ selectedReroutes.Add(hoveredReroute);
+ }
+ // Select it
+ else {
+ selectedReroutes = new List() { hoveredReroute };
+ Selection.activeObject = null;
+ }
+
+ }
+ // Deselect
+ else if (e.control || e.shift)
+ {
+ selectedReroutes.Remove(hoveredReroute);
+ }
+
+ e.Use();
+ currentActivity = NodeActivity.HoldNode;
+ }
+ // If mousedown on grid background, deselect all
+ else if (!IsHoveringNode) {
+ currentActivity = NodeActivity.HoldGrid;
+ if (!e.control && !e.shift) {
+ selectedReroutes.Clear();
+ Selection.activeObject = null;
+ }
+ }
+ }
+ break;
+ case EventType.MouseUp:
+ if (e.button == 0) {
+ //Port drag release
+ if (IsDraggingPort) {
+ // If connection is valid, save it
+ if (draggedOutputTarget != null && graphEditor.CanConnect(draggedOutput, draggedOutputTarget)) {
+ var node = draggedOutputTarget.node;
+ if (graph.nodes.Count != 0)
+ {
+ draggedOutput.Connect(draggedOutputTarget);
+ }
+
+ // ConnectionIndex can be -1 if the connection is removed instantly after creation
+ var connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget);
+ if (connectionIndex != -1) {
+ draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
+ if (NodeEditor.onUpdateNode != null)
+ {
+ NodeEditor.onUpdateNode(node);
+ }
+
+ EditorUtility.SetDirty(graph);
+ }
+ }
+ // Open context menu for auto-connection if there is no target node
+ else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) {
+ var menu = new GenericMenu();
+ graphEditor.AddContextMenuItems(menu, draggedOutput.ValueType);
+ menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
+ }
+ //Release dragged connection
+ draggedOutput = null;
+ draggedOutputTarget = null;
+ EditorUtility.SetDirty(graph);
+ if (NodeEditorPreferences.GetSettings().autoSave)
+ {
+ AssetDatabase.SaveAssets();
+ }
+ } else if (currentActivity == NodeActivity.DragNode) {
+ var nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node);
+ foreach (var node in nodes)
+ {
+ EditorUtility.SetDirty(node);
+ }
+
+ if (NodeEditorPreferences.GetSettings().autoSave)
+ {
+ AssetDatabase.SaveAssets();
+ }
+ } else if (!IsHoveringNode) {
+ // If click outside node, release field focus
+ if (!isPanning) {
+ EditorGUI.FocusTextInControl(null);
+ EditorGUIUtility.editingTextField = false;
+ }
+ if (NodeEditorPreferences.GetSettings().autoSave)
+ {
+ AssetDatabase.SaveAssets();
+ }
+ }
+
+ // If click node header, select it.
+ if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) {
+ selectedReroutes.Clear();
+ SelectNode(hoveredNode, false);
+
+ // Double click to center node
+ if (isDoubleClick) {
+ var nodeDimension = nodeSizes.ContainsKey(hoveredNode) ? nodeSizes[hoveredNode] / 2 : Vector2.zero;
+ panOffset = -hoveredNode.position - nodeDimension;
+ }
+ }
+
+ // If click reroute, select it.
+ if (IsHoveringReroute && !(e.control || e.shift)) {
+ selectedReroutes = new List() { hoveredReroute };
+ Selection.activeObject = null;
+ }
+
+ Repaint();
+ currentActivity = NodeActivity.Idle;
+ } else if (e.button == 1 || e.button == 2) {
+ if (!isPanning) {
+ if (IsDraggingPort) {
+ draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
+ } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) {
+ selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
+ selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1);
+ } else if (IsHoveringReroute) {
+ ShowRerouteContextMenu(hoveredReroute);
+ } else if (IsHoveringPort) {
+ ShowPortContextMenu(hoveredPort);
+ } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) {
+ if (!Selection.Contains(hoveredNode))
+ {
+ SelectNode(hoveredNode, false);
+ }
+
+ autoConnectOutput = null;
+ var menu = new GenericMenu();
+ NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu);
+ menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
+ e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places.
+ } else if (!IsHoveringNode) {
+ autoConnectOutput = null;
+ var menu = new GenericMenu();
+ graphEditor.AddContextMenuItems(menu);
+ menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
+ }
+ }
+ isPanning = false;
+ }
+ // Reset DoubleClick
+ isDoubleClick = false;
+ break;
+ case EventType.KeyDown:
+ if (EditorGUIUtility.editingTextField || GUIUtility.keyboardControl != 0)
+ {
+ break;
+ }
+ else if (e.keyCode == KeyCode.F)
+ {
+ Home();
+ }
+
+ if (NodeEditorUtilities.IsMac()) {
+ if (e.keyCode == KeyCode.Return)
+ {
+ RenameSelectedNode();
+ }
+ } else {
+ if (e.keyCode == KeyCode.F2)
+ {
+ RenameSelectedNode();
+ }
+ }
+ if (e.keyCode == KeyCode.A) {
+ if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) {
+ foreach (var node in graph.nodes) {
+ DeselectNode(node);
+ }
+ } else {
+ foreach (var node in graph.nodes) {
+ SelectNode(node, true);
+ }
+ }
+ Repaint();
+ }
+ break;
+ case EventType.ValidateCommand:
+ case EventType.ExecuteCommand:
+ if (e.commandName == "SoftDelete") {
+ if (e.type == EventType.ExecuteCommand)
+ {
+ RemoveSelectedNodes();
+ }
+
+ e.Use();
+ } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") {
+ if (e.type == EventType.ExecuteCommand)
+ {
+ RemoveSelectedNodes();
+ }
+
+ e.Use();
+ } else if (e.commandName == "Duplicate") {
+ if (e.type == EventType.ExecuteCommand)
+ {
+ DuplicateSelectedNodes();
+ }
+
+ e.Use();
+ } else if (e.commandName == "Copy") {
+ if (!EditorGUIUtility.editingTextField) {
+ if (e.type == EventType.ExecuteCommand)
+ {
+ CopySelectedNodes();
+ }
+
+ e.Use();
+ }
+ } else if (e.commandName == "Paste") {
+ if (!EditorGUIUtility.editingTextField) {
+ if (e.type == EventType.ExecuteCommand)
+ {
+ PasteNodes(WindowToGridPosition(lastMousePosition));
+ }
+
+ e.Use();
+ }
+ }
+ Repaint();
+ break;
+ case EventType.Ignore:
+ // If release mouse outside window
+ if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) {
+ Repaint();
+ currentActivity = NodeActivity.Idle;
+ }
+ break;
+ }
+ }
+
+ private void RecalculateDragOffsets(Event current) {
+ dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count];
+ // Selected nodes
+ for (var i = 0; i < Selection.objects.Length; i++) {
+ if (Selection.objects[i] is XNode.Node) {
+ var node = Selection.objects[i] as XNode.Node;
+ dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition);
+ }
+ }
+
+ // Selected reroutes
+ for (var i = 0; i < selectedReroutes.Count; i++) {
+ dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition);
+ }
+ }
+
+ /// Puts all selected nodes in focus. If no nodes are present, resets view and zoom to to origin
+ public void Home() {
+ var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList();
+ if (nodes.Count > 0) {
+ var minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
+ var maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y)));
+ panOffset = -(minPos + (maxPos - minPos) / 2f);
+ } else {
+ zoom = 2;
+ panOffset = Vector2.zero;
+ }
+ }
+
+ /// Remove nodes in the graph in Selection.objects
+ public void RemoveSelectedNodes() {
+ // We need to delete reroutes starting at the highest point index to avoid shifting indices
+ selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList();
+ for (var i = 0; i < selectedReroutes.Count; i++) {
+ selectedReroutes[i].RemovePoint();
+ }
+ selectedReroutes.Clear();
+ foreach (var item in Selection.objects) {
+ if (item is XNode.Node) {
+ var node = item as XNode.Node;
+ graphEditor.RemoveNode(node);
+ }
+ }
+ }
+
+ /// Initiate a rename on the currently selected node
+ public void RenameSelectedNode() {
+ if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) {
+ var node = Selection.activeObject as XNode.Node;
+ Vector2 size;
+ if (nodeSizes.TryGetValue(node, out size)) {
+ RenamePopup.Show(Selection.activeObject, size.x);
+ } else {
+ RenamePopup.Show(Selection.activeObject);
+ }
+ }
+ }
+
+ /// Draw this node on top of other nodes by placing it last in the graph.nodes list
+ public void MoveNodeToTop(XNode.Node node) {
+ int index;
+ while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) {
+ graph.nodes[index] = graph.nodes[index + 1];
+ graph.nodes[index + 1] = node;
+ }
+ }
+
+ /// Duplicate selected nodes and select the duplicates
+ public void DuplicateSelectedNodes() {
+ // Get selected nodes which are part of this graph
+ var selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray();
+ if (selectedNodes == null || selectedNodes.Length == 0)
+ {
+ return;
+ }
+
+ // Get top left node position
+ var topLeftNode = selectedNodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
+ InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30));
+ }
+
+ public void CopySelectedNodes() {
+ copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray();
+ }
+
+ public void PasteNodes(Vector2 pos) {
+ InsertDuplicateNodes(copyBuffer, pos);
+ }
+
+ private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) {
+ if (nodes == null || nodes.Length == 0)
+ {
+ return;
+ }
+
+ // Get top-left node
+ var topLeftNode = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y)));
+ var offset = topLeft - topLeftNode;
+
+ var newNodes = new UnityEngine.Object[nodes.Length];
+ var substitutes = new Dictionary();
+ for (var i = 0; i < nodes.Length; i++) {
+ var srcNode = nodes[i];
+ if (srcNode == null)
+ {
+ continue;
+ }
+
+ // Check if user is allowed to add more of given node type
+ XNode.Node.DisallowMultipleNodesAttribute disallowAttrib;
+ var nodeType = srcNode.GetType();
+ if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) {
+ var typeCount = graph.nodes.Count(x => x.GetType() == nodeType);
+ if (typeCount >= disallowAttrib.max)
+ {
+ continue;
+ }
+ }
+
+ var newNode = graphEditor.CopyNode(srcNode);
+ substitutes.Add(srcNode, newNode);
+ newNode.position = srcNode.position + offset;
+ newNodes[i] = newNode;
+ }
+
+ // Walk through the selected nodes again, recreate connections, using the new nodes
+ for (var i = 0; i < nodes.Length; i++) {
+ var srcNode = nodes[i];
+ if (srcNode == null)
+ {
+ continue;
+ }
+
+ foreach (var port in srcNode.Ports) {
+ for (var c = 0; c < port.ConnectionCount; c++) {
+ var inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c);
+ var outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c);
+
+ XNode.Node newNodeIn, newNodeOut;
+ if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) {
+ newNodeIn.UpdatePorts();
+ newNodeOut.UpdatePorts();
+ inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
+ outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
+ }
+ if (!inputPort.IsConnectedTo(outputPort))
+ {
+ inputPort.Connect(outputPort);
+ }
+ }
+ }
+ }
+ EditorUtility.SetDirty(graph);
+ // Select the new nodes
+ Selection.objects = newNodes;
+ }
+
+ /// Draw a connection as we are dragging it
+ public void DrawDraggedConnection() {
+ if (IsDraggingPort) {
+ var gradient = graphEditor.GetNoodleGradient(draggedOutput, null);
+ var thickness = graphEditor.GetNoodleThickness(draggedOutput, null);
+ var path = graphEditor.GetNoodlePath(draggedOutput, null);
+ var stroke = graphEditor.GetNoodleStroke(draggedOutput, null);
+
+ Rect fromRect;
+ if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect))
+ {
+ return;
+ }
+
+ var gridPoints = new List();
+ gridPoints.Add(fromRect.center);
+ for (var i = 0; i < draggedOutputReroutes.Count; i++) {
+ gridPoints.Add(draggedOutputReroutes[i]);
+ }
+ if (draggedOutputTarget != null)
+ {
+ gridPoints.Add(portConnectionPoints[draggedOutputTarget].center);
+ }
+ else
+ {
+ gridPoints.Add(WindowToGridPosition(Event.current.mousePosition));
+ }
+
+ DrawNoodle(gradient, path, stroke, thickness, gridPoints);
+
+ var portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(draggedOutput);
+ var bgcol = Color.black;
+ var frcol = gradient.colorKeys[0].color;
+ bgcol.a = 0.6f;
+ frcol.a = 0.6f;
+
+ // Loop through reroute points again and draw the points
+ for (var i = 0; i < draggedOutputReroutes.Count; i++) {
+ // Draw reroute point at position
+ var rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16));
+ rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8);
+ rect = GridToWindowRect(rect);
+
+ NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol, portStyle.normal.background, portStyle.active.background);
+ }
+ }
+ }
+
+ private bool IsHoveringTitle(XNode.Node node) {
+ var mousePos = Event.current.mousePosition;
+ //Get node position
+ var nodePos = GridToWindowPosition(node.position);
+ float width;
+ Vector2 size;
+ if (nodeSizes.TryGetValue(node, out size))
+ {
+ width = size.x;
+ }
+ else
+ {
+ width = 200;
+ }
+
+ var windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));
+ return windowRect.Contains(mousePos);
+ }
+
+ /// Attempt to connect dragged output to target node
+ public void AutoConnect(XNode.Node node) {
+ if (autoConnectOutput == null)
+ {
+ return;
+ }
+
+ // Find compatible input port
+ var inputPort = node.Ports.FirstOrDefault(x => x.IsInput && graphEditor.CanConnect(autoConnectOutput, x));
+ if (inputPort != null)
+ {
+ autoConnectOutput.Connect(inputPort);
+ }
+
+ // Save changes
+ EditorUtility.SetDirty(graph);
+ if (NodeEditorPreferences.GetSettings().autoSave)
+ {
+ AssetDatabase.SaveAssets();
+ }
+
+ autoConnectOutput = null;
+ }
+ }
+}
diff --git a/Scripts/Editor/NodeEditorAction.cs.meta b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAction.cs.meta
similarity index 100%
rename from Scripts/Editor/NodeEditorAction.cs.meta
rename to xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAction.cs.meta
diff --git a/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAssetModProcessor.cs b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAssetModProcessor.cs
new file mode 100644
index 0000000..873b5d2
--- /dev/null
+++ b/xNode/Packages/com.pillow.xnode/Runtime/Editor/NodeEditorAssetModProcessor.cs
@@ -0,0 +1,103 @@
+using UnityEditor;
+using UnityEngine;
+using System.IO;
+
+namespace XNodeEditor
+{
+ /// Deals with modified assets
+ internal class NodeEditorAssetModProcessor : AssetModificationProcessor
+ {
+ /// Automatically delete Node sub-assets before deleting their script.
+ /// This is important to do, because you can't delete null sub assets.
+ /// For another workaround, see: https://gitlab.com/RotaryHeart-UnityShare/subassetmissingscriptdelete
+ private static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options)
+ {
+ // Skip processing anything without the .cs extension
+ if (Path.GetExtension(path) != ".cs")
+ {
+ return AssetDeleteResult.DidNotDelete;
+ }
+
+ // Get the object that is requested for deletion
+ var obj = AssetDatabase.LoadAssetAtPath