diff --git a/Source/Editor/CustomEditors/Editors/ControlReferenceEditor.cs b/Source/Editor/CustomEditors/Editors/ControlReferenceEditor.cs index 174dc9b20..65a6dc1a1 100644 --- a/Source/Editor/CustomEditors/Editors/ControlReferenceEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ControlReferenceEditor.cs @@ -2,15 +2,9 @@ using System; using FlaxEditor.CustomEditors.Elements; -using FlaxEditor.GUI; -using FlaxEditor.GUI.Drag; -using FlaxEditor.SceneGraph; -using FlaxEditor.SceneGraph.GUI; -using FlaxEditor.Windows; -using FlaxEditor.Windows.Assets; +using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Utilities; using Object = FlaxEngine.Object; namespace FlaxEditor.CustomEditors.Editors; @@ -18,433 +12,17 @@ namespace FlaxEditor.CustomEditors.Editors; /// /// The reference picker control used for UIControls using ControlReference. /// -public class UIControlRefPickerControl : Control +internal class UIControlRefPickerControl : FlaxObjectRefPickerControl { - private Type _controlType; - private UIControl _value; - private ActorTreeNode _linkedTreeNode; - private string _valueName; - private bool _supportsPickDropDown; - - private bool _isMouseDown; - private Float2 _mouseDownPos; - private Float2 _mousePos; - - private bool _hasValidDragOver; - private DragActors _dragActors; - private DragHandlers _dragHandlers; - /// - /// The presenter using this control. + /// Type of the control to pick. /// - public IPresenterOwner PresenterContext; - - /// - /// Occurs when value gets changed. - /// - public event Action ValueChanged; - - /// - /// The type of the Control - /// - /// Throws exception if value is not a subclass of control. - public Type ControlType - { - get => _controlType; - set - { - if (_controlType == value) - return; - if (!value.IsSubclassOf(typeof(Control))) - throw new ArgumentException(string.Format("Invalid type for UIControlObjectRefPicker. Input type: {0}", value)); - _controlType = value; - _supportsPickDropDown = typeof(Control).IsAssignableFrom(value); - - // Deselect value if it's not valid now - if (!IsValid(_value)) - Value = null; - } - } - - /// - /// The UIControl value. - /// - public UIControl Value - { - get => _value; - set - { - if (_value == value) - return; - if (!IsValid(value)) - value = null; - - // Special case for missing objects (eg. referenced actor in script that is deleted in editor) - if (value != null && (Object.GetUnmanagedPtr(value) == IntPtr.Zero || value.ID == Guid.Empty)) - value = null; - - _value = value; - - // Get name to display - if (_value != null) - _valueName = _value.Name; - else - _valueName = string.Empty; - - // Update tooltip - if (_value is SceneObject sceneObject) - TooltipText = Utilities.Utils.GetTooltip(sceneObject); - else - TooltipText = string.Empty; - - OnValueChanged(); - } - } - - /// - /// Gets or sets the selected object value by identifier. - /// - public Guid ValueID - { - get => _value ? _value.ID : Guid.Empty; - set => Value = Object.Find(ref value); - } - - /// - /// Initializes a new instance of the class. - /// - public UIControlRefPickerControl() - : base(0, 0, 50, 16) - { - } - - private void OnValueChanged() - { - ValueChanged?.Invoke(); - } - - private void ShowDropDownMenu() - { - Focus(); - if (typeof(Control).IsAssignableFrom(_controlType)) - { - ActorSearchPopup.Show(this, new Float2(0, Height), IsValid, actor => - { - Value = actor as UIControl; - RootWindow.Focus(); - Focus(); - }, PresenterContext); - } - } - - private bool IsValid(Actor actor) - { - return actor == null || actor is UIControl a && a.Control.GetType() == _controlType; - } - - private bool ValidateDragActor(ActorNode a) - { - if (!IsValid(a.Actor)) - return false; - - if (PresenterContext is PrefabWindow prefabWindow) - { - if (prefabWindow.Tree == a.TreeNode.ParentTree) - return true; - } - else if (PresenterContext is PropertiesWindow || PresenterContext == null) - { - if (a.ParentScene != null) - return true; - } - return false; - } + public Type ControlType; /// - public override void Draw() + protected override bool IsValid(Object obj) { - base.Draw(); - - // Cache data - var style = Style.Current; - bool isSelected = _value != null; - bool isEnabled = EnabledInHierarchy; - var frameRect = new Rectangle(0, 0, Width, 16); - if (isSelected) - frameRect.Width -= 16; - if (_supportsPickDropDown) - frameRect.Width -= 16; - var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14); - var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14); - var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14); - - // Draw frame - Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal); - - // Check if has item selected - if (isSelected) - { - // Draw name - Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, $"{_valueName} ({Utilities.Utils.GetPropertyNameUI(ControlType.GetTypeDisplayName())})", nameRect, isEnabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); - Render2D.PopClip(); - - // Draw deselect button - Render2D.DrawSprite(style.Cross, button1Rect, isEnabled && button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey); - } - else - { - // Draw info - Render2D.PushClip(nameRect); - Render2D.DrawText(style.FontMedium, ControlType != null ? $"None ({Utilities.Utils.GetPropertyNameUI(ControlType.GetTypeDisplayName())})" : "-", nameRect, isEnabled ? style.ForegroundGrey : style.ForegroundGrey.AlphaMultiplied(0.75f), TextAlignment.Near, TextAlignment.Center); - Render2D.PopClip(); - } - - // Draw picker button - if (_supportsPickDropDown) - { - var pickerRect = isSelected ? button2Rect : button1Rect; - Render2D.DrawSprite(style.ArrowDown, pickerRect, isEnabled && pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey); - } - - // Check if drag is over - if (IsDragOver && _hasValidDragOver) - { - var bounds = new Rectangle(Float2.Zero, Size); - Render2D.FillRectangle(bounds, style.Selection); - Render2D.DrawRectangle(bounds, style.SelectionBorder); - } - } - - /// - public override void OnMouseEnter(Float2 location) - { - _mousePos = location; - _mouseDownPos = Float2.Minimum; - - base.OnMouseEnter(location); - } - - /// - public override void OnMouseLeave() - { - _mousePos = Float2.Minimum; - - // Check if start drag drop - if (_isMouseDown) - { - // Do the drag - DoDrag(); - - // Clear flag - _isMouseDown = false; - } - - base.OnMouseLeave(); - } - - /// - public override void OnMouseMove(Float2 location) - { - _mousePos = location; - - // Check if start drag drop - if (_isMouseDown && Float2.Distance(location, _mouseDownPos) > 10.0f) - { - // Do the drag - DoDrag(); - - // Clear flag - _isMouseDown = false; - } - - base.OnMouseMove(location); - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (button == MouseButton.Left) - { - // Clear flag - _isMouseDown = false; - } - - // Cache data - bool isSelected = _value != null; - var frameRect = new Rectangle(0, 0, Width, 16); - if (isSelected) - frameRect.Width -= 16; - if (_supportsPickDropDown) - frameRect.Width -= 16; - var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14); - var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14); - var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14); - - // Deselect - if (_value != null && button1Rect.Contains(ref location)) - Value = null; - - // Picker dropdown menu - if (_supportsPickDropDown && (isSelected ? button2Rect : button1Rect).Contains(ref location)) - { - ShowDropDownMenu(); - return true; - } - - if (button == MouseButton.Left) - { - _isMouseDown = false; - - // Highlight actor or script reference - if (!_hasValidDragOver && !IsDragOver && nameRect.Contains(location)) - { - Actor actor = _value; - if (actor != null) - { - if (_linkedTreeNode != null && _linkedTreeNode.Actor == actor) - { - _linkedTreeNode.ExpandAllParents(); - _linkedTreeNode.StartHighlight(); - } - else - { - _linkedTreeNode = Editor.Instance.Scene.GetActorNode(actor).TreeNode; - _linkedTreeNode.ExpandAllParents(); - Editor.Instance.Windows.SceneWin.SceneTreePanel.ScrollViewTo(_linkedTreeNode, true); - _linkedTreeNode.StartHighlight(); - } - return true; - } - } - - // Reset valid drag over if still true at this point - if (_hasValidDragOver) - _hasValidDragOver = false; - } - - return base.OnMouseUp(location, button); - } - - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (button == MouseButton.Left) - { - // Set flag - _isMouseDown = true; - _mouseDownPos = location; - } - - return base.OnMouseDown(location, button); - } - - /// - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - Focus(); - - // Check if has object selected - if (_value != null) - { - if (_linkedTreeNode != null) - { - _linkedTreeNode.StopHighlight(); - _linkedTreeNode = null; - } - - // Select object - if (_value is Actor actor) - Editor.Instance.SceneEditing.Select(actor); - } - - return base.OnMouseDoubleClick(location, button); - } - - /// - public override void OnSubmit() - { - base.OnSubmit(); - - // Picker dropdown menu - if (_supportsPickDropDown) - ShowDropDownMenu(); - } - - private void DoDrag() - { - // Do the drag drop operation if has selected element - if (_value != null) - { - if (_value is Actor actor) - DoDragDrop(DragActors.GetDragData(actor)); - } - } - - private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None; - - /// - public override DragDropEffect OnDragEnter(ref Float2 location, DragData data) - { - base.OnDragEnter(ref location, data); - - // Ensure to have valid drag helpers (uses lazy init) - if (_dragActors == null) - _dragActors = new DragActors(ValidateDragActor); - if (_dragHandlers == null) - { - _dragHandlers = new DragHandlers - { - _dragActors, - }; - } - - _hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None; - - return DragEffect; - } - - /// - public override DragDropEffect OnDragMove(ref Float2 location, DragData data) - { - base.OnDragMove(ref location, data); - - return DragEffect; - } - - /// - public override void OnDragLeave() - { - _hasValidDragOver = false; - _dragHandlers.OnDragLeave(); - - base.OnDragLeave(); - } - - /// - public override DragDropEffect OnDragDrop(ref Float2 location, DragData data) - { - var result = DragEffect; - - base.OnDragDrop(ref location, data); - - if (_dragActors.HasValidDrag) - { - Value = _dragActors.Objects[0].Actor as UIControl; - } - - return result; - } - - /// - public override void OnDestroy() - { - _value = null; - _controlType = null; - _valueName = null; - _linkedTreeNode = null; - - base.OnDestroy(); + return obj == null || (obj is UIControl control && control.Control.GetType() == ControlType); } } @@ -452,7 +30,7 @@ public class UIControlRefPickerControl : Control /// ControlReferenceEditor class. /// [CustomEditor(typeof(ControlReference<>)), DefaultEditor] -public class ControlReferenceEditor : CustomEditor +internal class ControlReferenceEditor : CustomEditor { private CustomElement _element; @@ -475,6 +53,7 @@ public class ControlReferenceEditor : CustomEditor { _element.CustomControl.PresenterContext = Presenter.Owner; _element.CustomControl.ControlType = genType; + _element.CustomControl.Type = new ScriptType(typeof(UIControl)); } _element.CustomControl.ValueChanged += () => { @@ -484,7 +63,7 @@ public class ControlReferenceEditor : CustomEditor Type t = typeof(ControlReference<>); Type tw = t.MakeGenericType(new Type[] { genericType }); var instance = Activator.CreateInstance(tw); - ((IControlReference)instance).UIControl = _element.CustomControl.Value; + ((IControlReference)instance).UIControl = (UIControl)_element.CustomControl.Value; SetValue(instance); } }; diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index bab748dcc..9a8b80d40 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Editors public IPresenterOwner PresenterContext; /// - /// Gets or sets the allowed objects type (given type and all sub classes). Must be type of any subclass. + /// Gets or sets the allowed objects type (given type and all subclasses). Must be type of any subclass. /// public ScriptType Type { @@ -61,7 +61,8 @@ namespace FlaxEditor.CustomEditors.Editors throw new ArgumentException(string.Format("Invalid type for FlaxObjectRefEditor. Input type: {0}", value != ScriptType.Null ? value.TypeName : "null")); _type = value; - _supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) || new ScriptType(typeof(Script)).IsAssignableFrom(value); + _supportsPickDropDown = new ScriptType(typeof(Actor)).IsAssignableFrom(value) || + new ScriptType(typeof(Script)).IsAssignableFrom(value); // Deselect value if it's not valid now if (!IsValid(_value)) @@ -80,7 +81,7 @@ namespace FlaxEditor.CustomEditors.Editors if (_value == value) return; if (!IsValid(value)) - throw new ArgumentException("Invalid object type."); + value = null; // Special case for missing objects (eg. referenced actor in script that is deleted in editor) if (value != null && (Object.GetUnmanagedPtr(value) == IntPtr.Zero || value.ID == Guid.Empty)) @@ -91,27 +92,17 @@ namespace FlaxEditor.CustomEditors.Editors // Get name to display if (_value is Script script) - { _valueName = script.Actor ? $"{type.Name} ({script.Actor.Name})" : type.Name; - } else if (_value != null) - { _valueName = _value.ToString(); - } else - { _valueName = string.Empty; - } // Update tooltip if (_value is SceneObject sceneObject) - { TooltipText = Utilities.Utils.GetTooltip(sceneObject); - } else - { TooltipText = string.Empty; - } OnValueChanged(); } @@ -150,7 +141,12 @@ namespace FlaxEditor.CustomEditors.Editors _type = ScriptType.Object; } - private bool IsValid(Object obj) + /// + /// Object validation check routine. + /// + /// Input object to check. + /// True if it can be assigned, otherwise false. + protected virtual bool IsValid(Object obj) { var type = TypeUtils.GetObjectType(obj); return obj == null || _type.IsAssignableFrom(type) && (CheckValid == null || CheckValid(obj, type)); @@ -168,6 +164,15 @@ namespace FlaxEditor.CustomEditors.Editors Focus(); }, PresenterContext); } + else if (new ScriptType(typeof(Control)).IsAssignableFrom(_type)) + { + ActorSearchPopup.Show(this, new Float2(0, Height), IsValid, actor => + { + Value = actor as UIControl; + RootWindow.Focus(); + Focus(); + }, PresenterContext); + } else { ScriptSearchPopup.Show(this, new Float2(0, Height), IsValid, script =>