using UnityEngine;
namespace InspectorToolkit
{
///
/// Validate a string field to only allow or disallow a set of pre-defined
/// characters on typing.
///
public class CharactersRangeAttribute : PropertyAttribute
{
public readonly string Characters;
public readonly CharacterRangeMode Mode;
public readonly bool IgnoreCase;
public CharactersRangeAttribute(string characters, CharacterRangeMode mode = CharacterRangeMode.Allow,
bool ignoreCase = true)
{
Characters = characters;
Mode = mode;
IgnoreCase = ignoreCase;
}
}
public enum CharacterRangeMode
{
///
/// Only characters in range will be allowed
///
Allow,
///
/// Characters in range will be removed from the string
///
Disallow,
///
/// Highlight the field if any of the specified characters were fould
///
WarningIfAny,
///
/// Highlight the field if some characters in string are not the characters from specified range
///
WarningIfNotMatch
}
}
#if UNITY_EDITOR
namespace InspectorToolkit.Internal
{
using UnityEditor;
using EditorTools;
using System.Linq;
[CustomPropertyDrawer(typeof(CharactersRangeAttribute))]
public class CharacterRangeAttributeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.String)
{
MyGUI.DrawColouredRect(position, MyGUI.Colors.Red);
EditorGUI.LabelField(position, new GUIContent("", "[CharacterRangeAttribute] used with non-string property"));
}
else
{
var charactersRange = (CharactersRangeAttribute)attribute;
var mode = charactersRange.Mode;
var ignoreCase = charactersRange.IgnoreCase;
var filter = charactersRange.Characters;
var allow = mode == CharacterRangeMode.Allow || mode == CharacterRangeMode.WarningIfNotMatch;
var warning = mode == CharacterRangeMode.WarningIfAny || mode == CharacterRangeMode.WarningIfNotMatch;
if (ignoreCase) filter = filter.ToUpper();
var filteredCharacters = property.stringValue.Distinct()
.Where(c =>
{
if (ignoreCase) c = char.ToUpper(c);
return filter.Contains(c) ^ allow;
});
bool ifMatch = mode == CharacterRangeMode.WarningIfAny;
bool ifNotMatch = mode == CharacterRangeMode.WarningIfNotMatch;
bool anyFiltered = filteredCharacters.Any();
bool warn = (ifMatch && anyFiltered || ifNotMatch && anyFiltered);
var originalPosition = position;
DrawWarning();
position.width -= 20;
property.stringValue = EditorGUI.TextField(position, label, property.stringValue);
DrawTooltip();
if (!warning)
{
property.stringValue = filteredCharacters.Aggregate(
property.stringValue,
(p, c) => p.Replace(c.ToString(), ""));
}
if (warn)
{
GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.objectField);
position = originalPosition;
position.y += EditorGUIUtility.singleLineHeight;
DrawWarning();
position.x += EditorGUIUtility.labelWidth;
var warningContent = new GUIContent("Containing disallowed characters!");
EditorGUI.LabelField(position, warningContent, EditorStyles.miniBoldLabel);
}
property.serializedObject.ApplyModifiedProperties();
void DrawWarning()
{
if (!warning) return;
if (property.stringValue.Length == 0) warn = false;
MyGUI.DrawColouredRect(position, warn ? MyGUI.Colors.Yellow : Color.clear);
}
void DrawTooltip()
{
string tooltip = "Characters range ";
if (mode == CharacterRangeMode.Allow || mode == CharacterRangeMode.WarningIfNotMatch) tooltip += "is allowed:";
else tooltip += "not allowed:";
tooltip += $"\n[{filter}]";
position.x += position.width + 2;
position.width = 18;
var tooltipContent = new GUIContent(MyGUI.EditorIcons.Help);
tooltipContent.tooltip = tooltip;
EditorGUI.LabelField(position, tooltipContent, EditorStyles.label);
}
}
}
}
}
#endif