Scripts/Editor/Windows/PropertyDrawers/WitPropertyDrawer.cs (211 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*/
using System;
using System.Reflection;
using UnityEngine;
using UnityEditor;
namespace Facebook.WitAi.Windows
{
// Edit Type
public enum WitPropertyEditType
{
NoEdit,
FreeEdit,
LockEdit
}
// Handles layout of of property sub properties
public abstract class WitPropertyDrawer : PropertyDrawer
{
// Whether editing
private int editIndex = -1;
// Whether to use a foldout
protected virtual bool FoldoutEnabled => true;
// Determine edit type for this drawer
protected virtual WitPropertyEditType EditType => WitPropertyEditType.NoEdit;
// Remove padding
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 0;
}
// Handles gui layout
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Return error
if (property.serializedObject == null)
{
string missingText = GetLocalizedText(property, LocalizedMissingKey);
WitEditorUI.LayoutErrorLabel(missingText);
return;
}
// Show foldout if desired
string titleText = GetLocalizedText(property, LocalizedTitleKey);
if (FoldoutEnabled)
{
property.isExpanded = WitEditorUI.LayoutFoldout(new GUIContent(titleText), property.isExpanded);
if (!property.isExpanded)
{
return;
}
}
// Show title only
else
{
WitEditorUI.LayoutLabel(titleText);
}
// Indent
GUILayout.BeginVertical();
EditorGUI.indentLevel++;
// Pre fields
OnGUIPreFields(position, property, label);
// Iterate all subfields
WitPropertyEditType editType = EditType;
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
Type fieldType = fieldInfo.FieldType;
if (fieldType.IsArray)
{
fieldType = fieldType.GetElementType();
}
FieldInfo[] subfields = fieldType.GetFields(flags);
for (int s = 0; s < subfields.Length; s++)
{
FieldInfo subfield = subfields[s];
if (ShouldLayoutField(property, subfield))
{
LayoutField(s, property, subfield, editType);
}
}
// Post fields
OnGUIPostFields(position, property, label);
// Undent
EditorGUI.indentLevel--;
GUILayout.EndVertical();
}
// Override pre fields
protected virtual void OnGUIPreFields(Rect position, SerializedProperty property, GUIContent label)
{
}
// Draw a specific property
protected virtual void LayoutField(int index, SerializedProperty property, FieldInfo subfield, WitPropertyEditType editType)
{
// Begin layout
GUILayout.BeginHorizontal();
// Get label content
string labelText = GetLocalizedText(property, subfield.Name);
GUIContent labelContent = new GUIContent(labelText);
// Determine if can edit
bool canEdit = editType == WitPropertyEditType.FreeEdit || (editType == WitPropertyEditType.LockEdit && editIndex == index);
bool couldEdit = GUI.enabled;
GUI.enabled = canEdit;
// Cannot edit, just show field
SerializedProperty subfieldProperty = property.FindPropertyRelative(subfield.Name);
if (!canEdit && subfieldProperty.type == "string")
{
// Get value text
string valText = subfieldProperty.stringValue;
if (string.IsNullOrEmpty(valText))
{
valText = GetDefaultFieldValue(property, subfield);
}
// Layout key
WitEditorUI.LayoutKeyLabel(labelText, valText);
}
// Can edit, allow edit
else
{
GUILayout.BeginVertical();
LayoutPropertyField(subfield, subfieldProperty, labelContent, canEdit);
GUILayout.EndVertical();
}
// Reset
GUI.enabled = couldEdit;
// Lock Settings
if (editType == WitPropertyEditType.LockEdit)
{
// Is Editing
if (editIndex == index)
{
// Clear Edit
if (WitEditorUI.LayoutIconButton(WitStyles.ResetIcon))
{
editIndex = -1;
string clearVal = "";
if (subfieldProperty.type != "string")
{
clearVal = GetDefaultFieldValue(property, subfield);
}
SetFieldStringValue(subfieldProperty, clearVal);
GUI.FocusControl(null);
}
// Accept Edit
if (WitEditorUI.LayoutIconButton(WitStyles.AcceptIcon))
{
editIndex = -1;
GUI.FocusControl(null);
}
}
// Not Editing
else
{
// Begin Editing
if (WitEditorUI.LayoutIconButton(WitStyles.EditIcon))
{
editIndex = index;
GUI.FocusControl(null);
}
}
}
// End layout
GUILayout.EndHorizontal();
}
// Layout property field
protected virtual void LayoutPropertyField(FieldInfo subfield, SerializedProperty subfieldProperty, GUIContent labelContent, bool canEdit)
{
// If can edit or not array default layout
if (canEdit || !subfield.FieldType.IsArray || subfieldProperty.arraySize <= 0)
{
EditorGUILayout.PropertyField(subfieldProperty, labelContent);
return;
}
// If cannot edit, handle here
subfieldProperty.isExpanded = WitEditorUI.LayoutFoldout(labelContent, subfieldProperty.isExpanded);
if (subfieldProperty.isExpanded)
{
EditorGUI.indentLevel++;
for (int i = 0; i < subfieldProperty.arraySize; i++)
{
SerializedProperty p = subfieldProperty.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(p);
}
EditorGUI.indentLevel--;
}
}
// Override post fields
protected virtual void OnGUIPostFields(Rect position, SerializedProperty property, GUIContent label)
{
}
// Get text for specified key
public const string LocalizedTitleKey = "title";
public const string LocalizedMissingKey = "missing";
protected virtual string GetLocalizedText(SerializedProperty property, string key)
{
Debug.Log("Missing Localization: " + key);
return key;
}
// Way to ignore certain properties
protected virtual bool ShouldLayoutField(SerializedProperty property, FieldInfo subfield)
{
switch (subfield.Name)
{
case "witConfiguration":
return false;
}
return true;
}
// Get field default value if applicable
protected virtual string GetDefaultFieldValue(SerializedProperty property, FieldInfo subfield)
{
return string.Empty;
}
// Get subfield value
protected virtual string GetFieldStringValue(SerializedProperty property, string fieldName)
{
SerializedProperty subfieldProperty = property.FindPropertyRelative(fieldName);
return GetFieldStringValue(subfieldProperty);
}
// Get subfield value
protected virtual string GetFieldStringValue(SerializedProperty subfieldProperty)
{
// Supported types
switch (subfieldProperty.type)
{
case "string":
return subfieldProperty.stringValue;
case "int":
return subfieldProperty.intValue.ToString();
case "bool":
return subfieldProperty.boolValue.ToString();
}
// No others are currently supported
return string.Empty;
}
// Set subfield value
protected virtual void SetFieldStringValue(SerializedProperty subfieldProperty, string newFieldValue)
{
// Supported types
switch (subfieldProperty.type)
{
case "string":
subfieldProperty.stringValue = newFieldValue;
break;
case "int":
int rI;
if (int.TryParse(newFieldValue, out rI))
{
subfieldProperty.intValue = rI;
}
break;
case "bool":
bool rB;
if (bool.TryParse(newFieldValue, out rB))
{
subfieldProperty.boolValue = rB;
}
break;
}
}
}
}