From 5ec46e53a1a71a4a8817e3d81610547d56798c06 Mon Sep 17 00:00:00 2001 From: Emre Dogan <48212096+EmreDogann@users.noreply.github.com> Date: Fri, 6 Oct 2023 00:55:54 +0100 Subject: [PATCH] Added inline UI for Node/Group renaming. --- Scripts/Editor/NodeEditor.cs | 13 ---- Scripts/Editor/NodeEditorAction.cs | 66 ++++++++++++++--- Scripts/Editor/NodeEditorGUI.cs | 12 +++- Scripts/Editor/NodeEditorResources.cs | 23 +++++- Scripts/Editor/RenameTextField.cs | 98 ++++++++++++++++++++++++++ Scripts/Editor/RenameTextField.cs.meta | 3 + 6 files changed, 190 insertions(+), 25 deletions(-) create mode 100644 Scripts/Editor/RenameTextField.cs create mode 100644 Scripts/Editor/RenameTextField.cs.meta diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 09bed80..e03973a 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -24,7 +24,6 @@ namespace XNodeEditor /// Fires every whenever a node was modified through the editor public static Action onUpdateNode; public static readonly Dictionary portPositions = new Dictionary(); - private Vector2 _lastClickPos; #if ODIN_INSPECTOR protected internal static bool inNodeEditor = false; @@ -32,18 +31,6 @@ namespace XNodeEditor public virtual void OnHeaderGUI() { - Event e = Event.current; - if (e.type == EventType.MouseDown) - { - if ((_lastClickPos - e.mousePosition).sqrMagnitude <= 5 * 5 && e.clickCount > 1) - { - Debug.Log("Renaming time!"); - return; - } - - _lastClickPos = e.mousePosition; - } - GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30)); } diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 50c6432..e694477 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -20,7 +20,8 @@ namespace XNodeEditor HoldNode, DragNode, HoldGrid, - DragGrid + DragGrid, + Renaming } public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } @@ -32,6 +33,8 @@ namespace XNodeEditor public bool IsHoveringPort => hoveredPort != null; public bool IsHoveringNode => hoveredNode != null; public bool IsHoveringReroute => hoveredReroute.port != null; + public bool IsSelectingRenamingObject => + currentActivity == NodeActivity.Renaming && IsHoveringNode && Selection.Contains(hoveredNode); /// Return the dragged port or null if not exist public NodePort DraggedOutputPort @@ -135,6 +138,7 @@ namespace XNodeEditor else if (currentActivity == NodeActivity.HoldNode) { RecalculateDragOffsets(e); + isDoubleClick = false; currentActivity = NodeActivity.DragNode; Repaint(); } @@ -277,8 +281,14 @@ namespace XNodeEditor } } } + + if (currentActivity == NodeActivity.Renaming) + { + currentActivity = NodeActivity.Idle; + } } - else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) + else if (IsHoveringNode && IsHoveringTitle(hoveredNode) && + (currentActivity != NodeActivity.Renaming || !IsSelectingRenamingObject)) { // If mousedown on node header, select or deselect if (!Selection.Contains(hoveredNode)) @@ -415,19 +425,30 @@ namespace XNodeEditor } // If click node header, select it. - if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) + if ((currentActivity == NodeActivity.HoldNode || !IsSelectingRenamingObject) && + !(e.control || e.shift)) { selectedReroutes.Clear(); SelectNode(hoveredNode, false); - // Double click to center node + // Double click to rename node if (isDoubleClick) { - Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) - ? nodeSizes[hoveredNode] / 2 - : Vector2.zero; - panOffset = -hoveredNode.position - nodeDimension; + RenameSelectedNodeTextField(); + currentActivity = NodeActivity.Renaming; + // Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) + // ? nodeSizes[hoveredNode] / 2 + // : Vector2.zero; + // panOffset = -hoveredNode.position - nodeDimension; } + else + { + currentActivity = NodeActivity.HoldNode; + } + } + else if (currentActivity != NodeActivity.Renaming) + { + currentActivity = NodeActivity.Idle; } // If click reroute, select it. @@ -438,7 +459,6 @@ namespace XNodeEditor } Repaint(); - currentActivity = NodeActivity.Idle; } else if (e.button == 1 || e.button == 2) { @@ -483,6 +503,11 @@ namespace XNodeEditor graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } + + if (currentActivity == NodeActivity.Renaming) + { + currentActivity = NodeActivity.Idle; + } } isPanning = false; @@ -604,6 +629,11 @@ namespace XNodeEditor break; } + + if (currentActivity != NodeActivity.Renaming && RenameTextField.IsActive) + { + RenameTextField.current.SaveAndClose(); + } } private void RecalculateDragOffsets(Event current) @@ -686,6 +716,24 @@ namespace XNodeEditor } } + + public void RenameSelectedNodeTextField() + { + if (Selection.objects.Length == 1 && Selection.activeObject is Node) + { + Node node = Selection.activeObject as Node; + Vector2 size; + if (nodeSizes.TryGetValue(node, out size)) + { + RenameTextField.Show(Selection.activeObject, size.x); + } + else + { + RenameTextField.Show(Selection.activeObject); + } + } + } + /// Draw this node on top of other nodes by placing it last in the graph.nodes list public void MoveNodeToTop(Node node) { diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 479ef02..18d46d6 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -156,7 +156,7 @@ namespace XNodeEditor } else { - graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, NodePort.IO.Input); + graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType); } } @@ -681,7 +681,15 @@ namespace XNodeEditor EditorGUI.BeginChangeCheck(); //Draw node contents - nodeEditor.OnHeaderGUI(); + if (currentActivity == NodeActivity.Renaming && Selection.activeObject == node) + { + RenameTextField.current.DrawRenameTextField(); + } + else + { + nodeEditor.OnHeaderGUI(); + } + nodeEditor.OnBodyGUI(); //If user changed a value, notify other scripts through onUpdateNode diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index e70a785..cc963e8 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -25,7 +25,7 @@ namespace XNodeEditor public static GUIStyle OutputPort => new GUIStyle(EditorStyles.label) { alignment = TextAnchor.UpperRight }; public class Styles { - public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip, nodeHighlight; + public GUIStyle inputPort, outputPort, nodeHeader, nodeHeaderRename, nodeBody, tooltip, nodeHighlight; public Styles() { @@ -49,6 +49,13 @@ namespace XNodeEditor nodeHeader.fontStyle = FontStyle.Bold; nodeHeader.normal.textColor = Color.white; + nodeHeaderRename = new GUIStyle(GUI.skin.textField); + nodeHeaderRename.alignment = TextAnchor.MiddleCenter; + nodeHeaderRename.fontStyle = FontStyle.Bold; + nodeHeaderRename.normal.textColor = Color.white; + nodeHeaderRename.fixedHeight = 18; + nodeHeaderRename.margin = new RectOffset(5, 5, 10, 8); + nodeBody = new GUIStyle(); nodeBody.normal.background = NodeEditorResources.nodeBody; nodeBody.border = new RectOffset(32, 32, 32, 32); @@ -63,6 +70,20 @@ namespace XNodeEditor } } + public static Texture2D GenerateSolidColorTexture(int width, int height, Color col) + { + var pix = new Color[width * height]; + for (int i = 0; i < pix.Length; ++i) + { + pix[i] = col; + } + + Texture2D result = new Texture2D(width, height); + result.SetPixels(pix); + result.Apply(); + return result; + } + public static Texture2D GenerateGridTexture(Color line, Color bg) { Texture2D tex = new Texture2D(64, 64); diff --git a/Scripts/Editor/RenameTextField.cs b/Scripts/Editor/RenameTextField.cs new file mode 100644 index 0000000..ceb64fc --- /dev/null +++ b/Scripts/Editor/RenameTextField.cs @@ -0,0 +1,98 @@ +using UnityEditor; +using UnityEngine; +using XNode; + +namespace XNodeEditor +{ + /// Utility for renaming assets + public class RenameTextField + { + private const string inputControlName = "nameInput"; + + public static RenameTextField current { get; private set; } + public static bool IsActive => current != null; + public Object target; + public string input; + + private bool firstFrame = true; + + /// Show a rename text field for an asset in the node header. Will trigger reimport of the asset on apply. + public static RenameTextField Show(Object target, float width = 200) + { + RenameTextField textField = new RenameTextField(); + if (current != null) + { + current = null; + } + + current = textField; + textField.target = target; + textField.input = target.name; + + return textField; + } + + public void DrawRenameTextField() + { + GUI.SetNextControlName(inputControlName); + input = GUILayout.TextField(input, NodeEditorResources.styles.nodeHeaderRename); + EditorGUI.FocusTextInControl(inputControlName); + + if (firstFrame) + { + TextEditor textEditor = + (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl); + textEditor.SelectAll(); + firstFrame = false; + } + + Event e = Event.current; + // If input is empty, revert name to default instead + if (input == null || input.Trim() == "") + { + if (e.isKey && e.keyCode == KeyCode.Return) + { + SaveAndClose(); + } + } + // Rename asset to input text + else + { + if (e.isKey && e.keyCode == KeyCode.Return) + { + SaveAndClose(); + } + } + + if (e.isKey && e.keyCode == KeyCode.Escape) + { + Close(); + } + } + + public void SaveAndClose() + { + target.name = input; + NodeEditor.GetEditor((Node)target, NodeEditorWindow.current).OnRename(); + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(target))) + { + AssetDatabase.SetMainObject((target as Node).graph, AssetDatabase.GetAssetPath(target)); + AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); + } + + Close(); + target.TriggerOnValidate(); + } + + public void Close() + { + firstFrame = true; + current = null; + NodeEditorWindow.current.Repaint(); + if (NodeEditorWindow.currentActivity == NodeEditorWindow.NodeActivity.Renaming) + { + NodeEditorWindow.currentActivity = NodeEditorWindow.NodeActivity.Idle; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/RenameTextField.cs.meta b/Scripts/Editor/RenameTextField.cs.meta new file mode 100644 index 0000000..29f40c9 --- /dev/null +++ b/Scripts/Editor/RenameTextField.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e19c9bf223d748e895841425ac714280 +timeCreated: 1696534896 \ No newline at end of file