diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index 703c3f51b..270d71057 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -195,6 +195,15 @@ namespace FlaxEditor.CustomEditors
Presenter.AfterLayout?.Invoke(layout);
}
+ ///
+ protected override void Deinitialize()
+ {
+ Editor = null;
+ _overrideEditor = null;
+
+ base.Deinitialize();
+ }
+
///
protected override void OnModified()
{
diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
index d1cdd8780..488d180cf 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -206,7 +206,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
if (_linkedPrefabId != Guid.Empty)
{
_linkedPrefabId = Guid.Empty;
- Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplying;
+ Editor.Instance.Prefabs.PrefabApplying -= OnPrefabApplying;
Editor.Instance.Prefabs.PrefabApplied -= OnPrefabApplied;
}
}
diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
index 583241915..b0239dc8f 100644
--- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs
@@ -1057,6 +1057,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
protected override void Deinitialize()
{
_scriptToggles = null;
+ _scripts.Clear();
base.Deinitialize();
}
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index bfcd8d046..aba1ddb81 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -819,6 +819,15 @@ namespace FlaxEditor.CustomEditors.Editors
OnGroupsEnd();
}
+ ///
+ protected override void Deinitialize()
+ {
+ _visibleIfCaches = null;
+ _visibleIfPropertiesListsCache = null;
+
+ base.Deinitialize();
+ }
+
///
public override void Refresh()
{
diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs
index 52eb708d7..dbbd5db11 100644
--- a/Source/Editor/GUI/Docking/DockWindow.cs
+++ b/Source/Editor/GUI/Docking/DockWindow.cs
@@ -486,7 +486,7 @@ namespace FlaxEditor.GUI.Docking
{
base.Focus();
- SelectTab();
+ SelectTab(false);
BringToFront();
}
diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs
index 8dc483f31..5d30d3c41 100644
--- a/Source/Editor/Modules/ContentDatabaseModule.cs
+++ b/Source/Editor/Modules/ContentDatabaseModule.cs
@@ -61,7 +61,7 @@ namespace FlaxEditor.Modules
public event Action WorkspaceModified;
///
- /// Occurs when workspace has will be rebuilt.
+ /// Occurs when workspace will be rebuilt.
///
public event Action WorkspaceRebuilding;
@@ -88,6 +88,9 @@ namespace FlaxEditor.Modules
// Register AssetItems serialization helper (serialize ref ID only)
FlaxEngine.Json.JsonSerializer.Settings.Converters.Add(new AssetItemConverter());
+
+ ScriptsBuilder.ScriptsReload += OnScriptsReload;
+ ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
}
private void OnContentAssetDisposing(Asset asset)
@@ -1230,8 +1233,6 @@ namespace FlaxEditor.Modules
LoadProjects(Game.Project);
}
- RebuildInternal();
-
Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
{
var path = obj.ResultUrl;
@@ -1313,6 +1314,52 @@ namespace FlaxEditor.Modules
}
}
+ private void OnScriptsReload()
+ {
+ var enabledEvents = _enableEvents;
+ _enableEvents = false;
+ _isDuringFastSetup = true;
+ var startItems = _itemsCreated;
+ foreach (var project in Projects)
+ {
+ if (project.Content != null)
+ {
+ //Dispose(project.Content.Folder);
+ for (int i = 0; i < project.Content.Folder.Children.Count; i++)
+ {
+ Dispose(project.Content.Folder.Children[i]);
+ i--;
+ }
+ }
+ if (project.Source != null)
+ {
+ //Dispose(project.Source.Folder);
+ for (int i = 0; i < project.Source.Folder.Children.Count; i++)
+ {
+ Dispose(project.Source.Folder.Children[i]);
+ i--;
+ }
+ }
+ }
+
+ List removeProxies = new List();
+ foreach (var proxy in Editor.Instance.ContentDatabase.Proxy)
+ {
+ if (proxy.GetType().IsCollectible)
+ removeProxies.Add(proxy);
+ }
+ foreach (var proxy in removeProxies)
+ RemoveProxy(proxy, false);
+
+ _isDuringFastSetup = false;
+ _enableEvents = enabledEvents;
+ }
+
+ private void OnScriptsReloadEnd()
+ {
+ RebuildInternal();
+ }
+
///
public override void OnUpdate()
{
@@ -1340,6 +1387,8 @@ namespace FlaxEditor.Modules
public override void OnExit()
{
FlaxEngine.Content.AssetDisposing -= OnContentAssetDisposing;
+ ScriptsBuilder.ScriptsReload -= OnScriptsReload;
+ ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
// Disable events
_enableEvents = false;
diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs
index c190f36aa..a45e999a9 100644
--- a/Source/Editor/Modules/ContentImportingModule.cs
+++ b/Source/Editor/Modules/ContentImportingModule.cs
@@ -391,6 +391,20 @@ namespace FlaxEditor.Modules
public override void OnInit()
{
ImportFileEntry.RegisterDefaultTypes();
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
+ }
+
+ private void OnScriptsReloadBegin()
+ {
+ // Remove import file types from scripting assemblies
+ List removeFileTypes = new List();
+ foreach (var pair in ImportFileEntry.FileTypes)
+ {
+ if (pair.Value.Method.IsCollectible || (pair.Value.Target != null && pair.Value.Target.GetType().IsCollectible))
+ removeFileTypes.Add(pair.Key);
+ }
+ foreach (var fileType in removeFileTypes)
+ ImportFileEntry.FileTypes.Remove(fileType);
}
///
@@ -451,6 +465,7 @@ namespace FlaxEditor.Modules
///
public override void OnExit()
{
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
EndWorker();
}
}
diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs
index d95ddcabe..186ae1405 100644
--- a/Source/Editor/Modules/WindowsModule.cs
+++ b/Source/Editor/Modules/WindowsModule.cs
@@ -5,10 +5,12 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using FlaxEditor.Content;
using FlaxEditor.GUI.Dialogs;
+using FlaxEditor.GUI.Docking;
using FlaxEditor.Windows;
using FlaxEditor.Windows.Assets;
using FlaxEditor.Windows.Profiler;
@@ -39,6 +41,7 @@ namespace FlaxEditor.Modules
public DockState DockState;
public DockPanel DockedTo;
+ public int DockedTabIndex;
public float? SplitterValue = null;
public bool SelectOnShow = false;
@@ -48,6 +51,8 @@ namespace FlaxEditor.Modules
public Float2 FloatSize;
public Float2 FloatPosition;
+ public Guid AssetItemID;
+
// Constructor, to allow for default values
public WindowRestoreData()
{
@@ -805,10 +810,60 @@ namespace FlaxEditor.Modules
Level.SceneSaving += OnSceneSaving;
Level.SceneUnloaded += OnSceneUnloaded;
Level.SceneUnloading += OnSceneUnloading;
- ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
+ Editor.ContentDatabase.WorkspaceRebuilt += OnWorkspaceRebuilt;
Editor.StateMachine.StateChanged += OnEditorStateChanged;
}
+ internal void AddToRestore(AssetEditorWindow win)
+ {
+ var type = win.GetType();
+ var winData = new WindowRestoreData();
+ var panel = win.ParentDockPanel;
+
+ // Ensure that this window is only selected following recompilation
+ // if it was the active tab in its dock panel. Otherwise, there is a
+ // risk of interrupting the user's workflow by potentially selecting
+ // background tabs.
+ var window = win.RootWindow?.Window;
+ winData.SelectOnShow = panel.SelectedTab == win;
+ winData.DockedTabIndex = 0;
+ if (panel is FloatWindowDockPanel && window != null && panel.TabsCount == 1)
+ {
+ winData.DockState = DockState.Float;
+ winData.FloatPosition = window.Position;
+ winData.FloatSize = window.ClientSize;
+ winData.Maximize = window.IsMaximized;
+ winData.Minimize = window.IsMinimized;
+ winData.DockedTo = panel;
+ }
+ else
+ {
+ for (int i = 0; i < panel.Tabs.Count; i++)
+ {
+ if (panel.Tabs[i] == win)
+ {
+ winData.DockedTabIndex = i;
+ break;
+ }
+ }
+ if (panel.TabsCount > 1)
+ {
+ winData.DockState = DockState.DockFill;
+ winData.DockedTo = panel;
+ }
+ else
+ {
+ winData.DockState = panel.TryGetDockState(out var splitterValue);
+ winData.DockedTo = panel.ParentDockPanel;
+ winData.SplitterValue = splitterValue;
+ }
+ }
+ winData.AssemblyName = type.Assembly.GetName().Name;
+ winData.TypeName = type.FullName;
+ winData.AssetItemID = win.Item.ID;
+ _restoreWindows.Add(winData);
+ }
+
internal void AddToRestore(CustomEditorWindow win)
{
var type = win.GetType();
@@ -841,7 +896,8 @@ namespace FlaxEditor.Modules
{
winData.DockState = DockState.DockFill;
winData.DockedTo = panel;
- }else
+ }
+ else
{
winData.DockState = panel.TryGetDockState(out var splitterValue);
winData.DockedTo = panel.ParentDockPanel;
@@ -853,38 +909,93 @@ namespace FlaxEditor.Modules
_restoreWindows.Add(winData);
}
- private void OnScriptsReloadEnd()
+ private void OnWorkspaceRebuilt()
{
- for (int i = 0; i < _restoreWindows.Count; i++)
+ // Go in reverse order to create floating Prefab windows first before docked windows
+ for (int i = _restoreWindows.Count - 1; i >= 0; i--)
{
var winData = _restoreWindows[i];
try
{
var assembly = Utils.GetAssemblyByName(winData.AssemblyName);
- if (assembly != null)
+ if (assembly == null)
+ continue;
+
+ var type = assembly.GetType(winData.TypeName);
+ if (type == null)
+ continue;
+
+ if (type.IsAssignableTo(typeof(AssetEditorWindow)))
{
- var type = assembly.GetType(winData.TypeName);
- if (type != null)
+ var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
+ var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
+ var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
+ win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
+ if (winData.DockState == DockState.Float)
{
- var win = (CustomEditorWindow)Activator.CreateInstance(type);
- win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
- if (winData.DockState == DockState.Float)
+ var window = win.RootWindow.Window;
+ window.Position = winData.FloatPosition;
+ if (winData.Maximize)
{
- var window = win.Window.RootWindow.Window;
- window.Position = winData.FloatPosition;
- if (winData.Maximize)
- {
- window.Maximize();
- }
- else if (winData.Minimize)
- {
- window.Minimize();
- }
- else
- {
- window.ClientSize = winData.FloatSize;
- }
+ window.Maximize();
+ }
+ else if (winData.Minimize)
+ {
+ window.Minimize();
+ }
+ else
+ {
+ window.ClientSize = winData.FloatSize;
+ }
+
+ // Update panel reference in other windows docked to this panel
+ foreach (ref var otherData in CollectionsMarshal.AsSpan(_restoreWindows))
+ {
+ if (otherData.DockedTo == winData.DockedTo)
+ otherData.DockedTo = win.ParentDockPanel;
+ }
+ }
+ var panel = win.ParentDockPanel;
+ int currentTabIndex = 0;
+ for (int pi = 0; pi < panel.TabsCount; pi++)
+ {
+ if (panel.Tabs[pi] == win)
+ {
+ currentTabIndex = pi;
+ break;
+ }
+ }
+ while (currentTabIndex > winData.DockedTabIndex)
+ {
+ win.ParentDockPanel.MoveTabLeft(currentTabIndex);
+ currentTabIndex--;
+ }
+ while (currentTabIndex < winData.DockedTabIndex)
+ {
+ win.ParentDockPanel.MoveTabRight(currentTabIndex);
+ currentTabIndex++;
+ }
+ }
+ else
+ {
+ var win = (CustomEditorWindow)Activator.CreateInstance(type);
+ win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue);
+ if (winData.DockState == DockState.Float)
+ {
+ var window = win.Window.RootWindow.Window;
+ window.Position = winData.FloatPosition;
+ if (winData.Maximize)
+ {
+ window.Maximize();
+ }
+ else if (winData.Minimize)
+ {
+ window.Minimize();
+ }
+ else
+ {
+ window.ClientSize = winData.FloatSize;
}
}
}
@@ -895,6 +1006,11 @@ namespace FlaxEditor.Modules
Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName));
}
}
+
+ // Restored windows stole the focus from Editor
+ if (_restoreWindows.Count > 0)
+ Editor.Instance.Windows.MainWindow.Focus();
+
_restoreWindows.Clear();
}
@@ -1050,7 +1166,7 @@ namespace FlaxEditor.Modules
Level.SceneSaving -= OnSceneSaving;
Level.SceneUnloaded -= OnSceneUnloaded;
Level.SceneUnloading -= OnSceneUnloading;
- ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
+ Editor.ContentDatabase.WorkspaceRebuilt -= OnWorkspaceRebuilt;
Editor.StateMachine.StateChanged -= OnEditorStateChanged;
// Close main window
diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs
index 5becb69dc..37afc896e 100644
--- a/Source/Editor/SceneGraph/SceneGraphNode.cs
+++ b/Source/Editor/SceneGraph/SceneGraphNode.cs
@@ -469,6 +469,7 @@ namespace FlaxEditor.SceneGraph
{
ChildNodes[i].OnDispose();
}
+ ChildNodes.Clear();
SceneGraphFactory.Nodes.Remove(ID);
}
diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs
index c33224072..30a15d7f4 100644
--- a/Source/Editor/Surface/AnimGraphSurface.cs
+++ b/Source/Editor/Surface/AnimGraphSurface.cs
@@ -161,6 +161,8 @@ namespace FlaxEditor.Surface
private void OnScriptsReloadBegin()
{
+ _nodesCache.Clear();
+
// Check if any of the nodes comes from the game scripts - those can be reloaded at runtime so prevent crashes
bool hasTypeFromGameScripts = Editor.Instance.CodeEditing.AnimGraphNodes.HasTypeFromGameScripts;
diff --git a/Source/Editor/Surface/BehaviorTreeSurface.cs b/Source/Editor/Surface/BehaviorTreeSurface.cs
index 647c438e8..2dba081b0 100644
--- a/Source/Editor/Surface/BehaviorTreeSurface.cs
+++ b/Source/Editor/Surface/BehaviorTreeSurface.cs
@@ -24,6 +24,7 @@ namespace FlaxEditor.Surface
public BehaviorTreeSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo)
: base(owner, onSave, undo, CreateStyle())
{
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
private static SurfaceStyle CreateStyle()
@@ -35,6 +36,11 @@ namespace FlaxEditor.Surface
return style;
}
+ private void OnScriptsReloadBegin()
+ {
+ _nodesCache.Clear();
+ }
+
private static void DrawBox(Box box)
{
var rect = new Rectangle(Float2.Zero, box.Size);
@@ -186,6 +192,7 @@ namespace FlaxEditor.Surface
{
if (IsDisposing)
return;
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
_nodesCache.Wait();
base.OnDestroy();
diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs
index 28ec17d53..66572324d 100644
--- a/Source/Editor/Surface/VisjectSurface.cs
+++ b/Source/Editor/Surface/VisjectSurface.cs
@@ -415,6 +415,15 @@ namespace FlaxEditor.Surface
// Init drag handlers
DragHandlers.Add(_dragAssets = new DragAssets(ValidateDragItem));
DragHandlers.Add(_dragParameters = new DragNames(SurfaceParameter.DragPrefix, ValidateDragParameter));
+
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
+ }
+
+ private void OnScriptsReloadBegin()
+ {
+ _activeVisjectCM = null;
+ _cmPrimaryMenu?.Dispose();
+ _cmPrimaryMenu = null;
}
///
@@ -1023,6 +1032,8 @@ namespace FlaxEditor.Surface
_activeVisjectCM = null;
_cmPrimaryMenu?.Dispose();
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
+
base.OnDestroy();
}
}
diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs
index ff0153e8a..672a527a9 100644
--- a/Source/Editor/Surface/VisualScriptSurface.cs
+++ b/Source/Editor/Surface/VisualScriptSurface.cs
@@ -62,6 +62,12 @@ namespace FlaxEditor.Surface
{
_supportsImplicitCastFromObjectToBoolean = true;
DragHandlers.Add(_dragActors = new DragActors(ValidateDragActor));
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
+ }
+
+ private void OnScriptsReloadBegin()
+ {
+ _nodesCache.Clear();
}
private bool ValidateDragActor(ActorNode actor)
@@ -631,6 +637,7 @@ namespace FlaxEditor.Surface
{
if (IsDisposing)
return;
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
_nodesCache.Wait();
base.OnDestroy();
diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs
index f781d49dc..e97cb7371 100644
--- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs
+++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs
@@ -58,6 +58,8 @@ namespace FlaxEditor.Windows.Assets
InputActions.Add(options => options.Save, Save);
UpdateTitle();
+
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
}
///
@@ -151,6 +153,8 @@ namespace FlaxEditor.Windows.Assets
///
public override void OnDestroy()
{
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
+
if (_item != null)
{
// Ensure to remove linkage to the item
@@ -160,6 +164,15 @@ namespace FlaxEditor.Windows.Assets
base.OnDestroy();
}
+ ///
+ protected virtual void OnScriptsReloadBegin()
+ {
+ if (!IsHidden)
+ {
+ Editor.Instance.Windows.AddToRestore(this);
+ }
+ }
+
#region IEditable Implementation
private bool _isEdited;
diff --git a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
index 3d6399365..212e0c82f 100644
--- a/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
+++ b/Source/Editor/Windows/Assets/BehaviorTreeWindow.cs
@@ -268,8 +268,11 @@ namespace FlaxEditor.Windows.Assets
UpdateKnowledge();
}
- private void OnScriptsReloadBegin()
+ ///
+ protected override void OnScriptsReloadBegin()
{
+ base.OnScriptsReloadBegin();
+
// TODO: impl hot-reload for BT to nicely refresh state (save asset, clear undo/properties, reload surface)
Close();
}
diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs
index dc1e1e71f..fec7ccb63 100644
--- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs
+++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs
@@ -124,8 +124,10 @@ namespace FlaxEditor.Windows.Assets
UpdateToolstrip();
}
- private void OnScriptsReloadBegin()
+ ///
+ protected override void OnScriptsReloadBegin()
{
+ base.OnScriptsReloadBegin();
Close();
}
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs
index 533735c93..f5d738880 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.cs
@@ -194,7 +194,6 @@ namespace FlaxEditor.Windows.Assets
Editor.Prefabs.PrefabApplied += OnPrefabApplied;
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
- ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
// Setup input actions
InputActions.Add(options => options.Undo, () =>
@@ -287,8 +286,10 @@ namespace FlaxEditor.Windows.Assets
return false;
}
- private void OnScriptsReloadBegin()
+ ///
+ protected override void OnScriptsReloadBegin()
{
+ base.OnScriptsReloadBegin();
_isScriptsReloading = true;
if (_asset == null || !_asset.IsLoaded)
@@ -316,19 +317,8 @@ namespace FlaxEditor.Windows.Assets
Graph.MainActor = null;
_viewport.Prefab = null;
_undo?.Clear(); // TODO: maybe don't clear undo?
- }
- private void OnScriptsReloadEnd()
- {
- _isScriptsReloading = false;
-
- if (_asset == null || !_asset.IsLoaded)
- return;
-
- // Restore
- OnPrefabOpened();
- _undo.Clear();
- ClearEditedFlag();
+ Close();
}
private void OnUndoEvent(IUndoAction action)
@@ -547,7 +537,6 @@ namespace FlaxEditor.Windows.Assets
{
Editor.Prefabs.PrefabApplied -= OnPrefabApplied;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
- ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
_undo.Dispose();
Graph.Dispose();
diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs
index 6089b2e07..f452c485a 100644
--- a/Source/Editor/Windows/ContentWindow.cs
+++ b/Source/Editor/Windows/ContentWindow.cs
@@ -29,6 +29,7 @@ namespace FlaxEditor.Windows
private const string ProjectDataLastViewedFolder = "LastViewedFolder";
private bool _isWorkspaceDirty;
private string _workspaceRebuildLocation;
+ private string _lastViewedFolderBeforeReload;
private SplitPanel _split;
private Panel _contentViewPanel;
private Panel _contentTreePanel;
@@ -144,26 +145,6 @@ namespace FlaxEditor.Windows
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
- // Content database events
- editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
- editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
- editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
- editor.ContentDatabase.WorkspaceRebuilt += () =>
- {
- var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
- if (selected is ContentFolder selectedFolder)
- {
- _navigationUnlocked = false;
- RefreshView(selectedFolder.Node);
- _tree.Select(selectedFolder.Node);
- UpdateItemsSearch();
- _navigationUnlocked = true;
- UpdateUI();
- }
- else
- ShowRoot();
- };
-
var options = Editor.Options;
options.OptionsChanged += OnOptionsChanged;
@@ -1036,6 +1017,61 @@ namespace FlaxEditor.Windows
///
public override void OnInit()
+ {
+ // Content database events
+ Editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true;
+ Editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved;
+ Editor.ContentDatabase.WorkspaceRebuilding += () => { _workspaceRebuildLocation = SelectedNode?.Path; };
+ Editor.ContentDatabase.WorkspaceRebuilt += () =>
+ {
+ var selected = Editor.ContentDatabase.Find(_workspaceRebuildLocation);
+ if (selected is ContentFolder selectedFolder)
+ {
+ _navigationUnlocked = false;
+ RefreshView(selectedFolder.Node);
+ _tree.Select(selectedFolder.Node);
+ UpdateItemsSearch();
+ _navigationUnlocked = true;
+ UpdateUI();
+ }
+ else if (_root != null)
+ ShowRoot();
+ };
+
+ Refresh();
+
+ // Load last viewed folder
+ if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
+ {
+ if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
+ _tree.Select(folder.Node);
+ }
+
+ ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
+ ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
+ }
+
+ private void OnScriptsReloadBegin()
+ {
+ var lastViewedFolder = _tree.Selection.Count == 1 ? _tree.SelectedNode as ContentTreeNode : null;
+ _lastViewedFolderBeforeReload = lastViewedFolder?.Path ?? string.Empty;
+
+ _tree.RemoveChild(_root);
+ _root = null;
+ }
+
+ private void OnScriptsReloadEnd()
+ {
+ Refresh();
+
+ if (!string.IsNullOrEmpty(_lastViewedFolderBeforeReload))
+ {
+ if (Editor.ContentDatabase.Find(_lastViewedFolderBeforeReload) is ContentFolder folder)
+ _tree.Select(folder.Node);
+ }
+ }
+
+ private void Refresh()
{
// Setup content root node
_root = new RootContentTreeNode
@@ -1072,13 +1108,6 @@ namespace FlaxEditor.Windows
// Update UI layout
_isLayoutLocked = false;
PerformLayout();
-
- // Load last viewed folder
- if (Editor.ProjectCache.TryGetCustomData(ProjectDataLastViewedFolder, out string lastViewedFolder))
- {
- if (Editor.ContentDatabase.Find(lastViewedFolder) is ContentFolder folder)
- _tree.Select(folder.Node);
- }
}
///
@@ -1226,6 +1255,8 @@ namespace FlaxEditor.Windows
_viewDropdown = null;
Editor.Options.OptionsChanged -= OnOptionsChanged;
+ ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
+ ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
base.OnDestroy();
}
diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs
index 4b347ea83..dd550b0f1 100644
--- a/Source/Editor/Windows/ToolboxWindow.cs
+++ b/Source/Editor/Windows/ToolboxWindow.cs
@@ -150,6 +150,22 @@ namespace FlaxEditor.Windows
_searchBox.Clear();
_groupSearch.DisposeChildren();
_groupSearch.PerformLayout();
+
+ // Remove tabs
+ var tabs = new List();
+ foreach (var child in _actorGroups.Children)
+ {
+ if (child is Tab tab)
+ {
+ if (tab.Text != "Search")
+ tabs.Add(tab);
+ }
+ }
+ foreach (var tab in tabs)
+ {
+ var group = _actorGroups.Children.Find(T => T == tab);
+ group.Dispose();
+ }
}
private void OnScriptsReloadEnd()
diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs
index c7507c778..148efb007 100644
--- a/Source/Engine/Engine/NativeInterop.Managed.cs
+++ b/Source/Engine/Engine/NativeInterop.Managed.cs
@@ -176,6 +176,7 @@ namespace FlaxEngine.Interop
_managedHandle.Free();
_unmanagedData = IntPtr.Zero;
}
+ _arrayType = _elementType = null;
ManagedArrayPool.Put(this);
}
@@ -442,22 +443,25 @@ namespace FlaxEngine.Interop
///
/// Tries to free all references to old weak handles so GC can collect them.
///
- internal static void TryCollectWeakHandles()
+ internal static void TryCollectWeakHandles(bool force = false)
{
- if (weakHandleAccumulator < nextWeakPoolCollection)
- return;
+ if (!force)
+ {
+ if (weakHandleAccumulator < nextWeakPoolCollection)
+ return;
- nextWeakPoolCollection = weakHandleAccumulator + 1000;
+ nextWeakPoolCollection = weakHandleAccumulator + 1000;
- // Try to swap pools after garbage collection or whenever the pool gets too large
- var gc0CollectionCount = GC.CollectionCount(0);
- if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
- return;
- nextWeakPoolGCCollection = gc0CollectionCount + 1;
+ // Try to swap pools after garbage collection or whenever the pool gets too large
+ var gc0CollectionCount = GC.CollectionCount(0);
+ if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
+ return;
+ nextWeakPoolGCCollection = gc0CollectionCount + 1;
- // Prevent huge allocations from swapping the pools in the middle of the operation
- if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
- return;
+ // Prevent huge allocations from swapping the pools in the middle of the operation
+ if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
+ return;
+ }
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp();
// Swap the pools and release the oldest pool for GC
diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
index 71caee1e9..8626f187c 100644
--- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs
+++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
@@ -1054,7 +1054,49 @@ namespace FlaxEngine.Interop
}
[UnmanagedCallersOnly]
- internal static void ReloadScriptingAssemblyLoadContext()
+ internal static void CreateScriptingAssemblyLoadContext()
+ {
+#if FLAX_EDITOR
+ if (scriptingAssemblyLoadContext != null)
+ {
+ // Wait for previous ALC to finish unloading, track it without holding strong references to it
+ GCHandle weakRef = GCHandle.Alloc(scriptingAssemblyLoadContext, GCHandleType.WeakTrackResurrection);
+ scriptingAssemblyLoadContext = null;
+#if true
+ // In case the ALC doesn't unload properly: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#debug-unloading-issues
+ while (true)
+#else
+ for (int attempts = 5; attempts > 0; attempts--)
+#endif
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+
+ if (!IsHandleAlive(weakRef))
+ break;
+ System.Threading.Thread.Sleep(1);
+ }
+ if (IsHandleAlive(weakRef))
+ Debug.LogWarning("Scripting AssemblyLoadContext was not unloaded.");
+ weakRef.Free();
+
+ static bool IsHandleAlive(GCHandle weakRef)
+ {
+ // Checking the target in scope somehow holds a reference to it...?
+ return weakRef.Target != null;
+ }
+ }
+
+ scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: true);
+ scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
+#else
+ scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible: false);
+#endif
+ DelegateHelpers.InitMethods();
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void UnloadScriptingAssemblyLoadContext()
{
#if FLAX_EDITOR
// Clear all caches which might hold references to assemblies in collectible ALC
@@ -1072,24 +1114,67 @@ namespace FlaxEngine.Interop
handle.Free();
propertyHandleCacheCollectible.Clear();
+ foreach (var key in assemblyHandles.Keys.Where(x => x.IsCollectible))
+ assemblyHandles.Remove(key);
+ foreach (var key in assemblyOwnedNativeLibraries.Keys.Where(x => x.IsCollectible))
+ assemblyOwnedNativeLibraries.Remove(key);
+
_typeSizeCache.Clear();
foreach (var pair in classAttributesCacheCollectible)
pair.Value.Free();
classAttributesCacheCollectible.Clear();
+ ArrayFactory.marshalledTypes.Clear();
+ ArrayFactory.arrayTypes.Clear();
+ ArrayFactory.createArrayDelegates.Clear();
+
FlaxEngine.Json.JsonSerializer.ResetCache();
+ DelegateHelpers.Release();
+
+ // Ensure both pools are empty
+ ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
+ ManagedHandle.ManagedHandlePool.TryCollectWeakHandles(true);
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+
+ {
+ // HACK: Workaround for TypeDescriptor holding references to collectible types (https://github.com/dotnet/runtime/issues/30656)
+
+ Type TypeDescriptionProviderType = typeof(System.ComponentModel.TypeDescriptionProvider);
+ MethodInfo clearCacheMethod = TypeDescriptionProviderType?.Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler")?.GetMethod("ClearCache");
+ if (clearCacheMethod != null)
+ clearCacheMethod.Invoke(null, new object[] { null });
+
+ Type TypeDescriptorType = typeof(System.ComponentModel.TypeDescriptor);
+
+ object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ System.Collections.Hashtable s_defaultProviders = (System.Collections.Hashtable)TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_internalSyncObject != null && s_defaultProviders != null)
+ {
+ lock (s_internalSyncObject)
+ s_defaultProviders.Clear();
+ }
+
+ object s_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ System.Collections.Hashtable s_providerTypeTable = (System.Collections.Hashtable)TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_providerTable != null && s_providerTypeTable != null)
+ {
+ lock (s_providerTable)
+ s_providerTypeTable.Clear();
+ TypeDescriptorType.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
+ ?.FieldType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear")
+ ?.Invoke(s_providerTable, new object[] { });
+ }
+ }
// Unload the ALC
- bool unloading = true;
- scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
scriptingAssemblyLoadContext.Unload();
+ scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;
- while (unloading)
- System.Threading.Thread.Sleep(1);
-
- InitScriptingAssemblyLoadContext();
- DelegateHelpers.InitMethods();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
#endif
}
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
index 35fe70221..7a9a8e5ff 100644
--- a/Source/Engine/Engine/NativeInterop.cs
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -73,19 +73,6 @@ namespace FlaxEngine.Interop
return nativeLibrary;
}
- private static void InitScriptingAssemblyLoadContext()
- {
-#if FLAX_EDITOR
- var isCollectible = true;
-#else
- var isCollectible = false;
-#endif
- scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible);
-#if FLAX_EDITOR
- scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving;
-#endif
- }
-
[UnmanagedCallersOnly]
internal static unsafe void Init()
{
@@ -97,8 +84,6 @@ namespace FlaxEngine.Interop
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
- InitScriptingAssemblyLoadContext();
- DelegateHelpers.InitMethods();
}
#if FLAX_EDITOR
@@ -1475,11 +1460,11 @@ namespace FlaxEngine.Interop
internal static class ArrayFactory
{
- private delegate Array CreateArrayDelegate(long size);
+ internal delegate Array CreateArrayDelegate(long size);
- private static ConcurrentDictionary marshalledTypes = new ConcurrentDictionary(1, 3);
- private static ConcurrentDictionary arrayTypes = new ConcurrentDictionary(1, 3);
- private static ConcurrentDictionary createArrayDelegates = new ConcurrentDictionary(1, 3);
+ internal static ConcurrentDictionary marshalledTypes = new ConcurrentDictionary(1, 3);
+ internal static ConcurrentDictionary arrayTypes = new ConcurrentDictionary(1, 3);
+ internal static ConcurrentDictionary createArrayDelegates = new ConcurrentDictionary(1, 3);
internal static Type GetMarshalledType(Type elementType)
{
@@ -1645,17 +1630,6 @@ namespace FlaxEngine.Interop
return RegisterType(type, true).typeHolder;
}
- internal static (TypeHolder typeHolder, ManagedHandle handle) GetTypeHolderAndManagedHandle(Type type)
- {
- if (managedTypes.TryGetValue(type, out (TypeHolder typeHolder, ManagedHandle handle) tuple))
- return tuple;
-#if FLAX_EDITOR
- if (managedTypesCollectible.TryGetValue(type, out tuple))
- return tuple;
-#endif
- return RegisterType(type, true);
- }
-
///
/// Returns a static ManagedHandle to TypeHolder for given Type, and caches it if needed.
///
@@ -1781,6 +1755,14 @@ namespace FlaxEngine.Interop
#endif
}
+ internal static void Release()
+ {
+ MakeNewCustomDelegateFunc = null;
+#if FLAX_EDITOR
+ MakeNewCustomDelegateFuncCollectible = null;
+#endif
+ }
+
internal static Type MakeNewCustomDelegate(Type[] parameters)
{
#if FLAX_EDITOR
diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h
index 35227df56..d975f5af7 100644
--- a/Source/Engine/Scripting/ManagedCLR/MCore.h
+++ b/Source/Engine/Scripting/ManagedCLR/MCore.h
@@ -45,9 +45,16 @@ public:
///
static void UnloadEngine();
+ ///
+ /// Creates the assembly load context for assemblies used by Scripting.
+ ///
+ static void CreateScriptingAssemblyLoadContext();
+
#if USE_EDITOR
- // Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
- static void ReloadScriptingAssemblyLoadContext();
+ ///
+ /// Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again).
+ ///
+ static void UnloadScriptingAssemblyLoadContext();
#endif
public:
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index ddafab614..29ccea8ae 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -330,9 +330,15 @@ void MCore::UnloadEngine()
ShutdownHostfxr();
}
+void MCore::CreateScriptingAssemblyLoadContext()
+{
+ static void* CreateScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("CreateScriptingAssemblyLoadContext"));
+ CallStaticMethod(CreateScriptingAssemblyLoadContextPtr);
+}
+
#if USE_EDITOR
-void MCore::ReloadScriptingAssemblyLoadContext()
+void MCore::UnloadScriptingAssemblyLoadContext()
{
// Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108)
for (auto e : CachedClassHandles)
@@ -377,8 +383,8 @@ void MCore::ReloadScriptingAssemblyLoadContext()
}
}
- static void* ReloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("ReloadScriptingAssemblyLoadContext"));
- CallStaticMethod(ReloadScriptingAssemblyLoadContextPtr);
+ static void* UnloadScriptingAssemblyLoadContextPtr = GetStaticMethodPointer(TEXT("UnloadScriptingAssemblyLoadContext"));
+ CallStaticMethod(UnloadScriptingAssemblyLoadContextPtr);
}
#endif
diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp
index 351e1569b..f4da8135c 100644
--- a/Source/Engine/Scripting/Runtime/Mono.cpp
+++ b/Source/Engine/Scripting/Runtime/Mono.cpp
@@ -715,9 +715,13 @@ void MCore::UnloadEngine()
#endif
}
+void MCore::CreateScriptingAssemblyLoadContext()
+{
+}
+
#if USE_EDITOR
-void MCore::ReloadScriptingAssemblyLoadContext()
+void MCore::UnloadScriptingAssemblyLoadContext()
{
}
diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp
index 8731ed00d..98bf1f012 100644
--- a/Source/Engine/Scripting/Runtime/None.cpp
+++ b/Source/Engine/Scripting/Runtime/None.cpp
@@ -58,9 +58,13 @@ void MCore::UnloadEngine()
MRootDomain = nullptr;
}
+void MCore::CreateScriptingAssemblyLoadContext()
+{
+}
+
#if USE_EDITOR
-void MCore::ReloadScriptingAssemblyLoadContext()
+void MCore::UnloadScriptingAssemblyLoadContext()
{
}
diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp
index 4731a088d..75d68394d 100644
--- a/Source/Engine/Scripting/Scripting.cpp
+++ b/Source/Engine/Scripting/Scripting.cpp
@@ -182,6 +182,8 @@ bool ScriptingService::Init()
return true;
}
+ MCore::CreateScriptingAssemblyLoadContext();
+
// Cache root domain
_rootDomain = MCore::GetRootDomain();
@@ -710,7 +712,8 @@ void Scripting::Reload(bool canTriggerSceneReload)
_hasGameModulesLoaded = false;
// Release and create a new assembly load context for user assemblies
- MCore::ReloadScriptingAssemblyLoadContext();
+ MCore::UnloadScriptingAssemblyLoadContext();
+ MCore::CreateScriptingAssemblyLoadContext();
// Give GC a try to cleanup old user objects and the other mess
MCore::GC::Collect();
diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs
index 66a34faf0..33aea8a26 100644
--- a/Source/Engine/Scripting/Scripting.cs
+++ b/Source/Engine/Scripting/Scripting.cs
@@ -170,6 +170,10 @@ namespace FlaxEngine
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
Localization.LocalizationChanged += OnLocalizationChanged;
+#if FLAX_EDITOR
+ FlaxEditor.ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
+ FlaxEditor.ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
+#endif
OnLocalizationChanged();
if (!Engine.IsEditor)
@@ -178,6 +182,19 @@ namespace FlaxEngine
}
}
+#if FLAX_EDITOR
+ private static void OnScriptsReloadBegin()
+ {
+ // Tooltip might hold references to scripting assemblies
+ Style.Current.SharedTooltip = null;
+ }
+
+ private static void OnScriptsReloadEnd()
+ {
+ Style.Current.SharedTooltip = new Tooltip();
+ }
+#endif
+
private static void OnLocalizationChanged()
{
// Invariant-globalization only (see InitHostfxr with Mono)
@@ -359,6 +376,10 @@ namespace FlaxEngine
MainThreadTaskScheduler.Dispose();
Json.JsonSerializer.Dispose();
+#if FLAX_EDITOR
+ FlaxEditor.ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
+ FlaxEditor.ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
+#endif
}
///
diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs
index 1d5838a33..cabd7d52c 100644
--- a/Source/Engine/UI/GUI/Tooltip.cs
+++ b/Source/Engine/UI/GUI/Tooltip.cs
@@ -130,6 +130,7 @@ namespace FlaxEngine.GUI
// Unlink
IsLayoutLocked = true;
Parent = null;
+ _showTarget = null;
// Close window
if (_window)