Skip to content

[editor] Handle selection history in Shader Graph and VFX Graph (WIP) #6695

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -487,20 +487,19 @@ public void UpdateTitle()
title = title + " (deleted)";
}

// get window icon
bool isSubGraph = graphObject?.graph?.isSubGraph ?? false;
Texture2D icon;
{
string theme = EditorGUIUtility.isProSkin ? "_dark" : "_light";
if (isSubGraph)
icon = Resources.Load<Texture2D>("Icons/sg_subgraph_icon_gray" + theme);
else
icon = Resources.Load<Texture2D>("Icons/sg_graph_icon_gray" + theme);
}

Texture2D icon = LoadWindowIcon(isSubGraph);
titleContent = new GUIContent(title, icon);
}

internal static Texture2D LoadWindowIcon(bool isSubGraph)
{
var theme = EditorGUIUtility.isProSkin ? "dark" : "light";
var kind = isSubGraph ? "subgraph" : "graph";
var path = $"Icons/sg_{kind}_icon_gray_{theme}";
return Resources.Load<Texture2D>(path);
}

void OnDestroy()
{
// Prompting the user if they want to close is mostly handled via the EditorWindow's system (hasUnsavedChanges).
Expand Down
63 changes: 62 additions & 1 deletion com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEditor.Graphing;
Expand Down Expand Up @@ -415,13 +414,75 @@ void CreateMasterPreview()
m_MasterPreviewView.previewResizeBorderFrame.OnResizeFinished += UpdateSerializedWindowLayout;
}

const string kSelectionKey = "Unity.ShaderGraphHistory";

void CreateInspector()
{
var inspectorViewModel = new InspectorViewModel() { parentView = this.graphView };
m_InspectorView = new InspectorView(inspectorViewModel);
graphView.OnSelectionChange += m_InspectorView.TriggerInspectorUpdate;
// Undo/redo actions that only affect selection don't trigger the above callback for some reason, so we also have to do this
Undo.undoRedoPerformed += (() => { m_InspectorView?.TriggerInspectorUpdate(graphView?.selection); });

graphView.OnSelectionChange += RecordSelectionHistory;
Selection.RegisterCustomHandler(kSelectionKey, CustomSelectionHandler, MaterialGraphEditWindow.LoadWindowIcon(false));
}

static bool s_ApplyingSelection;
static void CustomSelectionHandler(string data, Object active, Object[] selected)
{
var info = GraphSelection.FromJson(data);
if (info == null)
return;

var window = EditorWindow.focusedWindow as MaterialGraphEditWindow;
var gv = window?.graphEditorView?.m_GraphView;
if (gv == null)
return;

// apply selection to that graph view
s_ApplyingSelection = true;
info.ApplyToGraphView(gv, null);
window.graphEditorView.m_InspectorView.TriggerInspectorUpdate(gv.selection);
s_ApplyingSelection = false;
}

void RecordSelectionHistory(IEnumerable<ISelectable> selectionList)
{
if (s_ApplyingSelection || m_GraphView == null || m_GraphView.graph == null)
return;
var info = new GraphSelection();
var selNames = new List<string>();
var numEdges = 0;
foreach (var sel in selectionList)
{
if (sel is not GraphElement e)
continue;
// In some cases (e.g. window is being hidden), elements are removed from selection,
// but selection changed callback is invoked with the old selection set. The elements
// themselves are un-selected already though. Skip over those.
if (!e.selected)
continue;
info.elements.Add(e.viewDataKey);
// Figure out a suitable string name to show in history UI
if (e is Node node)
selNames.Add(node is IShaderNodeView nv ? nv.node.name : node.GetType().Name);
else if (e is SGBlackboardField field)
selNames.Add(field.text);
else if (e is StickyNote sn)
selNames.Add(sn.title);
else if (e is Edge)
++numEdges;
}
if (info.empty)
return;

if (selNames.Count > 3) selNames = selNames.Take(3).Append("...").ToList();
if (numEdges > 0)
selNames.Add($"{numEdges} edges");

var label = string.Join(", ", selNames);
Selection.SetCustomSelection(kSelectionKey, label, EditorJsonUtility.ToJson(info));
}

