Skip to content

Commit

Permalink
- Added createInputPort to [Node] attribute. This allows to hide the …
Browse files Browse the repository at this point in the history
…input port for a node. [#6](#6)

- Added comments to ContextMenu, EdgeDropMenu so it is easier to understand & extend
  • Loading branch information
Doppelkeks committed Mar 15, 2023
1 parent 880fc96 commit fb7049d
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 15 deletions.
9 changes: 8 additions & 1 deletion Attributes/NodeAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ namespace NewGraph {
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class NodeAttribute : Attribute {
/// <summary>
/// Should an input port be created?
/// </summary>
public bool createInputPort = true;

/// <summary>
/// Color of the node as a hex string, like #FFFFFFFF. Be aware: The last two characters are for alpha values!
/// </summary>
Expand Down Expand Up @@ -42,7 +47,8 @@ public class NodeAttribute : Attribute {
/// <param name="inputPortName">A custom name for the input port.</param>
/// <param name="inputPortCapacity">The maximum amount of allowed connections to the input port of this node.</param>
/// <param name="nodeName">A custom name for the node.</param>
public NodeAttribute(string color = null, string categories = "", string inputPortName = null, Capacity inputPortCapacity = Capacity.Multiple, string nodeName = null) {
/// <param name="createInputPort">The maximum amount of allowed connections to the input port of this node.</param>
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);
}
Expand All @@ -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;
}


Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- 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
2 changes: 1 addition & 1 deletion Documentation~
3 changes: 3 additions & 0 deletions Editor/Attributes/CustomContextMenuAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;

namespace NewGraph {
/// <summary>
/// Attribute that can be attached to a class that inherits from ContextMenu to change & customize the main context menu.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CustomContextMenuAttribute : Attribute {

Expand Down
3 changes: 3 additions & 0 deletions Editor/Attributes/CustomEdgeDropMenuAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;

namespace NewGraph {
/// <summary>
/// Attribute that can be attached to a class that inherits from EdgeDropMenu to change & customize the edge drop context menu.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CustomEdgeDropMenuAttribute : Attribute {

Expand Down
4 changes: 3 additions & 1 deletion Editor/Controllers/NodeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ public void DoForEachPortListProperty(Action<PortInfo, SerializedProperty> actio
}

public void DoForInputPortProperty(Action<PortInfo, SerializedProperty> action) {
action(propertyBag.inputPort, nodeDataProperty);
if (propertyBag.inputPort != null) {
action(propertyBag.inputPort, nodeDataProperty);
}
}

public void DoForNameProperty(Action<SerializedProperty> action) {
Expand Down
4 changes: 3 additions & 1 deletion Editor/Serialization/PropertyBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
61 changes: 56 additions & 5 deletions Editor/Views/ContextMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,100 @@
using static NewGraph.GraphSettingsSingleton;

namespace NewGraph {
/// <summary>
/// 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.
/// </summary>
public class ContextMenu : GraphSearchWindowProvider {
/// <summary>
/// static lookup table that gathers unique labels for node types.
/// </summary>
protected static Dictionary<Type, string> nodeTypeToCreationLabel = new Dictionary<Type, string>();
/// <summary>
/// the graph controller this context menu operates on.
/// </summary>
protected GraphController graphController;

/// <summary>
/// Initialize should be called after the object is created via CreateInstance
/// </summary>
/// <param name="graphController">The graph controller this context menu should operate on.</param>
public void Initialize(GraphController graphController) {
this.graphController = graphController;
Initialize(this.graphController.graphView.shortcutHandler);
}

/// <summary>
/// Instantiate a custom or default context menu
/// </summary>
/// <param name="graphController">The graph controller this context menu should operate on.</param>
/// <returns>The instance of the newly created context menu.</returns>
public static ContextMenu CreateContextMenu(GraphController graphController) {
// get all types that have the [CustomContextMenu] attribute
TypeCache.TypeCollection types = TypeCache.GetTypesWithAttribute<CustomContextMenuAttribute>();
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<ContextMenu>();
menu.Initialize(graphController);
return menu;
}

public virtual bool DefaultEnableCheck() {
/// <summary>
/// Default check wether a menu entry should be enabled.
/// </summary>
/// <returns>Should the menu entry be enabled?</returns>
public virtual bool DefaultEnabledCheck() {
return graphController.graphView.GetSelectedNodeCount() > 0;
}

/// <summary>
/// Default check for node entries and wether they should be enabled.
/// </summary>
/// <returns>Should the node entry be enabled?</returns>
public virtual bool DefaultNodeEnabledCheck() {
return graphController.graphData != null;
}

/// <summary>
/// Header label
/// </summary>
/// <returns></returns>
protected virtual string GetHeader() {
return Settings.searchWindowRootHeader;
}

/// <summary>
/// Creates/builds the context menu
/// </summary>
public virtual void BuildContextMenu() {
StartAddingMenuEntries(GetHeader());
AddNodeEntries();
ResolveNodeEntries(DefaultNodeEnabledCheck);
AddCommands();
}

/// <summary>
/// Add all node entries.
/// </summary>
protected virtual void AddNodeEntries() {
AddNodeEntriesDefault();
}

/// <summary>
/// Default implementation to create all node entries.
/// </summary>
protected void AddNodeEntriesDefault() {
// get all types across all assemblies that implement our INode interface
TypeCache.TypeCollection nodeTypes = TypeCache.GetTypesWithAttribute<NodeAttribute>();
Expand Down Expand Up @@ -89,19 +134,25 @@ protected void AddNodeEntriesDefault() {
}
}

/// <summary>
/// Add all command panel entries
/// </summary>
protected virtual void AddCommands() {
AddDefaultCommands();
}

/// <summary>
/// defualt implementation of all commands
/// </summary>
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);
}
}

Expand Down
40 changes: 36 additions & 4 deletions Editor/Views/EdgeDropMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,80 @@
using UnityEditor;

namespace NewGraph {
/// <summary>
/// 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.
/// </summary>
public class EdgeDropMenu : ContextMenu {

/// <summary>
/// the port this menu should operate on. This is required to be updated fromt he outside.
/// </summary>
public PortView port;


/// <summary>
/// Instantiate a custom or default edge drop menu
/// </summary>
/// <param name="graphController">The graph controller this edge drop menu should operate on.</param>
/// <returns>The instance of the newly created edge drop menu.</returns>
public static EdgeDropMenu CreateEdgeDropMenu(GraphController graphController) {
// get all types that have the [CustomEgeDropMenu] attribute
TypeCache.TypeCollection types = TypeCache.GetTypesWithAttribute<CustomEdgeDropMenuAttribute>();
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<EdgeDropMenu>();
menu.Initialize(graphController);
return menu;
}

/// <summary>
/// Add all node entries.
/// </summary>
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());
}
}
}

/// <summary>
/// The edge drop menu node entries should always be enabled.
/// </summary>
/// <returns></returns>
public override bool DefaultNodeEnabledCheck() {
return true;
}

public override bool DefaultEnableCheck() {
/// <summary>
/// The edge drop menu entries should always be enabled.
/// </summary>
/// <returns></returns>
public override bool DefaultEnabledCheck() {
return true;
}

/// <summary>
/// At least on default, the edge drop menu does not have a command panel.
/// </summary>
protected override void AddCommands() {}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit fb7049d

Please sign in to comment.