using System;
using Chronos.Controls.Editor;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Chronos.Reflection.Editor
{
public abstract class TargetedDrawer : PropertyDrawer
{
///
/// Whether the self-targeted attribute is defined on the inspected field.
///
protected bool isSelfTargeted;
protected bool showTargetField
{
get
{
return !isSelfTargeted || ShowSelfTargetField;
}
}
///
/// The UnityMember.target of the inspected property, of type Object.
///
protected SerializedProperty targetProperty;
#region Graphical Configuration
///
/// Whether the target field should be shown in self-targetting mode.
///
protected const bool ShowSelfTargetField = false;
///
/// The padding between the label and the target and member controls, in vertical display.
///
protected const float LabelPadding = 2;
///
/// The padding between the target and member controls, in vertical display.
///
protected const float InnerPadding = 5;
///
/// The padding below the drawer, in vertical display.
///
protected const float BottomPadding = 5;
#endregion
///
/// Initializes the members of the drawer via the specified property.
///
protected virtual void Update(SerializedProperty property)
{
this.targetProperty = property.FindPropertyRelative("_target");
isSelfTargeted = Attribute.IsDefined(fieldInfo, typeof(SelfTargetedAttribute));
}
///
/// Calculates the height of the drawer.
///
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
Update(property);
// Double the height and add the padding for self-targeting,
// because we'll display the controls on another line.
if (showTargetField && !string.IsNullOrEmpty(label.text))
{
return base.GetPropertyHeight(property, label) * 2 + LabelPadding + BottomPadding;
}
else
{
return base.GetPropertyHeight(property, label);
}
}
///
/// Renders the drawer.
///
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Update(property);
EditorGUI.BeginProperty(position, label, property);
// Hack the indent level for full control
position = EditorGUI.IndentedRect(position);
int oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Positioning
// When in self targeting mode, hide the target field and display
// the member popup as a normal field. When in manual targeting, push
// the field down below the label.
Rect targetPosition;
Rect memberPosition;
if (showTargetField)
{
if (!string.IsNullOrEmpty(label.text))
{
position.height = base.GetPropertyHeight(property, label);
position.y += EditorGUI.PrefixLabel(position, label).height + LabelPadding;
}
targetPosition = position;
memberPosition = position;
targetPosition.width *= (1f / 3f);
targetPosition.width -= (InnerPadding / 2);
memberPosition.width *= (2f / 3f);
memberPosition.width -= (InnerPadding / 2);
memberPosition.x = targetPosition.xMax + InnerPadding;
}
else
{
targetPosition = new Rect(0, 0, 0, 0);
memberPosition = EditorGUI.PrefixLabel(position, label);
}
// Render controls
RenderTargetControl(targetPosition);
RenderMemberControl(memberPosition);
// Restore the indent level
EditorGUI.indentLevel = oldIndent;
EditorGUI.EndProperty();
}
protected virtual void RenderTargetControl(Rect position)
{
// When in self targeting mode, assign the target property to the
// target object behind the scenes. When in manual targeting mode,
// display a standard Object property field.
if (isSelfTargeted)
{
foreach (var singleTargetProperty in targetProperty.Multiple())
{
singleTargetProperty.objectReferenceValue = GetSelfTarget(singleTargetProperty.serializedObject.targetObject);
}
}
if (showTargetField)
{
EditorGUI.BeginDisabledGroup(isSelfTargeted);
EditorGUI.PropertyField(position, targetProperty, GUIContent.none);
EditorGUI.EndDisabledGroup();
}
}
protected abstract void RenderMemberControl(Rect position);
///
/// Returns the object assigned as self-target from a serialized object.
///
protected virtual UnityObject GetSelfTarget(UnityObject obj)
{
return obj;
}
}
}