diff --git a/Source/Editor/CustomEditors/CustomEditorPresenter.cs b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
index 703c3f51b..d3533528a 100644
--- a/Source/Editor/CustomEditors/CustomEditorPresenter.cs
+++ b/Source/Editor/CustomEditors/CustomEditorPresenter.cs
@@ -79,6 +79,7 @@ namespace FlaxEditor.CustomEditors
_presenter = presenter;
AnchorPreset = AnchorPresets.StretchAll;
Offsets = Margin.Zero;
+ Pivot = Float2.Zero;
IsScrollable = true;
}
@@ -195,6 +196,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 a93969e36..e7c8d2699 100644
--- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs
@@ -205,7 +205,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/LayersMatrixEditor.cs b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs
index ea41f91e9..d82d6ecf7 100644
--- a/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs
@@ -56,6 +56,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var bottomLeftCell = new VerticalPanel
{
+ Pivot = Float2.Zero,
Spacing = 0,
TopMargin = 0,
BottomMargin = 0,
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/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
index 5497d085f..f76bd4d89 100644
--- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs
@@ -232,6 +232,7 @@ namespace FlaxEditor.CustomEditors.Editors
public void Setup(CollectionEditor editor, int index, bool canReorder = true)
{
+ Pivot = Float2.Zero;
HeaderHeight = 18;
_canReorder = canReorder;
EnableDropDownIcon = true;
@@ -884,6 +885,11 @@ namespace FlaxEditor.CustomEditors.Editors
set => _pickerValidator.FileExtension = value;
}
+ public DragAreaControl()
+ {
+ Pivot = Float2.Zero;
+ }
+
///
public override void Draw()
{
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/CustomEditors/Elements/Container/GroupElement.cs b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
index 972f64ba6..2d43e34c3 100644
--- a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs
@@ -18,6 +18,7 @@ namespace FlaxEditor.CustomEditors.Elements
///
public readonly DropPanel Panel = new DropPanel
{
+ Pivot = Float2.Zero,
ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight),
ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown),
EnableDropDownIcon = true,
diff --git a/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs b/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
index f78a1eac1..4f6b9436f 100644
--- a/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/HorizontalPanelElement.cs
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Elements
@@ -13,7 +14,10 @@ namespace FlaxEditor.CustomEditors.Elements
///
/// The panel.
///
- public readonly HorizontalPanel Panel = new HorizontalPanel();
+ public readonly HorizontalPanel Panel = new HorizontalPanel
+ {
+ Pivot = Float2.Zero,
+ };
///
public override ContainerControl ContainerControl => Panel;
diff --git a/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs b/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
index d1b0039cf..3d15d1526 100644
--- a/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
+++ b/Source/Editor/CustomEditors/Elements/Container/VerticalPanelElement.cs
@@ -1,5 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.CustomEditors.Elements
@@ -13,7 +14,10 @@ namespace FlaxEditor.CustomEditors.Elements
///
/// The panel.
///
- public readonly VerticalPanel Panel = new VerticalPanel();
+ public readonly VerticalPanel Panel = new VerticalPanel
+ {
+ Pivot = Float2.Zero,
+ };
///
public override ContainerControl ContainerControl => Panel;
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 7f2351426..ee9251a69 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -851,6 +851,11 @@ namespace FlaxEditor
{
LogWarning("Exception: " + ex.Message);
LogWarning(ex.StackTrace);
+ if (ex.InnerException != null)
+ {
+ LogWarning("Inner exception:");
+ LogWarning(ex.InnerException);
+ }
}
///
@@ -1509,7 +1514,8 @@ namespace FlaxEditor
var win = Windows.GameWin?.Root;
if (win?.RootWindow is WindowRootControl root)
{
- pos = Float2.Round(Windows.GameWin.Viewport.PointFromScreen(pos) * root.DpiScale);
+ pos = Windows.GameWin.Viewport.PointFromScreen(pos);
+ pos = Float2.Round(pos);
}
else
{
@@ -1522,7 +1528,8 @@ namespace FlaxEditor
var win = Windows.GameWin?.Root;
if (win?.RootWindow is WindowRootControl root)
{
- pos = Float2.Round(Windows.GameWin.Viewport.PointToScreen(pos / root.DpiScale));
+ pos = Windows.GameWin.Viewport.PointToScreen(pos);
+ pos = Float2.Round(pos);
}
else
{
@@ -1554,6 +1561,7 @@ namespace FlaxEditor
else
result = gameWin.Viewport.Size;
+ result *= root.DpiScale;
result = Float2.Round(result);
}
}
diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs
index 937d6232f..88ae18df7 100644
--- a/Source/Editor/GUI/Docking/DockWindow.cs
+++ b/Source/Editor/GUI/Docking/DockWindow.cs
@@ -464,7 +464,7 @@ namespace FlaxEditor.GUI.Docking
{
base.Focus();
- SelectTab();
+ SelectTab(false);
BringToFront();
}
diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs
index e23bb27f7..6034a8559 100644
--- a/Source/Editor/GUI/ItemsListContextMenu.cs
+++ b/Source/Editor/GUI/ItemsListContextMenu.cs
@@ -294,6 +294,7 @@ namespace FlaxEditor.GUI
Parent = _scrollPanel,
AnchorPreset = AnchorPresets.HorizontalStretchTop,
IsScrollable = true,
+ Pivot = Float2.Zero,
};
}
diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs
index 8dabba251..1575a82d5 100644
--- a/Source/Editor/GUI/Timeline/Timeline.cs
+++ b/Source/Editor/GUI/Timeline/Timeline.cs
@@ -833,6 +833,7 @@ namespace FlaxEditor.GUI.Timeline
{
AutoFocus = false,
AnchorPreset = AnchorPresets.HorizontalStretchTop,
+ Pivot = Float2.Zero,
Offsets = Margin.Zero,
IsScrollable = true,
BottomMargin = 40.0f,
diff --git a/Source/Editor/Gizmo/EditorPrimitives.cs b/Source/Editor/Gizmo/EditorPrimitives.cs
index bfc32a638..8172a5eb2 100644
--- a/Source/Editor/Gizmo/EditorPrimitives.cs
+++ b/Source/Editor/Gizmo/EditorPrimitives.cs
@@ -35,7 +35,7 @@ namespace FlaxEditor.Gizmo
///
public EditorPrimitives()
{
- Order = -100;
+ Order = 100;
}
///
diff --git a/Source/Editor/Gizmo/ViewportRubberBandSelector.cs b/Source/Editor/Gizmo/ViewportRubberBandSelector.cs
index 0381c6535..36b1884ad 100644
--- a/Source/Editor/Gizmo/ViewportRubberBandSelector.cs
+++ b/Source/Editor/Gizmo/ViewportRubberBandSelector.cs
@@ -11,7 +11,7 @@ namespace FlaxEngine.Gizmo;
///
/// Class for adding viewport rubber band selection.
///
-public class ViewportRubberBandSelector
+public sealed class ViewportRubberBandSelector
{
private bool _isMosueCaptured;
private bool _isRubberBandSpanning;
@@ -38,7 +38,7 @@ public class ViewportRubberBandSelector
/// True if selection started, otherwise false.
public bool TryStartingRubberBandSelection()
{
- if (!_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
+ if (!_isRubberBandSpanning && _owner.Gizmos.Active != null && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
{
_tryStartRubberBand = true;
return true;
@@ -90,7 +90,7 @@ public class ViewportRubberBandSelector
_rubberBandRect = new Rectangle(_cachedStartingMousePosition, Float2.Zero);
_tryStartRubberBand = false;
}
- else if (_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
+ else if (_isRubberBandSpanning && _owner.Gizmos.Active != null && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
{
_rubberBandRect.Width = mousePosition.X - _cachedStartingMousePosition.X;
_rubberBandRect.Height = mousePosition.Y - _cachedStartingMousePosition.Y;
@@ -162,8 +162,8 @@ public class ViewportRubberBandSelector
projection.Init(_owner.Viewport);
foreach (var node in nodes)
{
- // Check for custom can select code
- if (!node.CanSelectActorNodeWithSelector())
+ // Skip actors that cannot be selected
+ if (!node.CanSelectInViewport)
continue;
var a = node.Actor;
@@ -232,30 +232,15 @@ public class ViewportRubberBandSelector
}
///
- /// Used to draw the rubber band. Begins render 2D.
+ /// Draws the ruber band during owner viewport UI drawing.
///
- /// The GPU Context.
- /// The GPU texture target.
- /// The GPU texture target depth.
- public void Draw(GPUContext context, GPUTexture target, GPUTexture targetDepth)
- {
- // Draw RubberBand for rect selection
- if (!_isRubberBandSpanning)
- return;
- Render2D.Begin(context, target, targetDepth);
- Draw2D();
- Render2D.End();
- }
-
- ///
- /// Used to draw the rubber band. Use if already rendering 2D context.
- ///
- public void Draw2D()
+ public void Draw()
{
if (!_isRubberBandSpanning)
return;
- Render2D.FillRectangle(_rubberBandRect, Style.Current.Selection);
- Render2D.DrawRectangle(_rubberBandRect, Style.Current.SelectionBorder);
+ var style = Style.Current;
+ Render2D.FillRectangle(_rubberBandRect, style.Selection);
+ Render2D.DrawRectangle(_rubberBandRect, style.SelectionBorder);
}
///
diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs
index 4de7b593b..a21fb3b53 100644
--- a/Source/Editor/Modules/ContentDatabaseModule.cs
+++ b/Source/Editor/Modules/ContentDatabaseModule.cs
@@ -21,6 +21,7 @@ namespace FlaxEditor.Modules
private bool _enableEvents;
private bool _isDuringFastSetup;
private bool _rebuildFlag;
+ private bool _rebuildInitFlag;
private int _itemsCreated;
private int _itemsDeleted;
private readonly HashSet _dirtyNodes = new HashSet();
@@ -61,7 +62,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 +89,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)
@@ -817,6 +821,7 @@ namespace FlaxEditor.Modules
Profiler.BeginEvent("ContentDatabase.Rebuild");
var startTime = Platform.TimeSeconds;
_rebuildFlag = false;
+ _rebuildInitFlag = false;
_enableEvents = false;
// Load all folders
@@ -1230,8 +1235,6 @@ namespace FlaxEditor.Modules
LoadProjects(Game.Project);
}
- RebuildInternal();
-
Editor.ContentImporting.ImportFileEnd += (obj, failed) =>
{
var path = obj.ResultUrl;
@@ -1239,6 +1242,15 @@ namespace FlaxEditor.Modules
FlaxEngine.Scripting.InvokeOnUpdate(() => OnImportFileDone(path));
};
_enableEvents = true;
+ _rebuildInitFlag = true;
+ }
+
+ ///
+ public override void OnEndInit()
+ {
+ // Handle init when project was loaded without scripts loading ()
+ if (_rebuildInitFlag)
+ RebuildInternal();
}
private void OnImportFileDone(string path)
@@ -1313,6 +1325,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 +1398,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 65821cd43..d235a792e 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/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs
index 850e4df3d..0d37a464f 100644
--- a/Source/Editor/Modules/SceneModule.cs
+++ b/Source/Editor/Modules/SceneModule.cs
@@ -58,7 +58,7 @@ namespace FlaxEditor.Modules
: base(editor)
{
// After editor cache but before the windows
- InitOrder = -900;
+ InitOrder = -800;
}
///
diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs
index 928c02dcc..2fb382dd6 100644
--- a/Source/Editor/Modules/UIModule.cs
+++ b/Source/Editor/Modules/UIModule.cs
@@ -160,7 +160,7 @@ namespace FlaxEditor.Modules
internal UIModule(Editor editor)
: base(editor)
{
- InitOrder = -90;
+ InitOrder = -70;
VisjectSurfaceBackground = FlaxEngine.Content.LoadAsyncInternal("Editor/VisjectSurface");
ColorValueBox.ShowPickColorDialog += ShowPickColorDialog;
}
diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs
index fa7a04d9b..6c4af3b2f 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()
{
@@ -803,43 +808,64 @@ 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)
+ {
+ AddToRestore(win, win.GetType(), new WindowRestoreData
+ {
+ AssetItemID = win.Item.ID,
+ });
+ }
+
internal void AddToRestore(CustomEditorWindow win)
{
- var type = win.GetType();
+ AddToRestore(win.Window, win.GetType(), new WindowRestoreData());
+ }
+ private void AddToRestore(EditorWindow win, Type type, WindowRestoreData winData)
+ {
// Validate if can restore type
var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (constructor == null || type.IsGenericType)
return;
- var winData = new WindowRestoreData();
- var panel = win.Window.ParentDockPanel;
+ 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.
- winData.SelectOnShow = panel.SelectedTab == win.Window;
- if (panel is FloatWindowDockPanel)
+ 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;
- var window = win.Window.RootWindow.Window;
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
+ }
+ else
{
winData.DockState = panel.TryGetDockState(out var splitterValue);
winData.DockedTo = panel.ParentDockPanel;
@@ -851,38 +877,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;
}
}
}
@@ -893,6 +974,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();
}
@@ -1048,7 +1134,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/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs
index 0740fb84f..7d279f86b 100644
--- a/Source/Editor/SceneGraph/ActorNode.cs
+++ b/Source/Editor/SceneGraph/ActorNode.cs
@@ -192,7 +192,7 @@ namespace FlaxEditor.SceneGraph
GetAllChildActorNodes(nodes);
return nodes.ToArray();
}
-
+
///
/// Get all nested actor nodes under this actor node.
///
@@ -213,12 +213,18 @@ namespace FlaxEditor.SceneGraph
}
///
- /// Whether an actor node can be selected with a selector.
+ /// Whether an actor node can be selected with a selector inside editor viewport.
///
- /// True if the actor node can be selected
- public virtual bool CanSelectActorNodeWithSelector()
+ public virtual bool CanSelectInViewport
{
- return Actor && Actor.HideFlags is not (HideFlags.DontSelect or HideFlags.FullyHidden) && Actor is not EmptyActor && IsActive;
+ get
+ {
+ var actor = Actor;
+ return actor &&
+ actor.IsActiveInHierarchy &&
+ (actor.HideFlags & HideFlags.DontSelect) == HideFlags.None &&
+ actor.GetType() != typeof(EmptyActor);
+ }
}
///
diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs
index 9e380ff91..88ae8b892 100644
--- a/Source/Editor/SceneGraph/Actors/SceneNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs
@@ -33,12 +33,6 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
- ///
- public override bool CanSelectActorNodeWithSelector()
- {
- return false;
- }
-
///
/// Gets the scene.
///
@@ -53,6 +47,9 @@ namespace FlaxEditor.SceneGraph.Actors
{
}
+ ///
+ public override bool CanSelectInViewport => false;
+
///
public override bool CanCreatePrefab => false;
diff --git a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
index 428341477..fcea90d19 100644
--- a/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
+++ b/Source/Editor/SceneGraph/Actors/StaticModelNode.cs
@@ -169,7 +169,6 @@ namespace FlaxEditor.SceneGraph.Actors
var actor = new BoxCollider
{
StaticFlags = staticModelNode.Actor.StaticFlags,
- Transform = staticModelNode.Actor.Transform,
};
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
@@ -180,7 +179,6 @@ namespace FlaxEditor.SceneGraph.Actors
var actor = new SphereCollider
{
StaticFlags = staticModelNode.Actor.StaticFlags,
- Transform = staticModelNode.Actor.Transform,
};
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
createdNodes.Add(window is PrefabWindow pWindow ? pWindow.Graph.Root.Find(actor) : Editor.Instance.Scene.GetActorNode(actor));
@@ -191,7 +189,6 @@ namespace FlaxEditor.SceneGraph.Actors
var actor = new BoxCollider
{
StaticFlags = staticModelNode.Actor.StaticFlags,
- Transform = staticModelNode.Actor.Transform,
Size = new Float3(100.0f, 100.0f, 1.0f),
};
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
@@ -203,7 +200,6 @@ namespace FlaxEditor.SceneGraph.Actors
var actor = new CapsuleCollider
{
StaticFlags = staticModelNode.Actor.StaticFlags,
- Transform = staticModelNode.Actor.Transform,
Radius = 25.0f,
Height = 50.0f,
};
@@ -220,7 +216,6 @@ namespace FlaxEditor.SceneGraph.Actors
var actor = new MeshCollider
{
StaticFlags = staticModelNode.Actor.StaticFlags,
- Transform = staticModelNode.Actor.Transform,
CollisionData = collisionData,
};
staticModelNode.Root.Spawn(actor, staticModelNode.Actor);
diff --git a/Source/Editor/SceneGraph/Actors/UICanvasNode.cs b/Source/Editor/SceneGraph/Actors/UICanvasNode.cs
index ef7663e5d..af1406e5d 100644
--- a/Source/Editor/SceneGraph/Actors/UICanvasNode.cs
+++ b/Source/Editor/SceneGraph/Actors/UICanvasNode.cs
@@ -80,9 +80,6 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override bool CanSelectActorNodeWithSelector()
- {
- return Actor is UICanvas uiCanvas && uiCanvas.Is3D && base.CanSelectActorNodeWithSelector();
- }
+ public override bool CanSelectInViewport => base.CanSelectInViewport && Actor is UICanvas uiCanvas && uiCanvas.Is3D;
}
}
diff --git a/Source/Editor/SceneGraph/Actors/UIControlNode.cs b/Source/Editor/SceneGraph/Actors/UIControlNode.cs
index 57a956665..677571d92 100644
--- a/Source/Editor/SceneGraph/Actors/UIControlNode.cs
+++ b/Source/Editor/SceneGraph/Actors/UIControlNode.cs
@@ -42,29 +42,31 @@ namespace FlaxEditor.SceneGraph.Actors
}
///
- public override bool CanSelectActorNodeWithSelector()
+ public override bool CanSelectInViewport
{
- // Check if control and skip if canvas is 2D
- if (Actor is not UIControl uiControl)
- return false;
- UICanvas canvas = null;
- var controlParent = uiControl.Parent;
- while (controlParent != null && controlParent is not Scene)
+ get
{
- if (controlParent is UICanvas uiCanvas)
- {
- canvas = uiCanvas;
- break;
- }
- controlParent = controlParent.Parent;
- }
-
- if (canvas != null)
- {
- if (canvas.Is2D)
+ // Check if control and skip if canvas is 2D
+ if (Actor is not UIControl uiControl)
return false;
+ UICanvas canvas = null;
+ var controlParent = uiControl.Parent;
+ while (controlParent != null && controlParent is not Scene)
+ {
+ if (controlParent is UICanvas uiCanvas)
+ {
+ canvas = uiCanvas;
+ break;
+ }
+ controlParent = controlParent.Parent;
+ }
+ if (canvas != null)
+ {
+ if (canvas.Is2D)
+ return false;
+ }
+ return base.CanSelectInViewport;
}
- return base.CanSelectActorNodeWithSelector();
}
}
}
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/States/ChangingScenesState.cs b/Source/Editor/States/ChangingScenesState.cs
index 17fa30ff7..e1a95e6ab 100644
--- a/Source/Editor/States/ChangingScenesState.cs
+++ b/Source/Editor/States/ChangingScenesState.cs
@@ -164,10 +164,21 @@ namespace FlaxEditor.States
{
Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state.");
- // Bind events
- Level.SceneLoaded += OnSceneEvent;
- Level.SceneLoadError += OnSceneEvent;
- Level.SceneUnloaded += OnSceneEvent;
+ // Bind events, only bind loading event and error if re-loading the same scene to avoid issues.
+ if (_scenesToUnload.Count == 1 && _scenesToLoad.Count == 1)
+ {
+ if (_scenesToLoad[0] == _scenesToUnload[0].ID)
+ {
+ Level.SceneLoaded += OnSceneEvent;
+ Level.SceneLoadError += OnSceneEvent;
+ }
+ }
+ else
+ {
+ Level.SceneLoaded += OnSceneEvent;
+ Level.SceneLoadError += OnSceneEvent;
+ Level.SceneUnloaded += OnSceneEvent;
+ }
// Push scenes changing requests
for (int i = 0; i < _scenesToUnload.Count; i++)
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/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
index 21aa9ca7f..c835e658c 100644
--- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs
+++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs
@@ -235,6 +235,7 @@ namespace FlaxEditor.Surface.ContextMenu
{
Parent = panel1,
AnchorPreset = AnchorPresets.HorizontalStretchTop,
+ Pivot = Float2.Zero,
IsScrollable = true,
};
_groupsPanel = panel2;
@@ -292,6 +293,7 @@ namespace FlaxEditor.Surface.ContextMenu
X = 8,
Width = Width * 0.5f - 16,
AutoSize = true,
+ Pivot = Float2.Zero,
};
_descriptionOutputPanel = new VerticalPanel()
@@ -300,6 +302,7 @@ namespace FlaxEditor.Surface.ContextMenu
X = Width * 0.5f + 8,
Width = Width * 0.5f - 16,
AutoSize = true,
+ Pivot = Float2.Zero,
};
}
diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs
index d794d1642..08dd8e6fd 100644
--- a/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs
+++ b/Source/Editor/Surface/ContextMenu/VisjectCMGroup.cs
@@ -43,6 +43,7 @@ namespace FlaxEditor.Surface.ContextMenu
/// The group archetype.
public VisjectCMGroup(VisjectCM cm, GroupArchetype archetype)
{
+ Pivot = Float2.Zero;
ContextMenu = cm;
Archetypes.Add(archetype);
Name = archetype.Name;
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/Tools/Foliage/FoliageTypesTab.cs b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs
index f7a9d1ed3..93dc05de8 100644
--- a/Source/Editor/Tools/Foliage/FoliageTypesTab.cs
+++ b/Source/Editor/Tools/Foliage/FoliageTypesTab.cs
@@ -352,6 +352,7 @@ namespace FlaxEditor.Tools.Foliage
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = new Margin(4, 4, 4, 0),
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = splitPanel.Panel1
};
diff --git a/Source/Editor/Tools/Foliage/PaintTab.cs b/Source/Editor/Tools/Foliage/PaintTab.cs
index d1ff4cd81..29af974e8 100644
--- a/Source/Editor/Tools/Foliage/PaintTab.cs
+++ b/Source/Editor/Tools/Foliage/PaintTab.cs
@@ -204,6 +204,7 @@ namespace FlaxEditor.Tools.Foliage
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = new Margin(4, 4, 4, 0),
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = splitPanel.Panel1
};
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index 12c5ebfd0..5daa78fd2 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -1211,6 +1211,7 @@ namespace FlaxEditor.Utilities
{
Parent = panel1,
AnchorPreset = AnchorPresets.HorizontalStretchTop,
+ Pivot = Float2.Zero,
IsScrollable = true,
};
tree = new Tree(false)
diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs
index 943bb6e3c..ab1b8b312 100644
--- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs
+++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs
@@ -7,14 +7,11 @@ using FlaxEditor.Gizmo;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph;
using FlaxEditor.Scripting;
-using FlaxEditor.Tools;
using FlaxEditor.Viewport.Modes;
using FlaxEditor.Windows;
using FlaxEngine;
using FlaxEngine.Gizmo;
using FlaxEngine.GUI;
-using FlaxEngine.Tools;
-
using Object = FlaxEngine.Object;
namespace FlaxEditor.Viewport
@@ -26,10 +23,8 @@ namespace FlaxEditor.Viewport
public class MainEditorGizmoViewport : EditorGizmoViewport, IEditorPrimitivesOwner
{
private readonly Editor _editor;
-
private readonly ContextMenuButton _showGridButton;
private readonly ContextMenuButton _showNavigationButton;
-
private SelectionOutline _customSelectionOutline;
///
@@ -218,7 +213,7 @@ namespace FlaxEditor.Viewport
TransformGizmo.ApplyTransformation += ApplyTransform;
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
Gizmos.Active = TransformGizmo;
-
+
// Add rubber band selector
_rubberBandSelector = new ViewportRubberBandSelector(this);
@@ -375,10 +370,7 @@ namespace FlaxEditor.Viewport
{
Gizmos[i].Draw(ref renderContext);
}
-
- // Draw RubberBand for rect selection
- _rubberBandSelector.Draw(context, target, targetDepth);
-
+
// Draw selected objects debug shapes and visuals
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
{
@@ -594,6 +586,15 @@ namespace FlaxEditor.Viewport
}
}
+ ///
+ public override void Draw()
+ {
+ base.Draw();
+
+ // Draw rubber band for rectangle selection
+ _rubberBandSelector.Draw();
+ }
+
///
protected override void OrientViewport(ref Quaternion orientation)
{
@@ -609,7 +610,8 @@ namespace FlaxEditor.Viewport
base.OnMouseMove(location);
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
- bool canStart = !((Gizmos.Active.IsControllingMouse || Gizmos.Active is VertexPaintingGizmo || Gizmos.Active is ClothPaintingGizmo) || IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown);
+ bool canStart = !(IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown) &&
+ Gizmos.Active is TransformGizmo && !Gizmos.Active.IsControllingMouse;
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos, ViewFrustum);
}
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 0df851f5b..fe94b1b86 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 5bc6e62c5..e1bd19609 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();
}
@@ -307,6 +309,20 @@ namespace FlaxEditor.Windows.Assets
base.OnAssetLoadFailed();
}
+ ///
+ public override void OnLostFocus()
+ {
+ base.OnLostFocus();
+ _optionsCM?.Dispose();
+ }
+
+ ///
+ public override void OnExit()
+ {
+ base.OnExit();
+ _optionsCM?.Dispose();
+ }
+
///
public override void OnItemReimported(ContentItem item)
{
@@ -329,6 +345,7 @@ namespace FlaxEditor.Windows.Assets
_isRegisteredForScriptsReload = false;
ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin;
}
+ _optionsCM?.Dispose();
_typeText = null;
}
}
diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs
index 191a6911e..097d56af2 100644
--- a/Source/Editor/Windows/Assets/PrefabWindow.cs
+++ b/Source/Editor/Windows/Assets/PrefabWindow.cs
@@ -198,7 +198,6 @@ namespace FlaxEditor.Windows.Assets
Editor.Prefabs.PrefabApplied += OnPrefabApplied;
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
- ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
// Setup input actions
InputActions.Add(options => options.Undo, () =>
@@ -291,8 +290,10 @@ namespace FlaxEditor.Windows.Assets
return false;
}
- private void OnScriptsReloadBegin()
+ ///
+ protected override void OnScriptsReloadBegin()
{
+ base.OnScriptsReloadBegin();
_isScriptsReloading = true;
if (_asset == null || !_asset.IsLoaded)
@@ -320,19 +321,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)
@@ -555,7 +545,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/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs
index 6eddcb81c..eaf5b792f 100644
--- a/Source/Editor/Windows/DebugLogWindow.cs
+++ b/Source/Editor/Windows/DebugLogWindow.cs
@@ -390,6 +390,7 @@ namespace FlaxEditor.Windows
_entriesPanel = new VerticalPanel
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
+ Pivot = Float2.Zero,
Offsets = Margin.Zero,
IsScrollable = true,
Parent = _split.Panel1,
diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs
index cfa74d338..69359b349 100644
--- a/Source/Editor/Windows/OutputLogWindow.cs
+++ b/Source/Editor/Windows/OutputLogWindow.cs
@@ -582,7 +582,7 @@ namespace FlaxEditor.Windows
private void OnOutputTextChanged()
{
- if (IsLayoutLocked)
+ if (IsLayoutLocked || _output == null)
return;
_hScroll.Maximum = Mathf.Max(_output.TextSize.X, _hScroll.Minimum);
diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs
index 25dfb773c..282c1dfe9 100644
--- a/Source/Editor/Windows/PluginsWindow.cs
+++ b/Source/Editor/Windows/PluginsWindow.cs
@@ -162,11 +162,13 @@ namespace FlaxEditor.Windows
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
Parent = this,
};
var panel = new VerticalPanel
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
+ Pivot = Float2.Zero,
Offsets = Margin.Zero,
IsScrollable = true,
Parent = scroll,
@@ -187,6 +189,7 @@ namespace FlaxEditor.Windows
var vp = new Panel
{
AnchorPreset = AnchorPresets.StretchAll,
+ Offsets = Margin.Zero,
Parent = this,
};
_addPluginProjectButton = new Button
diff --git a/Source/Editor/Windows/Profiler/Assets.cs b/Source/Editor/Windows/Profiler/Assets.cs
index bfd14a8ac..3115aa967 100644
--- a/Source/Editor/Windows/Profiler/Assets.cs
+++ b/Source/Editor/Windows/Profiler/Assets.cs
@@ -68,6 +68,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs
index 7e3444bf0..2db0258fc 100644
--- a/Source/Editor/Windows/Profiler/CPU.cs
+++ b/Source/Editor/Windows/Profiler/CPU.cs
@@ -88,6 +88,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs
index 58fbc043c..baaf9708e 100644
--- a/Source/Editor/Windows/Profiler/GPU.cs
+++ b/Source/Editor/Windows/Profiler/GPU.cs
@@ -65,6 +65,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/Memory.cs b/Source/Editor/Windows/Profiler/Memory.cs
index 5b026fa20..1b594fdf8 100644
--- a/Source/Editor/Windows/Profiler/Memory.cs
+++ b/Source/Editor/Windows/Profiler/Memory.cs
@@ -2,6 +2,7 @@
#if USE_PROFILER
using System;
+using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Windows.Profiler
@@ -29,6 +30,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs
index 205af8f58..b4fae80c3 100644
--- a/Source/Editor/Windows/Profiler/MemoryGPU.cs
+++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs
@@ -69,6 +69,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs
index 68c94186b..af248fcc6 100644
--- a/Source/Editor/Windows/Profiler/Network.cs
+++ b/Source/Editor/Windows/Profiler/Network.cs
@@ -63,6 +63,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/Overall.cs b/Source/Editor/Windows/Profiler/Overall.cs
index e70c01a54..14d423aec 100644
--- a/Source/Editor/Windows/Profiler/Overall.cs
+++ b/Source/Editor/Windows/Profiler/Overall.cs
@@ -33,6 +33,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/Profiler/Physics.cs b/Source/Editor/Windows/Profiler/Physics.cs
index 3e5ec6605..c44a26beb 100644
--- a/Source/Editor/Windows/Profiler/Physics.cs
+++ b/Source/Editor/Windows/Profiler/Physics.cs
@@ -33,6 +33,7 @@ namespace FlaxEditor.Windows.Profiler
{
AnchorPreset = AnchorPresets.HorizontalStretchTop,
Offsets = Margin.Zero,
+ Pivot = Float2.Zero,
IsScrollable = true,
Parent = panel,
};
diff --git a/Source/Editor/Windows/SceneTreeWindow.RenameWindow.cs b/Source/Editor/Windows/SceneTreeWindow.RenameWindow.cs
index 6278a5f3b..fff8bcb04 100644
--- a/Source/Editor/Windows/SceneTreeWindow.RenameWindow.cs
+++ b/Source/Editor/Windows/SceneTreeWindow.RenameWindow.cs
@@ -92,6 +92,7 @@ namespace FlaxEditor.Windows
Parent = this,
AnchorPreset = AnchorPresets.StretchAll,
Offset = Vector2.Zero,
+ Pivot = Float2.Zero,
AutoSize = false,
Bounds = Rectangle.Empty
};
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/Animations/AnimationData.cpp b/Source/Engine/Animations/AnimationData.cpp
index e1115ea53..da285200a 100644
--- a/Source/Engine/Animations/AnimationData.cpp
+++ b/Source/Engine/Animations/AnimationData.cpp
@@ -73,7 +73,7 @@ void AnimationData::Swap(AnimationData& other)
Channels.Swap(other.Channels);
}
-void AnimationData::Dispose()
+void AnimationData::Release()
{
Name.Clear();
Duration = 0.0;
diff --git a/Source/Engine/Animations/AnimationData.h b/Source/Engine/Animations/AnimationData.h
index c37396243..1cf267a3d 100644
--- a/Source/Engine/Animations/AnimationData.h
+++ b/Source/Engine/Animations/AnimationData.h
@@ -170,5 +170,5 @@ public:
///
/// Releases data.
///
- void Dispose();
+ void Release();
};
diff --git a/Source/Engine/Content/Assets/Animation.cpp b/Source/Engine/Content/Assets/Animation.cpp
index 856586129..be5768af2 100644
--- a/Source/Engine/Content/Assets/Animation.cpp
+++ b/Source/Engine/Content/Assets/Animation.cpp
@@ -738,7 +738,7 @@ void Animation::unload(bool isReloading)
Level::ScriptsReloadStart.Unbind(this);
}
#endif
- Data.Dispose();
+ Data.Release();
for (const auto& e : Events)
{
for (const auto& k : e.Second.GetKeyframes())
diff --git a/Source/Engine/Content/Assets/AnimationGraph.cpp b/Source/Engine/Content/Assets/AnimationGraph.cpp
index 37996b5aa..d894a2c02 100644
--- a/Source/Engine/Content/Assets/AnimationGraph.cpp
+++ b/Source/Engine/Content/Assets/AnimationGraph.cpp
@@ -203,7 +203,7 @@ void AnimationGraph::FindDependencies(AnimGraphBase* graph)
{
for (const auto& node : graph->Nodes)
{
- if (node.Type == GRAPH_NODE_MAKE_TYPE(9, 24))
+ if (node.Type == GRAPH_NODE_MAKE_TYPE(9, 24) && node.Assets.Count() > 0)
{
const auto function = node.Assets[0].As();
if (function)
diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp
index 888cc303e..65bda2cb4 100644
--- a/Source/Engine/ContentImporters/ImportModel.cpp
+++ b/Source/Engine/ContentImporters/ImportModel.cpp
@@ -24,6 +24,7 @@
#include "Engine/Level/Scripts/ModelPrefab.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Utilities/RectPack.h"
+#include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "AssetsImportingManager.h"
@@ -171,6 +172,33 @@ bool SortMeshGroups(IGrouping const& i1, IGroupingGetType().GetDefaultInstance();
+ src->Serialize(writer, defaultInstance);
+ writer.EndObject();
+
+ // Parse json
+ rapidjson_flax::Document document;
+ document.Parse(buffer.GetString(), buffer.GetSize());
+
+ // Strip unwanted data
+ document.RemoveMember("ID");
+ document.RemoveMember("ParentID");
+ document.RemoveMember("PrefabID");
+ document.RemoveMember("PrefabObjectID");
+ if (stripName)
+ document.RemoveMember("Name");
+
+ // Deserialize destination
+ auto modifier = Cache::ISerializeModifier.Get();
+ dst->Deserialize(document, &*modifier);
+}
+
CreateAssetResult ImportModel::Import(CreateAssetContext& context)
{
// Get import options
@@ -659,6 +687,8 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
// Create prefab structure
Dictionary nodeToActor;
+ Dictionary newPrefabObjects; // Maps prefab object id to the restored and linked object
+ rapidjson_flax::StringBuffer jsonBuffer;
Array nodeActors;
Actor* rootActor = nullptr;
for (int32 nodeIndex = 0; nodeIndex < data.Nodes.Count(); nodeIndex++)
@@ -749,7 +779,6 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
// Link with object from prefab (if reimporting)
if (prefab)
{
- rapidjson_flax::StringBuffer buffer;
for (Actor* a : nodeActors)
{
for (const auto& i : prefab->ObjectsCache)
@@ -761,33 +790,12 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
continue;
// Preserve local changes made in the prefab
- {
- // Serialize
- buffer.Clear();
- CompactJsonWriter writer(buffer);
- writer.StartObject();
- const void* defaultInstance = o->GetType().GetDefaultInstance();
- o->Serialize(writer, defaultInstance);
- writer.EndObject();
-
- // Parse json
- rapidjson_flax::Document document;
- document.Parse(buffer.GetString(), buffer.GetSize());
-
- // Strip unwanted data
- document.RemoveMember("ID");
- document.RemoveMember("ParentID");
- document.RemoveMember("PrefabID");
- document.RemoveMember("PrefabObjectID");
- document.RemoveMember("Name");
-
- // Deserialize object
- auto modifier = Cache::ISerializeModifier.Get();
- a->Deserialize(document, &*modifier);
- }
+ CloneObject(jsonBuffer, o, a, true);
// Mark as this object already exists in prefab so will be preserved when updating it
- a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID());
+ const Guid prefabObjectId = o->GetPrefabObjectID();
+ a->LinkPrefab(o->GetPrefabID(), prefabObjectId);
+ newPrefabObjects.Add(prefabObjectId, a);
break;
}
}
@@ -808,12 +816,43 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, const M
{
if (i.Value->GetTypeHandle() == modelPrefabScript->GetTypeHandle())
{
- modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), i.Value->GetPrefabObjectID());
+ const Guid prefabObjectId = i.Value->GetPrefabObjectID();
+ modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
+ newPrefabObjects.Add(prefabObjectId, modelPrefabScript);
break;
}
}
}
}
+ if (prefab)
+ {
+ // Preserve existing objects added by user (eg. colliders, sfx, vfx, scripts)
+ for (const auto& i : prefab->ObjectsCache)
+ {
+ // Skip already restored objects
+ const Guid prefabObjectId = i.Key;
+ if (newPrefabObjects.ContainsKey(prefabObjectId))
+ continue;
+ SceneObject* defaultObject = i.Value;
+ // TODO: ignore objects that were imported previously but not now (eg. mesh was removed from source asset)
+
+ // Find parent to link
+ SceneObject* parent;
+ if (!newPrefabObjects.TryGet(defaultObject->GetParent()->GetPrefabObjectID(), parent))
+ continue;
+
+ // Duplicate object
+ SceneObject* restoredObject = (SceneObject*)Scripting::NewObject(defaultObject->GetTypeHandle());
+ if (!restoredObject)
+ continue;
+ CloneObject(jsonBuffer, defaultObject, restoredObject);
+ restoredObject->SetParent((Actor*)parent);
+
+ // Link with existing prefab instance
+ restoredObject->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
+ newPrefabObjects.Add(prefabObjectId, restoredObject);
+ }
+ }
// Create prefab instead of native asset
bool failed;
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 6c6858a20..245d8a150 100644
--- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs
+++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs
@@ -972,7 +972,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 false
+ // 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.Logger.LogHandler.LogWrite(LogType.Warning, "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
@@ -990,24 +1032,86 @@ 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_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+
+ // Added in .NET runtime 8.0.10, used as the main locking object
+ object s_commonSyncObject = TypeDescriptorType?.GetField("s_commonSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_commonSyncObject == null)
+ s_commonSyncObject = s_providerTable;
+
+ // Removed in .NET runtime 8.0.7
+ object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ object s_defaultProviders = TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_internalSyncObject != null && s_defaultProviders != null)
+ {
+ lock (s_internalSyncObject)
+ InvokeClear(s_defaultProviders);
+ }
+
+ // Replaces s_defaultProviders in 8.0.7
+ object s_defaultProviderInitialized = TypeDescriptorType?.GetField("s_defaultProviderInitialized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_commonSyncObject != null && s_defaultProviderInitialized != null)
+ {
+ lock (s_commonSyncObject)
+ InvokeClear(s_defaultProviderInitialized);
+ }
+
+ object s_providerTypeTable = TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
+ if (s_providerTable != null && s_providerTypeTable != null)
+ {
+ lock (s_commonSyncObject)
+ InvokeClear(s_providerTypeTable);
+ InvokeClear(s_providerTable);
+ }
+
+ static void InvokeClear(object instance)
+ {
+ Type type = instance.GetType();
+ Assertions.Assert.IsTrue(type.Name == "ConcurrentDictionary`2" || type.Name == "Hashtable" || type.Name == "WeakHashtable");
+ type.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear")?.Invoke(instance, Array.Empty
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 30b26760f..2b087bb43 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 (const 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 a0b55376f..953118fca 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 39fdba696..ca5df5f39 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)
@@ -368,6 +385,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/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp
index 14ce087cf..9ac973cc9 100644
--- a/Source/Engine/Scripting/ScriptingObject.cpp
+++ b/Source/Engine/Scripting/ScriptingObject.cpp
@@ -89,7 +89,11 @@ void SerializableScriptingObject::Deserialize(DeserializeStream& stream, ISerial
}
ScriptingObject::ScriptingObject(const SpawnParams& params)
- : _gcHandle(0)
+#if USE_NETCORE
+ : _gcHandle((MGCHandle)params.Managed)
+#elif !COMPILE_WITHOUT_CSHARP
+ : _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0)
+#endif
, _type(params.Type)
, _id(params.ID)
{
diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs
index e23bca199..537463e97 100644
--- a/Source/Engine/UI/GUI/Common/Dropdown.cs
+++ b/Source/Engine/UI/GUI/Common/Dropdown.cs
@@ -473,6 +473,7 @@ namespace FlaxEngine.GUI
{
AnchorPreset = AnchorPresets.StretchAll,
BackgroundColor = Color.Transparent,
+ Pivot = Float2.Zero,
IsScrollable = true,
AutoSize = true,
Parent = popup.MainPanel,
diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs
index dca68948c..7cb3e4d42 100644
--- a/Source/Engine/UI/GUI/Common/Label.cs
+++ b/Source/Engine/UI/GUI/Common/Label.cs
@@ -337,9 +337,7 @@ namespace FlaxEngine.GUI
size.X = _textSize.X + Margin.Width;
if (_autoHeight)
size.Y = _textSize.Y + Margin.Height;
- var pivotRelative = PivotRelative;
- Size = size;
- PivotRelative = pivotRelative;
+ Resize(ref size);
}
}
}
diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs
index 599dcde30..c51557530 100644
--- a/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs
+++ b/Source/Engine/UI/GUI/Common/RichTextBox.Parsing.cs
@@ -233,13 +233,7 @@ namespace FlaxEngine.GUI
{
ref TextBlock textBlock = ref textBlocks[i];
var textBlockSize = textBlock.Bounds.BottomRight - lineOrigin;
- var ascender = textBlock.Ascender;
- //if (ascender <= 0)
- {
- var textBlockFont = textBlock.Style.Font.GetFont();
- if (textBlockFont)
- ascender = textBlockFont.Ascender;
- }
+ var ascender = textBlock.GetAscender();
lineAscender = Mathf.Max(lineAscender, ascender);
lineSize = Float2.Max(lineSize, textBlockSize);
}
@@ -256,13 +250,7 @@ namespace FlaxEngine.GUI
case TextBlockStyle.Alignments.Baseline:
{
// Match the baseline of the line (use ascender)
- var ascender = textBlock.Ascender;
- if (ascender <= 0)
- {
- var textBlockFont = textBlock.Style.Font.GetFont();
- if (textBlockFont)
- ascender = textBlockFont.Ascender;
- }
+ var ascender = textBlock.GetAscender();
vOffset = lineAscender - ascender;
textBlock.Bounds.Location.Y += vOffset;
break;
diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
index 6c5240977..c892f8d2f 100644
--- a/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
+++ b/Source/Engine/UI/GUI/Common/RichTextBox.Tags.cs
@@ -176,7 +176,8 @@ namespace FlaxEngine.GUI
var font = imageBlock.Style.Font.GetFont();
if (font)
imageBlock.Bounds.Size = new Float2(font.Height);
- imageBlock.Bounds.Size.X *= image.Size.X / image.Size.Y; // Keep original aspect ratio
+ var imageSize = image.Size;
+ imageBlock.Bounds.Size.X *= imageSize.X / imageSize.Y; // Keep original aspect ratio
bool hasWidth = TryParseNumberTag(ref tag, "width", imageBlock.Bounds.Width, out var width);
imageBlock.Bounds.Width = width;
bool hasHeight = TryParseNumberTag(ref tag, "height", imageBlock.Bounds.Height, out var height);
@@ -185,9 +186,9 @@ namespace FlaxEngine.GUI
{
// Maintain aspect ratio after scaling by just width or height
if (hasHeight)
- imageBlock.Bounds.Size.X = imageBlock.Bounds.Size.Y * image.Size.X / image.Size.Y;
+ imageBlock.Bounds.Size.X = imageBlock.Bounds.Size.Y * imageSize.X / imageSize.Y;
else
- imageBlock.Bounds.Size.Y = imageBlock.Bounds.Size.X * image.Size.Y / image.Size.X;
+ imageBlock.Bounds.Size.Y = imageBlock.Bounds.Size.X * imageSize.Y / imageSize.X;
}
TryParseNumberTag(ref tag, "scale", 1.0f, out var scale);
imageBlock.Bounds.Size *= scale;
diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs
index 6054250f8..de2711b25 100644
--- a/Source/Engine/UI/GUI/Control.Bounds.cs
+++ b/Source/Engine/UI/GUI/Control.Bounds.cs
@@ -419,6 +419,19 @@ namespace FlaxEngine.GUI
}
}
+ ///
+ /// Resizes the control based on where the pivot is rather than just the top-left.
+ ///
+ [NoAnimate]
+ public void Resize(ref Float2 value)
+ {
+ if (_bounds.Size.Equals(ref value))
+ return;
+ var bounds = new Rectangle(_bounds.Location, value);
+ bounds.Location += (_bounds.Size - value) * Pivot; // Pivot-relative resizing
+ SetBounds(ref bounds);
+ }
+
///
/// Updates the control cached bounds (based on anchors and offsets).
///
diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs
index 53fdd5502..f88cae3a5 100644
--- a/Source/Engine/UI/GUI/Control.cs
+++ b/Source/Engine/UI/GUI/Control.cs
@@ -122,8 +122,6 @@ namespace FlaxEngine.GUI
if (_parent == value)
return;
- Defocus();
-
Float2 oldParentSize;
if (_parent != null)
{
diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs
index e053edd9c..28a1fb8c9 100644
--- a/Source/Engine/UI/GUI/Panels/DropPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs
@@ -585,7 +585,8 @@ namespace FlaxEngine.GUI
_cachedHeight = height;
if (_animationProgress >= 1.0f && _isClosed)
y = minHeight;
- Height = Mathf.Max(minHeight, y);
+ var size = new Float2(Width, Mathf.Max(minHeight, y));
+ Resize(ref size);
}
///
diff --git a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs
index 1c8e646e3..06d185f2d 100644
--- a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs
@@ -78,7 +78,7 @@ namespace FlaxEngine.GUI
size.X = left + right;
if (!ControlChildSize)
size.Y = maxHeight;
- Size = size;
+ Resize(ref size);
}
else if (_alignment != TextAlignment.Near && hasAnyLeft)
{
diff --git a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs
index aae95bd43..d5c6dfb9f 100644
--- a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs
+++ b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs
@@ -78,7 +78,7 @@ namespace FlaxEngine.GUI
size.Y = top + bottom;
if (!ControlChildSize)
size.X = maxWidth;
- Size = size;
+ Resize(ref size);
}
else if (_alignment != TextAlignment.Near && hasAnyTop)
{
diff --git a/Source/Engine/UI/GUI/TextBlock.cs b/Source/Engine/UI/GUI/TextBlock.cs
index a0a5b246b..ac494d895 100644
--- a/Source/Engine/UI/GUI/TextBlock.cs
+++ b/Source/Engine/UI/GUI/TextBlock.cs
@@ -31,5 +31,18 @@ namespace FlaxEngine.GUI
/// The custom tag.
///
public object Tag;
+
+ internal float GetAscender()
+ {
+ float ascender = Ascender;
+ if (Mathf.IsZero(ascender))
+ {
+ // Use ascender from the font
+ var textBlockFont = Style.Font.GetFont();
+ if (textBlockFont)
+ ascender = textBlockFont.Ascender;
+ }
+ return ascender;
+ }
}
}
diff --git a/Source/Engine/UI/GUI/TextBlockStyle.cs b/Source/Engine/UI/GUI/TextBlockStyle.cs
index 7166bc4f1..fb70a7bf6 100644
--- a/Source/Engine/UI/GUI/TextBlockStyle.cs
+++ b/Source/Engine/UI/GUI/TextBlockStyle.cs
@@ -79,7 +79,7 @@ namespace FlaxEngine.GUI
public Color Color;
///
- /// The text shadow color (tint and opacity). Set to transparent to disable shadow drawing.
+ /// The text shadow color (tint and opacity). Transparent color disables shadow drawing.
///
[EditorOrder(30)]
public Color ShadowColor;
diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs
index 40aae0067..8c8e05b43 100644
--- a/Source/Engine/UI/GUI/Tooltip.cs
+++ b/Source/Engine/UI/GUI/Tooltip.cs
@@ -120,6 +120,7 @@ namespace FlaxEngine.GUI
// Unlink
IsLayoutLocked = true;
Parent = null;
+ _showTarget = null;
// Close window
if (_window)
diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp
index a7af9c8c7..f0a2f25e7 100644
--- a/Source/Engine/UI/UICanvas.cpp
+++ b/Source/Engine/UI/UICanvas.cpp
@@ -23,11 +23,11 @@ MMethod* UICanvas_EndPlay = nullptr;
MMethod* UICanvas_ParentChanged = nullptr;
#define UICANVAS_INVOKE(event) \
- auto instance = GetManagedInstance(); \
- if (instance) \
+ auto* managed = GetManagedInstance(); \
+ if (managed) \
{ \
MObject* exception = nullptr; \
- UICanvas_##event->Invoke(instance, nullptr, &exception); \
+ UICanvas_##event->Invoke(managed, nullptr, &exception); \
if (exception) \
{ \
MException ex(exception); \
diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs
index c92b618a9..96443dd74 100644
--- a/Source/Engine/UI/UICanvas.cs
+++ b/Source/Engine/UI/UICanvas.cs
@@ -314,7 +314,8 @@ namespace FlaxEngine
{
_guiRoot = new CanvasRootControl(this)
{
- IsLayoutLocked = false
+ IsLayoutLocked = false,
+ Pivot = Float2.Zero,
};
}
diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp
index 72eed1f27..692843361 100644
--- a/Source/Engine/UI/UIControl.cpp
+++ b/Source/Engine/UI/UIControl.cpp
@@ -22,10 +22,11 @@ MMethod* UIControl_BeginPlay = nullptr;
MMethod* UIControl_EndPlay = nullptr;
#define UICONTROL_INVOKE(event) \
- if (HasManagedInstance()) \
+ auto* managed = GetManagedInstance(); \
+ if (managed) \
{ \
MObject* exception = nullptr; \
- UIControl_##event->Invoke(GetManagedInstance(), nullptr, &exception); \
+ UIControl_##event->Invoke(managed, nullptr, &exception); \
if (exception) \
{ \
MException ex(exception); \
diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs
index 72e3f9625..1d31fcf27 100644
--- a/Source/Engine/UI/UIControl.cs
+++ b/Source/Engine/UI/UIControl.cs
@@ -35,38 +35,58 @@ namespace FlaxEngine
// Set value
_control = value;
+ if (_control == null)
+ return;
- // Link the new one (events and parent)
- if (_control != null)
+ // Setup control
+ var isDuringPlay = IsDuringPlay;
+ _blockEvents = true;
+ var container = _control as ContainerControl;
+ if (container != null)
{
- // Setup control
- _blockEvents = true;
- var containerControl = _control as ContainerControl;
- if (containerControl != null)
- containerControl.UnlockChildrenRecursive();
- _control.Visible = IsActive;
- _control.Parent = GetParent();
- _control.IndexInParent = OrderInParent;
- _control.Location = new Float2(LocalPosition);
- _control.LocationChanged += OnControlLocationChanged;
-
- // Link children UI controls
- if (containerControl != null)
+ if (isDuringPlay)
+ container.UnlockChildrenRecursive(); // Enable layout changes to any dynamically added UI
+ else
+ container.LockChildrenRecursive(); // Block layout changes during deserialization
+ }
+ _control.Visible = IsActive;
+ {
+ var parent = GetParent();
+ if (parent != null && !parent.IsLayoutLocked && !isDuringPlay)
{
- var children = ChildrenCount;
- var parent = Parent;
- for (int i = 0; i < children; i++)
+ // Reparent but prevent layout if we're during pre-game setup (eg. deserialization) to avoid UI breaking during auto-layout resizing
+ parent.IsLayoutLocked = true;
+ _control.Parent = parent;
+ parent.IsLayoutLocked = false;
+ }
+ else
+ {
+ _control.Parent = parent;
+ }
+ }
+ _control.IndexInParent = OrderInParent;
+ _control.Location = new Float2(LocalPosition);
+ _control.LocationChanged += OnControlLocationChanged;
+
+ // Link children UI controls
+ if (container != null)
+ {
+ var children = ChildrenCount;
+ var parent = Parent;
+ for (int i = 0; i < children; i++)
+ {
+ var child = GetChild(i) as UIControl;
+ if (child != null && child.HasControl && child != parent)
{
- var child = GetChild(i) as UIControl;
- if (child != null && child.HasControl && child != parent)
- {
- child.Control.Parent = containerControl;
- }
+ child.Control.Parent = container;
}
}
+ }
- // Refresh
- _blockEvents = false;
+ // Refresh layout
+ _blockEvents = false;
+ if (isDuringPlay)
+ {
if (prevControl == null && _control.Parent != null)
_control.Parent.PerformLayout();
else
@@ -326,11 +346,13 @@ namespace FlaxEngine
{
if ((_control == null || _control.GetType() != controlType) && controlType != null)
{
+ // Create a new control
Control = (Control)Activator.CreateInstance(controlType);
}
if (_control != null)
{
+ // Populate control object with properties
Json.JsonSerializer.Deserialize(_control, json);
// Synchronize actor with control location
@@ -374,16 +396,32 @@ namespace FlaxEngine
internal void BeginPlay()
{
- if (_control != null)
+ var control = _control;
+ if (control == null)
+ return;
+
+ // Setup control
+ control.Visible = IsActive && control.Visible;
+ control.Parent = GetParent();
+ control.IndexInParent = OrderInParent;
+
+ // Setup navigation (all referenced controls are now loaded)
+ Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right);
+ control.NavTargetUp = up?.Control;
+ control.NavTargetDown = down?.Control;
+ control.NavTargetLeft = left?.Control;
+ control.NavTargetRight = right?.Control;
+
+ // Refresh layout (BeginPlay is called for parents first, then skip if already called by outer control)
+ var container = control as ContainerControl;
+ if (control.Parent == null || (container != null && container.IsLayoutLocked))
{
- _control.Visible = IsActive && _control.Visible;
- _control.Parent = GetParent();
- _control.IndexInParent = OrderInParent;
- Internal_GetNavTargets(__unmanagedPtr, out UIControl up, out UIControl down, out UIControl left, out UIControl right);
- _control.NavTargetUp = up?.Control;
- _control.NavTargetDown = down?.Control;
- _control.NavTargetLeft = left?.Control;
- _control.NavTargetRight = right?.Control;
+ if (container != null)
+ {
+ //container.UnlockChildrenRecursive();
+ container.IsLayoutLocked = false; // Forces whole children tree lock/unlock sequence in PerformLayout
+ }
+ control.PerformLayout();
}
}
diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp
index 9bdc5709d..3df80230e 100644
--- a/Source/Engine/Visject/ShaderGraph.cpp
+++ b/Source/Engine/Visject/ShaderGraph.cpp
@@ -364,8 +364,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
// Fmod
case 40:
{
- Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
- Value v2 = tryGetValue(node->GetBox(1), Value::Zero);
+ Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero);
+ Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero);
value = writeFunction2(node, v1, v2, TEXT("fmod"));
break;
}
diff --git a/Source/Tools/Flax.Build/ProjectInfo.cs b/Source/Tools/Flax.Build/ProjectInfo.cs
index c64627e3c..2c3a600dd 100644
--- a/Source/Tools/Flax.Build/ProjectInfo.cs
+++ b/Source/Tools/Flax.Build/ProjectInfo.cs
@@ -260,7 +260,11 @@ namespace Flax.Build
return true;
var rules = Builder.GenerateRulesAssembly();
var target = rules.GetTarget(name);
- return target == null || target.Modules.TrueForAll(x => !rules.GetModule(x).BuildNativeCode);
+ return target == null || target.Modules.TrueForAll(moduleName =>
+ {
+ var module = rules.GetModule(moduleName);
+ return module != null && !module.BuildNativeCode;
+ });
}
///