/// <inheritdoc /> public override void OnDestroy() { if (IsDisposing) { return; } if (_cmStateMachineMenu != null) { _cmStateMachineMenu.Dispose(); _cmStateMachineMenu = null; } if (_cmStateMachineTransitionMenu != null) { _cmStateMachineTransitionMenu.Dispose(); _cmStateMachineTransitionMenu = null; } if (_isRegisteredForScriptsReload) { _isRegisteredForScriptsReload = false; ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; } NodesCache.Wait(); base.OnDestroy(); }
/// <inheritdoc /> protected override void OnContextChanged() { base.OnContextChanged(); VisjectCM menu = null; // Override surface primary context menu for state machine editing if (Context?.Context is Animation.StateMachine) { if (_cmStateMachineMenu == null) { _cmStateMachineMenu = new VisjectCM(StateMachineGroupArchetypes, (arch) => true); _cmStateMachineMenu.ShowExpanded = true; } menu = _cmStateMachineMenu; } // Override surface primary context menu for state machine transition editing if (Context?.Context is Animation.StateMachineTransition) { if (_cmStateMachineTransitionMenu == null) { _cmStateMachineTransitionMenu = new VisjectCM(NodeFactory.DefaultGroups, CanSpawnNodeType, null, GetCustomNodes()); _cmStateMachineTransitionMenu.AddGroup(StateMachineTransitionGroupArchetype); } menu = _cmStateMachineTransitionMenu; } SetPrimaryMenu(menu); }
/// <inheritdoc /> protected override void OnShowPrimaryMenu(VisjectCM activeCM, Vector2 location, Box startBox) { Profiler.BeginEvent("Setup Anim Graph Context Menu"); NodesCache.Get(activeCM); Profiler.EndEvent(); base.OnShowPrimaryMenu(activeCM, location, startBox); activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged; }
public static void Get(VisjectCM contextMenu) { Wait(); lock (_locker) { if (_cache == null) { _cache = new Dictionary <KeyValuePair <string, ushort>, GroupArchetype>(); } contextMenu.LockChildrenRecursive(); // Check if has cached groups if (_cache.Count != 0) { // Check if context menu doesn't have the recent cached groups if (!contextMenu.Groups.Any(g => g.Archetype.Tag is int asInt && asInt == _version)) { var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray(); foreach (var g in groups) { contextMenu.RemoveGroup(g); } foreach (var g in _cache.Values) { contextMenu.AddGroup(g); } } } else { // Remove any old groups from context menu var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray(); foreach (var g in groups) { contextMenu.RemoveGroup(g); } // Register for scripting types reload Editor.Instance.CodeEditing.TypesCleared += OnCodeEditingTypesCleared; // Run caching on an async _task = Task.Run(OnActiveContextMenuShowAsync); _taskContextMenu = contextMenu; } contextMenu.UnlockChildrenRecursive(); } }
/// <inheritdoc /> public override void Dispose() { if (_cmStateMachineMenu != null) { _cmStateMachineMenu.Dispose(); _cmStateMachineMenu = null; } if (_cmStateMachineTransitionMenu != null) { _cmStateMachineTransitionMenu.Dispose(); _cmStateMachineTransitionMenu = null; } base.Dispose(); }
/// <inheritdoc /> protected override void OnShowPrimaryMenu(VisjectCM activeCM, Vector2 location, Box startBox) { // Check if show additional nodes in the current surface context if (activeCM != _cmStateMachineMenu) { Profiler.BeginEvent("Setup Anim Graph Context Menu"); NodesCache.Get(activeCM); Profiler.EndEvent(); base.OnShowPrimaryMenu(activeCM, location, startBox); activeCM.VisibleChanged += OnActiveContextMenuVisibleChanged; } else { base.OnShowPrimaryMenu(activeCM, location, startBox); } }
/// <inheritdoc /> public override void Dispose() { if (_cmStateMachineMenu != null) { _cmStateMachineMenu.Dispose(); _cmStateMachineMenu = null; } if (_cmStateMachineTransitionMenu != null) { _cmStateMachineTransitionMenu.Dispose(); _cmStateMachineTransitionMenu = null; } if (_isRegisteredForScriptsReload) { _isRegisteredForScriptsReload = false; ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; } base.Dispose(); }
/// <inheritdoc /> protected override void OnContextChanged() { base.OnContextChanged(); VisjectCM menu = null; // Override surface primary context menu for state machine editing if (Context?.Context is Animation.StateMachine) { if (_cmStateMachineMenu == null) { _cmStateMachineMenu = new VisjectCM(new VisjectCM.InitInfo { Groups = StateMachineGroupArchetypes, CanSpawnNode = arch => true, }); _cmStateMachineMenu.ShowExpanded = true; } menu = _cmStateMachineMenu; } // Override surface primary context menu for state machine transition editing if (Context?.Context is Animation.StateMachineTransition) { if (_cmStateMachineTransitionMenu == null) { _cmStateMachineTransitionMenu = new VisjectCM(new VisjectCM.InitInfo { Groups = NodeFactory.DefaultGroups, CanSpawnNode = CanUseNodeType, ParametersGetter = null, CustomNodesGroup = GetCustomNodes(), }); _cmStateMachineTransitionMenu.AddGroup(StateMachineTransitionGroupArchetype); } menu = _cmStateMachineTransitionMenu; } SetPrimaryMenu(menu); }
/// <summary> /// Sets the primary menu for the Visject nodes spawning. Can be overriden per surface or surface context. Set to null to restore the default menu. /// </summary> /// <param name="menu">The menu to override with (use null if restore the default value).</param> protected virtual void SetPrimaryMenu(VisjectCM menu) { menu = menu ?? _cmPrimaryMenu; if (menu == _activeVisjectCM) { return; } if (_activeVisjectCM != null) { _activeVisjectCM.ItemClicked -= OnPrimaryMenuButtonClick; _activeVisjectCM.VisibleChanged -= OnPrimaryMenuVisibleChanged; } _activeVisjectCM = menu; if (_activeVisjectCM != null) { _activeVisjectCM.ItemClicked += OnPrimaryMenuButtonClick; _activeVisjectCM.VisibleChanged += OnPrimaryMenuVisibleChanged; } }
private static void OnActiveContextMenuShowAsync() { Profiler.BeginEvent("Setup Anim Graph Context Menu (async)"); foreach (var scriptType in Editor.Instance.CodeEditing.All.Get()) { if (!SurfaceUtils.IsValidVisualScriptType(scriptType)) { continue; } // Skip Newtonsoft.Json stuff var scriptTypeTypeName = scriptType.TypeName; if (scriptTypeTypeName.StartsWith("Newtonsoft.Json.")) { continue; } var scriptTypeName = scriptType.Name; // Enum if (scriptType.IsEnum) { // Create node archetype var node = (NodeArchetype)Archetypes.Constants.Nodes[10].Clone(); node.DefaultValues[0] = Activator.CreateInstance(scriptType.Type); node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = scriptTypeName; node.Description = Editor.Instance.CodeDocs.GetTooltip(scriptType); // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 2); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(243, 156, 18), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } // Add node to the group ((IList <NodeArchetype>)group.Archetypes).Add(node); continue; } // Structure if (scriptType.IsValueType) { if (scriptType.IsVoid) { continue; } // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 4); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(155, 89, 182), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } var tooltip = Editor.Instance.CodeDocs.GetTooltip(scriptType); // Create Pack node archetype var node = (NodeArchetype)Archetypes.Packing.Nodes[6].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Pack " + scriptTypeName; node.Description = tooltip; ((IList <NodeArchetype>)group.Archetypes).Add(node); // Create Unpack node archetype node = (NodeArchetype)Archetypes.Packing.Nodes[13].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Unpack " + scriptTypeName; node.Description = tooltip; ((IList <NodeArchetype>)group.Archetypes).Add(node); } } // Add group to context menu (on a main thread) FlaxEngine.Scripting.InvokeOnUpdate(() => { lock (_locker) { _taskContextMenu.AddGroups(_cache.Values); _taskContextMenu = null; } }); Profiler.EndEvent(); lock (_locker) { _task = null; } }
/// <summary> /// Called when showing primary context menu. /// </summary> /// <param name="activeCM">The active context menu to show.</param> /// <param name="location">The display location on the surface control.</param> /// <param name="startBox">The start box.</param> protected virtual void OnShowPrimaryMenu(VisjectCM activeCM, Vector2 location, Box startBox) { activeCM.Show(this, location, startBox); }
private static void OnActiveContextMenuShowAsync() { Profiler.BeginEvent("Setup Visual Script Context Menu (async)"); #if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING var searchStartTime = DateTime.Now; var searchHitsCount = 0; #endif foreach (var scriptType in Editor.Instance.CodeEditing.All.Get()) { if (!SurfaceUtils.IsValidVisualScriptType(scriptType)) { continue; } // Skip Newtonsoft.Json stuff var scriptTypeTypeName = scriptType.TypeName; if (scriptTypeTypeName.StartsWith("Newtonsoft.Json.")) { continue; } var scriptTypeName = scriptType.Name; // Enum if (scriptType.IsEnum) { // Create node archetype var node = (NodeArchetype)Archetypes.Constants.Nodes[10].Clone(); node.DefaultValues[0] = Activator.CreateInstance(scriptType.Type); node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = scriptTypeName; node.Description = scriptTypeTypeName; var attributes = scriptType.GetAttributes(false); var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); if (tooltipAttribute != null) { node.Description += "\n" + tooltipAttribute.Text; } // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 2); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(243, 156, 18), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } // Add node to the group ((IList <NodeArchetype>)group.Archetypes).Add(node); continue; } // Structure if (scriptType.IsValueType) { if (scriptType.IsVoid) { continue; } // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 4); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(155, 89, 182), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } var attributes = scriptType.GetAttributes(false); var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); // Create Pack node archetype var node = (NodeArchetype)Archetypes.Packing.Nodes[6].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Pack " + scriptTypeName; node.Description = scriptTypeTypeName; if (tooltipAttribute != null) { node.Description += "\n" + tooltipAttribute.Text; } ((IList <NodeArchetype>)group.Archetypes).Add(node); // Create Unpack node archetype node = (NodeArchetype)Archetypes.Packing.Nodes[13].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Unpack " + scriptTypeName; node.Description = scriptTypeTypeName; if (tooltipAttribute != null) { node.Description += "\n" + tooltipAttribute.Text; } ((IList <NodeArchetype>)group.Archetypes).Add(node); } foreach (var member in scriptType.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) { if (member.IsGeneric) { continue; } if (member.IsMethod) { // Skip methods not declared in this type if (member.Type is MethodInfo m && m.GetBaseDefinition().DeclaringType != m.DeclaringType) { continue; } var name = member.Name; if (name == "ToString") { continue; } // Skip if searching by name doesn't return a match var members = scriptType.GetMembers(name, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); if (!members.Contains(member)) { continue; } // Check if method is valid for Visual Script usage if (SurfaceUtils.IsValidVisualScriptInvokeMethod(member, out var parameters)) { // Create node archetype var node = (NodeArchetype)Archetypes.Function.Nodes[3].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.DefaultValues[1] = name; node.DefaultValues[2] = parameters.Length; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = SurfaceUtils.GetMethodDisplayName((string)node.DefaultValues[1]); node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); node.SubTitle = string.Format(" (in {0})", scriptTypeName); node.Tag = member; // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 16); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(109, 160, 24), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } // Add node to the group ((IList <NodeArchetype>)group.Archetypes).Add(node); #if DEBUG_INVOKE_METHODS_SEARCHING Editor.LogWarning(scriptTypeTypeName + " -> " + member.GetSignature()); searchHitsCount++; #endif } } else if (member.IsField) { var name = member.Name; // Skip if searching by name doesn't return a match var members = scriptType.GetMembers(name, MemberTypes.Field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); if (!members.Contains(member)) { continue; } // Check if field is valid for Visual Script usage if (SurfaceUtils.IsValidVisualScriptField(member)) { if (member.HasGet) { // Create node archetype var node = (NodeArchetype)Archetypes.Function.Nodes[6].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.DefaultValues[1] = name; node.DefaultValues[2] = member.ValueType.TypeName; node.DefaultValues[3] = member.IsStatic; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Get " + name; node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); node.SubTitle = string.Format(" (in {0})", scriptTypeName); // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 16); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(109, 160, 24), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } // Add node to the group ((IList <NodeArchetype>)group.Archetypes).Add(node); #if DEBUG_FIELDS_SEARCHING Editor.LogWarning(scriptTypeTypeName + " -> Get " + member.GetSignature()); searchHitsCount++; #endif } if (member.HasSet) { // Create node archetype var node = (NodeArchetype)Archetypes.Function.Nodes[7].Clone(); node.DefaultValues[0] = scriptTypeTypeName; node.DefaultValues[1] = name; node.DefaultValues[2] = member.ValueType.TypeName; node.DefaultValues[3] = member.IsStatic; node.Flags &= ~NodeFlags.NoSpawnViaGUI; node.Title = "Set " + name; node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); node.SubTitle = string.Format(" (in {0})", scriptTypeName); // Create group archetype var groupKey = new KeyValuePair <string, ushort>(scriptTypeName, 16); if (!_cache.TryGetValue(groupKey, out var group)) { group = new GroupArchetype { GroupID = groupKey.Value, Name = groupKey.Key, Color = new Color(109, 160, 24), Tag = _version, Archetypes = new List <NodeArchetype>(), }; _cache.Add(groupKey, group); } // Add node to the group ((IList <NodeArchetype>)group.Archetypes).Add(node); #if DEBUG_FIELDS_SEARCHING Editor.LogWarning(scriptTypeTypeName + " -> Set " + member.GetSignature()); searchHitsCount++; #endif } } } } } // Add group to context menu (on a main thread) FlaxEngine.Scripting.InvokeOnUpdate(() => { #if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING var addStartTime = DateTime.Now; #endif lock (_locker) { _taskContextMenu.AddGroups(_cache.Values); _taskContextMenu = null; } #if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING Editor.LogError($"Added items to VisjectCM in: {(DateTime.Now - addStartTime).TotalMilliseconds} ms"); #endif }); #if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING Editor.LogError($"Collected {searchHitsCount} items in: {(DateTime.Now - searchStartTime).TotalMilliseconds} ms"); #endif Profiler.EndEvent(); lock (_locker) { _task = null; } }