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/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs index 900fc6c8d..0d8f949ec 100644 --- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs @@ -707,30 +707,59 @@ 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 && 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(""); 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, 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() { 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/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/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; } 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); } ///