From fb7049d0700ab3ae0fd26a38b01f03510fad9243 Mon Sep 17 00:00:00 2001 From: Matthias Guntrum Date: Thu, 16 Mar 2023 00:02:17 +0100 Subject: [PATCH] - Added createInputPort to [Node] attribute. This allows to hide the input port for a node. [#6](https://github.com/Gentlymad-Studios/NewGraph/issues/6) - Added comments to ContextMenu, EdgeDropMenu so it is easier to understand & extend --- Attributes/NodeAttribute.cs | 9 ++- CHANGELOG.md | 7 ++- Documentation~ | 2 +- .../Attributes/CustomContextMenuAttribute.cs | 3 + .../Attributes/CustomEdgeDropMenuAttribute.cs | 3 + Editor/Controllers/NodeController.cs | 4 +- Editor/Serialization/PropertyBag.cs | 4 +- Editor/Views/ContextMenu.cs | 61 +++++++++++++++++-- Editor/Views/EdgeDropMenu.cs | 40 ++++++++++-- package.json | 2 +- 10 files changed, 120 insertions(+), 15 deletions(-) diff --git a/Attributes/NodeAttribute.cs b/Attributes/NodeAttribute.cs index 81e085d..6ef59c3 100644 --- a/Attributes/NodeAttribute.cs +++ b/Attributes/NodeAttribute.cs @@ -8,6 +8,11 @@ namespace NewGraph { /// [AttributeUsage(AttributeTargets.Class)] public class NodeAttribute : Attribute { + /// + /// Should an input port be created? + /// + public bool createInputPort = true; + /// /// Color of the node as a hex string, like #FFFFFFFF. Be aware: The last two characters are for alpha values! /// @@ -42,7 +47,8 @@ public class NodeAttribute : Attribute { /// A custom name for the input port. /// The maximum amount of allowed connections to the input port of this node. /// A custom name for the node. - public NodeAttribute(string color = null, string categories = "", string inputPortName = null, Capacity inputPortCapacity = Capacity.Multiple, string nodeName = null) { + /// The maximum amount of allowed connections to the input port of this node. + public NodeAttribute(string color = null, string categories = "", string inputPortName = null, Capacity inputPortCapacity = Capacity.Multiple, string nodeName = null, bool createInputPort = true) { if (color != null) { ColorUtility.TryParseHtmlString(color, out this.color); } @@ -52,6 +58,7 @@ public NodeAttribute(string color = null, string categories = "", string inputPo this.inputPortName = inputPortName; this.inputPortCapacity = inputPortCapacity; this.categories = categories; + this.createInputPort = createInputPort; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 1427895..31f225a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,4 +169,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.2.8] - 2023-03-15 ### Fixed -- Fixed possible ArgumentException caused by the new ContextMenu architecture \ No newline at end of file +- Fixed possible ArgumentException caused by the new ContextMenu architecture + +## [0.2.9] - 2023-03-16 +### Added +- Added createInputPort to [Node] attribute. This allows to hide the input port for a node. [#6](https://github.com/Gentlymad-Studios/NewGraph/issues/6) +- Added comments to ContextMenu, EdgeDropMenu so it is easier to understand & extend \ No newline at end of file diff --git a/Documentation~ b/Documentation~ index ed9da80..2821ff9 160000 --- a/Documentation~ +++ b/Documentation~ @@ -1 +1 @@ -Subproject commit ed9da80ba1a56f3e9c5a78e2d1761d43755617d5 +Subproject commit 2821ff9d93a468f9af96b8ad238d4e2f65329ff6 diff --git a/Editor/Attributes/CustomContextMenuAttribute.cs b/Editor/Attributes/CustomContextMenuAttribute.cs index db7318b..ea4a724 100644 --- a/Editor/Attributes/CustomContextMenuAttribute.cs +++ b/Editor/Attributes/CustomContextMenuAttribute.cs @@ -1,6 +1,9 @@ using System; namespace NewGraph { + /// + /// Attribute that can be attached to a class that inherits from ContextMenu to change & customize the main context menu. + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class CustomContextMenuAttribute : Attribute { diff --git a/Editor/Attributes/CustomEdgeDropMenuAttribute.cs b/Editor/Attributes/CustomEdgeDropMenuAttribute.cs index 3527d17..a2d9c70 100644 --- a/Editor/Attributes/CustomEdgeDropMenuAttribute.cs +++ b/Editor/Attributes/CustomEdgeDropMenuAttribute.cs @@ -1,6 +1,9 @@ using System; namespace NewGraph { + /// + /// Attribute that can be attached to a class that inherits from EdgeDropMenu to change & customize the edge drop context menu. + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class CustomEdgeDropMenuAttribute : Attribute { diff --git a/Editor/Controllers/NodeController.cs b/Editor/Controllers/NodeController.cs index ce359c2..23e4021 100644 --- a/Editor/Controllers/NodeController.cs +++ b/Editor/Controllers/NodeController.cs @@ -102,7 +102,9 @@ public void DoForEachPortListProperty(Action actio } public void DoForInputPortProperty(Action action) { - action(propertyBag.inputPort, nodeDataProperty); + if (propertyBag.inputPort != null) { + action(propertyBag.inputPort, nodeDataProperty); + } } public void DoForNameProperty(Action action) { diff --git a/Editor/Serialization/PropertyBag.cs b/Editor/Serialization/PropertyBag.cs index 7ada957..fafdf0e 100644 --- a/Editor/Serialization/PropertyBag.cs +++ b/Editor/Serialization/PropertyBag.cs @@ -34,7 +34,9 @@ public class PropertyBag { private PropertyBag(NodeAttribute nodeAttribute, Type nodeType, SerializedProperty nodeProperty) { this.nodeType = nodeType; - inputPort = new PortInfo(nodeProperty.propertyPath, nodeType, new PortBaseAttribute(nodeAttribute.inputPortName, nodeAttribute.inputPortCapacity, PortDirection.Input), Settings.defaultInputName); + if (nodeAttribute.createInputPort) { + inputPort = new PortInfo(nodeProperty.propertyPath, nodeType, new PortBaseAttribute(nodeAttribute.inputPortName, nodeAttribute.inputPortCapacity, PortDirection.Input), Settings.defaultInputName); + } InitializeAttributebehaviors(); RetrieveAll(nodeProperty); } diff --git a/Editor/Views/ContextMenu.cs b/Editor/Views/ContextMenu.cs index f25b1d7..dd0a232 100644 --- a/Editor/Views/ContextMenu.cs +++ b/Editor/Views/ContextMenu.cs @@ -6,44 +6,83 @@ using static NewGraph.GraphSettingsSingleton; namespace NewGraph { + /// + /// Main ContextMenu that is being used when performing a right-click in the graph view. + /// This can be inherited by a custom class that has the [CustomContextMenu] attribute. + /// Beware: This class inherits from GraphSearchWindowProvider and therefore is a ScriptableObject by nature. + /// public class ContextMenu : GraphSearchWindowProvider { + /// + /// static lookup table that gathers unique labels for node types. + /// protected static Dictionary nodeTypeToCreationLabel = new Dictionary(); + /// + /// the graph controller this context menu operates on. + /// protected GraphController graphController; + /// + /// Initialize should be called after the object is created via CreateInstance + /// + /// The graph controller this context menu should operate on. public void Initialize(GraphController graphController) { this.graphController = graphController; Initialize(this.graphController.graphView.shortcutHandler); } + /// + /// Instantiate a custom or default context menu + /// + /// The graph controller this context menu should operate on. + /// The instance of the newly created context menu. public static ContextMenu CreateContextMenu(GraphController graphController) { + // get all types that have the [CustomContextMenu] attribute TypeCache.TypeCollection types = TypeCache.GetTypesWithAttribute(); ContextMenu menu; foreach (Type type in types) { + // is the detected type actually inheriting from ContextMenu? if (type.ImplementsOrInherits(typeof(ContextMenu))) { + // create an instance of the custom ContextMenu type! menu = CreateInstance(type) as ContextMenu; menu.Initialize(graphController); return menu; } } + // no custom editor found or ineligible menu = CreateInstance(); menu.Initialize(graphController); return menu; } - public virtual bool DefaultEnableCheck() { + /// + /// Default check wether a menu entry should be enabled. + /// + /// Should the menu entry be enabled? + public virtual bool DefaultEnabledCheck() { return graphController.graphView.GetSelectedNodeCount() > 0; } + /// + /// Default check for node entries and wether they should be enabled. + /// + /// Should the node entry be enabled? public virtual bool DefaultNodeEnabledCheck() { return graphController.graphData != null; } + /// + /// Header label + /// + /// protected virtual string GetHeader() { return Settings.searchWindowRootHeader; } + /// + /// Creates/builds the context menu + /// public virtual void BuildContextMenu() { StartAddingMenuEntries(GetHeader()); AddNodeEntries(); @@ -51,10 +90,16 @@ public virtual void BuildContextMenu() { AddCommands(); } + /// + /// Add all node entries. + /// protected virtual void AddNodeEntries() { AddNodeEntriesDefault(); } + /// + /// Default implementation to create all node entries. + /// protected void AddNodeEntriesDefault() { // get all types across all assemblies that implement our INode interface TypeCache.TypeCollection nodeTypes = TypeCache.GetTypesWithAttribute(); @@ -89,19 +134,25 @@ protected void AddNodeEntriesDefault() { } } + /// + /// Add all command panel entries + /// protected virtual void AddCommands() { AddDefaultCommands(); } + /// + /// defualt implementation of all commands + /// protected void AddDefaultCommands() { AddSeparator(Settings.searchWindowCommandHeader); AddShortcutEntry(Actions.Frame, SearchTreeEntry.AlwaysEnabled, graphController.FrameGraph); AddShortcutEntry(Actions.Rename, () => graphController.graphView.GetSelectedNodeCount() == 1, graphController.OnRename); - AddShortcutEntry(Actions.Cut, DefaultEnableCheck, graphController.OnCut); - AddShortcutEntry(Actions.Copy, DefaultEnableCheck, graphController.OnCopy); + AddShortcutEntry(Actions.Cut, DefaultEnabledCheck, graphController.OnCut); + AddShortcutEntry(Actions.Copy, DefaultEnabledCheck, graphController.OnCopy); AddShortcutEntry(Actions.Paste, () => graphController.copyPasteHandler.HasNodes(), graphController.OnPaste); - AddShortcutEntry(Actions.Duplicate, DefaultEnableCheck, graphController.OnDuplicate); - AddShortcutEntry(Actions.Delete, () => graphController.graphView.HasSelectedEdges() || DefaultEnableCheck(), graphController.OnDelete); + AddShortcutEntry(Actions.Duplicate, DefaultEnabledCheck, graphController.OnDuplicate); + AddShortcutEntry(Actions.Delete, () => graphController.graphView.HasSelectedEdges() || DefaultEnabledCheck(), graphController.OnDelete); } } diff --git a/Editor/Views/EdgeDropMenu.cs b/Editor/Views/EdgeDropMenu.cs index 832a29c..a565269 100644 --- a/Editor/Views/EdgeDropMenu.cs +++ b/Editor/Views/EdgeDropMenu.cs @@ -3,48 +3,80 @@ using UnityEditor; namespace NewGraph { + /// + /// The EdgeDropMenu is being used after dropping an edge into empty space in the graph. + /// This can be inherited by a custom class that has the [CustomEdgeDropMenu] attribute. + /// Beware: This class inherits from GraphSearchWindowProvider and therefore is a ScriptableObject by nature. + /// public class EdgeDropMenu : ContextMenu { - + /// + /// the port this menu should operate on. This is required to be updated fromt he outside. + /// public PortView port; - + + /// + /// Instantiate a custom or default edge drop menu + /// + /// The graph controller this edge drop menu should operate on. + /// The instance of the newly created edge drop menu. public static EdgeDropMenu CreateEdgeDropMenu(GraphController graphController) { + // get all types that have the [CustomEgeDropMenu] attribute TypeCache.TypeCollection types = TypeCache.GetTypesWithAttribute(); EdgeDropMenu menu; foreach (Type type in types) { + // is the detected type actually inheriting from ContextMenu? if (type.ImplementsOrInherits(typeof(EdgeDropMenu))) { + // create an instance of the custom ContextMenu type! menu = CreateInstance(type) as EdgeDropMenu; menu.Initialize(graphController); return menu; } } + // no custom editor found or ineligible menu = CreateInstance(); menu.Initialize(graphController); return menu; } + /// + /// Add all node entries. + /// protected override void AddNodeEntries() { PortView currentPort = port; foreach (Type type in currentPort.connectableTypes) { if (nodeTypeToCreationLabel.ContainsKey(type)) { void CreateNodeAndConnect() { NodeView nodeView = graphController.CreateNewNode(type, false); - graphController.ConnectPorts(currentPort, nodeView.inputPort); + if (nodeView.inputPort != null) { + graphController.ConnectPorts(currentPort, nodeView.inputPort); + } } AddNodeEntry(nodeTypeToCreationLabel[type], (obj) => CreateNodeAndConnect()); } } } + /// + /// The edge drop menu node entries should always be enabled. + /// + /// public override bool DefaultNodeEnabledCheck() { return true; } - public override bool DefaultEnableCheck() { + /// + /// The edge drop menu entries should always be enabled. + /// + /// + public override bool DefaultEnabledCheck() { return true; } + /// + /// At least on default, the edge drop menu does not have a command panel. + /// protected override void AddCommands() {} } } diff --git a/package.json b/package.json index a91aaba..940fb76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.gentlymad.newgraph", - "version": "0.2.8", + "version": "0.2.9", "displayName": "NewGraph", "description": "A general data oriented node graph solution. This is build upon the idea to visualize complex data structures as graph networks without having to modify already established data classes, except adding [Node], [Port] and [SerializeReference] attributes to call classes that should show in the Graph View.", "unity": "2022.2",