1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 17:26:02 +08:00

Added GroupNode

This commit is contained in:
Oli 2023-01-10 21:11:23 +00:00
parent 524bcc389b
commit d181f432fc
10 changed files with 357 additions and 15 deletions

145
Editor/GroupNodeEditor.cs Normal file
View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using XNode;
using System.Linq;
using UnityEditor;
namespace XNodeEditor
{
[CustomNodeEditor(typeof(XNode.GroupNode))]
public class GroupNodeEditor : NodeEditor
{
private static Vector4 padding = new Vector4(32, 48, 32, 32);
private bool _selected = false;
private UnityEngine.Object[] lastSelection;
public override void OnHeaderGUI()
{
if (target is XNode.GroupNode node)
{
bool selectChildren = NodeEditorWindow.currentActivity is NodeEditorWindow.NodeActivity.HoldNode or NodeEditorWindow.NodeActivity.DragNode;
if (!_selected && Selection.activeObject == target && selectChildren)
{
_selected = true;
var selection = Selection.objects.ToList();
lastSelection = selection.ToArray();
GetChildren(node, ref selection);
//selection.AddRange(GetChildren(node));
Selection.objects = selection.Distinct().ToArray();
NodeEditorWindow.current.Repaint();
}
else if (_selected && !selectChildren)
{
_selected = false;
Selection.objects = lastSelection;
}
}
base.OnHeaderGUI();
}
private void GetChildren(XNode.GroupNode group, ref List<UnityEngine.Object> list)
{
foreach (var child in group.children)
{
if (list.Contains(child)) continue;
list.Add(child);
if (child is XNode.GroupNode _group) GetChildren(_group, ref list);
}
}
public override void OnBodyGUI()
{
var e = Event.current;
var node = target as XNode.GroupNode;
var nodes = node.graph.nodes;
if (e.type == EventType.Repaint)
{
if (nodes.FirstOrDefault() != node)
{
nodes.Remove(node);
int targetIndex = 0;
for (int i = 0; i < nodes.Count; i++)
{
if (nodes[i] is XNode.GroupNode _group)
{
if (_group.children.Contains(node))
{
targetIndex = i + 1;
break;
}
}
else break;
}
nodes.Insert(targetIndex, node);
}
}
if (node == null || node.children == null || node.children.Count == 0) return;
node.position = new Vector2(
node.children.Min(x => x.position.x) - padding.x,
node.children.Min(x => x.position.y) - padding.y
);
GUILayout.Label("");
}
public override Color GetTint()
{
Type type = target.GetType();
if (type.TryGetAttributeTint(out Color color)) return color;
if (!(target is XNode.GroupNode node)) return NodeEditorPreferences.GetSettings().tintColor;
return node.color;
}
public override int GetWidth()
{
int min = base.GetWidth();
var node = target as XNode.GroupNode;
if (node == null || node.children == null || node.children.Count == 0) return min + (int)padding.x + (int)padding.z;
return Mathf.Max(min,
node.children.Max(x =>
GetEditor(x, window).GetWidth() + (int)x.position.x) - (int)target.position.x)
+ (int)padding.z;
}
public override int GetMinHeight()
{
int min = base.GetMinHeight();
var node = target as XNode.GroupNode;
if (node == null || node.children == null || node.children.Count == 0) return min + (int)padding.y + (int)padding.w;
return Mathf.Max(min,
node.children.Max(x => GetEditor(x, window).GetMinHeight() + (int) x.position.y - (int) target.position.y))
+ (int) padding.w;
}
private static int GetNodeWidth(Node node)
{
Type type = node.GetType();
return (type.TryGetAttributeWidth(out int width) ? width : 208);
}
private static int GetNodeHeight(Node node)
{
Type type = node.GetType();
return (type.TryGetAttributeHeight(out int height) ? height : 100);
}
private static Texture2D nodeGroupBody { get { return _nodeGroupBody != null ? _nodeGroupBody : _nodeGroupBody = Resources.Load<Texture2D>("xnode_group"); } }
private static Texture2D _nodeGroupBody;
public override GUIStyle GetBodyStyle()
{
var style = new GUIStyle(base.GetBodyStyle())
{
normal =
{
background = nodeGroupBody
}
};
return style;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 24f2e755a9b5423fb2792a1b075ca612
timeCreated: 1673303313

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using XNode;
#if ODIN_INSPECTOR #if ODIN_INSPECTOR
using Sirenix.OdinInspector.Editor; using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities; using Sirenix.Utilities;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using XNode;
using XNodeEditor.Internal; using XNodeEditor.Internal;
#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU #if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
using GenericMenu = XNodeEditor.AdvancedGenericMenu; using GenericMenu = XNodeEditor.AdvancedGenericMenu;
@ -148,7 +149,6 @@ namespace XNodeEditor {
} }
} else if (e.button == 2) { } else if (e.button == 2) {
panOffset += e.delta * zoom; panOffset += e.delta * zoom;
isPanning = true;
} }
break; break;
case EventType.MouseDown: case EventType.MouseDown:
@ -204,7 +204,7 @@ namespace XNodeEditor {
currentActivity = NodeActivity.HoldNode; currentActivity = NodeActivity.HoldNode;
} }
// If mousedown on grid background, deselect all // If mousedown on grid background, deselect all
else if (!IsHoveringNode) { else if (!IsHoveringNode || hoveredNode is XNode.GroupNode) {
currentActivity = NodeActivity.HoldGrid; currentActivity = NodeActivity.HoldGrid;
if (!e.control && !e.shift) { if (!e.control && !e.shift) {
selectedReroutes.Clear(); selectedReroutes.Clear();
@ -212,6 +212,7 @@ namespace XNodeEditor {
} }
} }
} }
else if (e.button == 2) isPanning = true;
break; break;
case EventType.MouseUp: case EventType.MouseUp:
if (e.button == 0) { if (e.button == 0) {
@ -245,7 +246,7 @@ namespace XNodeEditor {
IEnumerable<XNode.Node> nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); IEnumerable<XNode.Node> nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node);
foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node); foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node);
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} else if (!IsHoveringNode) { } else if (!IsHoveringNode || hoveredNode is XNode.GroupNode) {
// If click outside node, release field focus // If click outside node, release field focus
if (!isPanning) { if (!isPanning) {
EditorGUI.FocusTextInControl(null); EditorGUI.FocusTextInControl(null);
@ -260,10 +261,7 @@ namespace XNodeEditor {
SelectNode(hoveredNode, false); SelectNode(hoveredNode, false);
// Double click to center node // Double click to center node
if (isDoubleClick) { if (isDoubleClick) RenameSelectedNode();
Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) ? nodeSizes[hoveredNode] / 2 : Vector2.zero;
panOffset = -hoveredNode.position - nodeDimension;
}
} }
// If click reroute, select it. // If click reroute, select it.
@ -276,7 +274,9 @@ namespace XNodeEditor {
currentActivity = NodeActivity.Idle; currentActivity = NodeActivity.Idle;
} else if (e.button == 1) { } else if (e.button == 1) {
if (!isPanning) { if (!isPanning) {
if (IsDraggingPort) { if (currentActivity == NodeActivity.DragNode && Selection.activeObject is Node)
ToggleSelectionGroup();
else if (IsDraggingPort) {
draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
} else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) {
selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
@ -292,7 +292,7 @@ namespace XNodeEditor {
NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu); NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu);
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); 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. 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) { } else if (!IsHoveringNode || hoveredNode is GroupNode) {
autoConnectOutput = null; autoConnectOutput = null;
GenericMenu menu = new GenericMenu(); GenericMenu menu = new GenericMenu();
graphEditor.AddContextMenuItems(menu); graphEditor.AddContextMenuItems(menu);
@ -301,6 +301,11 @@ namespace XNodeEditor {
} }
isPanning = false; isPanning = false;
} }
else if (e.button == 2)
{
isPanning = false;
Repaint();
}
// Reset DoubleClick // Reset DoubleClick
isDoubleClick = false; isDoubleClick = false;
break; break;
@ -349,6 +354,12 @@ namespace XNodeEditor {
} }
Repaint(); Repaint();
break; break;
case EventType.Repaint:
if (currentActivity == NodeActivity.DragNode)
EditorGUIUtility.AddCursorRect(new Rect(0,0,10000,10000), MouseCursor.MoveArrow);
else if (isPanning)
EditorGUIUtility.AddCursorRect(new Rect(0,0,10000,10000), MouseCursor.Pan);
break;
case EventType.Ignore: case EventType.Ignore:
// If release mouse outside window // If release mouse outside window
if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) {
@ -417,6 +428,44 @@ namespace XNodeEditor {
} }
} }
public void ToggleSelectionGroup()
{
var nodes = Selection.objects.OfType<Node>().ToArray();
var allGroups = graph.nodes.OfType<XNode.GroupNode>().ToArray();
var groups = allGroups.Where(group => !Selection.objects.Contains(group)).ToArray();
bool anyGrouped = false;
foreach (var group in groups)
{
foreach (var node in nodes)
{
if (group.children.Contains(node))
{
group.children.Remove(node);
anyGrouped = true;
}
}
}
if (!anyGrouped)
{
nodes = nodes.Where(node => !allGroups.Any(group => group.children.Contains(node))).ToArray();
foreach (var group in groups.Reverse())
{
var editor = NodeEditor.GetEditor(group, this);
editor.target = group;
var rect = new Rect(group.position.x, group.position.y, editor.GetWidth(), editor.GetMinHeight());
if (rect.Contains(WindowToGridPosition(Event.current.mousePosition)))
{
group.children.AddRange(nodes);
break;
}
}
}
Repaint();
}
/// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary> /// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary>
public void MoveNodeToTop(XNode.Node node) { public void MoveNodeToTop(XNode.Node node) {
int index; int index;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using XNode;
using XNodeEditor.Internal; using XNodeEditor.Internal;
#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU #if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
using GenericMenu = XNodeEditor.AdvancedGenericMenu; using GenericMenu = XNodeEditor.AdvancedGenericMenu;
@ -106,15 +107,17 @@ namespace XNodeEditor {
/// <summary> Show right-click context menu for hovered reroute </summary> /// <summary> Show right-click context menu for hovered reroute </summary>
void ShowRerouteContextMenu(RerouteReference reroute) { void ShowRerouteContextMenu(RerouteReference reroute) {
GenericMenu contextMenu = new GenericMenu(); reroute.RemovePoint();
contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint()); //GenericMenu contextMenu = new GenericMenu();
contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); //contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint());
//contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} }
/// <summary> Show right-click context menu for hovered port </summary> /// <summary> Show right-click context menu for hovered port </summary>
void ShowPortContextMenu(XNode.NodePort hoveredPort) { void ShowPortContextMenu(XNode.NodePort hoveredPort) {
GenericMenu contextMenu = new GenericMenu(); hoveredPort.ClearConnections();
/*GenericMenu contextMenu = new GenericMenu();
foreach (var port in hoveredPort.GetConnections()) { foreach (var port in hoveredPort.GetConnections()) {
var name = port.node.name; var name = port.node.name;
var index = hoveredPort.GetConnectionIndex(port); var index = hoveredPort.GetConnectionIndex(port);
@ -130,7 +133,7 @@ namespace XNodeEditor {
else else
graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Input); graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Input);
} }
contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));*/
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
} }
@ -533,7 +536,8 @@ namespace XNodeEditor {
//If dragging a selection box, add nodes inside to selection //If dragging a selection box, add nodes inside to selection
if (currentActivity == NodeActivity.DragGrid) { if (currentActivity == NodeActivity.DragGrid) {
if (windowRect.Overlaps(selectionBox)) preSelection.Add(node); if (node is XNode.GroupNode ? selectionBox.Overlaps(new Rect(windowRect) {height = 32} )
: windowRect.Overlaps(selectionBox)) preSelection.Add(node);
} }
//Check if we are hovering any of this nodes ports //Check if we are hovering any of this nodes ports

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,124 @@
fileFormatVersion: 2
guid: 8eff5f1a77870ce4189eb84684342245
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

12
Runtime/GroupNode.cs Normal file
View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using UnityEngine;
namespace XNode
{
[NodeWidth(208), NodeHeight(176)]
public class GroupNode : Node
{
public Color color = new Color(1.0F,1.0F,1.0F,1.0F);
public List<Node> children = new List<Node>();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39b1316a0b0f4e389d24c32de475b0cd
timeCreated: 1673303110