You're breathtaking!
This commit is contained in:
105
Source/Editor/CustomEditors/Editors/ActorLayerEditor.cs
Normal file
105
Source/Editor/CustomEditors/Editors/ActorLayerEditor.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for picking actor layer. Instead of choosing bit mask or layer index it shows a combo box with simple layer picking by name.
|
||||
/// </summary>
|
||||
public sealed class ActorLayerEditor : CustomEditor
|
||||
{
|
||||
private ComboBoxElement element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.ComboBox();
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
|
||||
// Set layer names
|
||||
element.ComboBox.SetItems(LayersAndTagsSettings.GetCurrentLayers());
|
||||
}
|
||||
|
||||
private void GetActorsTree(List<Actor> list, Actor a)
|
||||
{
|
||||
list.Add(a);
|
||||
int cnt = a.ChildrenCount;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
GetActorsTree(list, a.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
int value = comboBox.SelectedIndex;
|
||||
if (value == -1)
|
||||
value = 0;
|
||||
|
||||
// If selected is single actor that has children, ask if apply layer to the sub objects as well
|
||||
if (Values.IsSingleObject && (int)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)
|
||||
{
|
||||
var valueText = comboBox.SelectedItem;
|
||||
|
||||
// Ask user
|
||||
var result = MessageBox.Show(
|
||||
string.Format("Do you want to change layer to \"{0}\" for all child actors as well?", valueText),
|
||||
"Change actor layer", MessageBoxButtons.YesNoCancel);
|
||||
if (result == DialogResult.Cancel)
|
||||
return;
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
// Note: this possibly breaks the design a little bit
|
||||
// But it's the easiest way to set value for selected actor and its children with one undo action
|
||||
List<Actor> actors = new List<Actor>(32);
|
||||
GetActorsTree(actors, actor);
|
||||
if (Presenter.Undo != null)
|
||||
{
|
||||
using (new UndoMultiBlock(Presenter.Undo, actors.ToArray(), "Change layer"))
|
||||
{
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
actors[i].Layer = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
actors[i].Layer = value;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values on many actor selected
|
||||
}
|
||||
else
|
||||
{
|
||||
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for picking actor static flags. Overrides the default enum editor logic to provide more useful functionalities.
|
||||
/// </summary>
|
||||
public sealed class ActorStaticFlagsEditor : EnumEditor
|
||||
{
|
||||
private void GetActorsTree(List<Actor> list, Actor a)
|
||||
{
|
||||
list.Add(a);
|
||||
int cnt = a.ChildrenCount;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
GetActorsTree(list, a.GetChild(i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnValueChanged()
|
||||
{
|
||||
var value = (StaticFlags)element.EnumComboBox.EnumTypeValue;
|
||||
|
||||
// If selected is single actor that has children, ask if apply flags to the sub objects as well
|
||||
if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren)
|
||||
{
|
||||
// Ask user
|
||||
var result = MessageBox.Show(
|
||||
"Do you want to set static flags to all child actors as well?",
|
||||
"Change actor static flags", MessageBoxButtons.YesNoCancel);
|
||||
if (result == DialogResult.Cancel)
|
||||
return;
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
// Note: this possibly breaks the design a little bit
|
||||
// But it's the easiest way to set value for selected actor and its children with one undo action
|
||||
List<Actor> actors = new List<Actor>(32);
|
||||
GetActorsTree(actors, actor);
|
||||
if (Presenter.Undo != null && Presenter.Undo.Enabled)
|
||||
{
|
||||
using (new UndoMultiBlock(Presenter.Undo, actors.ToArray(), "Change static flags"))
|
||||
{
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
actors[i].StaticFlags = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < actors.Count; i++)
|
||||
{
|
||||
actors[i].StaticFlags = value;
|
||||
}
|
||||
}
|
||||
|
||||
OnUnDirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
base.OnValueChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Source/Editor/CustomEditors/Editors/ActorTagEditor.cs
Normal file
57
Source/Editor/CustomEditors/Editors/ActorTagEditor.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for picking actor tag. Instead of choosing tag index or entering tag text it shows a combo box with simple tag picking by name.
|
||||
/// </summary>
|
||||
public sealed class ActorTagEditor : CustomEditor
|
||||
{
|
||||
private ComboBoxElement element;
|
||||
private const string NoTagText = "Untagged";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.ComboBox();
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
|
||||
// Set tag names
|
||||
element.ComboBox.AddItem(NoTagText);
|
||||
element.ComboBox.AddItems(LayersAndTagsSettings.GetCurrentTags());
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
string value = comboBox.SelectedItem;
|
||||
if (value == NoTagText)
|
||||
value = string.Empty;
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values on many actor selected
|
||||
}
|
||||
else
|
||||
{
|
||||
string value = (string)Values[0];
|
||||
if (string.IsNullOrEmpty(value))
|
||||
value = NoTagText;
|
||||
element.ComboBox.SelectedItem = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
Normal file
74
Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Actor transform editor.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public static class ActorTransformEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorX = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Y axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorY = new Color(0.239215f, 1.0f, 0.047058f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// The Z axis color.
|
||||
/// </summary>
|
||||
public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// Custom editor for actor position/scale property.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.Vector3Editor" />
|
||||
public class PositionScaleEditor : Vector3Editor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
var grayOutFactor = 0.6f;
|
||||
XElement.FloatValue.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
||||
XElement.FloatValue.BorderSelectedColor = AxisColorX;
|
||||
YElement.FloatValue.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
||||
YElement.FloatValue.BorderSelectedColor = AxisColorY;
|
||||
ZElement.FloatValue.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
||||
ZElement.FloatValue.BorderSelectedColor = AxisColorZ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom editor for actor orientation property.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.QuaternionEditor" />
|
||||
public class OrientationEditor : QuaternionEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
// Override colors
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
var grayOutFactor = 0.6f;
|
||||
XElement.FloatValue.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor);
|
||||
XElement.FloatValue.BorderSelectedColor = AxisColorX;
|
||||
YElement.FloatValue.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor);
|
||||
YElement.FloatValue.BorderSelectedColor = AxisColorY;
|
||||
ZElement.FloatValue.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor);
|
||||
ZElement.FloatValue.BorderSelectedColor = AxisColorZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Source/Editor/CustomEditors/Editors/ArrayEditor.cs
Normal file
73
Source/Editor/CustomEditors/Editors/ArrayEditor.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit arrays.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Array)), DefaultEditor]
|
||||
public class ArrayEditor : CollectionEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override int Count => (Values[0] as Array)?.Length ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IList Allocate(int size)
|
||||
{
|
||||
var arrayType = Values.Type;
|
||||
var elementType = arrayType.GetElementType();
|
||||
return Array.CreateInstance(elementType, size);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Resize(int newSize)
|
||||
{
|
||||
var array = Values[0] as Array;
|
||||
var oldSize = array?.Length ?? 0;
|
||||
|
||||
if (oldSize != newSize)
|
||||
{
|
||||
// Allocate new array
|
||||
var arrayType = Values.Type;
|
||||
var elementType = arrayType.GetElementType();
|
||||
var newValues = Array.CreateInstance(elementType, newSize);
|
||||
|
||||
var sharedCount = Mathf.Min(oldSize, newSize);
|
||||
if (array != null && sharedCount > 0)
|
||||
{
|
||||
// Copy old values
|
||||
Array.Copy(array, 0, newValues, 0, sharedCount);
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
{
|
||||
Array.Copy(array, oldSize - 1, newValues, i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IList CloneValues()
|
||||
{
|
||||
var array = Values[0] as Array;
|
||||
if (array == null)
|
||||
return null;
|
||||
|
||||
var size = array.Length;
|
||||
var arrayType = Values.Type;
|
||||
var elementType = arrayType.GetElementType();
|
||||
var cloned = Array.CreateInstance(elementType, size);
|
||||
|
||||
Array.Copy(array, 0, cloned, 0, size);
|
||||
|
||||
return cloned;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
Normal file
104
Source/Editor/CustomEditors/Editors/AssetRefEditor.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="AssetItem"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(AssetItem)), DefaultEditor]
|
||||
public sealed class AssetItemRefEditor : AssetRefEditor
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="SceneReference"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(SceneReference)), DefaultEditor]
|
||||
public sealed class SceneRefEditor : AssetRefEditor
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="FlaxEngine.Asset"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Supports editing reference to the asset using various containers: <see cref="Asset"/> or <see cref="AssetItem"/> or <see cref="Guid"/>.</remarks>
|
||||
[CustomEditor(typeof(Asset)), DefaultEditor]
|
||||
public class AssetRefEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<AssetPicker> _element;
|
||||
private ScriptType _type;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
_type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
|
||||
float height = 48;
|
||||
var attributes = Values.GetAttributes();
|
||||
var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute);
|
||||
if (assetReference != null)
|
||||
{
|
||||
if (assetReference.UseSmallPicker)
|
||||
height = 32;
|
||||
|
||||
if (!string.IsNullOrEmpty(assetReference.TypeName))
|
||||
{
|
||||
var customType = TypeUtils.GetType(assetReference.TypeName);
|
||||
if (customType != ScriptType.Null)
|
||||
_type = customType;
|
||||
else
|
||||
Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName));
|
||||
}
|
||||
}
|
||||
|
||||
_element = layout.Custom<AssetPicker>();
|
||||
_element.CustomControl.AssetType = _type;
|
||||
_element.CustomControl.Height = height;
|
||||
_element.CustomControl.SelectedItemChanged += OnSelectedItemChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged()
|
||||
{
|
||||
if (typeof(AssetItem).IsAssignableFrom(_type.Type))
|
||||
SetValue(_element.CustomControl.SelectedItem);
|
||||
else if (_type.Type == typeof(Guid))
|
||||
SetValue(_element.CustomControl.SelectedID);
|
||||
else if (_type.Type == typeof(SceneReference))
|
||||
SetValue(new SceneReference(_element.CustomControl.SelectedID));
|
||||
else
|
||||
SetValue(_element.CustomControl.SelectedAsset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
if (Values[0] is AssetItem assetItem)
|
||||
_element.CustomControl.SelectedItem = assetItem;
|
||||
else if (Values[0] is Guid guid)
|
||||
_element.CustomControl.SelectedID = guid;
|
||||
else if (Values[0] is SceneReference sceneAsset)
|
||||
_element.CustomControl.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
else
|
||||
_element.CustomControl.SelectedAsset = Values[0] as Asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Source/Editor/CustomEditors/Editors/BooleanEditor.cs
Normal file
41
Source/Editor/CustomEditors/Editors/BooleanEditor.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit bool value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(bool)), DefaultEditor]
|
||||
public sealed class BooleanEditor : CustomEditor
|
||||
{
|
||||
private CheckBoxElement element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.Checkbox();
|
||||
element.CheckBox.StateChanged += (box) => SetValue(box.Checked);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
element.CheckBox.Intermediate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.CheckBox.Checked = (bool)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
307
Source/Editor/CustomEditors/Editors/CollectionEditor.cs
Normal file
307
Source/Editor/CustomEditors/Editors/CollectionEditor.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit arrays/list.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public abstract class CollectionEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The custom implementation of the collection items labels that can be used to reorder items.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.GUI.PropertyNameLabel" />
|
||||
private class CollectionItemLabel : PropertyNameLabel
|
||||
{
|
||||
/// <summary>
|
||||
/// The editor.
|
||||
/// </summary>
|
||||
public CollectionEditor Editor;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the item (zero-based).
|
||||
/// </summary>
|
||||
public readonly int Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CollectionItemLabel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
public CollectionItemLabel(CollectionEditor editor, int index)
|
||||
: base("Element " + index)
|
||||
{
|
||||
Editor = editor;
|
||||
Index = index;
|
||||
|
||||
SetupContextMenu += OnSetupContextMenu;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
|
||||
{
|
||||
menu.AddSeparator();
|
||||
|
||||
var moveUpButton = menu.AddButton("Move up", OnMoveUpClicked);
|
||||
moveUpButton.Enabled = Index > 0;
|
||||
|
||||
var moveDownButton = menu.AddButton("Move down", OnMoveDownClicked);
|
||||
moveDownButton.Enabled = Index + 1 < Editor.Count;
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked);
|
||||
}
|
||||
|
||||
private void OnMoveUpClicked(ContextMenuButton button)
|
||||
{
|
||||
Editor.Move(Index, Index - 1);
|
||||
}
|
||||
|
||||
private void OnMoveDownClicked(ContextMenuButton button)
|
||||
{
|
||||
Editor.Move(Index, Index + 1);
|
||||
}
|
||||
|
||||
private void OnRemoveClicked(ContextMenuButton button)
|
||||
{
|
||||
Editor.Remove(Index);
|
||||
}
|
||||
}
|
||||
|
||||
private IntegerValueElement _size;
|
||||
private int _elementsCount;
|
||||
private bool _readOnly;
|
||||
private bool _canReorderItems;
|
||||
private bool _notNullItems;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the collection.
|
||||
/// </summary>
|
||||
public abstract int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the collection elements.
|
||||
/// </summary>
|
||||
public ScriptType ElementType
|
||||
{
|
||||
get
|
||||
{
|
||||
var type = Values.Type;
|
||||
return new ScriptType(type.IsGenericType ? type.GetGenericArguments()[0] : type.GetElementType());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_readOnly = false;
|
||||
_canReorderItems = true;
|
||||
_notNullItems = false;
|
||||
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
return;
|
||||
|
||||
var size = Count;
|
||||
|
||||
// Try get CollectionAttribute for collection editor meta
|
||||
var attributes = Values.GetAttributes();
|
||||
Type overrideEditorType = null;
|
||||
float spacing = 0.0f;
|
||||
var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
// TODO: handle NotNullItems by filtering child editors SetValue
|
||||
|
||||
_readOnly = collection.ReadOnly;
|
||||
_canReorderItems = collection.CanReorderItems;
|
||||
_notNullItems = collection.NotNullItems;
|
||||
overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type;
|
||||
spacing = collection.Spacing;
|
||||
}
|
||||
|
||||
// Size
|
||||
if (_readOnly)
|
||||
{
|
||||
layout.Label("Size", size.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = layout.IntegerValue("Size");
|
||||
_size.IntValue.MinValue = 0;
|
||||
_size.IntValue.MaxValue = ushort.MaxValue;
|
||||
_size.IntValue.Value = size;
|
||||
_size.IntValue.ValueChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
// Elements
|
||||
if (size > 0)
|
||||
{
|
||||
var elementType = ElementType;
|
||||
if (_canReorderItems)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (i != 0 && spacing > 0f)
|
||||
{
|
||||
if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
propertiesListElement.Space(spacing);
|
||||
else
|
||||
layout.Space(spacing);
|
||||
}
|
||||
|
||||
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
|
||||
layout.Object(new CollectionItemLabel(this, i), new ListValueContainer(elementType, i, Values), overrideEditor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (i != 0 && spacing > 0f)
|
||||
{
|
||||
if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
propertiesListElement.Space(spacing);
|
||||
else
|
||||
layout.Space(spacing);
|
||||
}
|
||||
|
||||
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
|
||||
layout.Object("Element " + i, new ListValueContainer(elementType, i, Values), overrideEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
// Add/Remove buttons
|
||||
if (!_readOnly)
|
||||
{
|
||||
var area = layout.Space(20);
|
||||
var addButton = new Button(area.ContainerControl.Width - (16 + 16 + 2 + 2), 2, 16, 16)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add new item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl
|
||||
};
|
||||
addButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
var removeButton = new Button(addButton.Right + 2, addButton.Y, 16, 16)
|
||||
{
|
||||
Text = "-",
|
||||
TooltipText = "Remove last item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = size > 0
|
||||
};
|
||||
removeButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count - 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSizeChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(_size.IntValue.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the specified item at the given index and swaps it with the other item. It supports undo.
|
||||
/// </summary>
|
||||
/// <param name="srcIndex">Index of the source item.</param>
|
||||
/// <param name="dstIndex">Index of the destination item to swap with.</param>
|
||||
private void Move(int srcIndex, int dstIndex)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var cloned = CloneValues();
|
||||
|
||||
var tmp = cloned[dstIndex];
|
||||
cloned[dstIndex] = cloned[srcIndex];
|
||||
cloned[srcIndex] = tmp;
|
||||
|
||||
SetValue(cloned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the item at the specified index. It supports undo.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to remove.</param>
|
||||
private void Remove(int index)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var newValues = Allocate(Count - 1);
|
||||
var oldValues = (IList)Values[0];
|
||||
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
newValues[i] = oldValues[i];
|
||||
}
|
||||
|
||||
for (int i = index; i < newValues.Count; i++)
|
||||
{
|
||||
newValues[i] = oldValues[i + 1];
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates the collection of the specified size.
|
||||
/// </summary>
|
||||
/// <param name="size">The size.</param>
|
||||
/// <returns>The collection.</returns>
|
||||
protected abstract IList Allocate(int size);
|
||||
|
||||
/// <summary>
|
||||
/// Resizes collection to the specified new size.
|
||||
/// </summary>
|
||||
/// <param name="newSize">The new size.</param>
|
||||
protected abstract void Resize(int newSize);
|
||||
|
||||
/// <summary>
|
||||
/// Clones the collection values.
|
||||
/// </summary>
|
||||
protected abstract IList CloneValues();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
return;
|
||||
|
||||
// Check if collection has been resized (by UI or from external source)
|
||||
if (Count != _elementsCount)
|
||||
{
|
||||
RebuildLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Source/Editor/CustomEditors/Editors/ColorEditor.cs
Normal file
48
Source/Editor/CustomEditors/Editors/ColorEditor.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Color value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Color)), DefaultEditor]
|
||||
public sealed class ColorEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<ColorValueBox> element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.Custom<ColorValueBox>();
|
||||
element.CustomControl.ValueChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var token = element.CustomControl.IsSliding ? this : null;
|
||||
SetValue(element.CustomControl.Value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values on ColorValueBox
|
||||
}
|
||||
else
|
||||
{
|
||||
element.CustomControl.Value = (Color)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
Source/Editor/CustomEditors/Editors/ColorTrackball.cs
Normal file
133
Source/Editor/CustomEditors/Editors/ColorTrackball.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI.Dialogs;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom implementation of the inspector used to edit Vector4 color value type properties with color grading trackball.
|
||||
/// </summary>
|
||||
public sealed class ColorTrackball : CustomEditor
|
||||
{
|
||||
private FloatValueElement _xElement;
|
||||
private FloatValueElement _yElement;
|
||||
private FloatValueElement _zElement;
|
||||
private FloatValueElement _wElement;
|
||||
private CustomElement<ColorSelector> _trackball;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
float trackBallSize = 80.0f;
|
||||
float margin = 4.0f;
|
||||
|
||||
// Panel
|
||||
var masterPanel = layout.CustomContainer<GridPanel>();
|
||||
var masterPanelControl = masterPanel.CustomControl;
|
||||
masterPanelControl.ClipChildren = false;
|
||||
masterPanelControl.SlotPadding = new Margin(0, 0, margin, margin);
|
||||
masterPanelControl.Height = trackBallSize + margin + margin;
|
||||
masterPanelControl.ColumnFill = new[]
|
||||
{
|
||||
-trackBallSize,
|
||||
1.0f
|
||||
};
|
||||
masterPanelControl.RowFill = new[] { 1.0f };
|
||||
|
||||
// Trackball
|
||||
_trackball = masterPanel.Custom<ColorSelector>();
|
||||
_trackball.CustomControl.ColorChanged += OnColorWheelChanged;
|
||||
|
||||
// Scale editor
|
||||
{
|
||||
var grid = masterPanel.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.SlotPadding = new Margin(4, 2, 2, 2);
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.SlotsHorizontally = 1;
|
||||
gridControl.SlotsVertically = 4;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
_xElement = CreateFloatEditor(grid, limit, Color.Red);
|
||||
_yElement = CreateFloatEditor(grid, limit, Color.Green);
|
||||
_zElement = CreateFloatEditor(grid, limit, Color.Blue);
|
||||
_wElement = CreateFloatEditor(grid, limit, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
private FloatValueElement CreateFloatEditor(LayoutElementsContainer layout, LimitAttribute limit, Color borderColor)
|
||||
{
|
||||
var element = layout.FloatValue();
|
||||
element.SetLimits(limit);
|
||||
element.FloatValue.ValueChanged += OnValueChanged;
|
||||
var back = FlaxEngine.GUI.Style.Current.TextBoxBackground;
|
||||
var grayOutFactor = 0.6f;
|
||||
element.FloatValue.BorderColor = Color.Lerp(borderColor, back, grayOutFactor);
|
||||
element.FloatValue.BorderSelectedColor = borderColor;
|
||||
return element;
|
||||
}
|
||||
|
||||
private void OnColorWheelChanged(Color color)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
SetValue(new Vector4(color.R, color.G, color.B, _wElement.FloatValue.Value));
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
SetValue(new Vector4(
|
||||
_xElement.FloatValue.Value,
|
||||
_yElement.FloatValue.Value,
|
||||
_zElement.FloatValue.Value,
|
||||
_wElement.FloatValue.Value));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
var value = (Vector4)Values[0];
|
||||
var color = new Vector3(value);
|
||||
var scale = value.W;
|
||||
float min = color.MinValue;
|
||||
float max = color.MaxValue;
|
||||
if (min < 0.0f)
|
||||
{
|
||||
// Negative color case (e.g. offset)
|
||||
}
|
||||
else if (max > 1.0f)
|
||||
{
|
||||
// HDR color case
|
||||
color /= max;
|
||||
scale *= max;
|
||||
}
|
||||
_xElement.Value = color.X;
|
||||
_yElement.Value = color.Y;
|
||||
_zElement.Value = color.Z;
|
||||
_wElement.Value = scale;
|
||||
_trackball.CustomControl.Color = Vector3.Abs(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
449
Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
Normal file
449
Source/Editor/CustomEditors/Editors/DictionaryEditor.cs
Normal file
@@ -0,0 +1,449 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit key-value dictionaries.
|
||||
/// </summary>
|
||||
public class DictionaryEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The custom implementation of the dictionary items labels that can be used to remove items or edit keys.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.GUI.PropertyNameLabel" />
|
||||
private class DictionaryItemLabel : PropertyNameLabel
|
||||
{
|
||||
private DictionaryEditor _editor;
|
||||
private object _key;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DictionaryItemLabel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
public DictionaryItemLabel(DictionaryEditor editor, object key)
|
||||
: base(key?.ToString() ?? "<null>")
|
||||
{
|
||||
_editor = editor;
|
||||
_key = key;
|
||||
|
||||
SetupContextMenu += OnSetupContextMenu;
|
||||
}
|
||||
|
||||
private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor)
|
||||
{
|
||||
menu.AddSeparator();
|
||||
|
||||
menu.AddButton("Remove", OnRemoveClicked).Enabled = !_editor._readOnly;
|
||||
menu.AddButton("Edit", OnEditClicked).Enabled = _editor._canEditKeys;
|
||||
}
|
||||
|
||||
private void OnRemoveClicked(ContextMenuButton button)
|
||||
{
|
||||
_editor.Remove(_key);
|
||||
}
|
||||
|
||||
private void OnEditClicked(ContextMenuButton button)
|
||||
{
|
||||
var keyType = _editor.Values.Type.GetGenericArguments()[0];
|
||||
if (keyType == typeof(string) || keyType.IsPrimitive)
|
||||
{
|
||||
var popup = RenamePopup.Show(Parent, Bounds, Text, false);
|
||||
popup.Validate += (renamePopup, value) =>
|
||||
{
|
||||
object newKey;
|
||||
if (keyType.IsPrimitive)
|
||||
newKey = JsonSerializer.Deserialize(value, keyType);
|
||||
else
|
||||
newKey = value;
|
||||
return !((IDictionary)_editor.Values[0]).Contains(newKey);
|
||||
};
|
||||
popup.Renamed += renamePopup =>
|
||||
{
|
||||
object newKey;
|
||||
if (keyType.IsPrimitive)
|
||||
newKey = JsonSerializer.Deserialize(renamePopup.Text, keyType);
|
||||
else
|
||||
newKey = renamePopup.Text;
|
||||
|
||||
_editor.ChangeKey(_key, newKey);
|
||||
_key = newKey;
|
||||
Text = _key.ToString();
|
||||
};
|
||||
}
|
||||
else if (keyType.IsEnum)
|
||||
{
|
||||
var popup = RenamePopup.Show(Parent, Bounds, Text, false);
|
||||
var picker = new EnumComboBox(keyType)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
Parent = popup,
|
||||
EnumTypeValue = _key,
|
||||
};
|
||||
picker.ValueChanged += () =>
|
||||
{
|
||||
popup.Hide();
|
||||
object newKey = picker.EnumTypeValue;
|
||||
if (!((IDictionary)_editor.Values[0]).Contains(newKey))
|
||||
{
|
||||
_editor.ChangeKey(_key, newKey);
|
||||
_key = newKey;
|
||||
Text = _key.ToString();
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("Missing editing for dictionary key type " + keyType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
OnEditClicked(null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_editor = null;
|
||||
_key = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
private IntegerValueElement _size;
|
||||
private int _elementsCount;
|
||||
private bool _readOnly;
|
||||
private bool _notNullItems;
|
||||
private bool _canEditKeys;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this editor[can edit the specified dictionary type.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of the dictionary.</param>
|
||||
/// <returns>True if can edit, otherwise false.</returns>
|
||||
public static bool CanEditType(Type type)
|
||||
{
|
||||
// Ensure it's a generic dictionary type
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the collection.
|
||||
/// </summary>
|
||||
public int Count => (Values[0] as IDictionary)?.Count ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_readOnly = false;
|
||||
_notNullItems = false;
|
||||
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
return;
|
||||
|
||||
var type = Values.Type;
|
||||
var size = Count;
|
||||
var argTypes = type.GetGenericArguments();
|
||||
var keyType = argTypes[0];
|
||||
var valueType = argTypes[1];
|
||||
_canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum;
|
||||
|
||||
// Try get CollectionAttribute for collection editor meta
|
||||
var attributes = Values.GetAttributes();
|
||||
Type overrideEditorType = null;
|
||||
float spacing = 0.0f;
|
||||
if (attributes != null)
|
||||
{
|
||||
var collection = (CollectionAttribute)attributes.FirstOrDefault(x => x is CollectionAttribute);
|
||||
if (collection != null)
|
||||
{
|
||||
// TODO: handle ReadOnly and NotNullItems by filtering child editors SetValue
|
||||
|
||||
_readOnly = collection.ReadOnly;
|
||||
_notNullItems = collection.NotNullItems;
|
||||
overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type;
|
||||
spacing = collection.Spacing;
|
||||
}
|
||||
}
|
||||
|
||||
// Size
|
||||
if (_readOnly || !_canEditKeys)
|
||||
{
|
||||
layout.Label("Size", size.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = layout.IntegerValue("Size");
|
||||
_size.IntValue.MinValue = 0;
|
||||
_size.IntValue.MaxValue = ushort.MaxValue;
|
||||
_size.IntValue.Value = size;
|
||||
_size.IntValue.ValueChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
// Elements
|
||||
if (size > 0)
|
||||
{
|
||||
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
|
||||
var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (i != 0 && spacing > 0f)
|
||||
{
|
||||
if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
propertiesListElement.Space(spacing);
|
||||
else
|
||||
layout.Space(spacing);
|
||||
}
|
||||
|
||||
var key = keys.ElementAt(i);
|
||||
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
|
||||
layout.Object(new DictionaryItemLabel(this, key), new DictionaryValueContainer(new ScriptType(valueType), key, Values), overrideEditor);
|
||||
}
|
||||
}
|
||||
_elementsCount = size;
|
||||
|
||||
// Add/Remove buttons
|
||||
if (!_readOnly && _canEditKeys)
|
||||
{
|
||||
var area = layout.Space(20);
|
||||
var addButton = new Button(area.ContainerControl.Width - (16 + 16 + 2 + 2), 2, 16, 16)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add new item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl
|
||||
};
|
||||
addButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count + 1);
|
||||
};
|
||||
var removeButton = new Button(addButton.Right + 2, addButton.Y, 16, 16)
|
||||
{
|
||||
Text = "-",
|
||||
TooltipText = "Remove last item",
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Parent = area.ContainerControl,
|
||||
Enabled = size > 0
|
||||
};
|
||||
removeButton.Clicked += () =>
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(Count - 1);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSizeChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(_size.IntValue.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the item of the specified key. It supports undo.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item to remove.</param>
|
||||
private void Remove(object key)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
// Allocate new collection
|
||||
var dictionary = Values[0] as IDictionary;
|
||||
var type = Values.Type;
|
||||
var newValues = (IDictionary)type.CreateInstance();
|
||||
|
||||
// Copy all keys/values except the specified one
|
||||
if (dictionary != null)
|
||||
{
|
||||
foreach (var e in dictionary.Keys)
|
||||
{
|
||||
if (e == key)
|
||||
continue;
|
||||
newValues[e] = dictionary[e];
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the key of the item.
|
||||
/// </summary>
|
||||
/// <param name="oldKey">The old key value.</param>
|
||||
/// <param name="newKey">The new key value.</param>
|
||||
protected void ChangeKey(object oldKey, object newKey)
|
||||
{
|
||||
var dictionary = (IDictionary)Values[0];
|
||||
var newValues = (IDictionary)Values.Type.CreateInstance();
|
||||
foreach (var e in dictionary.Keys)
|
||||
{
|
||||
if (Equals(e, oldKey))
|
||||
newValues[newKey] = dictionary[e];
|
||||
else
|
||||
newValues[e] = dictionary[e];
|
||||
}
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes collection to the specified new size.
|
||||
/// </summary>
|
||||
/// <param name="newSize">The new size.</param>
|
||||
protected void Resize(int newSize)
|
||||
{
|
||||
var dictionary = Values[0] as IDictionary;
|
||||
var oldSize = dictionary?.Count ?? 0;
|
||||
|
||||
if (oldSize == newSize)
|
||||
return;
|
||||
|
||||
// Allocate new collection
|
||||
var type = Values.Type;
|
||||
var argTypes = type.GetGenericArguments();
|
||||
var keyType = argTypes[0];
|
||||
var valueType = argTypes[1];
|
||||
var newValues = (IDictionary)type.CreateInstance();
|
||||
|
||||
// Copy all keys/values
|
||||
int itemsLeft = newSize;
|
||||
if (dictionary != null)
|
||||
{
|
||||
foreach (var e in dictionary.Keys)
|
||||
{
|
||||
if (itemsLeft == 0)
|
||||
break;
|
||||
newValues[e] = dictionary[e];
|
||||
itemsLeft--;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new items (find unique keys)
|
||||
int newItemsLeft = newSize - oldSize;
|
||||
while (newItemsLeft-- > 0)
|
||||
{
|
||||
if (keyType.IsPrimitive)
|
||||
{
|
||||
long uniqueKey = 0;
|
||||
bool isUnique;
|
||||
do
|
||||
{
|
||||
isUnique = true;
|
||||
foreach (var e in newValues.Keys)
|
||||
{
|
||||
var asLong = Convert.ToInt64(e);
|
||||
if (asLong == uniqueKey)
|
||||
{
|
||||
uniqueKey++;
|
||||
isUnique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!isUnique);
|
||||
|
||||
newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
|
||||
}
|
||||
else if (keyType.IsEnum)
|
||||
{
|
||||
var enumValues = Enum.GetValues(keyType);
|
||||
int uniqueKeyIndex = 0;
|
||||
bool isUnique;
|
||||
do
|
||||
{
|
||||
isUnique = true;
|
||||
foreach (var e in newValues.Keys)
|
||||
{
|
||||
if (Equals(e, enumValues.GetValue(uniqueKeyIndex)))
|
||||
{
|
||||
uniqueKeyIndex++;
|
||||
isUnique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!isUnique && uniqueKeyIndex < enumValues.Length);
|
||||
|
||||
newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
|
||||
}
|
||||
else if (keyType == typeof(string))
|
||||
{
|
||||
string uniqueKey = "Key";
|
||||
bool isUnique;
|
||||
do
|
||||
{
|
||||
isUnique = true;
|
||||
foreach (var e in newValues.Keys)
|
||||
{
|
||||
if ((string)e == uniqueKey)
|
||||
{
|
||||
uniqueKey += "*";
|
||||
isUnique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!isUnique);
|
||||
|
||||
newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
// No support for different collections for now
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
return;
|
||||
|
||||
// Check if collection has been resized (by UI or from external source)
|
||||
if (Count != _elementsCount)
|
||||
{
|
||||
RebuildLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Source/Editor/CustomEditors/Editors/DoubleEditor.cs
Normal file
73
Source/Editor/CustomEditors/Editors/DoubleEditor.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit double value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(double)), DefaultEditor]
|
||||
public sealed class DoubleEditor : CustomEditor
|
||||
{
|
||||
private DoubleValueElement _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = null;
|
||||
|
||||
// Try get limit attribute for value min/max range setting and slider speed
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
if (limit != null)
|
||||
{
|
||||
// Use double value editor with limit
|
||||
var doubleValue = layout.DoubleValue();
|
||||
doubleValue.SetLimits((LimitAttribute)limit);
|
||||
doubleValue.DoubleValue.ValueChanged += OnValueChanged;
|
||||
doubleValue.DoubleValue.SlidingEnd += ClearToken;
|
||||
_element = doubleValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_element == null)
|
||||
{
|
||||
// Use double value editor
|
||||
var doubleValue = layout.DoubleValue();
|
||||
doubleValue.DoubleValue.ValueChanged += OnValueChanged;
|
||||
doubleValue.DoubleValue.SlidingEnd += ClearToken;
|
||||
_element = doubleValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = _element.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
SetValue(_element.Value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.Value = (double)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Source/Editor/CustomEditors/Editors/EnumEditor.cs
Normal file
70
Source/Editor/CustomEditors/Editors/EnumEditor.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit float value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Enum)), DefaultEditor]
|
||||
public class EnumEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The enum element.
|
||||
/// </summary>
|
||||
protected EnumElement element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var mode = EnumDisplayAttribute.FormatMode.Default;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes?.FirstOrDefault(x => x is EnumDisplayAttribute) is EnumDisplayAttribute enumDisplay)
|
||||
{
|
||||
mode = enumDisplay.Mode;
|
||||
}
|
||||
|
||||
if (HasDifferentTypes)
|
||||
{
|
||||
// No support for different enum types
|
||||
}
|
||||
else
|
||||
{
|
||||
var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType();
|
||||
element = layout.Enum(enumType, null, mode);
|
||||
element.EnumComboBox.ValueChanged += OnValueChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when value get changed. Allows to override default value setter logic.
|
||||
/// </summary>
|
||||
protected virtual void OnValueChanged()
|
||||
{
|
||||
SetValue(element.EnumComboBox.EnumTypeValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// No support for different enum values
|
||||
}
|
||||
else
|
||||
{
|
||||
element.EnumComboBox.EnumTypeValue = Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
499
Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
Normal file
499
Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs
Normal file
@@ -0,0 +1,499 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom control type used to pick reference to <see cref="FlaxEngine.Object"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
public class FlaxObjectRefPickerControl : Control
|
||||
{
|
||||
private ScriptType _type;
|
||||
private Object _value;
|
||||
private string _valueName;
|
||||
private bool _supportsPickDropDown;
|
||||
|
||||
private bool _isMouseDown;
|
||||
private Vector2 _mouseDownPos;
|
||||
private Vector2 _mousePos;
|
||||
|
||||
private bool _hasValidDragOver;
|
||||
private DragActors _dragActors;
|
||||
private DragActors _dragActorsWithScript;
|
||||
private DragAssets _dragAssets;
|
||||
private DragScripts _dragScripts;
|
||||
private DragHandlers _dragHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed objects type (given type and all sub classes). Must be <see cref="Object"/> type of any subclass.
|
||||
/// </summary>
|
||||
public ScriptType Type
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type == value)
|
||||
return;
|
||||
if (value == ScriptType.Null || (value.Type != typeof(Object) && !value.IsSubclassOf(new ScriptType(typeof(Object)))))
|
||||
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);
|
||||
|
||||
// Deselect value if it's not valid now
|
||||
if (!IsValid(_value))
|
||||
Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected object value.
|
||||
/// </summary>
|
||||
public Object Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (_value == value)
|
||||
return;
|
||||
if (!IsValid(value))
|
||||
throw new ArgumentException("Invalid object type.");
|
||||
|
||||
// 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;
|
||||
var type = TypeUtils.GetObjectType(_value);
|
||||
|
||||
// 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)
|
||||
{
|
||||
var str = sceneObject is Actor actor ? actor.Name : type.Name;
|
||||
var o = sceneObject.Parent;
|
||||
while (o)
|
||||
{
|
||||
str = o.Name + " -> " + str;
|
||||
o = o.Parent;
|
||||
}
|
||||
TooltipText = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
TooltipText = string.Empty;
|
||||
}
|
||||
|
||||
OnValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected object value by identifier.
|
||||
/// </summary>
|
||||
public Guid ValueID
|
||||
{
|
||||
get => _value ? _value.ID : Guid.Empty;
|
||||
set => Value = Object.Find<Object>(ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when value gets changed.
|
||||
/// </summary>
|
||||
public event Action ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for objects validation. Cane be used to implement a rule for objects to pick.
|
||||
/// </summary>
|
||||
public Func<Object, ScriptType, bool> CheckValid;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FlaxObjectRefPickerControl"/> class.
|
||||
/// </summary>
|
||||
public FlaxObjectRefPickerControl()
|
||||
: base(0, 0, 50, 16)
|
||||
{
|
||||
_type = new ScriptType(typeof(Object));
|
||||
}
|
||||
|
||||
private bool IsValid(Object obj)
|
||||
{
|
||||
var type = TypeUtils.GetObjectType(obj);
|
||||
return obj == null || _type.IsAssignableFrom(type) && (CheckValid == null || CheckValid(obj, type));
|
||||
}
|
||||
|
||||
private void ShowDropDownMenu()
|
||||
{
|
||||
Focus();
|
||||
if (new ScriptType(typeof(Actor)).IsAssignableFrom(_type))
|
||||
{
|
||||
ActorSearchPopup.Show(this, new Vector2(0, Height), IsValid, actor =>
|
||||
{
|
||||
Value = actor;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptSearchPopup.Show(this, new Vector2(0, Height), IsValid, script =>
|
||||
{
|
||||
Value = script;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when value gets changed.
|
||||
/// </summary>
|
||||
protected virtual void OnValueChanged()
|
||||
{
|
||||
ValueChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
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 ? style.BorderHighlighted : style.BorderNormal);
|
||||
|
||||
// Check if has item selected
|
||||
if (isSelected)
|
||||
{
|
||||
// Draw name
|
||||
Render2D.PushClip(nameRect);
|
||||
Render2D.DrawText(style.FontMedium, _valueName, 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.DrawText(style.FontMedium, "-", nameRect, isEnabled ? Color.OrangeRed : Color.DarkOrange, TextAlignment.Near, TextAlignment.Center);
|
||||
}
|
||||
|
||||
// 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)
|
||||
Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), style.BackgroundSelected * 0.4f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseEnter(Vector2 location)
|
||||
{
|
||||
_mousePos = location;
|
||||
_mouseDownPos = Vector2.Minimum;
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
_mousePos = Vector2.Minimum;
|
||||
|
||||
// Check if start drag drop
|
||||
if (_isMouseDown)
|
||||
{
|
||||
// Do the drag
|
||||
DoDrag();
|
||||
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
}
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Vector2 location)
|
||||
{
|
||||
_mousePos = location;
|
||||
|
||||
// Check if start drag drop
|
||||
if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f)
|
||||
{
|
||||
// Do the drag
|
||||
DoDrag();
|
||||
|
||||
// Clear flag
|
||||
_isMouseDown = false;
|
||||
}
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Vector2 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 base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Vector2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
// Set flag
|
||||
_isMouseDown = true;
|
||||
_mouseDownPos = location;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||
{
|
||||
Focus();
|
||||
|
||||
// Check if has object selected
|
||||
if (_value != null)
|
||||
{
|
||||
// Select object
|
||||
if (_value is Actor actor)
|
||||
Editor.Instance.SceneEditing.Select(actor);
|
||||
else if (_value is Script script && script.Actor)
|
||||
Editor.Instance.SceneEditing.Select(script.Actor);
|
||||
else if (_value is Asset asset)
|
||||
Editor.Instance.Windows.ContentWin.Select(asset);
|
||||
}
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
private void DoDrag()
|
||||
{
|
||||
// Do the drag drop operation if has selected element
|
||||
if (_value != null)
|
||||
{
|
||||
if (_value is Actor actor)
|
||||
DoDragDrop(DragActors.GetDragData(actor));
|
||||
else if (_value is Asset asset)
|
||||
DoDragDrop(DragAssets.GetDragData(asset));
|
||||
else if (_value is Script script)
|
||||
DoDragDrop(DragScripts.GetDragData(script));
|
||||
}
|
||||
}
|
||||
|
||||
private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragEnter(ref location, data);
|
||||
|
||||
// Ensure to have valid drag helpers (uses lazy init)
|
||||
if (_dragActors == null)
|
||||
_dragActors = new DragActors(x => IsValid(x.Actor));
|
||||
if (_dragActorsWithScript == null)
|
||||
_dragActorsWithScript = new DragActors(ValidateDragActorWithScript);
|
||||
if (_dragAssets == null)
|
||||
_dragAssets = new DragAssets(ValidateDragAsset);
|
||||
if (_dragScripts == null)
|
||||
_dragScripts = new DragScripts(IsValid);
|
||||
if (_dragHandlers == null)
|
||||
{
|
||||
_dragHandlers = new DragHandlers
|
||||
{
|
||||
_dragActors,
|
||||
_dragActorsWithScript,
|
||||
_dragAssets,
|
||||
_dragScripts,
|
||||
};
|
||||
}
|
||||
|
||||
_hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None;
|
||||
|
||||
// Special case when dragging the actor with script to link script reference
|
||||
if (_dragActorsWithScript.HasValidDrag)
|
||||
{
|
||||
var script = _dragActorsWithScript.Objects[0].Actor.Scripts.First(IsValid);
|
||||
_dragActorsWithScript.Objects.Clear();
|
||||
_dragScripts.Objects.Add(script);
|
||||
}
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
private bool ValidateDragAsset(AssetItem assetItem)
|
||||
{
|
||||
// Check if can accept assets
|
||||
if (!new ScriptType(typeof(Asset)).IsAssignableFrom(_type))
|
||||
return false;
|
||||
|
||||
// Load or get asset
|
||||
var id = assetItem.ID;
|
||||
var obj = Object.Find<Asset>(ref id);
|
||||
if (obj == null)
|
||||
return false;
|
||||
|
||||
// Check it
|
||||
return IsValid(obj);
|
||||
}
|
||||
|
||||
private bool ValidateDragActorWithScript(ActorNode node)
|
||||
{
|
||||
return node.Actor.Scripts.Any(IsValid);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragMove(ref location, data);
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_hasValidDragOver = false;
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = DragEffect;
|
||||
|
||||
base.OnDragDrop(ref location, data);
|
||||
|
||||
if (_dragActors.HasValidDrag)
|
||||
{
|
||||
Value = _dragActors.Objects[0].Actor;
|
||||
}
|
||||
else if (_dragAssets.HasValidDrag)
|
||||
{
|
||||
ValueID = _dragAssets.Objects[0].ID;
|
||||
}
|
||||
else if (_dragScripts.HasValidDrag)
|
||||
{
|
||||
ValueID = _dragScripts.Objects[0].ID;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_value = null;
|
||||
_type = ScriptType.Null;
|
||||
_valueName = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="FlaxEngine.Object"/>.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Object)), DefaultEditor]
|
||||
public sealed class FlaxObjectRefEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<FlaxObjectRefPickerControl> _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
_element = layout.Custom<FlaxObjectRefPickerControl>();
|
||||
_element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
_element.CustomControl.Value = Values[0] as Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Source/Editor/CustomEditors/Editors/FloatEditor.cs
Normal file
91
Source/Editor/CustomEditors/Editors/FloatEditor.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit float value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(float)), DefaultEditor]
|
||||
public sealed class FloatEditor : CustomEditor
|
||||
{
|
||||
private IFloatValueEditor _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = null;
|
||||
|
||||
// Try get limit attribute for value min/max range setting and slider speed
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
var range = attributes.FirstOrDefault(x => x is RangeAttribute);
|
||||
if (range != null)
|
||||
{
|
||||
// Use slider
|
||||
var slider = layout.Slider();
|
||||
slider.SetLimits((RangeAttribute)range);
|
||||
slider.Slider.ValueChanged += OnValueChanged;
|
||||
slider.Slider.SlidingEnd += ClearToken;
|
||||
_element = slider;
|
||||
return;
|
||||
}
|
||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
if (limit != null)
|
||||
{
|
||||
// Use float value editor with limit
|
||||
var floatValue = layout.FloatValue();
|
||||
floatValue.SetLimits((LimitAttribute)limit);
|
||||
floatValue.FloatValue.ValueChanged += OnValueChanged;
|
||||
floatValue.FloatValue.SlidingEnd += ClearToken;
|
||||
_element = floatValue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_element == null)
|
||||
{
|
||||
// Use float value editor
|
||||
var floatValue = layout.FloatValue();
|
||||
floatValue.FloatValue.ValueChanged += OnValueChanged;
|
||||
floatValue.FloatValue.SlidingEnd += ClearToken;
|
||||
_element = floatValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = _element.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
SetValue(_element.Value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = Values[0];
|
||||
if (value is float asFloat)
|
||||
_element.Value = asFloat;
|
||||
else if (value is double asDouble)
|
||||
_element.Value = (float)asDouble;
|
||||
else
|
||||
throw new Exception("Invalid value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
607
Source/Editor/CustomEditors/Editors/GenericEditor.cs
Normal file
607
Source/Editor/CustomEditors/Editors/GenericEditor.cs
Normal file
@@ -0,0 +1,607 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used when no specified inspector is provided for the type. Inspector
|
||||
/// displays GUI for all the inspectable fields in the object.
|
||||
/// </summary>
|
||||
public class GenericEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes object property/field information for custom editors pipeline.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.IComparable" />
|
||||
protected class ItemInfo : IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// The member information from reflection.
|
||||
/// </summary>
|
||||
public ScriptMemberInfo Info;
|
||||
|
||||
/// <summary>
|
||||
/// The order attribute.
|
||||
/// </summary>
|
||||
public EditorOrderAttribute Order;
|
||||
|
||||
/// <summary>
|
||||
/// The display attribute.
|
||||
/// </summary>
|
||||
public EditorDisplayAttribute Display;
|
||||
|
||||
/// <summary>
|
||||
/// The tooltip attribute.
|
||||
/// </summary>
|
||||
public TooltipAttribute Tooltip;
|
||||
|
||||
/// <summary>
|
||||
/// The custom editor attribute.
|
||||
/// </summary>
|
||||
public CustomEditorAttribute CustomEditor;
|
||||
|
||||
/// <summary>
|
||||
/// The custom editor alias attribute.
|
||||
/// </summary>
|
||||
public CustomEditorAliasAttribute CustomEditorAlias;
|
||||
|
||||
/// <summary>
|
||||
/// The space attribute.
|
||||
/// </summary>
|
||||
public SpaceAttribute Space;
|
||||
|
||||
/// <summary>
|
||||
/// The header attribute.
|
||||
/// </summary>
|
||||
public HeaderAttribute Header;
|
||||
|
||||
/// <summary>
|
||||
/// The visible if attribute.
|
||||
/// </summary>
|
||||
public VisibleIfAttribute VisibleIf;
|
||||
|
||||
/// <summary>
|
||||
/// The read-only attribute usage flag.
|
||||
/// </summary>
|
||||
public bool IsReadOnly;
|
||||
|
||||
/// <summary>
|
||||
/// The expand groups flag.
|
||||
/// </summary>
|
||||
public bool ExpandGroups;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name.
|
||||
/// </summary>
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether use dedicated group.
|
||||
/// </summary>
|
||||
public bool UseGroup => Display?.Group != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the overridden custom editor for item editing.
|
||||
/// </summary>
|
||||
public CustomEditor OverrideEditor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CustomEditor != null)
|
||||
return (CustomEditor)Activator.CreateInstance(CustomEditor.Type);
|
||||
if (CustomEditorAlias != null)
|
||||
return (CustomEditor)TypeUtils.CreateInstance(CustomEditorAlias.TypeName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tooltip text (may be null if not provided).
|
||||
/// </summary>
|
||||
public string TooltipText => Tooltip?.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The reflection information.</param>
|
||||
public ItemInfo(ScriptMemberInfo info)
|
||||
: this(info, info.GetAttributes(true))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The reflection information.</param>
|
||||
/// <param name="attributes">The attributes.</param>
|
||||
public ItemInfo(ScriptMemberInfo info, object[] attributes)
|
||||
{
|
||||
Info = info;
|
||||
Order = (EditorOrderAttribute)attributes.FirstOrDefault(x => x is EditorOrderAttribute);
|
||||
Display = (EditorDisplayAttribute)attributes.FirstOrDefault(x => x is EditorDisplayAttribute);
|
||||
Tooltip = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||
CustomEditor = (CustomEditorAttribute)attributes.FirstOrDefault(x => x is CustomEditorAttribute);
|
||||
CustomEditorAlias = (CustomEditorAliasAttribute)attributes.FirstOrDefault(x => x is CustomEditorAliasAttribute);
|
||||
Space = (SpaceAttribute)attributes.FirstOrDefault(x => x is SpaceAttribute);
|
||||
Header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute);
|
||||
VisibleIf = (VisibleIfAttribute)attributes.FirstOrDefault(x => x is VisibleIfAttribute);
|
||||
IsReadOnly = attributes.FirstOrDefault(x => x is ReadOnlyAttribute) != null;
|
||||
ExpandGroups = attributes.FirstOrDefault(x => x is ExpandGroupsAttribute) != null;
|
||||
|
||||
IsReadOnly |= !info.HasSet;
|
||||
DisplayName = Display?.Name ?? CustomEditorsUtil.GetPropertyNameUI(info.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values.
|
||||
/// </summary>
|
||||
/// <param name="instanceValues">The instance values.</param>
|
||||
/// <returns>The values container.</returns>
|
||||
public ValueContainer GetValues(ValueContainer instanceValues)
|
||||
{
|
||||
return new ValueContainer(Info, instanceValues);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is ItemInfo other)
|
||||
{
|
||||
// By order
|
||||
if (Order != null)
|
||||
{
|
||||
if (other.Order != null)
|
||||
return Order.Order - other.Order.Order;
|
||||
return -1;
|
||||
}
|
||||
if (other.Order != null)
|
||||
return 1;
|
||||
|
||||
// By group name
|
||||
if (Display?.Group != null)
|
||||
{
|
||||
if (other.Display?.Group != null)
|
||||
return string.Compare(Display.Group, other.Display.Group, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
// By name
|
||||
return string.Compare(Info.Name, other.Info.Name, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Info.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether can merge two item infos to present them at once.
|
||||
/// </summary>
|
||||
/// <param name="a">The a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns><c>true</c> if can merge two item infos to present them at once; otherwise, <c>false</c>.</returns>
|
||||
public static bool CanMerge(ItemInfo a, ItemInfo b)
|
||||
{
|
||||
if (a.Info.DeclaringType != b.Info.DeclaringType)
|
||||
return false;
|
||||
return a.Info.Name == b.Info.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private struct VisibleIfCache
|
||||
{
|
||||
public ScriptMemberInfo Target;
|
||||
public ScriptMemberInfo Source;
|
||||
public PropertiesListElement PropertiesList;
|
||||
public bool Invert;
|
||||
public int LabelIndex;
|
||||
|
||||
public bool GetValue(object instance)
|
||||
{
|
||||
var value = (bool)Source.GetValue(instance);
|
||||
if (Invert)
|
||||
value = !value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private VisibleIfCache[] _visibleIfCaches;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items for the type
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>The items.</returns>
|
||||
protected virtual List<ItemInfo> GetItemsForType(ScriptType type)
|
||||
{
|
||||
return GetItemsForType(type, type.IsClass, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items for the type
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="useProperties">True if use type properties.</param>
|
||||
/// <param name="useFields">True if use type fields.</param>
|
||||
/// <returns>The items.</returns>
|
||||
protected List<ItemInfo> GetItemsForType(ScriptType type, bool useProperties, bool useFields)
|
||||
{
|
||||
var items = new List<ItemInfo>();
|
||||
|
||||
if (useProperties)
|
||||
{
|
||||
// Process properties
|
||||
var properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
items.Capacity = Math.Max(items.Capacity, items.Count + properties.Length);
|
||||
for (int i = 0; i < properties.Length; i++)
|
||||
{
|
||||
var p = properties[i];
|
||||
|
||||
// Skip properties without getter or setter
|
||||
if (!p.HasGet || !p.HasSet)
|
||||
continue;
|
||||
|
||||
var attributes = p.GetAttributes(true);
|
||||
|
||||
// Skip hidden fields, handle special attributes
|
||||
if ((!p.IsPublic && !attributes.Any(x => x is ShowInEditorAttribute)) || attributes.Any(x => x is HideInEditorAttribute))
|
||||
continue;
|
||||
|
||||
items.Add(new ItemInfo(p, attributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (useFields)
|
||||
{
|
||||
// Process fields
|
||||
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
items.Capacity = Math.Max(items.Capacity, items.Count + fields.Length);
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var f = fields[i];
|
||||
|
||||
var attributes = f.GetAttributes(true);
|
||||
|
||||
// Skip hidden fields, handle special attributes
|
||||
if ((!f.IsPublic && !attributes.Any(x => x is ShowInEditorAttribute)) || attributes.Any(x => x is HideInEditorAttribute))
|
||||
continue;
|
||||
|
||||
items.Add(new ItemInfo(f, attributes));
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static ScriptMemberInfo GetVisibleIfSource(ScriptType type, VisibleIfAttribute visibleIf)
|
||||
{
|
||||
var property = type.GetProperty(visibleIf.MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
if (property != ScriptMemberInfo.Null)
|
||||
{
|
||||
if (!property.HasGet)
|
||||
{
|
||||
Debug.LogError("Invalid VisibleIf rule. Property has missing getter " + visibleIf.MemberName);
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
if (property.ValueType.Type != typeof(bool))
|
||||
{
|
||||
Debug.LogError("Invalid VisibleIf rule. Property has to return bool type " + visibleIf.MemberName);
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
var field = type.GetField(visibleIf.MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||
if (field != ScriptMemberInfo.Null)
|
||||
{
|
||||
if (field.ValueType.Type != typeof(bool))
|
||||
{
|
||||
Debug.LogError("Invalid VisibleIf rule. Field has to be bool type " + visibleIf.MemberName);
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
Debug.LogError("Invalid VisibleIf rule. Cannot find member " + visibleIf.MemberName);
|
||||
return ScriptMemberInfo.Null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the property for the given item.
|
||||
/// </summary>
|
||||
/// <param name="itemLayout">The item layout.</param>
|
||||
/// <param name="itemValues">The item values.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
|
||||
{
|
||||
int labelIndex = 0;
|
||||
if ((item.IsReadOnly || item.VisibleIf != null) &&
|
||||
itemLayout.Children.Count > 0 &&
|
||||
itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement)
|
||||
{
|
||||
labelIndex = propertiesListElement.Labels.Count;
|
||||
}
|
||||
|
||||
itemLayout.Property(item.DisplayName, itemValues, item.OverrideEditor, item.TooltipText);
|
||||
|
||||
if (item.IsReadOnly && itemLayout.Children.Count > 0)
|
||||
{
|
||||
PropertiesListElement list = null;
|
||||
int firstChildControlIndex = 0;
|
||||
bool disableSingle = true;
|
||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||
if (control is GroupElement group && group.Children.Count > 0)
|
||||
{
|
||||
list = group.Children[0] as PropertiesListElement;
|
||||
disableSingle = false; // Disable all nested editors
|
||||
}
|
||||
else if (control is PropertiesListElement list1)
|
||||
{
|
||||
list = list1;
|
||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
||||
}
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
// Disable controls added to the editor
|
||||
var count = list.Properties.Children.Count;
|
||||
for (int j = firstChildControlIndex; j < count; j++)
|
||||
{
|
||||
var child = list.Properties.Children[j];
|
||||
if (disableSingle && child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
child.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.VisibleIf != null)
|
||||
{
|
||||
PropertiesListElement list;
|
||||
if (itemLayout.Children.Count > 0 && itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement list1)
|
||||
{
|
||||
list = list1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: support inlined objects hiding?
|
||||
return;
|
||||
}
|
||||
|
||||
// Get source member used to check rule
|
||||
var sourceMember = GetVisibleIfSource(item.Info.DeclaringType, item.VisibleIf);
|
||||
if (sourceMember == ScriptType.Null)
|
||||
return;
|
||||
|
||||
// Find the target control to show/hide
|
||||
|
||||
// Resize cache
|
||||
if (_visibleIfCaches == null)
|
||||
_visibleIfCaches = new VisibleIfCache[8];
|
||||
int count = 0;
|
||||
while (count < _visibleIfCaches.Length && _visibleIfCaches[count].Target != ScriptType.Null)
|
||||
count++;
|
||||
if (count >= _visibleIfCaches.Length)
|
||||
Array.Resize(ref _visibleIfCaches, count * 2);
|
||||
|
||||
// Add item
|
||||
_visibleIfCaches[count] = new VisibleIfCache
|
||||
{
|
||||
Target = item.Info,
|
||||
Source = sourceMember,
|
||||
PropertiesList = list,
|
||||
LabelIndex = labelIndex,
|
||||
Invert = item.VisibleIf.Invert,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_visibleIfCaches = null;
|
||||
|
||||
// Collect items to edit
|
||||
List<ItemInfo> items;
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
var value = Values[0];
|
||||
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)
|
||||
{
|
||||
layout = layout.Space(20);
|
||||
|
||||
const float ButtonSize = 14.0f;
|
||||
var button = new Button
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Create a new instance of the object",
|
||||
Height = ButtonSize,
|
||||
Width = ButtonSize,
|
||||
X = layout.ContainerControl.Width - ButtonSize - 4,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
Parent = layout.ContainerControl
|
||||
};
|
||||
button.Clicked += () =>
|
||||
{
|
||||
var newType = Values.Type;
|
||||
SetValue(newType.CreateInstance());
|
||||
RebuildLayoutOnRefresh();
|
||||
};
|
||||
}
|
||||
|
||||
layout.Label("<null>");
|
||||
return;
|
||||
}
|
||||
|
||||
items = GetItemsForType(TypeUtils.GetObjectType(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
var types = ValuesTypes;
|
||||
items = new List<ItemInfo>(GetItemsForType(types[0]));
|
||||
for (int i = 1; i < types.Length && items.Count > 0; i++)
|
||||
{
|
||||
var otherItems = GetItemsForType(types[i]);
|
||||
|
||||
// Merge items
|
||||
for (int j = 0; j < items.Count && items.Count > 0; j++)
|
||||
{
|
||||
bool isInvalid = true;
|
||||
for (int k = 0; k < otherItems.Count; k++)
|
||||
{
|
||||
var a = items[j];
|
||||
var b = otherItems[k];
|
||||
|
||||
if (ItemInfo.CanMerge(a, b))
|
||||
{
|
||||
isInvalid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
{
|
||||
items.RemoveAt(j--);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort items
|
||||
items.Sort();
|
||||
|
||||
// Add items
|
||||
GroupElement lastGroup = null;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
|
||||
// Check if use group
|
||||
LayoutElementsContainer itemLayout;
|
||||
if (item.UseGroup)
|
||||
{
|
||||
if (lastGroup == null || lastGroup.Panel.HeaderText != item.Display.Group)
|
||||
lastGroup = layout.Group(item.Display.Group);
|
||||
itemLayout = lastGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastGroup = null;
|
||||
itemLayout = layout;
|
||||
}
|
||||
|
||||
// Space
|
||||
if (item.Space != null)
|
||||
itemLayout.Space(item.Space.Height);
|
||||
|
||||
// Header
|
||||
if (item.Header != null)
|
||||
itemLayout.Header(item.Header.Text);
|
||||
|
||||
// Peek values
|
||||
ValueContainer itemValues;
|
||||
try
|
||||
{
|
||||
itemValues = item.GetValues(Values);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning("Failed to get object values for item " + item);
|
||||
Editor.LogWarning(ex.Message);
|
||||
Editor.LogWarning(ex.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn property editor
|
||||
SpawnProperty(itemLayout, itemValues, item);
|
||||
|
||||
// Expand all parent groups if need to
|
||||
if (item.ExpandGroups)
|
||||
{
|
||||
var c = itemLayout.ContainerControl;
|
||||
do
|
||||
{
|
||||
if (c is DropPanel dropPanel)
|
||||
dropPanel.Open(false);
|
||||
else if (c is CustomEditorPresenter.PresenterPanel)
|
||||
break;
|
||||
c = c.Parent;
|
||||
} while (c != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
if (_visibleIfCaches != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _visibleIfCaches.Length; i++)
|
||||
{
|
||||
var c = _visibleIfCaches[i];
|
||||
|
||||
if (c.Target == ScriptMemberInfo.Null)
|
||||
break;
|
||||
|
||||
// Check rule (all objects must allow to show this property)
|
||||
bool visible = true;
|
||||
for (int j = 0; j < Values.Count; j++)
|
||||
{
|
||||
if (Values[j] != null && !c.GetValue(Values[j]))
|
||||
{
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the visibility (note: there may be no label)
|
||||
if (c.LabelIndex != -1 && c.PropertiesList.Labels.Count > c.LabelIndex)
|
||||
{
|
||||
var label = c.PropertiesList.Labels[c.LabelIndex];
|
||||
label.Visible = visible;
|
||||
for (int j = label.FirstChildControlIndex; j < c.PropertiesList.Properties.Children.Count; j++)
|
||||
{
|
||||
var child = c.PropertiesList.Properties.Children[j];
|
||||
if (child is PropertyNameLabel)
|
||||
break;
|
||||
|
||||
child.Visible = visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning(ex);
|
||||
Editor.LogError("Failed to update VisibleIf rules. " + ex.Message);
|
||||
|
||||
// Remove rules to prevent error in loop
|
||||
_visibleIfCaches = null;
|
||||
}
|
||||
}
|
||||
|
||||
base.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Source/Editor/CustomEditors/Editors/GuidEditor.cs
Normal file
53
Source/Editor/CustomEditors/Editors/GuidEditor.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Guid properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Guid)), DefaultEditor]
|
||||
public sealed class GuidEditor : CustomEditor
|
||||
{
|
||||
private TextBoxElement _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = layout.TextBox();
|
||||
_element.TextBox.EditEnd += OnEditEnd;
|
||||
}
|
||||
|
||||
private void OnEditEnd()
|
||||
{
|
||||
Guid value;
|
||||
if (Guid.TryParse(_element.Text, out value))
|
||||
{
|
||||
SetValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
_element.TextBox.Text = string.Empty;
|
||||
_element.TextBox.WatermarkText = "Different values";
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.TextBox.Text = ((Guid)Values[0]).ToString("D");
|
||||
_element.TextBox.WatermarkText = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Source/Editor/CustomEditors/Editors/IBrushEditor.cs
Normal file
27
Source/Editor/CustomEditors/Editors/IBrushEditor.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit <see cref="IBrush"/> type properties.
|
||||
/// </summary>
|
||||
/// <seealso cref="IBrush"/>
|
||||
/// <seealso cref="ObjectSwitcherEditor"/>
|
||||
[CustomEditor(typeof(IBrush)), DefaultEditor]
|
||||
public sealed class IBrushEditor : ObjectSwitcherEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override OptionType[] Options => new[]
|
||||
{
|
||||
new OptionType("Texture", typeof(TextureBrush)),
|
||||
new OptionType("Sprite", typeof(SpriteBrush)),
|
||||
new OptionType("GPU Texture", typeof(GPUTextureBrush)),
|
||||
new OptionType("Material", typeof(MaterialBrush)),
|
||||
new OptionType("Solid Color", typeof(SolidColorBrush)),
|
||||
new OptionType("Linear Gradient", typeof(LinearGradientBrush)),
|
||||
};
|
||||
}
|
||||
}
|
||||
87
Source/Editor/CustomEditors/Editors/Int2Editor.cs
Normal file
87
Source/Editor/CustomEditors/Editors/Int2Editor.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Int2 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Int2)), DefaultEditor]
|
||||
public class Int2Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement YElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 2;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.IntegerValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.IntValue.ValueChanged += OnValueChanged;
|
||||
XElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.IntegerValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.IntValue.ValueChanged += OnValueChanged;
|
||||
YElement.IntValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Int2(
|
||||
XElement.IntValue.Value,
|
||||
YElement.IntValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Int2)Values[0];
|
||||
XElement.IntValue.Value = value.X;
|
||||
YElement.IntValue.Value = value.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Source/Editor/CustomEditors/Editors/Int3Editor.cs
Normal file
99
Source/Editor/CustomEditors/Editors/Int3Editor.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Int3 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Int3)), DefaultEditor]
|
||||
public class Int3Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement YElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Z component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement ZElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 3;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.IntegerValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.IntValue.ValueChanged += OnValueChanged;
|
||||
XElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.IntegerValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.IntValue.ValueChanged += OnValueChanged;
|
||||
YElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.IntegerValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.IntValue.ValueChanged += OnValueChanged;
|
||||
ZElement.IntValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Int3(
|
||||
XElement.IntValue.Value,
|
||||
YElement.IntValue.Value,
|
||||
ZElement.IntValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Int3)Values[0];
|
||||
XElement.IntValue.Value = value.X;
|
||||
YElement.IntValue.Value = value.Y;
|
||||
ZElement.IntValue.Value = value.Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Source/Editor/CustomEditors/Editors/Int4Editor.cs
Normal file
111
Source/Editor/CustomEditors/Editors/Int4Editor.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Int4 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Int4)), DefaultEditor]
|
||||
public class Int4Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement YElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Z component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement ZElement;
|
||||
|
||||
/// <summary>
|
||||
/// The W component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement WElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 4;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.IntegerValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.IntValue.ValueChanged += OnValueChanged;
|
||||
XElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.IntegerValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.IntValue.ValueChanged += OnValueChanged;
|
||||
YElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.IntegerValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.IntValue.ValueChanged += OnValueChanged;
|
||||
ZElement.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
WElement = grid.IntegerValue();
|
||||
WElement.SetLimits(limit);
|
||||
WElement.IntValue.ValueChanged += OnValueChanged;
|
||||
WElement.IntValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Int4(
|
||||
XElement.IntValue.Value,
|
||||
YElement.IntValue.Value,
|
||||
ZElement.IntValue.Value,
|
||||
WElement.IntValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Int4)Values[0];
|
||||
XElement.IntValue.Value = value.X;
|
||||
YElement.IntValue.Value = value.Y;
|
||||
ZElement.IntValue.Value = value.Z;
|
||||
WElement.IntValue.Value = value.W;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
474
Source/Editor/CustomEditors/Editors/IntegerEditor.cs
Normal file
474
Source/Editor/CustomEditors/Editors/IntegerEditor.cs
Normal file
@@ -0,0 +1,474 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit integer value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(int)), DefaultEditor]
|
||||
public sealed class IntegerEditor : CustomEditor
|
||||
{
|
||||
private IIntegerValueEditor _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = null;
|
||||
|
||||
// Try get limit attribute for value min/max range setting and slider speed
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
var range = attributes.FirstOrDefault(x => x is RangeAttribute);
|
||||
if (range != null)
|
||||
{
|
||||
// Use slider
|
||||
var element = layout.Slider();
|
||||
element.SetLimits((RangeAttribute)range);
|
||||
element.Slider.ValueChanged += OnValueChanged;
|
||||
element.Slider.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
return;
|
||||
}
|
||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
if (limit != null)
|
||||
{
|
||||
// Use int value editor with limit
|
||||
var element = layout.IntegerValue();
|
||||
element.SetLimits((LimitAttribute)limit);
|
||||
element.IntValue.ValueChanged += OnValueChanged;
|
||||
element.IntValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_element == null)
|
||||
{
|
||||
// Use int value editor
|
||||
var element = layout.IntegerValue();
|
||||
element.IntValue.ValueChanged += OnValueChanged;
|
||||
element.IntValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = _element.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
SetValue(_element.Value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.Value = (int)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit signed integer value type properties (maps to the full range of long type).
|
||||
/// </summary>
|
||||
public abstract class SignedIntegerValueEditor : CustomEditor
|
||||
{
|
||||
private SignedIntegerValueElement _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = null;
|
||||
|
||||
GetLimits(out var min, out var max);
|
||||
|
||||
// Try get limit attribute for value min/max range setting and slider speed
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
if (limit != null)
|
||||
{
|
||||
// Use int value editor with limit
|
||||
var element = layout.SignedIntegerValue();
|
||||
element.LongValue.SetLimits((LimitAttribute)limit);
|
||||
element.LongValue.MinValue = Mathf.Max(element.LongValue.MinValue, min);
|
||||
element.LongValue.MaxValue = Mathf.Min(element.LongValue.MaxValue, max);
|
||||
element.LongValue.ValueChanged += OnValueChanged;
|
||||
element.LongValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_element == null)
|
||||
{
|
||||
// Use int value editor
|
||||
var element = layout.SignedIntegerValue();
|
||||
element.LongValue.MinValue = Mathf.Max(element.LongValue.MinValue, min);
|
||||
element.LongValue.MaxValue = Mathf.Min(element.LongValue.MaxValue, max);
|
||||
element.LongValue.ValueChanged += OnValueChanged;
|
||||
element.LongValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = _element.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
SetValue(SetValue(_element.Value), token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.Value = GetValue(Values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value limits.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
protected abstract void GetLimits(out long min, out long max);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value as long.
|
||||
/// </summary>
|
||||
/// <param name="value">The value from object.</param>
|
||||
/// <returns>The value for editor.</returns>
|
||||
protected abstract long GetValue(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value from long.
|
||||
/// </summary>
|
||||
/// <param name="value">The value from editor.</param>
|
||||
/// <returns>The value to object.</returns>
|
||||
protected abstract object SetValue(long value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit sbyte value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(sbyte)), DefaultEditor]
|
||||
public sealed class SByteEditor : SignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out long min, out long max)
|
||||
{
|
||||
min = sbyte.MinValue;
|
||||
max = sbyte.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long GetValue(object value)
|
||||
{
|
||||
return (sbyte)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(long value)
|
||||
{
|
||||
return (sbyte)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit short value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(short)), DefaultEditor]
|
||||
public sealed class ShortEditor : SignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out long min, out long max)
|
||||
{
|
||||
min = short.MinValue;
|
||||
max = short.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long GetValue(object value)
|
||||
{
|
||||
return (short)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(long value)
|
||||
{
|
||||
return (short)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit long value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(long)), DefaultEditor]
|
||||
public sealed class LongEditor : SignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out long min, out long max)
|
||||
{
|
||||
min = long.MinValue;
|
||||
max = long.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long GetValue(object value)
|
||||
{
|
||||
return (long)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(long value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit unsigned integer value type properties (maps to the full range of ulong type).
|
||||
/// </summary>
|
||||
public abstract class UnsignedIntegerValueEditor : CustomEditor
|
||||
{
|
||||
private UnsignedIntegerValueElement _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_element = null;
|
||||
|
||||
GetLimits(out var min, out var max);
|
||||
|
||||
// Try get limit attribute for value min/max range setting and slider speed
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
var limit = attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
if (limit != null)
|
||||
{
|
||||
// Use int value editor with limit
|
||||
var element = layout.UnsignedIntegerValue();
|
||||
element.ULongValue.SetLimits((LimitAttribute)limit);
|
||||
element.ULongValue.MinValue = Mathf.Max(element.ULongValue.MinValue, min);
|
||||
element.ULongValue.MaxValue = Mathf.Min(element.ULongValue.MaxValue, max);
|
||||
element.ULongValue.ValueChanged += OnValueChanged;
|
||||
element.ULongValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_element == null)
|
||||
{
|
||||
// Use int value editor
|
||||
var element = layout.UnsignedIntegerValue();
|
||||
element.ULongValue.MinValue = Mathf.Max(element.ULongValue.MinValue, min);
|
||||
element.ULongValue.MaxValue = Mathf.Min(element.ULongValue.MaxValue, max);
|
||||
element.ULongValue.ValueChanged += OnValueChanged;
|
||||
element.ULongValue.SlidingEnd += ClearToken;
|
||||
_element = element;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = _element.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
SetValue(SetValue(_element.Value), token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.Value = GetValue(Values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value limits.
|
||||
/// </summary>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
protected abstract void GetLimits(out ulong min, out ulong max);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value as long.
|
||||
/// </summary>
|
||||
/// <param name="value">The value from object.</param>
|
||||
/// <returns>The value for editor.</returns>
|
||||
protected abstract ulong GetValue(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value from long.
|
||||
/// </summary>
|
||||
/// <param name="value">The value from editor.</param>
|
||||
/// <returns>The value to object.</returns>
|
||||
protected abstract object SetValue(ulong value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit byte value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(byte)), DefaultEditor]
|
||||
public sealed class ByteEditor : UnsignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out ulong min, out ulong max)
|
||||
{
|
||||
min = byte.MinValue;
|
||||
max = byte.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ulong GetValue(object value)
|
||||
{
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(ulong value)
|
||||
{
|
||||
return (byte)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit char value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(char)), DefaultEditor]
|
||||
public sealed class CharEditor : UnsignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out ulong min, out ulong max)
|
||||
{
|
||||
min = char.MinValue;
|
||||
max = char.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ulong GetValue(object value)
|
||||
{
|
||||
return (char)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(ulong value)
|
||||
{
|
||||
return (char)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit ushort value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ushort)), DefaultEditor]
|
||||
public sealed class UShortEditor : UnsignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out ulong min, out ulong max)
|
||||
{
|
||||
min = ushort.MinValue;
|
||||
max = ushort.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ulong GetValue(object value)
|
||||
{
|
||||
return (ushort)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(ulong value)
|
||||
{
|
||||
return (ushort)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit uint value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(uint)), DefaultEditor]
|
||||
public sealed class UintEditor : UnsignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out ulong min, out ulong max)
|
||||
{
|
||||
min = uint.MinValue;
|
||||
max = uint.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ulong GetValue(object value)
|
||||
{
|
||||
return (uint)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(ulong value)
|
||||
{
|
||||
return (uint)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit ulong value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ulong)), DefaultEditor]
|
||||
public sealed class ULongEditor : UnsignedIntegerValueEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void GetLimits(out ulong min, out ulong max)
|
||||
{
|
||||
min = ulong.MinValue;
|
||||
max = ulong.MaxValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ulong GetValue(object value)
|
||||
{
|
||||
return (ulong)value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override object SetValue(ulong value)
|
||||
{
|
||||
return (ulong)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Source/Editor/CustomEditors/Editors/ListEditor.cs
Normal file
92
Source/Editor/CustomEditors/Editors/ListEditor.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit lists.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(List<>)), DefaultEditor]
|
||||
public class ListEditor : CollectionEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override int Count => (Values[0] as IList)?.Count ?? 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IList Allocate(int size)
|
||||
{
|
||||
var listType = Values.Type;
|
||||
var list = (IList)listType.CreateInstance();
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
list.Add(defaultValue);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Resize(int newSize)
|
||||
{
|
||||
var list = Values[0] as IList;
|
||||
var oldSize = list?.Count ?? 0;
|
||||
|
||||
if (oldSize != newSize)
|
||||
{
|
||||
// Allocate new list
|
||||
var listType = Values.Type;
|
||||
var newValues = (IList)listType.CreateInstance();
|
||||
|
||||
var sharedCount = Mathf.Min(oldSize, newSize);
|
||||
if (list != null && sharedCount > 0)
|
||||
{
|
||||
// Copy old values
|
||||
for (int i = 0; i < sharedCount; i++)
|
||||
{
|
||||
newValues.Add(list[i]);
|
||||
}
|
||||
|
||||
// Fill new entries with the last value
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
{
|
||||
newValues.Add(list[oldSize - 1]);
|
||||
}
|
||||
}
|
||||
else if (newSize > 0)
|
||||
{
|
||||
// Fill new entries
|
||||
var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType);
|
||||
for (int i = oldSize; i < newSize; i++)
|
||||
{
|
||||
newValues.Add(defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newValues);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IList CloneValues()
|
||||
{
|
||||
var list = Values[0] as IList;
|
||||
if (list == null)
|
||||
return null;
|
||||
|
||||
var size = list.Count;
|
||||
var listType = Values.Type;
|
||||
var cloned = (IList)listType.CreateInstance();
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
cloned.Add(list[i]);
|
||||
}
|
||||
|
||||
return cloned;
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Source/Editor/CustomEditors/Editors/MatrixEditor.cs
Normal file
89
Source/Editor/CustomEditors/Editors/MatrixEditor.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Matrix value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Matrix)), DefaultEditor]
|
||||
public class MatrixEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The 16 components editors.
|
||||
/// </summary>
|
||||
protected readonly FloatValueElement[] Elements = new FloatValueElement[16];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight * 4;
|
||||
gridControl.SlotsHorizontally = 4;
|
||||
gridControl.SlotsVertically = 4;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
var elemnt = grid.FloatValue();
|
||||
elemnt.SetLimits(limit);
|
||||
elemnt.FloatValue.ValueChanged += OnValueChanged;
|
||||
elemnt.FloatValue.SlidingEnd += ClearToken;
|
||||
Elements[i] = elemnt;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = false;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
isSliding = Elements[i].IsSliding;
|
||||
}
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Matrix();
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
value[i] = Elements[i].FloatValue.Value;
|
||||
}
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Matrix)Values[0];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
Elements[i].FloatValue.Value = value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit <see cref="ModelInstanceEntry"/> value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ModelInstanceEntry)), DefaultEditor]
|
||||
public sealed class ModelInstanceEntryEditor : GenericEditor
|
||||
{
|
||||
private GroupElement _group;
|
||||
private bool _updateName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_updateName = true;
|
||||
var group = layout.Group("Entry");
|
||||
_group = group;
|
||||
|
||||
base.Initialize(group);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
if (_updateName &&
|
||||
_group != null &&
|
||||
ParentEditor?.ParentEditor != null &&
|
||||
ParentEditor.ParentEditor.Values.Count > 0)
|
||||
{
|
||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
||||
if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel)
|
||||
{
|
||||
var model = staticModel.Model;
|
||||
if (model && model.IsLoaded)
|
||||
{
|
||||
_group.Panel.HeaderText = "Entry " + model.MaterialSlots[entryIndex].Name;
|
||||
_updateName = false;
|
||||
}
|
||||
}
|
||||
else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel)
|
||||
{
|
||||
var model = animatedModel.SkinnedModel;
|
||||
if (model && model.IsLoaded)
|
||||
{
|
||||
_group.Panel.HeaderText = "Entry " + model.MaterialSlots[entryIndex].Name;
|
||||
_updateName = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
Normal file
182
Source/Editor/CustomEditors/Editors/ObjectSwitcherEditor.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Base implementation of the inspector used to edit properties of the given abstract or interface type that contain a setter to assign a derived implementation type.
|
||||
/// </summary>
|
||||
public abstract class ObjectSwitcherEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines type that can be assigned to the modified property.
|
||||
/// </summary>
|
||||
public struct OptionType
|
||||
{
|
||||
/// <summary>
|
||||
/// The type name used to show in the type selector dropdown menu (for user interface).
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The type.
|
||||
/// </summary>
|
||||
public Type Type;
|
||||
|
||||
/// <summary>
|
||||
/// The creator function that spawns the object of the given type.
|
||||
/// </summary>
|
||||
public Func<Type, object> Creator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionType"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
public OptionType(Type type)
|
||||
{
|
||||
Name = type.Name;
|
||||
Type = type;
|
||||
Creator = Activator.CreateInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionType"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public OptionType(string name, Type type)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Creator = Activator.CreateInstance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionType"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="creator">The instance creation function.</param>
|
||||
public OptionType(string name, Type type, Func<Type, object> creator)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
Creator = creator;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options collection for the property value assignment.
|
||||
/// </summary>
|
||||
protected abstract OptionType[] Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the type ComboBox property name for the object type picking.
|
||||
/// </summary>
|
||||
protected virtual string TypeComboBoxName => "Type";
|
||||
|
||||
private OptionType[] _options;
|
||||
private ScriptType _type;
|
||||
|
||||
private ScriptType Type => Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// Get the target options
|
||||
_options = Options;
|
||||
if (_options == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
int selectedIndex = -1;
|
||||
bool hasDifferentTypes = Values.HasDifferentTypes;
|
||||
var type = Type;
|
||||
_type = type;
|
||||
|
||||
// Type
|
||||
var typeEditor = layout.ComboBox(TypeComboBoxName, "Type of the object value. Use it to change the object.");
|
||||
for (int i = 0; i < _options.Length; i++)
|
||||
{
|
||||
typeEditor.ComboBox.AddItem(_options[i].Name);
|
||||
selectedIndex = _options[i].Type == type.Type ? i : selectedIndex;
|
||||
}
|
||||
typeEditor.ComboBox.SupportMultiSelect = false;
|
||||
typeEditor.ComboBox.SelectedIndex = hasDifferentTypes ? -1 : selectedIndex;
|
||||
typeEditor.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
|
||||
// Editing different types is not supported
|
||||
if (Values.HasDifferentTypes)
|
||||
{
|
||||
var property = layout.AddPropertyItem("Value");
|
||||
property.Label("Different Values");
|
||||
return;
|
||||
}
|
||||
|
||||
// Nothing to edit
|
||||
if (Values.HasNull)
|
||||
{
|
||||
var property = layout.AddPropertyItem("Value");
|
||||
property.Label("<null>");
|
||||
return;
|
||||
}
|
||||
|
||||
// Value
|
||||
var values = new CustomValueContainer(type, (instance, index) => instance, (instance, index, value) => { });
|
||||
values.AddRange(Values);
|
||||
var editor = CustomEditorsUtil.CreateEditor(type);
|
||||
var style = editor.Style;
|
||||
if (style == DisplayStyle.InlineIntoParent)
|
||||
{
|
||||
layout.Object(values, editor);
|
||||
}
|
||||
else if (style == DisplayStyle.Group)
|
||||
{
|
||||
var group = layout.Group("Value", true);
|
||||
group.Panel.Open(false);
|
||||
group.Object(values, editor);
|
||||
|
||||
// Remove empty group
|
||||
if (group.ContainerControl.ChildrenCount == 0)
|
||||
{
|
||||
layout.Children.Remove(group);
|
||||
group.Panel.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.AddPropertyItem("Value").Object(values, editor);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
object value = null;
|
||||
if (comboBox.SelectedIndex != -1)
|
||||
{
|
||||
var option = _options[comboBox.SelectedIndex];
|
||||
value = option.Creator(option.Type);
|
||||
}
|
||||
SetValue(value);
|
||||
RebuildLayoutOnRefresh();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
// Check if type has been modified outside the editor (eg. from code)
|
||||
if (Type != _type)
|
||||
{
|
||||
if (ParentEditor != null)
|
||||
ParentEditor.RebuildLayout();
|
||||
else
|
||||
RebuildLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
Normal file
90
Source/Editor/CustomEditors/Editors/QuaternionEditor.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Quaternion value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Quaternion)), DefaultEditor]
|
||||
public class QuaternionEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component element
|
||||
/// </summary>
|
||||
protected FloatValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component element
|
||||
/// </summary>
|
||||
protected FloatValueElement YElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Z component element
|
||||
/// </summary>
|
||||
protected FloatValueElement ZElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 3;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
XElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
YElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
ZElement.FloatValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
float x = XElement.FloatValue.Value;
|
||||
float y = YElement.FloatValue.Value;
|
||||
float z = ZElement.FloatValue.Value;
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
Quaternion value;
|
||||
Quaternion.Euler(x, y, z, out value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Quaternion)Values[0];
|
||||
var euler = value.EulerAngles;
|
||||
XElement.FloatValue.Value = euler.X;
|
||||
YElement.FloatValue.Value = euler.Y;
|
||||
ZElement.FloatValue.Value = euler.Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Source/Editor/CustomEditors/Editors/SkeletonNodeEditor.cs
Normal file
59
Source/Editor/CustomEditors/Editors/SkeletonNodeEditor.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for picking skeleton node. Instead of choosing node index or entering node text it shows a combo box with simple tag picking by name.
|
||||
/// </summary>
|
||||
public sealed class SkeletonNodeEditor : CustomEditor
|
||||
{
|
||||
private ComboBoxElement element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.ComboBox();
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
|
||||
// Set node names
|
||||
if (ParentEditor != null
|
||||
&& ParentEditor.Values.Count == 1 && ParentEditor.Values[0] is BoneSocket boneSocket
|
||||
&& boneSocket.Parent is AnimatedModel animatedModel && animatedModel.SkinnedModel
|
||||
&& !animatedModel.SkinnedModel.WaitForLoaded())
|
||||
{
|
||||
var nodes = animatedModel.SkinnedModel.Nodes;
|
||||
for (int nodeIndex = 0; nodeIndex < nodes.Length; nodeIndex++)
|
||||
element.ComboBox.AddItem(nodes[nodeIndex].Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
string value = comboBox.SelectedItem;
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values on many actor selected
|
||||
}
|
||||
else
|
||||
{
|
||||
string value = (string)Values[0];
|
||||
element.ComboBox.SelectedItem = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Source/Editor/CustomEditors/Editors/SpriteHandleEditor.cs
Normal file
113
Source/Editor/CustomEditors/Editors/SpriteHandleEditor.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit <see cref="SpriteHandle"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.CustomEditor" />
|
||||
[CustomEditor(typeof(SpriteHandle)), DefaultEditor]
|
||||
public class SpriteHandleEditor : CustomEditor
|
||||
{
|
||||
private ComboBox _spritePicker;
|
||||
private ValueContainer _atlasValues;
|
||||
private ValueContainer _indexValues;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
// Atlas
|
||||
var atlasField = typeof(SpriteHandle).GetField("Atlas");
|
||||
var atlasValues = new ValueContainer(new ScriptMemberInfo(atlasField), Values);
|
||||
layout.Property("Atlas", atlasValues, null, "The target atlas texture used as a sprite image source.");
|
||||
_atlasValues = atlasValues;
|
||||
|
||||
// Sprite
|
||||
var spriteIndexField = typeof(SpriteHandle).GetField("Index");
|
||||
_indexValues = new ValueContainer(new ScriptMemberInfo(spriteIndexField), Values);
|
||||
var spriteLabel = layout.AddPropertyItem("Sprite", "The selected sprite from the atlas.");
|
||||
|
||||
// Check state
|
||||
if (atlasValues.HasDifferentValues)
|
||||
{
|
||||
spriteLabel.Label("Different values");
|
||||
return;
|
||||
}
|
||||
var value = (SpriteAtlas)atlasValues[0];
|
||||
if (value == null)
|
||||
{
|
||||
spriteLabel.Label("Pick atlas first");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: don't stall use Refresh to rebuild UI when sprite atlas gets loaded
|
||||
if (value.WaitForLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// List all sprites from the atlas asset
|
||||
var spritesCount = value.SpritesCount;
|
||||
var spritePicker = spriteLabel.ComboBox();
|
||||
spritePicker.ComboBox.Items.Capacity = spritesCount;
|
||||
for (int i = 0; i < spritesCount; i++)
|
||||
{
|
||||
spritePicker.ComboBox.AddItem(value.GetSprite(i).Name);
|
||||
}
|
||||
spritePicker.ComboBox.SupportMultiSelect = false;
|
||||
spritePicker.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
_spritePicker = spritePicker.ComboBox;
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox obj)
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
SpriteHandle value;
|
||||
value.Atlas = (SpriteAtlas)_atlasValues[0];
|
||||
value.Index = _spritePicker.SelectedIndex;
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues || _spritePicker == null)
|
||||
return;
|
||||
|
||||
// Fetch the instance values
|
||||
_atlasValues.Refresh(Values);
|
||||
_indexValues.Refresh(Values);
|
||||
|
||||
// Update selection
|
||||
int selectedIndex = -1;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var idx = (int)_indexValues[i];
|
||||
if (idx != -1 && idx < _spritePicker.Items.Count)
|
||||
selectedIndex = idx;
|
||||
}
|
||||
_spritePicker.SelectedIndex = selectedIndex;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnDirty(CustomEditor editor, object value, object token = null)
|
||||
{
|
||||
// Check if Atlas has been changed
|
||||
if (editor is AssetRefEditor)
|
||||
{
|
||||
// Rebuild layout to update the dropdown menu with sprites list
|
||||
RebuildLayoutOnRefresh();
|
||||
}
|
||||
|
||||
return base.OnDirty(editor, value, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Source/Editor/CustomEditors/Editors/StringEditor.cs
Normal file
53
Source/Editor/CustomEditors/Editors/StringEditor.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit string properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(string)), DefaultEditor]
|
||||
public sealed class StringEditor : CustomEditor
|
||||
{
|
||||
private TextBoxElement _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
bool isMultiLine = false;
|
||||
|
||||
var attributes = Values.GetAttributes();
|
||||
var multiLine = attributes?.FirstOrDefault(x => x is MultilineTextAttribute);
|
||||
if (multiLine != null)
|
||||
{
|
||||
isMultiLine = true;
|
||||
}
|
||||
|
||||
_element = layout.TextBox(isMultiLine);
|
||||
_element.TextBox.EditEnd += () => SetValue(_element.Text);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
_element.TextBox.Text = string.Empty;
|
||||
_element.TextBox.WatermarkText = "Different values";
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.TextBox.Text = (string)Values[0];
|
||||
_element.TextBox.WatermarkText = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Source/Editor/CustomEditors/Editors/StyleEditor.cs
Normal file
53
Source/Editor/CustomEditors/Editors/StyleEditor.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit styles.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Style)), DefaultEditor]
|
||||
public class StyleEditor : CustomEditor
|
||||
{
|
||||
private CustomElement<StyleValueEditor> _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this editor.
|
||||
/// </summary>
|
||||
/// <param name="layout">The layout builder.</param>
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var style = (Style)Values[0];
|
||||
|
||||
_element = layout.Custom<StyleValueEditor>();
|
||||
_element.CustomControl.Value = style;
|
||||
_element.CustomControl.ValueChanged += OnValueChanged;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
SetValue(_element.CustomControl.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
_element.CustomControl.Value = (Style)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
451
Source/Editor/CustomEditors/Editors/TypeEditor.cs
Normal file
451
Source/Editor/CustomEditors/Editors/TypeEditor.cs
Normal file
@@ -0,0 +1,451 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom control type used to pick reference to <see cref="System.Type"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
[HideInEditor]
|
||||
public class TypePickerControl : Control
|
||||
{
|
||||
private ScriptType _type;
|
||||
private ScriptType _value;
|
||||
private string _valueName;
|
||||
|
||||
private Vector2 _mousePos;
|
||||
|
||||
private bool _hasValidDragOver;
|
||||
private DragActors _dragActors;
|
||||
private DragScripts _dragScripts;
|
||||
private DragHandlers _dragHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the allowed type (given type and all sub classes). Must be <see cref="System.Type"/> type of any subclass.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The allowed type.
|
||||
/// </value>
|
||||
public ScriptType Type
|
||||
{
|
||||
get => _type;
|
||||
set
|
||||
{
|
||||
if (_type == value)
|
||||
return;
|
||||
if (value == ScriptType.Null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
|
||||
_type = value;
|
||||
|
||||
// Deselect value if it's not valid now
|
||||
if (!IsValid(_value))
|
||||
Value = ScriptType.Null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected types value.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The value.
|
||||
/// </value>
|
||||
public ScriptType Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (_value == value)
|
||||
return;
|
||||
if (value != ScriptType.Null && !IsValid(value))
|
||||
throw new ArgumentException(string.Format("Invalid type {0}. Checked base type {1}.", value.TypeName ?? "<null>", _type.TypeName ?? "<null>"));
|
||||
|
||||
_value = value;
|
||||
|
||||
// Get name to display
|
||||
if (_value)
|
||||
{
|
||||
_valueName = _value.Name;
|
||||
TooltipText = _value.TypeName;
|
||||
|
||||
var attributes = _value.GetAttributes(false);
|
||||
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
|
||||
if (tooltipAttribute != null)
|
||||
{
|
||||
TooltipText += "\n" + tooltipAttribute.Text;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_valueName = string.Empty;
|
||||
TooltipText = string.Empty;
|
||||
}
|
||||
|
||||
ValueChanged?.Invoke();
|
||||
TypePickerValueChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected type fullname (namespace and class).
|
||||
/// </summary>
|
||||
public string ValueTypeName
|
||||
{
|
||||
get => _value.TypeName ?? string.Empty;
|
||||
set => Value = TypeUtils.GetType(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when value gets changed.
|
||||
/// </summary>
|
||||
public event Action ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when value gets changed.
|
||||
/// </summary>
|
||||
public event Action<TypePickerControl> TypePickerValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for types validation. Cane be used to implement a rule for types to pick.
|
||||
/// </summary>
|
||||
public Func<ScriptType, bool> CheckValid;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypePickerControl"/> class.
|
||||
/// </summary>
|
||||
public TypePickerControl()
|
||||
: base(0, 0, 50, 16)
|
||||
{
|
||||
_type = new ScriptType(typeof(object));
|
||||
}
|
||||
|
||||
private bool IsValid(ScriptType obj)
|
||||
{
|
||||
return _type.IsAssignableFrom(obj) && (CheckValid == null || CheckValid(obj));
|
||||
}
|
||||
|
||||
private void ShowDropDownMenu()
|
||||
{
|
||||
Focus();
|
||||
TypeSearchPopup.Show(this, new Vector2(0, Height), IsValid, scriptType =>
|
||||
{
|
||||
Value = scriptType;
|
||||
RootWindow.Focus();
|
||||
Focus();
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
// Cache data
|
||||
var style = Style.Current;
|
||||
bool isSelected = _value != ScriptType.Null;
|
||||
var frameRect = new Rectangle(0, 0, Width - 16, 16);
|
||||
if (isSelected && _type == ScriptType.Null)
|
||||
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, IsMouseOver ? style.BorderHighlighted : style.BorderNormal);
|
||||
|
||||
// Check if has item selected
|
||||
if (isSelected)
|
||||
{
|
||||
// Draw deselect button
|
||||
if (_type == ScriptType.Null)
|
||||
Render2D.DrawSprite(style.Cross, button1Rect, button1Rect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Draw name
|
||||
Render2D.PushClip(nameRect);
|
||||
Render2D.DrawText(style.FontMedium, _valueName, nameRect, style.Foreground, TextAlignment.Near, TextAlignment.Center);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw info
|
||||
Render2D.DrawText(style.FontMedium, "-", nameRect, Color.OrangeRed, TextAlignment.Near, TextAlignment.Center);
|
||||
}
|
||||
|
||||
// Draw picker button
|
||||
var pickerRect = isSelected && _type == ScriptType.Null ? button2Rect : button1Rect;
|
||||
Render2D.DrawSprite(style.ArrowDown, pickerRect, pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if drag is over
|
||||
if (IsDragOver && _hasValidDragOver)
|
||||
Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), style.BackgroundSelected * 0.4f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseEnter(Vector2 location)
|
||||
{
|
||||
_mousePos = location;
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseMove(Vector2 location)
|
||||
{
|
||||
_mousePos = location;
|
||||
|
||||
base.OnMouseMove(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
_mousePos = Vector2.Minimum;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Vector2 location, MouseButton button)
|
||||
{
|
||||
// Cache data
|
||||
bool isSelected = _value != ScriptType.Null;
|
||||
var frameRect = new Rectangle(0, 0, Width - 16, 16);
|
||||
if (isSelected && _type == ScriptType.Null)
|
||||
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 && button1Rect.Contains(ref location) && _type == ScriptType.Null)
|
||||
Value = ScriptType.Null;
|
||||
|
||||
// Picker dropdown menu
|
||||
if ((isSelected && _type == ScriptType.Null ? button2Rect : button1Rect).Contains(ref location))
|
||||
ShowDropDownMenu();
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
|
||||
{
|
||||
// Navigate to types from game project
|
||||
if (button == MouseButton.Left && _value != ScriptType.Null)
|
||||
{
|
||||
var item = _value.ContentItem;
|
||||
if (item != null)
|
||||
Editor.Instance.ContentEditing.Open(item);
|
||||
}
|
||||
|
||||
return base.OnMouseDoubleClick(location, button);
|
||||
}
|
||||
|
||||
private DragDropEffect DragEffect => _hasValidDragOver ? DragDropEffect.Move : DragDropEffect.None;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragEnter(ref location, data);
|
||||
|
||||
// Ensure to have valid drag helpers (uses lazy init)
|
||||
if (_dragActors == null)
|
||||
_dragActors = new DragActors(x => IsValid(TypeUtils.GetObjectType(x.Actor)));
|
||||
if (_dragScripts == null)
|
||||
_dragScripts = new DragScripts(x => IsValid(TypeUtils.GetObjectType(x)));
|
||||
if (_dragHandlers == null)
|
||||
{
|
||||
_dragHandlers = new DragHandlers();
|
||||
_dragHandlers.Add(_dragActors);
|
||||
_dragHandlers.Add(_dragScripts);
|
||||
}
|
||||
|
||||
_hasValidDragOver = _dragHandlers.OnDragEnter(data) != DragDropEffect.None;
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
base.OnDragMove(ref location, data);
|
||||
|
||||
return DragEffect;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_hasValidDragOver = false;
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = DragEffect;
|
||||
|
||||
base.OnDragDrop(ref location, data);
|
||||
|
||||
if (_dragActors.HasValidDrag)
|
||||
{
|
||||
Value = TypeUtils.GetObjectType(_dragActors.Objects[0].Actor);
|
||||
}
|
||||
else if (_dragScripts.HasValidDrag)
|
||||
{
|
||||
Value = TypeUtils.GetObjectType(_dragScripts.Objects[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_value = ScriptType.Null;
|
||||
_type = ScriptType.Null;
|
||||
_valueName = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for type reference editors.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.CustomEditor" />
|
||||
public class TypeEditorBase : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The element.
|
||||
/// </summary>
|
||||
protected CustomElement<TypePickerControl> _element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
if (!HasDifferentTypes)
|
||||
{
|
||||
_element = layout.Custom<TypePickerControl>();
|
||||
|
||||
var attributes = Values.GetAttributes();
|
||||
var typeReference = (TypeReferenceAttribute)attributes?.FirstOrDefault(x => x is TypeReferenceAttribute);
|
||||
if (typeReference != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(typeReference.TypeName))
|
||||
{
|
||||
var customType = TypeUtils.GetType(typeReference.TypeName);
|
||||
if (customType)
|
||||
_element.CustomControl.Type = customType;
|
||||
else
|
||||
Editor.LogError(string.Format("Unknown type '{0}' to use for type picker filter.", typeReference.TypeName));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(typeReference.CheckMethod))
|
||||
{
|
||||
var parentType = ParentEditor.Values[0].GetType();
|
||||
var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
if (method.ReturnType == typeof(bool))
|
||||
{
|
||||
var methodParameters = method.GetParameters();
|
||||
if (methodParameters.Length == 1 && methodParameters[0].ParameterType == typeof(ScriptType))
|
||||
_element.CustomControl.CheckValid += type => { return (bool)method.Invoke(ParentEditor.Values[0], new object[] { type }); };
|
||||
else if (methodParameters.Length == 1 && methodParameters[0].ParameterType == typeof(Type))
|
||||
_element.CustomControl.CheckValid += type => { return type.Type != null && (bool)method.Invoke(ParentEditor.Values[0], new object[] { type.Type }); };
|
||||
else
|
||||
Editor.LogError(string.Format("Unknown method '{0}' to use for type picker filter check function (object type: {1}). It must contain a single parameter of type System.Type.", typeReference.CheckMethod, parentType.FullName));
|
||||
}
|
||||
else
|
||||
Editor.LogError(string.Format("Invalid method '{0}' to use for type picker filter check function (object type: {1}). It must return boolean value.", typeReference.CheckMethod, parentType.FullName));
|
||||
}
|
||||
else
|
||||
Editor.LogError(string.Format("Unknown method '{0}' to use for type picker filter check function (object type: {1}).", typeReference.CheckMethod, parentType.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_element = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="System.Type"/>. Used to pick classes.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Type)), DefaultEditor]
|
||||
public class TypeEditor : TypeEditorBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (_element != null)
|
||||
{
|
||||
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value.Type);
|
||||
|
||||
if (_element.CustomControl.Type == new ScriptType(typeof(object)))
|
||||
{
|
||||
_element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
_element.CustomControl.Value = new ScriptType(Values[0] as Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit reference to the <see cref="FlaxEditor.Scripting.ScriptType"/>. Used to pick classes.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ScriptType)), DefaultEditor]
|
||||
public class ScriptTypeEditor : TypeEditorBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (_element != null)
|
||||
{
|
||||
_element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
_element.CustomControl.Value = (ScriptType)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Source/Editor/CustomEditors/Editors/Vector2Editor.cs
Normal file
87
Source/Editor/CustomEditors/Editors/Vector2Editor.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Vector2 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Vector2)), DefaultEditor]
|
||||
public class Vector2Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement YElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 2;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
XElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
YElement.FloatValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Vector2(
|
||||
XElement.FloatValue.Value,
|
||||
YElement.FloatValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Vector2)Values[0];
|
||||
XElement.FloatValue.Value = value.X;
|
||||
YElement.FloatValue.Value = value.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Source/Editor/CustomEditors/Editors/Vector3Editor.cs
Normal file
99
Source/Editor/CustomEditors/Editors/Vector3Editor.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Vector3 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Vector3)), DefaultEditor]
|
||||
public class Vector3Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement YElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Z component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement ZElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 3;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
XElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
YElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
ZElement.FloatValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Vector3(
|
||||
XElement.FloatValue.Value,
|
||||
YElement.FloatValue.Value,
|
||||
ZElement.FloatValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Vector3)Values[0];
|
||||
XElement.FloatValue.Value = value.X;
|
||||
YElement.FloatValue.Value = value.Y;
|
||||
ZElement.FloatValue.Value = value.Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Source/Editor/CustomEditors/Editors/Vector4Editor.cs
Normal file
111
Source/Editor/CustomEditors/Editors/Vector4Editor.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Vector4 value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Vector4)), DefaultEditor]
|
||||
public class Vector4Editor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The X component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement XElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement YElement;
|
||||
|
||||
/// <summary>
|
||||
/// The Z component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement ZElement;
|
||||
|
||||
/// <summary>
|
||||
/// The W component editor.
|
||||
/// </summary>
|
||||
protected FloatValueElement WElement;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 4;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
LimitAttribute limit = null;
|
||||
var attributes = Values.GetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute);
|
||||
}
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
XElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
YElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
ZElement.FloatValue.SlidingEnd += ClearToken;
|
||||
|
||||
WElement = grid.FloatValue();
|
||||
WElement.SetLimits(limit);
|
||||
WElement.FloatValue.ValueChanged += OnValueChanged;
|
||||
WElement.FloatValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding || WElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Vector4(
|
||||
XElement.FloatValue.Value,
|
||||
YElement.FloatValue.Value,
|
||||
ZElement.FloatValue.Value,
|
||||
WElement.FloatValue.Value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = (Vector4)Values[0];
|
||||
XElement.FloatValue.Value = value.X;
|
||||
YElement.FloatValue.Value = value.Y;
|
||||
ZElement.FloatValue.Value = value.Z;
|
||||
WElement.FloatValue.Value = value.W;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
114
Source/Editor/CustomEditors/Editors/VersionEditor.cs
Normal file
114
Source/Editor/CustomEditors/Editors/VersionEditor.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Version value type properties.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(Version)), DefaultEditor]
|
||||
public class VersionEditor : CustomEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The Version.Major component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement Major;
|
||||
|
||||
/// <summary>
|
||||
/// The Version.Minor component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement Minor;
|
||||
|
||||
/// <summary>
|
||||
/// The Version.Build component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement Build;
|
||||
|
||||
/// <summary>
|
||||
/// The Version.Revision component editor.
|
||||
/// </summary>
|
||||
protected IntegerValueElement Revision;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
var gridControl = grid.CustomControl;
|
||||
gridControl.ClipChildren = false;
|
||||
gridControl.Height = TextBox.DefaultHeight;
|
||||
gridControl.SlotsHorizontally = 4;
|
||||
gridControl.SlotsVertically = 1;
|
||||
|
||||
Major = grid.IntegerValue();
|
||||
Major.IntValue.SetLimits(0, 100000000);
|
||||
Major.IntValue.ValueChanged += OnValueChanged;
|
||||
Major.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
Minor = grid.IntegerValue();
|
||||
Minor.IntValue.SetLimits(0, 100000000);
|
||||
Minor.IntValue.ValueChanged += OnValueChanged;
|
||||
Minor.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
Build = grid.IntegerValue();
|
||||
Build.IntValue.SetLimits(-1, 100000000);
|
||||
Build.IntValue.ValueChanged += OnValueChanged;
|
||||
Build.IntValue.SlidingEnd += ClearToken;
|
||||
|
||||
Revision = grid.IntegerValue();
|
||||
Revision.IntValue.SetLimits(-1, 100000000);
|
||||
Revision.IntValue.ValueChanged += OnValueChanged;
|
||||
Revision.IntValue.SlidingEnd += ClearToken;
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
var isSliding = Major.IsSliding || Minor.IsSliding || Build.IsSliding || Revision.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
Version value;
|
||||
var major = Major.IntValue.Value;
|
||||
var minor = Minor.IntValue.Value;
|
||||
var build = Build.IntValue.Value;
|
||||
var revision = Revision.IntValue.Value;
|
||||
if (build > -1)
|
||||
{
|
||||
if (revision > 0)
|
||||
value = new Version(major, minor, build, revision);
|
||||
else
|
||||
value = new Version(major, minor, build);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = new Version(major, minor);
|
||||
}
|
||||
SetValue(value, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
}
|
||||
else if (Values[0] is Version value)
|
||||
{
|
||||
Major.IntValue.Value = value.Major;
|
||||
Minor.IntValue.Value = value.Minor;
|
||||
Build.IntValue.Value = value.Build;
|
||||
Revision.IntValue.Value = value.Revision;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user