An easy to use Unity 3D library for creating illustrated Interactive Fiction games and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
8.4 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace UnityTest
{
public class PropertyPathSelector
{
private readonly DropDownControl<string> m_ThisDropDown = new DropDownControl<string>();
private readonly Func<string, string> m_ReplaceDotWithSlashAndAddGoGroup = s => s.Replace('.', '/');
private readonly string m_Name;
private bool m_FocusBackToEdit;
private SelectedPathError m_Error;
public PropertyPathSelector(string name)
{
m_Name = name;
m_ThisDropDown.convertForGUIContent = m_ReplaceDotWithSlashAndAddGoGroup;
m_ThisDropDown.tooltip = "Select the path to the value you want to use for comparison.";
}
public void Draw(GameObject go, ActionBase comparer, string propertPath, Type[] accepatbleTypes, Action<GameObject> onSelectedGo, Action<string> onSelectedPath)
{
var newGo = (GameObject)EditorGUILayout.ObjectField(m_Name, go, typeof(GameObject), true);
if (newGo != go)
onSelectedGo(newGo);
if (go != null)
{
var newPath = DrawListOfMethods(go, comparer, propertPath, accepatbleTypes, m_ThisDropDown);
if (newPath != propertPath)
onSelectedPath(newPath);
}
}
private string DrawListOfMethods(GameObject go, ActionBase comparer, string propertPath, Type[] accepatbleTypes, DropDownControl<string> dropDown)
{
string result = propertPath;
if (accepatbleTypes == null)
{
result = DrawManualPropertyEditField(go, propertPath, accepatbleTypes, dropDown);
}
else
{
bool isPropertyOrFieldFound = true;
if (string.IsNullOrEmpty(result))
{
var options = GetFieldsAndProperties(go, comparer, result, accepatbleTypes);
isPropertyOrFieldFound = options.Any();
if (isPropertyOrFieldFound)
{
result = options.First();
}
}
if (isPropertyOrFieldFound)
{
dropDown.Draw(go.name + '.', result,
() =>
{
try
{
var options = GetFieldsAndProperties(go, comparer, result, accepatbleTypes);
return options.ToArray();
}
catch (Exception)
{
Debug.LogWarning("An exception was thrown while resolving a property list. Resetting property path.");
result = "";
return new string[0];
}
}, s => result = s);
}
else
{
result = DrawManualPropertyEditField(go, propertPath, accepatbleTypes, dropDown);
}
}
return result;
}
private static List<string> GetFieldsAndProperties(GameObject go, ActionBase comparer, string extendPath, Type[] accepatbleTypes)
{
var propertyResolver = new PropertyResolver {AllowedTypes = accepatbleTypes, ExcludedFieldNames = comparer.GetExcludedFieldNames()};
var options = propertyResolver.GetFieldsAndPropertiesFromGameObject(go, comparer.GetDepthOfSearch(), extendPath).ToList();
options.Sort((x, y) =>
{
if (char.IsLower(x[0]))
return -1;
if (char.IsLower(y[0]))
return 1;
return x.CompareTo(y);
});
return options;
}
private string DrawManualPropertyEditField(GameObject go, string propertPath, Type[] acceptableTypes, DropDownControl<string> dropDown)
{
var propertyResolver = new PropertyResolver { AllowedTypes = acceptableTypes };
IList<string> list;
var loadProps = new Func<string[]>(() =>
{
try
{
list = propertyResolver.GetFieldsAndPropertiesUnderPath(go, propertPath);
}
catch (ArgumentException)
{
list = propertyResolver.GetFieldsAndPropertiesUnderPath(go, "");
}
return list.ToArray();
});
EditorGUILayout.BeginHorizontal();
var labelSize = EditorStyles.label.CalcSize(new GUIContent(go.name + '.'));
GUILayout.Label(go.name + (propertPath.Length > 0 ? "." : ""), EditorStyles.label, GUILayout.Width(labelSize.x));
string btnName = "hintBtn";
if (GUI.GetNameOfFocusedControl() == btnName
&& Event.current.type == EventType.KeyDown
&& Event.current.keyCode == KeyCode.DownArrow)
{
Event.current.Use();
dropDown.PrintMenu(loadProps());
GUI.FocusControl("");
m_FocusBackToEdit = true;
}
EditorGUI.BeginChangeCheck();
GUI.SetNextControlName(btnName);
var result = GUILayout.TextField(propertPath, EditorStyles.textField);
if (EditorGUI.EndChangeCheck())
{
m_Error = DoesPropertyExist(go, result);
}
if (m_FocusBackToEdit)
{
m_FocusBackToEdit = false;
GUI.FocusControl(btnName);
}
if (GUILayout.Button("Clear", EditorStyles.miniButton, GUILayout.Width(38)))
{
result = "";
GUI.FocusControl(null);
m_FocusBackToEdit = true;
m_Error = DoesPropertyExist(go, result);
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Label("", GUILayout.Width(labelSize.x));
dropDown.Draw("", result ?? "", loadProps, s =>
{
result = s;
GUI.FocusControl(null);
m_FocusBackToEdit = true;
m_Error = DoesPropertyExist(go, result);
});
EditorGUILayout.EndHorizontal();
switch (m_Error)
{
case SelectedPathError.InvalidPath:
EditorGUILayout.HelpBox("This property does not exist", MessageType.Error);
break;
case SelectedPathError.MissingComponent:
EditorGUILayout.HelpBox("This property or field is not attached or set. It will fail unless it will be attached before the check is perfomed.", MessageType.Warning);
break;
}
return result;
}
private SelectedPathError DoesPropertyExist(GameObject go, string propertPath)
{
try
{
object obj;
if (MemberResolver.TryGetValue(go, propertPath, out obj))
return SelectedPathError.None;
return SelectedPathError.InvalidPath;
}
catch (TargetInvocationException e)
{
if (e.InnerException is MissingComponentException)
return SelectedPathError.MissingComponent;
throw;
}
}
private enum SelectedPathError
{
None,
MissingComponent,
InvalidPath
}
}
}