From 2723f91905c040a6502a2898e309ce171a47f73f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 21 Jul 2026 23:39:01 +0200 Subject: [PATCH 1/4] Fix errors on Editor shutdown --- Source/Editor/Windows/EditorOptionsWindow.cs | 2 ++ Source/Engine/UI/GUI/WindowRootControl.cs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/EditorOptionsWindow.cs b/Source/Editor/Windows/EditorOptionsWindow.cs index dc12885a5..fee0bf30b 100644 --- a/Source/Editor/Windows/EditorOptionsWindow.cs +++ b/Source/Editor/Windows/EditorOptionsWindow.cs @@ -310,6 +310,8 @@ namespace FlaxEditor.Windows private void OnSelectedTabChanged(Tabs tabs) { + if (IsDisposing) + return; ApplySearchFilter(); } diff --git a/Source/Engine/UI/GUI/WindowRootControl.cs b/Source/Engine/UI/GUI/WindowRootControl.cs index 3b4786db0..739ff2485 100644 --- a/Source/Engine/UI/GUI/WindowRootControl.cs +++ b/Source/Engine/UI/GUI/WindowRootControl.cs @@ -119,7 +119,8 @@ namespace FlaxEngine.GUI /// True if move to the front by force, otherwise false. public void BringToFront(bool force = false) { - _window.BringToFront(force); + if (_window && _window.IsVisible) + _window.BringToFront(force); } /// From 7e48786ffc995c93a8a6893ff4c28a6b4a879a41 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 22 Jul 2026 11:18:32 +0200 Subject: [PATCH 2/4] Add type picker when creating abstract class or interface --- .../CustomEditors/Editors/GenericEditor.cs | 49 ++++++++++++++----- Source/Editor/GUI/Popups/TypeSearchPopup.cs | 28 +++++++++++ 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs index 900fc6c8d..fb16a1d56 100644 --- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs @@ -711,21 +711,44 @@ namespace FlaxEditor.CustomEditors.Editors { // Check if it's an object type that can be created in editor var type = Values.Type; - if (type != ScriptMemberInfo.Null && type.CanCreateInstance) + if (type != ScriptMemberInfo.Null) { - layout = layout.Space(20); - - const float ButtonSize = 14.0f; - var button = new Button + ScriptType[] types = null; + if (type.IsAbstract || type.IsInterface) { - Text = "+", - TooltipText = "Create a new instance of the object", - Size = new Float2(ButtonSize, ButtonSize), - AnchorPreset = AnchorPresets.MiddleRight, - Parent = layout.ContainerControl, - Location = new Float2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f), - }; - button.Clicked += () => SetValue(Values.Type.CreateInstance()); + // Show picker with all types that implement specific class/interface but are not abstract + types = Editor.Instance.CodeEditing.All.Get().Where(x => !x.IsAbstract && x.CanCreateInstance && type.IsAssignableFrom(x)).ToArray(); + } + else if (type.CanCreateInstance) + { + types = [type]; + } + + if (types != null && types.Length != 0) + { + layout = layout.Space(20); + + const float ButtonSize = 14.0f; + var button = new Button + { + Text = "+", + TooltipText = "Create a new instance of the object", + Size = new Float2(ButtonSize, ButtonSize), + AnchorPreset = AnchorPresets.MiddleRight, + Parent = layout.ContainerControl, + Location = new Float2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f), + }; + if (types.Length == 1) + { + // Single type + button.Clicked += () => SetValue(Values.Type.CreateInstance()); + } + else + { + // Picker + button.Clicked += () => FlaxEditor.GUI.TypeSearchPopup.Show(button, new Float2(0, button.Height), types, scriptType => { SetValue(scriptType.CreateInstance()); }); + } + } } layout.Label(""); diff --git a/Source/Editor/GUI/Popups/TypeSearchPopup.cs b/Source/Editor/GUI/Popups/TypeSearchPopup.cs index 8d853e66a..85fcccb56 100644 --- a/Source/Editor/GUI/Popups/TypeSearchPopup.cs +++ b/Source/Editor/GUI/Popups/TypeSearchPopup.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using FlaxEditor.History; @@ -131,6 +132,18 @@ namespace FlaxEditor.GUI SortItems(); } + private TypeSearchPopup(IEnumerable items, Action selected) + { + _isValid = null; + _selected = selected; + + ItemClicked += OnItemClicked; + + foreach (var item in items) + AddItem(new TypeItemView(item)); + SortItems(); + } + private bool IsHideAttributes(object[] attributes) { return attributes.FirstOrDefault(IsHideAttribute) == null; @@ -162,6 +175,21 @@ namespace FlaxEditor.GUI return popup; } + /// + /// Shows the popup. + /// + /// The show target. + /// The show target location. + /// Collection of types to display available to pick. + /// Event called on asset item pick. + /// The dialog. + public static TypeSearchPopup Show(Control showTarget, Float2 showTargetLocation, IEnumerable items, Action selected) + { + var popup = new TypeSearchPopup(items, selected); + popup.Show(showTarget, showTargetLocation); + return popup; + } + /// public override void OnDestroy() { From d92f2a0437f7bfa396a05db628d41967ad53e896 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 22 Jul 2026 11:29:07 +0200 Subject: [PATCH 3/4] Add `IsScriptingObject` utility to `ScriptType` --- Source/Editor/CustomEditors/CustomEditor.cs | 4 ++-- Source/Editor/Scripting/ScriptType.cs | 5 +++++ Source/Editor/Surface/Elements/InputBox.cs | 2 +- Source/Editor/Surface/SurfaceStyle.cs | 2 +- Source/Editor/Surface/VisjectSurface.Connecting.cs | 2 +- Source/Editor/Utilities/ObjectSnapshot.cs | 6 +++--- Source/Engine/AI/BehaviorTree.cs | 2 +- 7 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 253fbc590..e18e7029b 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -643,7 +643,7 @@ namespace FlaxEditor.CustomEditors text = text.Remove(idx, endIdx - idx); } } - else if (ScriptType.FlaxObject.IsAssignableFrom(Values.Type)) + else if (Values.Type.IsScriptingObject) { // Object reference text = JsonSerializer.GetStringID(value as FlaxEngine.Object); @@ -691,7 +691,7 @@ namespace FlaxEditor.CustomEditors return false; } } - else if (ScriptType.FlaxObject.IsAssignableFrom(Values.Type)) + else if (Values.Type.IsScriptingObject) { // Object reference if (text.Length != 32) diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 0fd57214f..6db13e07a 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -849,6 +849,11 @@ namespace FlaxEditor.Scripting /// public bool IsVoid => _managed == typeof(void); + /// + /// Gets a value indicating whether this type is (eg. script or actor). + /// + public bool IsScriptingObject => FlaxObject.IsAssignableFrom(this); + /// /// Gets a value indicating whether the type is static. /// diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 15548b9d9..73e3f82ba 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -1544,7 +1544,7 @@ namespace FlaxEditor.Surface.Elements object obj; var type = CurrentType; - if (ScriptType.FlaxObject.IsAssignableFrom(type)) + if (type.IsScriptingObject) { // Object reference if (text.Length != 32) diff --git a/Source/Editor/Surface/SurfaceStyle.cs b/Source/Editor/Surface/SurfaceStyle.cs index 2997e1d21..e6bc82fe3 100644 --- a/Source/Editor/Surface/SurfaceStyle.cs +++ b/Source/Editor/Surface/SurfaceStyle.cs @@ -212,7 +212,7 @@ namespace FlaxEditor.Surface color = Colors.Enum; else if (type.IsValueType) color = Colors.Structures; - else if (ScriptType.FlaxObject.IsAssignableFrom(type) || type.IsInterface) + else if (type.IsScriptingObject || type.IsInterface) color = Colors.Object; else if (hint == ConnectionsHint.Vector) color = Colors.Vector; diff --git a/Source/Editor/Surface/VisjectSurface.Connecting.cs b/Source/Editor/Surface/VisjectSurface.Connecting.cs index 6f60ca2e2..22616f6ad 100644 --- a/Source/Editor/Surface/VisjectSurface.Connecting.cs +++ b/Source/Editor/Surface/VisjectSurface.Connecting.cs @@ -72,7 +72,7 @@ namespace FlaxEditor.Surface // Implicit casting is supported for object reference to test whenever it is valid var toType = to.Type; - if (supportsImplicitCastFromObjectToBoolean && toType == typeof(bool) && ScriptType.FlaxObject.IsAssignableFrom(from)) + if (supportsImplicitCastFromObjectToBoolean && toType == typeof(bool) && from.IsScriptingObject) { return true; } diff --git a/Source/Editor/Utilities/ObjectSnapshot.cs b/Source/Editor/Utilities/ObjectSnapshot.cs index c62a29795..a4b1317ee 100644 --- a/Source/Editor/Utilities/ObjectSnapshot.cs +++ b/Source/Editor/Utilities/ObjectSnapshot.cs @@ -65,7 +65,7 @@ namespace FlaxEditor.Utilities && memberValue != null && !refStack.Contains(memberValue)) { - if (memberType.IsArray && !ScriptType.FlaxObject.IsAssignableFrom(memberType.GetElementType())) + if (memberType.IsArray && !memberType.GetElementType().IsScriptingObject) { // Array var array = (Array)memberValue; @@ -79,7 +79,7 @@ namespace FlaxEditor.Utilities } refStack.Pop(); } - else if (typeof(IList).IsAssignableFrom(memberType.Type) && !ScriptType.FlaxObject.IsAssignableFrom(memberType.GetElementType())) + else if (typeof(IList).IsAssignableFrom(memberType.Type) && !memberType.GetElementType().IsScriptingObject) { // List var list = (IList)memberValue; @@ -106,7 +106,7 @@ namespace FlaxEditor.Utilities GetEntries(new MemberInfoPath.Entry(member.Member, key), membersPath, result, values, refStack, valueType, value); } } - else if (memberType.IsClass && !ScriptType.FlaxObject.IsAssignableFrom(memberType)) + else if (memberType.IsClass && !memberType.IsScriptingObject) { // Object refStack.Push(memberValue); diff --git a/Source/Engine/AI/BehaviorTree.cs b/Source/Engine/AI/BehaviorTree.cs index aebc07343..78ddf2932 100644 --- a/Source/Engine/AI/BehaviorTree.cs +++ b/Source/Engine/AI/BehaviorTree.cs @@ -81,7 +81,7 @@ namespace FlaxEngine !type.IsAbstract && !type.IsArray && !type.IsVoid && - (type.IsStructure || ScriptType.FlaxObject.IsAssignableFrom(type)) && + (type.IsStructure || type.IsScriptingObject) && type.IsPublic && type.CanCreateInstance; } From 55a756532b855d2ae388500a02cf4d78e6623e98 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 22 Jul 2026 11:30:49 +0200 Subject: [PATCH 4/4] Add cross button to unset abstract class or interface object value --- Source/Editor/CustomEditors/Editors/GenericEditor.cs | 8 +++++++- .../CustomEditors/Elements/Container/GroupElement.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs index fb16a1d56..0d8f949ec 100644 --- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs @@ -707,10 +707,10 @@ namespace FlaxEditor.CustomEditors.Editors if (!HasDifferentTypes) { var value = Values[0]; + var type = Values.Type; if (value == null) { // Check if it's an object type that can be created in editor - var type = Values.Type; if (type != ScriptMemberInfo.Null) { ScriptType[] types = null; @@ -754,6 +754,12 @@ namespace FlaxEditor.CustomEditors.Editors layout.Label(""); return; } + if (!type.IsArray && !type.IsStructure && !type.IsScriptingObject && (type.IsAbstract || type.IsInterface) && value.GetType() != type.Type && layout is GroupElement group) + { + // Add button to unset the value to null (eg. to edit it to different type) + var button = group.AddHeaderButton("Reset value to null", 0, FlaxEngine.GUI.Style.Current.Cross); + button.Clicked += (_, _) => SetValue(null); + } items = GetItemsForType(TypeUtils.GetObjectType(value)); } diff --git a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs index a3397d10a..79ce070c0 100644 --- a/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs +++ b/Source/Editor/CustomEditors/Elements/Container/GroupElement.cs @@ -55,7 +55,7 @@ namespace FlaxEditor.CustomEditors.Elements const float padding = 2.0f; var settingsButtonSize = Panel.HeaderHeight; Panel.HeaderTextMargin = Panel.HeaderTextMargin with { Right = settingsButtonSize + Utilities.Constants.UIMargin }; -; return new Image + return new Image { TooltipText = tooltipText, AutoFocus = true,