Created
September 13, 2020 18:57
-
-
Save oxysoft/dfe81ef124ffcf527a7f33670afdf9c2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using Sirenix.OdinInspector.Editor; | |
using Sirenix.OdinInspector.Editor.Validation; | |
using Sirenix.Utilities.Editor; | |
using UnityEngine; | |
[DrawerPriority(0.0, 10000.1)] | |
public class MiniValidationDrawer<T> : OdinValueDrawer<T>, IDisposable | |
{ | |
private List<ValidationResult> _validationResults; | |
private bool _rerunFullValidation; | |
private object _shakeGroupKey; | |
private ValidationComponent _validationComponent; | |
private static readonly Color BG = new Color(1, 0, 0, 0.15f); | |
private static readonly Color Shadow = new Color(0, 0, 0, 0.3f); | |
private static readonly Color Band = new Color(1, 0, 0, 0.5f); | |
private static readonly Color WarnBG = new Color(0.79f, 0.52f, 0f, 0.15f); | |
private static readonly Color WarnShadow = new Color(0, 0, 0, .3f); | |
private static readonly Color WarnBand = new Color(1f, 0.62f, 0.22f, 0.5f); | |
protected override bool CanDrawValueProperty(InspectorProperty property) | |
{ | |
ValidationComponent component = property.GetComponent<ValidationComponent>(); | |
return component != null && component.ValidatorLocator.PotentiallyHasValidatorsFor(property); | |
} | |
protected override void Initialize() | |
{ | |
_validationComponent = Property.GetComponent<ValidationComponent>(); | |
_validationComponent.ValidateProperty(ref _validationResults); | |
if (_validationResults.Count > 0) | |
{ | |
_shakeGroupKey = UniqueDrawerKey.Create(Property, this); | |
Property.Tree.OnUndoRedoPerformed += OnUndoRedoPerformed; | |
ValueEntry.OnValueChanged += OnValueChanged; | |
ValueEntry.OnChildValueChanged += OnChildValueChanged; | |
} | |
else | |
{ | |
SkipWhenDrawing = true; | |
} | |
} | |
protected override void DrawPropertyLayout(GUIContent label) | |
{ | |
if (_validationResults.Count == 0) | |
{ | |
CallNextDrawer(label); | |
} | |
else | |
{ | |
GUILayout.BeginVertical(); | |
SirenixEditorGUI.BeginShakeableGroup(_shakeGroupKey); | |
ValidationResult error = null; | |
ValidationResult warning = null; | |
for (var i = 0; i < _validationResults.Count; ++i) | |
{ | |
ValidationResult result = _validationResults[i]; | |
if (Event.current.type == EventType.Layout && (_rerunFullValidation || result.Setup.Validator.RevalidationCriteria == RevalidationCriteria.Always)) | |
{ | |
ValidationResultType resultType = result.ResultType; | |
result.Setup.ParentInstance = Property.ParentValues[0]; | |
result.Setup.Value = ValueEntry.Values[0]; | |
result.RerunValidation(); | |
if (resultType != result.ResultType && result.ResultType != ValidationResultType.Valid) | |
SirenixEditorGUI.StartShakingGroup(_shakeGroupKey); | |
} | |
switch (result.ResultType) | |
{ | |
case ValidationResultType.Error: | |
error = result; | |
break; | |
case ValidationResultType.Warning: | |
warning = result; | |
break; | |
case ValidationResultType.Valid when !string.IsNullOrEmpty(result.Message): | |
SirenixEditorGUI.InfoMessageBox(result.Message); | |
break; | |
} | |
} | |
if (Event.current.type == EventType.Layout) | |
_rerunFullValidation = false; | |
// The hack materializes: we skip the next drawer which should be the default ValidationDrawer | |
Property.GetActiveDrawerChain().MoveNext(); | |
CallNextDrawer(label); | |
if (error != null) | |
{ | |
if (label != null) | |
label.tooltip = $"ERROR: {error.Message}"; | |
Rect rect = GUIHelper.GetCurrentLayoutRect(); | |
if (Event.current.type == EventType.Repaint) | |
{ | |
SirenixEditorGUI.DrawSolidRect(rect, BG); | |
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, Shadow); | |
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, BG); | |
} | |
} | |
else if (warning != null) | |
{ | |
label.tooltip = $"WARNING: {warning.Message}"; | |
Rect rect = GUIHelper.GetCurrentLayoutRect(); | |
if (Event.current.type == EventType.Repaint) | |
{ | |
SirenixEditorGUI.DrawSolidRect(rect, WarnBG); | |
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, WarnShadow); | |
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, WarnBand); | |
} | |
} | |
SirenixEditorGUI.EndShakeableGroup(_shakeGroupKey); | |
GUILayout.EndVertical(); | |
} | |
} | |
public void Dispose() | |
{ | |
if (_validationResults.Count > 0) | |
{ | |
Property.Tree.OnUndoRedoPerformed -= OnUndoRedoPerformed; | |
ValueEntry.OnValueChanged -= OnValueChanged; | |
ValueEntry.OnChildValueChanged -= OnChildValueChanged; | |
} | |
_validationResults = null; | |
} | |
private void OnUndoRedoPerformed() | |
{ | |
_rerunFullValidation = true; | |
} | |
private void OnValueChanged(int index) | |
{ | |
_rerunFullValidation = true; | |
} | |
private void OnChildValueChanged(int index) | |
{ | |
_rerunFullValidation = true; | |
} | |
} | |
using UnityEngine; |
I'm not sure why but I had to duplicate line 97 to get rid of message box
This doesn't work with new Odin version :(
Can you update your tool please!
I updated your code. Compatible with new versions of Odin. Anyway u will not read this, because you abandoned this script long time ago. So I hope somebody will say thanks to me (i spent a lot of hours trying to fix it):
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
[DrawerPriority(0.0, 10000.1)]
public class MiniValidationDrawer<T> : OdinValueDrawer<T>, IDisposable
{
private List<ValidationResult> _validationResults;
private bool _rerunFullValidation;
private object _shakeGroupKey;
private ValidationComponent _validationComponent;
private static readonly Color BG = new Color(1, 0, 0, 0.15f);
private static readonly Color Shadow = new Color(0, 0, 0, 0.3f);
private static readonly Color Band = new Color(1, 0, 0, 0.5f);
private static readonly Color WarnBG = new Color(0.79f, 0.52f, 0f, 0.15f);
private static readonly Color WarnShadow = new Color(0, 0, 0, .3f);
private static readonly Color WarnBand = new Color(1f, 0.62f, 0.22f, 0.5f);
protected override bool CanDrawValueProperty(InspectorProperty property)
{
ValidationComponent component = property.GetComponent<ValidationComponent>();
return component != null && component.ValidatorLocator.PotentiallyHasValidatorsFor(property);
}
protected override void Initialize()
{
_validationComponent = Property.GetComponent<ValidationComponent>();
_validationComponent.ValidateProperty(ref _validationResults);
if (_validationResults.Count > 0)
{
_shakeGroupKey = UniqueDrawerKey.Create(Property, this);
Property.Tree.OnUndoRedoPerformed += OnUndoRedoPerformed;
ValueEntry.OnValueChanged += OnValueChanged;
ValueEntry.OnChildValueChanged += OnChildValueChanged;
}
else
{
SkipWhenDrawing = true;
}
}
protected override void DrawPropertyLayout(GUIContent label)
{
if (_validationResults.Count == 0)
{
CallNextDrawer(label);
}
else
{
GUILayout.BeginVertical();
SirenixEditorGUI.BeginShakeableGroup(_shakeGroupKey);
ValidationResult error = null;
ValidationResult warning = null;
for (var i = 0; i < _validationResults.Count; ++i)
{
ValidationResult result = _validationResults[i];
if (Event.current.type == EventType.Layout && (_rerunFullValidation || result.Setup.Validator != null && ((Validator) result.Setup.Validator).RevalidationCriteria == RevalidationCriteria.Always))
{
_rerunFullValidation = false;
ValidationResultType resultType = result.ResultType;
result.Setup.ParentInstance = Property.ParentValues[0];
_validationComponent.ValidatorLocator.GetValidators(Property)[i].Initialize(Property);
_validationComponent.ValidatorLocator.GetValidators(Property)[i].RunValidation(ref result);
if (resultType != result.ResultType && result.ResultType != ValidationResultType.Valid)
SirenixEditorGUI.StartShakingGroup(_shakeGroupKey);
}
switch (result.ResultType)
{
case ValidationResultType.Error:
error = result;
break;
case ValidationResultType.Warning:
warning = result;
break;
case ValidationResultType.Valid when !string.IsNullOrEmpty(result.Message):
SirenixEditorGUI.InfoMessageBox(result.Message);
break;
}
}
if (Event.current.type == EventType.Layout)
_rerunFullValidation = false;
// The hack materializes: we skip the next drawer which should be the default ValidationDrawer
Property.GetActiveDrawerChain().MoveNext();
CallNextDrawer(label);
if (error != null)
{
if (label != null)
label.tooltip = $"ERROR: {error.Message}";
Rect rect = GUIHelper.GetCurrentLayoutRect();
if (Event.current.type == EventType.Repaint)
{
SirenixEditorGUI.DrawSolidRect(rect, BG);
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, Shadow);
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, BG);
}
}
else if (warning != null)
{
label.tooltip = $"WARNING: {warning.Message}";
Rect rect = GUIHelper.GetCurrentLayoutRect();
if (Event.current.type == EventType.Repaint)
{
SirenixEditorGUI.DrawSolidRect(rect, WarnBG);
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, WarnShadow);
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, WarnBand);
}
}
SirenixEditorGUI.EndShakeableGroup(_shakeGroupKey);
GUILayout.EndVertical();
}
}
public void Dispose()
{
if (_validationResults.Count > 0)
{
Property.Tree.OnUndoRedoPerformed -= OnUndoRedoPerformed;
ValueEntry.OnValueChanged -= OnValueChanged;
ValueEntry.OnChildValueChanged -= OnChildValueChanged;
}
_validationResults = null;
}
private void OnUndoRedoPerformed()
{
_rerunFullValidation = true;
}
private void OnValueChanged(int index)
{
_rerunFullValidation = true;
}
private void OnChildValueChanged(int index)
{
_rerunFullValidation = true;
}
}
#endif
Thank you @funnymanwin! Extremely helpful. I have also updated it to include tooltips of the error when hovering over the property:
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
[DrawerPriority(0.0, 10000.1)]
public class MiniValidationDrawer<T> : OdinValueDrawer<T>, IDisposable
{
private List<ValidationResult> _validationResults;
private bool _rerunFullValidation;
private object _shakeGroupKey;
private ValidationComponent _validationComponent;
private static readonly Color BG = new Color(1, 0, 0, 0.15f);
private static readonly Color Shadow = new Color(0, 0, 0, 0.3f);
private static readonly Color Band = new Color(1, 0, 0, 0.5f);
private static readonly Color WarnBG = new Color(0.79f, 0.52f, 0f, 0.15f);
private static readonly Color WarnShadow = new Color(0, 0, 0, .3f);
private static readonly Color WarnBand = new Color(1f, 0.62f, 0.22f, 0.5f);
private GUIStyle _tooltipStyle;
protected override bool CanDrawValueProperty(InspectorProperty property)
{
ValidationComponent component = property.GetComponent<ValidationComponent>();
return component != null && component.ValidatorLocator.PotentiallyHasValidatorsFor(property);
}
protected override void Initialize()
{
_validationComponent = Property.GetComponent<ValidationComponent>();
_validationComponent.ValidateProperty(ref _validationResults);
if (_validationResults.Count > 0)
{
_shakeGroupKey = UniqueDrawerKey.Create(Property, this);
Property.Tree.OnUndoRedoPerformed += OnUndoRedoPerformed;
ValueEntry.OnValueChanged += OnValueChanged;
ValueEntry.OnChildValueChanged += OnChildValueChanged;
}
else
{
SkipWhenDrawing = true;
}
_tooltipStyle = new GUIStyle(GUI.skin.label)
{
normal = { textColor = Color.white },
active = { textColor = Color.white },
hover = { textColor = Color.white },
onNormal = { textColor = Color.white },
onActive = { textColor = Color.white },
onHover = { textColor = Color.white },
};
}
protected override void DrawPropertyLayout(GUIContent label)
{
if (_validationResults.Count == 0)
{
CallNextDrawer(label);
}
else
{
GUILayout.BeginVertical();
SirenixEditorGUI.BeginShakeableGroup(_shakeGroupKey);
ValidationResult error = null;
ValidationResult warning = null;
for (var i = 0; i < _validationResults.Count; ++i)
{
ValidationResult result = _validationResults[i];
if (Event.current.type == EventType.Layout && (_rerunFullValidation || result.Setup.Validator != null && ((Validator) result.Setup.Validator).RevalidationCriteria == RevalidationCriteria.Always))
{
_rerunFullValidation = false;
ValidationResultType resultType = result.ResultType;
result.Setup.ParentInstance = Property.ParentValues[0];
_validationComponent.ValidatorLocator.GetValidators(Property)[i].Initialize(Property);
_validationComponent.ValidatorLocator.GetValidators(Property)[i].RunValidation(ref result);
if (resultType != result.ResultType && result.ResultType != ValidationResultType.Valid)
SirenixEditorGUI.StartShakingGroup(_shakeGroupKey);
}
switch (result.ResultType)
{
case ValidationResultType.Error:
error = result;
break;
case ValidationResultType.Warning:
warning = result;
break;
case ValidationResultType.Valid when !string.IsNullOrEmpty(result.Message):
SirenixEditorGUI.InfoMessageBox(result.Message);
break;
}
}
if (Event.current.type == EventType.Layout)
_rerunFullValidation = false;
// The hack materializes: we skip the next drawer which should be the default ValidationDrawer
Property.GetActiveDrawerChain().MoveNext();
CallNextDrawer(label);
Rect rect = GUIHelper.GetCurrentLayoutRect();
if (error != null)
{
if (Event.current.type == EventType.Repaint)
{
SirenixEditorGUI.DrawSolidRect(rect, BG);
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, Shadow);
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, BG);
}
EditorGUI.LabelField(rect, new GUIContent(" ", $"ERROR: {error.Message}"), _tooltipStyle);
}
else if (warning != null)
{
if (Event.current.type == EventType.Repaint)
{
SirenixEditorGUI.DrawSolidRect(rect, WarnBG);
SirenixEditorGUI.DrawBorders(rect, 0, 0, 1, 0, WarnShadow);
SirenixEditorGUI.DrawBorders(rect, 3, 0, 0, 0, WarnBand);
}
EditorGUI.LabelField(rect, new GUIContent(" ", $"WARNING: {warning.Message}"), _tooltipStyle);
}
SirenixEditorGUI.EndShakeableGroup(_shakeGroupKey);
GUILayout.EndVertical();
}
}
public void Dispose()
{
if (_validationResults.Count > 0)
{
Property.Tree.OnUndoRedoPerformed -= OnUndoRedoPerformed;
ValueEntry.OnValueChanged -= OnValueChanged;
ValueEntry.OnChildValueChanged -= OnChildValueChanged;
}
_validationResults = null;
}
private void OnUndoRedoPerformed()
{
_rerunFullValidation = true;
}
private void OnValueChanged(int index)
{
_rerunFullValidation = true;
}
private void OnChildValueChanged(int index)
{
_rerunFullValidation = true;
}
}
#endif
@funnymanwin
Thank you. Yours is very helpful.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
MiniValidationDrawer
Prerequisite: Odin Inspector
Simply drop this script into your project's Editor folder or assembly.
The tooltip of labels (when mouse hovered) will be replaced with the error message. Also works with warnings.
Before

After
