From 07046a94c13272d87bce180bf0e6e7360eb5e74c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 23 Sep 2026 22:36:23 +0200 Subject: [PATCH] Revert "Merge branch 'GoaLitiuM-asset_window_reload_fix'" Caused regression on various windows being misplaces after hot-reload (eg. BT editor). This reverts commit 470a2220eeb5e31810fb3ad5b667c1b77160ef81, reversing changes made to 4d6fe760c1319300bca0438bc0f6c5061ea68be8. # Conflicts: # Source/Editor/GUI/Docking/FloatWindowDockPanel.cs --- Source/Editor/CustomEditorWindow.cs | 15 +- Source/Editor/GUI/Docking/DockWindow.cs | 48 ---- .../GUI/Docking/FloatWindowDockPanel.cs | 2 +- Source/Editor/Modules/WindowsModule.cs | 216 +++++++++--------- .../Editor/Windows/Assets/JsonAssetWindow.cs | 1 + 5 files changed, 117 insertions(+), 165 deletions(-) diff --git a/Source/Editor/CustomEditorWindow.cs b/Source/Editor/CustomEditorWindow.cs index 4a94e0a34..4711ee9b2 100644 --- a/Source/Editor/CustomEditorWindow.cs +++ b/Source/Editor/CustomEditorWindow.cs @@ -1,6 +1,5 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System; using FlaxEditor.CustomEditors; using FlaxEditor.GUI.Docking; using FlaxEditor.Windows; @@ -92,19 +91,7 @@ namespace FlaxEditor Editor.Instance.Windows.AddToRestore(this); } Window.Close(ClosingReason.ScriptsReload); - } - - /// - /// Reattaches the window control to existing floating window. - /// - /// The window handle. - /// Initial window state. - /// The panel to dock to, if any. - /// Only used if is set. If true the window will be selected after docking it. - /// The splitter value to use if toDock is not null. If not specified, a default value will be used. - public void Restore(IntPtr windowHandle, DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null) - { - _win.Restore(windowHandle, state, toDock, autoSelect, splitterValue); + Window.Dispose(); } /// diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index bf08695d8..a6c98a1f6 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -1,9 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. -using System; using System.Xml; using System.Globalization; -using System.Linq; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; @@ -234,46 +232,6 @@ namespace FlaxEditor.GUI.Docking } } - /// - /// Reattaches the window control to existing floating window. - /// - /// The window handle. - /// Initial window state. - /// Panel to dock to it. - /// Only used if is set. If true the window will be selected after docking it. - /// Only used if is set. The splitter value to use. If not specified, a default value will be used. - public void Restore(IntPtr windowHandle, DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null) - { - if (state != DockState.Float) - { - Show(state, toDock, autoSelect, splitterValue); - return; - } - - Undock(); - - // Find the existing window and remove all controls from it - var window = Editor.GetWindows().First(x => x.NativePtr == windowHandle); - var windowGUI = window.GUI; - while (windowGUI.Children.Count > 0) - windowGUI.Children[^1].Dispose(); - windowGUI.EndTrackingMouse(); - - // Create dock panel for the window - var dockPanel = new FloatWindowDockPanel(_masterPanel, windowGUI); - dockPanel.DockWindowInternal(DockState.DockFill, this); - - // Perform layout - Visible = true; - windowGUI.UnlockChildrenRecursive(); - windowGUI.PerformLayout(); - - OnShow(); - - // Perform layout again - windowGUI.PerformLayout(); - } - /// /// Shows the window. /// @@ -374,12 +332,6 @@ namespace FlaxEditor.GUI.Docking } else { - if (reason == ClosingReason.ScriptsReload && _dockedTo is FloatWindowDockPanel floatPanel) - { - // Unlink the window to keep it alive during scripts reload - floatPanel.UnlinkWindow(); - } - // Undock Undock(); diff --git a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs index f8eb26ded..211715f5b 100644 --- a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs +++ b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs @@ -270,8 +270,8 @@ namespace FlaxEditor.GUI.Docking public override void OnDestroy() { _masterPanel?.FloatingPanels.Remove(this); - UnlinkWindow(); + base.OnDestroy(); } } diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 8074ab814..1a03c7c57 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -34,12 +33,11 @@ namespace FlaxEditor.Modules private float _projectIconScreenshotTimeout = -1; private string _windowsLayoutPath; - private class WindowRestoreData + private struct WindowRestoreData { public string AssemblyName; public string TypeName; - public IntPtr WindowHandle; public DockState DockState; public DockPanel DockedTo; public int DockedTabIndex; @@ -47,10 +45,20 @@ namespace FlaxEditor.Modules public bool SelectOnShow = false; + public bool Maximize; + public bool Minimize; + public Float2 FloatSize; + public Float2 FloatPosition; + public Guid AssetItemID; + + // Constructor, to allow for default values + public WindowRestoreData() + { + } } - private readonly Dictionary _restoreWindows = new(); + private readonly List _restoreWindows = new List(); /// /// The main editor window. @@ -814,14 +822,10 @@ namespace FlaxEditor.Modules internal void AddToRestore(AssetEditorWindow win) { - var assetItemId = win.Item.ID; - if (!_restoreWindows.TryGetValue(assetItemId, out var winData)) + AddToRestore(win, win.GetType(), new WindowRestoreData { - winData = new WindowRestoreData(); - _restoreWindows.Add(assetItemId, winData); - } - winData.AssetItemID = assetItemId; - AddToRestore(win, win.GetType(), winData); + AssetItemID = win.Item.ID, + }); } internal void AddToRestore(CustomEditorWindow win) @@ -832,65 +836,62 @@ namespace FlaxEditor.Modules if (constructor == null || type.IsGenericType) return; - // TODO: Restore data for custom editors - var assetItemId = Guid.NewGuid(); - if (!_restoreWindows.TryGetValue(assetItemId, out var winData)) - { - winData = new WindowRestoreData(); - _restoreWindows.Add(assetItemId, winData); - } - winData.AssetItemID = assetItemId; - AddToRestore(win.Window, type, winData); + AddToRestore(win.Window, type, new WindowRestoreData()); } private void AddToRestore(EditorWindow win, Type type, WindowRestoreData winData) { + // 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; var panel = win.ParentDockPanel; - winData.AssemblyName = type.Assembly.GetName().Name; - winData.TypeName = type.FullName; - if (panel is FloatWindowDockPanel) + winData.SelectOnShow = panel.SelectedTab == win; + winData.DockedTabIndex = 0; + if (panel is FloatWindowDockPanel && window != null && panel.TabsCount == 1) { - // Populate data for other tabs now, the tab index may change after tab is destroyed - if (winData.DockedTo == null) - { - for (int i = 0; i < panel.Tabs.Count; i++) - { - if (panel.Tabs[i] is AssetEditorWindow assetEditorWindow) - { - window ??= assetEditorWindow.RootWindow?.Window; // The window handle is sometimes missing in some tabs - var assetItemId = assetEditorWindow.Item.ID; - if (!_restoreWindows.TryGetValue(assetItemId, out var tabWinData)) - { - tabWinData = new WindowRestoreData(); - _restoreWindows.Add(assetItemId, tabWinData); - } - - tabWinData.DockedTabIndex = i; - tabWinData.SelectOnShow = panel.SelectedTab == assetEditorWindow; - tabWinData.DockState = DockState.DockFill; - tabWinData.DockedTo = panel; - } - } - winData.DockState = DockState.Float; - winData.WindowHandle = window?.NativePtr ?? IntPtr.Zero; - } + winData.DockState = DockState.Float; + winData.FloatPosition = window.Position; + winData.FloatSize = window.ClientSize; + winData.Maximize = window.IsMaximized; + winData.Minimize = window.IsMinimized; + winData.DockedTo = panel; } else { - winData.DockState = panel.TryGetDockState(out var splitterValue); - winData.DockedTo = panel.ParentDockPanel; - winData.SplitterValue = splitterValue; + 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; + _restoreWindows.Add(winData); } private void OnWorkspaceRebuilt() { - var assetEditorWindows = new Dictionary(); - - // Create float windows first, then add docked windows in order of the tabs - foreach (var (_, winData) in _restoreWindows) + // 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); @@ -907,24 +908,75 @@ namespace FlaxEditor.Modules var assetType = assetItem.GetType(); var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType }); var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem }); - var previouslyDockedTo = winData.DockedTo; - win.Restore(winData.WindowHandle, winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue); + + win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue); if (winData.DockState == DockState.Float) { - // Update panel reference in other windows docked to this panel - foreach (var key in _restoreWindows.Keys) + var window = win.RootWindow.Window; + window.Position = winData.FloatPosition; + if (winData.Maximize) { - ref var otherData = ref CollectionsMarshal.GetValueRefOrNullRef(_restoreWindows, key); - if (otherData.DockedTo == previouslyDockedTo) + 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; } } - assetEditorWindows.Add(winData.AssetItemID, win); + 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++; + } + panel.PerformLayout(true); } else { var win = (CustomEditorWindow)Activator.CreateInstance(type); - win.Restore(winData.WindowHandle, winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue); + 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; + } + } } } catch (Exception ex) @@ -933,46 +985,6 @@ namespace FlaxEditor.Modules Editor.LogWarning(string.Format("Failed to restore window {0} (assembly: {1})", winData.TypeName, winData.AssemblyName)); } } - - // Reorder tabs to previous order - foreach (var (_, winData) in _restoreWindows) - { - var win = assetEditorWindows.GetValueOrDefault(winData.AssetItemID); - if (win == null) - continue; - - 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++; - } - } - - // Restore last selected tab - foreach (var (_, winData) in _restoreWindows) - { - var win = assetEditorWindows.GetValueOrDefault(winData.AssetItemID); - if (win != null && winData.SelectOnShow) - { - win.ParentDockPanel.SelectTab(win, false); - win.ParentDockPanel.PerformLayout(true); - } - } // Restored windows stole the focus from Editor if (_restoreWindows.Count > 0) diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index 75acd74dd..5cbf78f2a 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -148,6 +148,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnScriptsReloadBegin() { base.OnScriptsReloadBegin(); + Close(ClosingReason.ScriptsReload); }