using System; using System.IO; using UnityEditor; using UnityEngine; namespace Unity.VisualScripting { public static class EditorApplicationUtility { internal static void Initialize() { Recursion.safeMode = true; OptimizedReflection.safeMode = true; Ensure.IsActive = true; Selection.selectionChanged += OnSelectionChange; #if UNITY_2018_1_OR_NEWER EditorApplication.projectChanged += OnProjectChange; #else EditorApplication.projectWindowChanged += OnProjectChange; #endif #if UNITY_2018_1_OR_NEWER EditorApplication.hierarchyChanged += OnHierarchyChange; #else EditorApplication.hierarchyWindowChanged += OnHierarchyChange; #endif Undo.undoRedoPerformed += OnUndoRedo; } #region Version private static SemanticVersion? _unityVersion; private static readonly SemanticVersion fallbackUnityVersion = "2017.4.0"; public static SemanticVersion unityVersion { get { if (_unityVersion == null) { var unityVersionString = Application.unityVersion; if (SemanticVersion.TryParse(unityVersionString, out var parsedUnityVersion)) { _unityVersion = parsedUnityVersion; } else { Debug.LogWarning($"Failed to parse Unity version string '{unityVersionString}', falling back to {fallbackUnityVersion}"); _unityVersion = fallbackUnityVersion; } } return _unityVersion.Value; } } #endregion #region Assembly Lock public static bool isAssemblyReloadLocked { get; private set; } private static bool wantedScriptChangesDuringPlay; public static void LockReloadAssemblies() { isAssemblyReloadLocked = true; EditorApplication.LockReloadAssemblies(); } public static void UnlockReloadAssemblies() { EditorApplication.UnlockReloadAssemblies(); isAssemblyReloadLocked = false; } #if VISUAL_SCRIPT_INTERNAL [MenuItem("Tools/Bolt/Internal/Force Unlock Assembly Reload", priority = LudiqProduct.DeveloperToolsMenuPriority + 601)] #endif public static void ClearProgressBar() { EditorApplication.UnlockReloadAssemblies(); isAssemblyReloadLocked = false; } public static bool WantsScriptChangesDuringPlay() { return EditorPrefs.GetInt("ScriptCompilationDuringPlay", 0) == 0; } #endregion #region Events public static event Action onAssemblyReload; public static event Action onEnterPlayMode; public static event Action onExitPlayMode; public static event Action onEnterEditMode; public static event Action onExitEditMode; public static event Action onModeChange; public static event Action onPause; public static event Action onResume; public static event Action onPauseChange; public static event Action onSelectionChange; public static event Action onProjectChange; public static event Action onHierarchyChange; public static event Action onUndoRedo; private static void OnSelectionChange() { if (PluginContainer.initialized) { if (!VSUsageUtility.isVisualScriptingUsed) return; LudiqGUIUtility.BeginNotActuallyOnGUI(); onSelectionChange?.Invoke(); LudiqGUIUtility.EndNotActuallyOnGUI(); } } private static void OnProjectChange() { if (PluginContainer.initialized) { if (!VSUsageUtility.isVisualScriptingUsed) return; LudiqGUIUtility.BeginNotActuallyOnGUI(); onProjectChange?.Invoke(); LudiqGUIUtility.EndNotActuallyOnGUI(); } } private static void OnHierarchyChange() { if (PluginContainer.initialized) { if (!VSUsageUtility.isVisualScriptingUsed) return; LudiqGUIUtility.BeginNotActuallyOnGUI(); onHierarchyChange?.Invoke(); LudiqGUIUtility.EndNotActuallyOnGUI(); } } private static void OnUndoRedo() { if (PluginContainer.initialized) { if (!VSUsageUtility.isVisualScriptingUsed) return; LudiqGUIUtility.BeginNotActuallyOnGUI(); onUndoRedo?.Invoke(); LudiqGUIUtility.EndNotActuallyOnGUI(); } } private static void OnPause() { if (!VSUsageUtility.isVisualScriptingUsed) return; onPause?.Invoke(); } private static void OnResume() { if (!VSUsageUtility.isVisualScriptingUsed) return; onResume?.Invoke(); } private static void OnPauseChange() { if (!VSUsageUtility.isVisualScriptingUsed) return; onPauseChange?.Invoke(); } private static void OnEnteredEditMode() { if (!VSUsageUtility.isVisualScriptingUsed) return; if (wantedScriptChangesDuringPlay) { UnlockReloadAssemblies(); AssetDatabase.Refresh(); } onEnterEditMode?.Invoke(); } private static void OnExitingEditMode() { if (!VSUsageUtility.isVisualScriptingUsed) return; onExitEditMode?.Invoke(); } private static void OnEnteredPlayMode() { if (!VSUsageUtility.isVisualScriptingUsed) return; onEnterPlayMode?.Invoke(); wantedScriptChangesDuringPlay = WantsScriptChangesDuringPlay(); if (wantedScriptChangesDuringPlay) { LockReloadAssemblies(); } } private static void OnExitingPlayMode() { if (!VSUsageUtility.isVisualScriptingUsed) return; onExitPlayMode?.Invoke(); } private static void OnModeChange() { if (!VSUsageUtility.isVisualScriptingUsed) return; onModeChange?.Invoke(); } private static void OnAssemblyReload() { if (!VSUsageUtility.isVisualScriptingUsed) return; onAssemblyReload?.Invoke(); } internal static void BeforeInitializeAfterPlugins() { EditorApplication.pauseStateChanged += delegate (PauseState pauseState) { switch (pauseState) { case PauseState.Paused: OnPause(); break; case PauseState.Unpaused: OnResume(); break; } OnPauseChange(); }; EditorApplication.playModeStateChanged += delegate (PlayModeStateChange stateChange) { switch (stateChange) { case PlayModeStateChange.EnteredEditMode: OnEnteredEditMode(); break; case PlayModeStateChange.ExitingEditMode: OnExitingEditMode(); break; case PlayModeStateChange.EnteredPlayMode: OnEnteredPlayMode(); break; case PlayModeStateChange.ExitingPlayMode: OnExitingPlayMode(); break; } OnModeChange(); }; } internal static void AfterInitializeAfterPlugins() { OnAssemblyReload(); if (!EditorApplication.isPlayingOrWillChangePlaymode) { // Playmode state changed does not get called when // the editor assemblies load, therefore we have to // manually invoke enter edit mode. // This won't cause a double invoke because oddly, // assemblies do not get reloaded when you exit // play mode and get back into the edit mode. This may // cause issues somewhere in the Ludiq / Bolt source, // because most of it was coded assuming that they did. OnEnteredEditMode(); } } #endregion } }