mirror of
https://github.com/Siccity/xNode.git
synced 2025-12-20 17:26:02 +08:00
Added GroupNode
This commit is contained in:
parent
524bcc389b
commit
d181f432fc
145
Editor/GroupNodeEditor.cs
Normal file
145
Editor/GroupNodeEditor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Editor/GroupNodeEditor.cs.meta
Normal file
3
Editor/GroupNodeEditor.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24f2e755a9b5423fb2792a1b075ca612
|
||||
timeCreated: 1673303313
|
||||
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
#if ODIN_INSPECTOR
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using Sirenix.Utilities;
|
||||
|
||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
using XNodeEditor.Internal;
|
||||
#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
|
||||
using GenericMenu = XNodeEditor.AdvancedGenericMenu;
|
||||
@ -148,7 +149,6 @@ namespace XNodeEditor {
|
||||
}
|
||||
} else if (e.button == 2) {
|
||||
panOffset += e.delta * zoom;
|
||||
isPanning = true;
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDown:
|
||||
@ -204,7 +204,7 @@ namespace XNodeEditor {
|
||||
currentActivity = NodeActivity.HoldNode;
|
||||
}
|
||||
// If mousedown on grid background, deselect all
|
||||
else if (!IsHoveringNode) {
|
||||
else if (!IsHoveringNode || hoveredNode is XNode.GroupNode) {
|
||||
currentActivity = NodeActivity.HoldGrid;
|
||||
if (!e.control && !e.shift) {
|
||||
selectedReroutes.Clear();
|
||||
@ -212,6 +212,7 @@ namespace XNodeEditor {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (e.button == 2) isPanning = true;
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
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);
|
||||
foreach (XNode.Node node in nodes) EditorUtility.SetDirty(node);
|
||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||
} else if (!IsHoveringNode) {
|
||||
} else if (!IsHoveringNode || hoveredNode is XNode.GroupNode) {
|
||||
// If click outside node, release field focus
|
||||
if (!isPanning) {
|
||||
EditorGUI.FocusTextInControl(null);
|
||||
@ -260,10 +261,7 @@ namespace XNodeEditor {
|
||||
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 (isDoubleClick) RenameSelectedNode();
|
||||
}
|
||||
|
||||
// If click reroute, select it.
|
||||
@ -276,7 +274,9 @@ namespace XNodeEditor {
|
||||
currentActivity = NodeActivity.Idle;
|
||||
} else if (e.button == 1) {
|
||||
if (!isPanning) {
|
||||
if (IsDraggingPort) {
|
||||
if (currentActivity == NodeActivity.DragNode && Selection.activeObject is Node)
|
||||
ToggleSelectionGroup();
|
||||
else if (IsDraggingPort) {
|
||||
draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition));
|
||||
} else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) {
|
||||
selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint());
|
||||
@ -292,7 +292,7 @@ namespace XNodeEditor {
|
||||
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) {
|
||||
} else if (!IsHoveringNode || hoveredNode is GroupNode) {
|
||||
autoConnectOutput = null;
|
||||
GenericMenu menu = new GenericMenu();
|
||||
graphEditor.AddContextMenuItems(menu);
|
||||
@ -301,6 +301,11 @@ namespace XNodeEditor {
|
||||
}
|
||||
isPanning = false;
|
||||
}
|
||||
else if (e.button == 2)
|
||||
{
|
||||
isPanning = false;
|
||||
Repaint();
|
||||
}
|
||||
// Reset DoubleClick
|
||||
isDoubleClick = false;
|
||||
break;
|
||||
@ -349,6 +354,12 @@ namespace XNodeEditor {
|
||||
}
|
||||
Repaint();
|
||||
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:
|
||||
// If release mouse outside window
|
||||
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>
|
||||
public void MoveNodeToTop(XNode.Node node) {
|
||||
int index;
|
||||
|
||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
using XNodeEditor.Internal;
|
||||
#if UNITY_2019_1_OR_NEWER && USE_ADVANCED_GENERIC_MENU
|
||||
using GenericMenu = XNodeEditor.AdvancedGenericMenu;
|
||||
@ -106,15 +107,17 @@ namespace XNodeEditor {
|
||||
|
||||
/// <summary> Show right-click context menu for hovered reroute </summary>
|
||||
void ShowRerouteContextMenu(RerouteReference reroute) {
|
||||
GenericMenu contextMenu = new GenericMenu();
|
||||
contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint());
|
||||
contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||
reroute.RemovePoint();
|
||||
//GenericMenu contextMenu = new GenericMenu();
|
||||
//contextMenu.AddItem(new GUIContent("Remove"), false, () => reroute.RemovePoint());
|
||||
//contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||
if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
/// <summary> Show right-click context menu for hovered port </summary>
|
||||
void ShowPortContextMenu(XNode.NodePort hoveredPort) {
|
||||
GenericMenu contextMenu = new GenericMenu();
|
||||
hoveredPort.ClearConnections();
|
||||
/*GenericMenu contextMenu = new GenericMenu();
|
||||
foreach (var port in hoveredPort.GetConnections()) {
|
||||
var name = port.node.name;
|
||||
var index = hoveredPort.GetConnectionIndex(port);
|
||||
@ -130,7 +133,7 @@ namespace XNodeEditor {
|
||||
else
|
||||
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();
|
||||
}
|
||||
|
||||
@ -533,7 +536,8 @@ namespace XNodeEditor {
|
||||
|
||||
//If dragging a selection box, add nodes inside to selection
|
||||
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
|
||||
|
||||
BIN
Editor/Resources/xnode_group.png
Normal file
BIN
Editor/Resources/xnode_group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
124
Editor/Resources/xnode_group.png.meta
Normal file
124
Editor/Resources/xnode_group.png.meta
Normal 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
12
Runtime/GroupNode.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
3
Runtime/GroupNode.cs.meta
Normal file
3
Runtime/GroupNode.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39b1316a0b0f4e389d24c32de475b0cd
|
||||
timeCreated: 1673303110
|
||||
Loading…
x
Reference in New Issue
Block a user