void OnKeyDown(KeyDownEvent evt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ void UpdateAttachedLabel()
{
m_VFXIcon.style.display = DisplayStyle.Flex;
m_pickedObjectLabel[0].style.paddingLeft = 18;
m_VFXIcon.style.backgroundImage = VFXView.LoadImage(EditorGUIUtility.isProSkin ? "vfx_graph_icon_gray_dark" : "vfx_graph_icon_gray_light");
m_VFXIcon.style.backgroundImage = VFXView.LoadWindowIcon();
}
else
{
Expand Down
72 changes: 57 additions & 15 deletions com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.IO;
using System.Linq;
using System.Reflection;

using UnityEditor.Experimental;

using UnityEditor.Experimental.GraphView;
Expand All @@ -17,7 +16,7 @@
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEngine.Profiling;

using Object = UnityEngine.Object;
using PositionType = UnityEngine.UIElements.Position;
using Task = UnityEditor.VersionControl.Task;

Expand Down Expand Up @@ -271,6 +270,8 @@ void DisconnectController(VFXViewController previousController)
SceneView.duringSceneGui -= OnSceneGUI;
}

const string kSelectionKey = "Unity.VisualEffectGraphHistory";

void ConnectController()
{
schedule.Execute(() =>
Expand Down Expand Up @@ -310,8 +311,27 @@ void ConnectController()
}

SceneView.duringSceneGui += OnSceneGUI;
Selection.RegisterCustomHandler(kSelectionKey, CustomSelectionHandler, LoadWindowIcon());
}

static void CustomSelectionHandler(string data, Object active, Object[] selected)
{
var info = GraphSelection.FromJson(data);
if (info == null)
return;

var window = EditorWindow.focusedWindow as VFXViewWindow;
var gv = window?.graphView;
if (gv?.controller == null)
return;

// apply selection
info.ApplyToGraphView(gv, gv.blackboard);
Selection.activeObject = active;
Selection.objects = selected;
}


IEnumerable<Type> GetAcceptedTypeNodes()
{
if (!controller.model.isSubgraph)
Expand Down Expand Up @@ -489,10 +509,15 @@ public static VisualTreeAsset LoadUXML(string text)

public static Texture2D LoadImage(string text)
{
string path = string.Format("{0}/VFX/{1}.png", VisualEffectAssetEditorUtility.editorResourcesPath, text);
string path = $"{VisualEffectAssetEditorUtility.editorResourcesPath}/VFX/{text}.png";
return EditorGUIUtility.LoadIcon(path);
}

internal static Texture2D LoadWindowIcon()
{
return LoadImage(EditorGUIUtility.isProSkin ? "vfx_graph_icon_gray_dark" : "vfx_graph_icon_gray_light");
}

SelectionDragger m_SelectionDragger;
RectangleSelector m_RectangleSelector;

Expand Down Expand Up @@ -1986,22 +2011,39 @@ public void UpdateGlobalSelection()
{
if (controller == null) return;

var objectSelected = selection.OfType<VFXNodeUI>().Select(t => t.controller.model).Concat(selection.OfType<VFXContextUI>().Select(t => t.controller.model).Cast<VFXModel>()).Where(t => t != null).ToArray();

if (objectSelected.Length > 0)
var toSelect = new List<Object>();
var sel = new GraphSelection();
var selNames = new List<string>();
foreach (var s in selection)
{
Selection.objects = objectSelected;
Selection.objects = objectSelected;
return;
if (s is VFXNodeUI node)
{
var obj = node.controller.model;
if (obj != null)
{
toSelect.Add(obj);
sel.elements.Add(node.viewDataKey);
selNames.Add(!string.IsNullOrEmpty(obj.name) ? obj.name : obj.GetType().Name);
}
}
else if (s is BlackboardField field)
{
var obj = field.GetFirstAncestorOfType<VFXBlackboardRow>().controller.model;
if (obj != null)
{
toSelect.Add(obj);
sel.elements.Add(field.viewDataKey);
selNames.Add(obj.exposedName);
}
}
}

var blackBoardSelected = selection.OfType<BlackboardField>().Select(t => t.GetFirstAncestorOfType<VFXBlackboardRow>().controller.model).ToArray();

if (blackBoardSelected.Length > 0)
{
Selection.objects = blackBoardSelected;
if (sel.empty)
return;
}

if (selNames.Count > 3) selNames = selNames.Take(3).Append("...").ToList();
var label = string.Join(", ", selNames);
Selection.SetCustomSelection(kSelectionKey, label, EditorJsonUtility.ToJson(sel), null, toSelect.ToArray());
}

internal void SelectAsset()
Expand Down