using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEditor.Experimental.GraphView; using UnityEditor.ShaderGraph.Drawing; using UnityEditor.ShortcutManagement; using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.ShaderGraph { static class ShaderGraphShortcuts { static MaterialGraphEditWindow GetFocusedShaderGraphEditorWindow() { return EditorWindow.focusedWindow as MaterialGraphEditWindow; } static GraphEditorView GetGraphEditorView() { return GetFocusedShaderGraphEditorWindow().graphEditorView; } static MaterialGraphView GetGraphView() { return GetGraphEditorView().graphView; } static bool GetMousePositionIsInGraphView(out Vector2 pos) { pos = default; var graphView = GetGraphView(); var windowRoot = GetFocusedShaderGraphEditorWindow().rootVisualElement; var windowMousePosition = windowRoot.ChangeCoordinatesTo(windowRoot.parent, graphView.cachedMousePosition); if (!graphView.worldBound.Contains(windowMousePosition)) return false; // don't create nodes if they aren't on the graph view. pos = graphView.contentViewContainer.WorldToLocal(graphView.cachedMousePosition); return true; } static void CreateNode() where T : AbstractMaterialNode { if (!GetMousePositionIsInGraphView(out var graphMousePosition)) return; var positionRect = new Rect(graphMousePosition, Vector2.zero); var graphView = GetGraphView(); var graph = graphView.graph; AbstractMaterialNode node = Activator.CreateInstance(); var drawState = node.drawState; drawState.position = positionRect; node.drawState = drawState; graph.owner.RegisterCompleteObjectUndo("Add " + node.name); graphView.graph.AddNode(node); } static HashSet<(KeyCode key, ShortcutModifiers modifier)> reservedShortcuts = new HashSet<(KeyCode key, ShortcutModifiers modifier)> { (KeyCode.A, ShortcutModifiers.None), // Frame All (KeyCode.F, ShortcutModifiers.None), // Frame Selection (KeyCode.Space, ShortcutModifiers.None), // Summon Searcher (for node creation) (KeyCode.C, ShortcutModifiers.Action), // Copy (KeyCode.X, ShortcutModifiers.Action), // cut (KeyCode.V, ShortcutModifiers.Action), // Paste (KeyCode.Z, ShortcutModifiers.Action), // Undo (KeyCode.Y, ShortcutModifiers.Action), // Redo (KeyCode.D, ShortcutModifiers.Action), // Duplicate }; static void CheckBindings(string name) { if (!ShortcutManager.instance.IsShortcutOverridden(name)) return; var customBinding = ShortcutManager.instance.GetShortcutBinding(name); foreach(var keyCombo in customBinding.keyCombinationSequence) { if (reservedShortcuts.Contains((keyCombo.keyCode, keyCombo.modifiers))) { throw new Exception($"The binding for {name} ({keyCombo}) conflicts with a built-in shortcut. Please go to Edit->Shortcuts... and change the binding."); } } } internal static string GetKeycodeForContextMenu(string id) { const string kKeycodePrefixAlt = "&"; const string kKeycodePrefixShift = "#"; const string kKeycodePrefixAction = "%"; const string kKeycodePrefixControl = "^"; const string kKeycodePrefixNoModifier = "_"; var binding = ShortcutManager.instance.GetShortcutBinding(id); foreach (var keyCombo in binding.keyCombinationSequence) { var sb = new StringBuilder(); if (keyCombo.alt) sb.Append(kKeycodePrefixAlt); if (keyCombo.shift) sb.Append(kKeycodePrefixShift); if (keyCombo.action) sb.Append(kKeycodePrefixAction); if (keyCombo.control) sb.Append(kKeycodePrefixControl); if (keyCombo.modifiers == ShortcutModifiers.None) sb.Append(kKeycodePrefixNoModifier); sb.Append(keyCombo.keyCode); return sb.ToString(); } return ""; } #region File [Shortcut("ShaderGraph/File: Save", typeof(MaterialGraphEditWindow), KeyCode.S, ShortcutModifiers.Action)] static void Save(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); GetFocusedShaderGraphEditorWindow().SaveAsset(); } [Shortcut("ShaderGraph/File: Save As...", typeof(MaterialGraphEditWindow), KeyCode.S, ShortcutModifiers.Action | ShortcutModifiers.Shift)] static void SaveAs(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); GetFocusedShaderGraphEditorWindow().SaveAs(); } [Shortcut("ShaderGraph/File: Close Tab", typeof(MaterialGraphEditWindow), KeyCode.F4, ShortcutModifiers.Action)] static void CloseTab(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); var editorWindow = GetFocusedShaderGraphEditorWindow(); if (editorWindow.PromptSaveIfDirtyOnQuit()) editorWindow.Close(); } #endregion #region Toolbar [Shortcut("ShaderGraph/Toolbar: Toggle Blackboard", typeof(MaterialGraphEditWindow), KeyCode.Alpha1, ShortcutModifiers.Shift)] static void ToggleBlackboard(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); var graphEditor = GetGraphEditorView(); graphEditor.viewSettings.isBlackboardVisible = !graphEditor.viewSettings.isBlackboardVisible; graphEditor.UserViewSettingsChangeCheck(graphEditor.colorManager.activeIndex); } [Shortcut("ShaderGraph/Toolbar: Toggle Inspector", typeof(MaterialGraphEditWindow), KeyCode.Alpha2, ShortcutModifiers.Shift)] static void ToggleInspector(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); var graphEditor = GetGraphEditorView(); graphEditor.viewSettings.isInspectorVisible = !graphEditor.viewSettings.isInspectorVisible; graphEditor.UserViewSettingsChangeCheck(graphEditor.colorManager.activeIndex); } [Shortcut("ShaderGraph/Toolbar: Toggle Main Preview", typeof(MaterialGraphEditWindow), KeyCode.Alpha3, ShortcutModifiers.Shift)] static void ToggleMainPreview(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); var graphEditor = GetGraphEditorView(); graphEditor.viewSettings.isPreviewVisible = !graphEditor.viewSettings.isPreviewVisible; graphEditor.UserViewSettingsChangeCheck(graphEditor.colorManager.activeIndex); } [Shortcut("ShaderGraph/Toolbar: Cycle Color Mode", typeof(MaterialGraphEditWindow), KeyCode.Alpha4, ShortcutModifiers.Shift)] static void CycleColorMode(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); var graphEditor = GetGraphEditorView(); var nextIndex = graphEditor.colorManager.activeIndex + 1; if (nextIndex >= graphEditor.colorManager.providersCount) nextIndex = 0; graphEditor.UserViewSettingsChangeCheck(nextIndex); } #endregion #region Selection internal const string summonDocumentationShortcutID = "ShaderGraph/Selection: Summon Documentation"; [Shortcut(summonDocumentationShortcutID, typeof(MaterialGraphEditWindow), KeyCode.F1)] static void Documentation(ShortcutArguments args) { CheckBindings(summonDocumentationShortcutID); foreach (var selected in GetGraphView().selection) if (selected is IShaderNodeView nodeView && nodeView.node.documentationURL != null) { System.Diagnostics.Process.Start(nodeView.node.documentationURL); break; } } internal const string nodeGroupShortcutID = "ShaderGraph/Selection: Node Group"; [Shortcut(nodeGroupShortcutID, typeof(MaterialGraphEditWindow), KeyCode.G, ShortcutModifiers.Action)] static void Group(ShortcutArguments args) { CheckBindings(nodeGroupShortcutID); var graphView = GetGraphView(); foreach(var selected in graphView.selection) if ((selected is IShaderNodeView nodeView && nodeView.node is AbstractMaterialNode) || selected.GetType() == typeof(Drawing.StickyNote)) { graphView.GroupSelection(); break; } } internal const string nodeUnGroupShortcutID = "ShaderGraph/Selection: Node Ungroup"; [Shortcut(nodeUnGroupShortcutID, typeof(MaterialGraphEditWindow), KeyCode.U, ShortcutModifiers.Action)] static void UnGroup(ShortcutArguments args) { CheckBindings(nodeUnGroupShortcutID); var graphView = GetGraphView(); foreach (var selected in graphView.selection) if ((selected is IShaderNodeView nodeView && nodeView.node is AbstractMaterialNode) || selected.GetType() == typeof(Drawing.StickyNote)) { graphView.RemoveFromGroupNode(); break; } } internal const string nodePreviewShortcutID = "ShaderGraph/Selection: Toggle Node Previews"; [Shortcut(nodePreviewShortcutID, typeof(MaterialGraphEditWindow), KeyCode.T, ShortcutModifiers.Action)] static void ToggleNodePreviews(ShortcutArguments args) { CheckBindings(nodePreviewShortcutID); bool shouldHide = false; // Toggle all node previews if none are selected. Otherwise, update only the selected node previews. var selection = GetGraphView().selection; if (selection.Count == 0) { var graph = GetGraphView().graph; var nodes = graph.GetNodes(); foreach (AbstractMaterialNode node in nodes) if (node.previewExpanded && node.hasPreview) { shouldHide = true; break; } graph.owner.RegisterCompleteObjectUndo("Toggle Previews"); foreach (AbstractMaterialNode node in nodes) node.previewExpanded = !shouldHide; } else { foreach (var selected in selection) if (selected is IShaderNodeView nodeView) { if (nodeView.node.previewExpanded && nodeView.node.hasPreview) { shouldHide = true; break; } } GetGraphView().SetPreviewExpandedForSelectedNodes(!shouldHide); } } internal const string nodeCollapsedShortcutID = "ShaderGraph/Selection: Toggle Node Collapsed"; [Shortcut(nodeCollapsedShortcutID, typeof(MaterialGraphEditWindow), KeyCode.P, ShortcutModifiers.Action)] static void ToggleNodeCollapsed(ShortcutArguments args) { CheckBindings(nodeCollapsedShortcutID); bool shouldCollapse = false; foreach (var selected in GetGraphView().selection) if (selected is MaterialNodeView nodeView) { if (nodeView.expanded && nodeView.CanToggleNodeExpanded()) { shouldCollapse = true; break; } } GetGraphView().SetNodeExpandedForSelectedNodes(!shouldCollapse); } internal const string createRedirectNodeShortcutID = "ShaderGraph/Selection: Insert Redirect"; [Shortcut(createRedirectNodeShortcutID, typeof(MaterialGraphEditWindow), KeyCode.R, ShortcutModifiers.Action)] static void InsertRedirect(ShortcutArguments args) { CheckBindings(createRedirectNodeShortcutID); if (!GetMousePositionIsInGraphView(out var graphMousePosition)) return; foreach (var selected in GetGraphView().selection) { if (selected is Edge edge) { int weight = 1; var pos = graphMousePosition * weight; int count = weight; foreach(var cp in edge.edgeControl.controlPoints) { pos += cp; count++; } pos /= count; pos = GetGraphView().contentViewContainer.LocalToWorld(pos); GetGraphView().CreateRedirectNode(pos, edge); } } } #endregion #region Add Specific Node [Shortcut("ShaderGraph/Add Node: Lerp", typeof(MaterialGraphEditWindow), KeyCode.L, ShortcutModifiers.Alt)] static void CreateLerp(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Multiply", typeof(MaterialGraphEditWindow), KeyCode.M, ShortcutModifiers.Alt)] static void CreateMultiply(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Add", typeof(MaterialGraphEditWindow), KeyCode.A, ShortcutModifiers.Alt)] static void CreateAdd(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Sample Texture 2D", typeof(MaterialGraphEditWindow), KeyCode.X, ShortcutModifiers.Alt)] static void CreateSampleTexture2D(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Float", typeof(MaterialGraphEditWindow), KeyCode.Alpha1, ShortcutModifiers.Alt)] static void CreateFloat(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Vector2", typeof(MaterialGraphEditWindow), KeyCode.Alpha2, ShortcutModifiers.Alt)] static void CreateVec2(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Vector3", typeof(MaterialGraphEditWindow), KeyCode.Alpha3, ShortcutModifiers.Alt)] static void CreateVec3(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Vector4", typeof(MaterialGraphEditWindow), KeyCode.Alpha4, ShortcutModifiers.Alt)] static void CreateVec4(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Split", typeof(MaterialGraphEditWindow), KeyCode.E, ShortcutModifiers.Alt)] static void CreateSplit(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Tiling and Offset", typeof(MaterialGraphEditWindow), KeyCode.O, ShortcutModifiers.Alt)] static void CreateTilingAndOffset(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Time", typeof(MaterialGraphEditWindow), KeyCode.T, ShortcutModifiers.Alt)] static void CreateTime(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Position", typeof(MaterialGraphEditWindow), KeyCode.V, ShortcutModifiers.Alt)] static void CreatePosition(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Subtract", typeof(MaterialGraphEditWindow), KeyCode.S, ShortcutModifiers.Alt)] static void CreateSubtract(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: UV", typeof(MaterialGraphEditWindow), KeyCode.U, ShortcutModifiers.Alt)] static void CreateUV(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: One Minus", typeof(MaterialGraphEditWindow), KeyCode.I, ShortcutModifiers.Alt)] static void CreateOneMinus(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Branch", typeof(MaterialGraphEditWindow), KeyCode.Y, ShortcutModifiers.Alt)] static void CreateBranch(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Divide", typeof(MaterialGraphEditWindow), KeyCode.D, ShortcutModifiers.Alt)] static void CreateDivide(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Combine", typeof(MaterialGraphEditWindow), KeyCode.K, ShortcutModifiers.Alt)] static void CreateCombine(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Power", typeof(MaterialGraphEditWindow), KeyCode.P, ShortcutModifiers.Alt)] static void CreatePower(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Saturate", typeof(MaterialGraphEditWindow), KeyCode.Q, ShortcutModifiers.Alt)] static void CreateSaturate(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Remap", typeof(MaterialGraphEditWindow), KeyCode.R, ShortcutModifiers.Alt)] static void CreateRemap(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Normal Vector", typeof(MaterialGraphEditWindow), KeyCode.N, ShortcutModifiers.Alt)] static void CreateNormalVector(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Color", typeof(MaterialGraphEditWindow), KeyCode.C, ShortcutModifiers.Alt)] static void CreateColor(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Blend", typeof(MaterialGraphEditWindow), KeyCode.B, ShortcutModifiers.Alt)] static void CreateBlend(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Step", typeof(MaterialGraphEditWindow), KeyCode.J, ShortcutModifiers.Alt)] static void CreateStep(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Clamp", typeof(MaterialGraphEditWindow), KeyCode.Equals, ShortcutModifiers.Alt)] static void CreateClamp(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Smoothstep", typeof(MaterialGraphEditWindow), KeyCode.BackQuote, ShortcutModifiers.Alt)] static void CreateSmoothstep(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Fresnel", typeof(MaterialGraphEditWindow), KeyCode.F, ShortcutModifiers.Alt)] static void CreateFresnel(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Custom Function", typeof(MaterialGraphEditWindow), KeyCode.Semicolon, ShortcutModifiers.Alt)] static void CreateCFN(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Dot Product", typeof(MaterialGraphEditWindow), KeyCode.Period, ShortcutModifiers.Alt)] static void CreateDotProduct(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Normalize", typeof(MaterialGraphEditWindow), KeyCode.Z, ShortcutModifiers.Alt)] static void CreateNormalize(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Absolute", typeof(MaterialGraphEditWindow), KeyCode.Backslash, ShortcutModifiers.Alt)] static void CreateAbsolute(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Negate", typeof(MaterialGraphEditWindow), KeyCode.Minus, ShortcutModifiers.Alt)] static void CreateNegate(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Fraction", typeof(MaterialGraphEditWindow), KeyCode.Slash, ShortcutModifiers.Alt)] static void CreateFraction(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Swizzle", typeof(MaterialGraphEditWindow), KeyCode.W, ShortcutModifiers.Alt)] static void CreateSwizzle(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Gradient", typeof(MaterialGraphEditWindow), KeyCode.G, ShortcutModifiers.Alt)] static void CreateGradient(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Cross Product", typeof(MaterialGraphEditWindow), KeyCode.H, ShortcutModifiers.Alt)] static void CreateCrossProduct(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Boolean", typeof(MaterialGraphEditWindow), KeyCode.Alpha0, ShortcutModifiers.Alt)] static void CreateBoolean(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Floor", typeof(MaterialGraphEditWindow), KeyCode.LeftBracket, ShortcutModifiers.Alt)] static void CreateFloor(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } [Shortcut("ShaderGraph/Add Node: Ceiling", typeof(MaterialGraphEditWindow), KeyCode.RightBracket, ShortcutModifiers.Alt)] static void CreateCeiling(ShortcutArguments args) { CheckBindings(MethodInfo.GetCurrentMethod().GetCustomAttribute().displayName); CreateNode(); } #endregion } }