diff --git a/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs b/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs index 492983e580e..78e7994e631 100644 --- a/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs +++ b/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs @@ -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("Icons/sg_subgraph_icon_gray" + theme); - else - icon = Resources.Load("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(path); + } + void OnDestroy() { // Prompting the user if they want to close is mostly handled via the EditorWindow's system (hasUnsavedChanges). diff --git a/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs b/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs index a4331f6e495..2d71f2f690c 100644 --- a/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs +++ b/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using UnityEngine; using UnityEditor.Graphing; @@ -415,6 +414,8 @@ void CreateMasterPreview() m_MasterPreviewView.previewResizeBorderFrame.OnResizeFinished += UpdateSerializedWindowLayout; } + const string kSelectionKey = "Unity.ShaderGraphHistory"; + void CreateInspector() { var inspectorViewModel = new InspectorViewModel() { parentView = this.graphView }; @@ -422,6 +423,66 @@ void CreateInspector() 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 selectionList) + { + if (s_ApplyingSelection || m_GraphView == null || m_GraphView.graph == null) + return; + var info = new GraphSelection(); + var selNames = new List(); + 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) diff --git a/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXAttachPanel.cs b/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXAttachPanel.cs index 53ffa7bb1b7..972e659a8e2 100644 --- a/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXAttachPanel.cs +++ b/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXAttachPanel.cs @@ -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 { diff --git a/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs b/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs index 9d9764d7465..80c5f7a7cf6 100644 --- a/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs +++ b/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Reflection; - using UnityEditor.Experimental; using UnityEditor.Experimental.GraphView; @@ -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; @@ -271,6 +270,8 @@ void DisconnectController(VFXViewController previousController) SceneView.duringSceneGui -= OnSceneGUI; } + const string kSelectionKey = "Unity.VisualEffectGraphHistory"; + void ConnectController() { schedule.Execute(() => @@ -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 GetAcceptedTypeNodes() { if (!controller.model.isSubgraph) @@ -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; @@ -1986,22 +2011,39 @@ public void UpdateGlobalSelection() { if (controller == null) return; - var objectSelected = selection.OfType().Select(t => t.controller.model).Concat(selection.OfType().Select(t => t.controller.model).Cast()).Where(t => t != null).ToArray(); - - if (objectSelected.Length > 0) + var toSelect = new List(); + var sel = new GraphSelection(); + var selNames = new List(); + 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().controller.model; + if (obj != null) + { + toSelect.Add(obj); + sel.elements.Add(field.viewDataKey); + selNames.Add(obj.exposedName); + } + } } - var blackBoardSelected = selection.OfType().Select(t => t.GetFirstAncestorOfType().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()