From 892cfdf07ddf5f905e3777682a8f3391f3fc7806 Mon Sep 17 00:00:00 2001 From: VladV Date: Sat, 21 May 2022 11:49:12 +0300 Subject: [PATCH] Integration with Odin Inspector and Validator --- .../Odin/DrawWithTriInspectorDrawer.cs | 40 ------ Editor.Integrations/Odin/OdinFieldDrawer.cs | 69 ++++++++++ ...Drawer.cs.meta => OdinFieldDrawer.cs.meta} | 0 .../Odin/OdinFieldValidator.cs | 50 +++++++ .../Odin/OdinFieldValidator.cs.meta | 3 + Editor.Integrations/Odin/OdinObjectDrawer.cs | 54 ++++++++ .../Odin/OdinObjectDrawer.cs.meta | 3 + .../Odin/OdinObjectValidator.cs | 52 ++++++++ .../Odin/OdinObjectValidator.cs.meta | 3 + Editor.Integrations/Odin/TriOdinExtensions.cs | 33 +++++ .../Odin/TriOdinExtensions.cs.meta | 3 + .../Odin/TriPropertyTreeForOdin.cs | 74 ++++------ Editor/Editors/TriMonoBehaviourEditor.cs | 11 -- Editor/Editors/TriMonoBehaviourEditor.cs.meta | 3 - Editor/Editors/TriScriptableObjectEditor.cs | 10 -- .../Editors/TriScriptableObjectEditor.cs.meta | 3 - Editor/TriEditor.cs | 17 ++- Editor/TriProperty.cs | 23 +++- Editor/TriPropertyTree.cs | 126 +++++------------- Editor/TriPropertyTree.cs.meta | 4 +- Editor/TriPropertyTreeForSerializedObject.cs | 67 ++++++++++ ...TriPropertyTreeForSerializedObject.cs.meta | 3 + README.md | 64 ++------- Runtime/TriMonoBehaviour.cs | 8 -- Runtime/TriMonoBehaviour.cs.meta | 3 - Runtime/TriScriptableObject.cs | 8 -- Runtime/TriScriptableObject.cs.meta | 3 - package.json | 2 +- 28 files changed, 439 insertions(+), 300 deletions(-) delete mode 100644 Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs create mode 100644 Editor.Integrations/Odin/OdinFieldDrawer.cs rename Editor.Integrations/Odin/{DrawWithTriInspectorDrawer.cs.meta => OdinFieldDrawer.cs.meta} (100%) create mode 100644 Editor.Integrations/Odin/OdinFieldValidator.cs create mode 100644 Editor.Integrations/Odin/OdinFieldValidator.cs.meta create mode 100644 Editor.Integrations/Odin/OdinObjectDrawer.cs create mode 100644 Editor.Integrations/Odin/OdinObjectDrawer.cs.meta create mode 100644 Editor.Integrations/Odin/OdinObjectValidator.cs create mode 100644 Editor.Integrations/Odin/OdinObjectValidator.cs.meta create mode 100644 Editor.Integrations/Odin/TriOdinExtensions.cs create mode 100644 Editor.Integrations/Odin/TriOdinExtensions.cs.meta delete mode 100644 Editor/Editors/TriMonoBehaviourEditor.cs delete mode 100644 Editor/Editors/TriMonoBehaviourEditor.cs.meta delete mode 100644 Editor/Editors/TriScriptableObjectEditor.cs delete mode 100644 Editor/Editors/TriScriptableObjectEditor.cs.meta create mode 100644 Editor/TriPropertyTreeForSerializedObject.cs create mode 100644 Editor/TriPropertyTreeForSerializedObject.cs.meta delete mode 100644 Runtime/TriMonoBehaviour.cs delete mode 100644 Runtime/TriMonoBehaviour.cs.meta delete mode 100644 Runtime/TriScriptableObject.cs delete mode 100644 Runtime/TriScriptableObject.cs.meta diff --git a/Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs b/Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs deleted file mode 100644 index 8eb6795..0000000 --- a/Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Sirenix.OdinInspector.Editor; -using Sirenix.Utilities.Editor; -using UnityEditor; -using UnityEngine; - -namespace TriInspector.Editor.Integrations.Odin -{ - [DrawerPriority(0.0, 0.0, 6000.0)] - public class DrawWithTriInspectorDrawer : OdinAttributeDrawer, IDisposable - { - private TriPropertyTreeForOdin _propertyTree; - - protected override void Initialize() - { - base.Initialize(); - - _propertyTree = new TriPropertyTreeForOdin(ValueEntry); - } - - public void Dispose() - { - _propertyTree?.Dispose(); - } - - protected override void DrawPropertyLayout(GUIContent label) - { - var propertyState = ValueEntry.Property.State; - - propertyState.Expanded = SirenixEditorGUI.Foldout(propertyState.Expanded, label); - - if (propertyState.Expanded) - { - EditorGUI.indentLevel++; - _propertyTree.Draw(); - EditorGUI.indentLevel--; - } - } - } -} \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinFieldDrawer.cs b/Editor.Integrations/Odin/OdinFieldDrawer.cs new file mode 100644 index 0000000..73ac8cd --- /dev/null +++ b/Editor.Integrations/Odin/OdinFieldDrawer.cs @@ -0,0 +1,69 @@ +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.Utilities.Editor; +using TriInspector.Utilities; +using UnityEngine; + +namespace TriInspector.Editor.Integrations.Odin +{ + [DrawerPriority(0.0, 10000.0, 1.0)] + public class OdinFieldDrawer : OdinAttributeDrawer, IDisposable + { + private TriPropertyTree _propertyTree; + + public override bool CanDrawTypeFilter(Type type) + { + if (typeof(UnityEngine.Object).IsAssignableFrom(type)) + { + return false; + } + + return true; + } + + protected override bool CanDrawAttributeValueProperty(InspectorProperty property) + { + if (property.IsTreeRoot) + { + return false; + } + + return true; + } + + protected override void Initialize() + { + base.Initialize(); + + _propertyTree = new TriPropertyTreeForOdin(ValueEntry); + _propertyTree.Initialize(TriEditorMode.None); + } + + public void Dispose() + { + _propertyTree?.Dispose(); + } + + protected override void DrawPropertyLayout(GUIContent label) + { + var propertyState = ValueEntry.Property.State; + + propertyState.Expanded = SirenixEditorGUI.Foldout(propertyState.Expanded, label); + + if (propertyState.Expanded) + { + using (TriGuiHelper.PushIndentLevel()) + { + _propertyTree.Update(); + + if (_propertyTree.ValidationRequired) + { + _propertyTree.RunValidation(); + } + + _propertyTree.Draw(); + } + } + } + } +} \ No newline at end of file diff --git a/Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs.meta b/Editor.Integrations/Odin/OdinFieldDrawer.cs.meta similarity index 100% rename from Editor.Integrations/Odin/DrawWithTriInspectorDrawer.cs.meta rename to Editor.Integrations/Odin/OdinFieldDrawer.cs.meta diff --git a/Editor.Integrations/Odin/OdinFieldValidator.cs b/Editor.Integrations/Odin/OdinFieldValidator.cs new file mode 100644 index 0000000..4078d92 --- /dev/null +++ b/Editor.Integrations/Odin/OdinFieldValidator.cs @@ -0,0 +1,50 @@ +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Validation; +using TriInspector.Editor.Integrations.Odin; + +[assembly: RegisterValidator(typeof(OdinFieldValidator<>))] + +namespace TriInspector.Editor.Integrations.Odin +{ + public class OdinFieldValidator : AttributeValidator, IDisposable + { + private TriPropertyTreeForOdin _propertyTree; + + public override RevalidationCriteria RevalidationCriteria { get; } + = RevalidationCriteria.OnValueChangeOrChildValueChange; + + public override bool CanValidateProperty(InspectorProperty property) + { + if (typeof(UnityEngine.Object).IsAssignableFrom(property.Info.TypeOfValue)) + { + return false; + } + + if (property.IsTreeRoot) + { + return false; + } + + return true; + } + + protected override void Initialize() + { + _propertyTree = new TriPropertyTreeForOdin(ValueEntry); + _propertyTree.Initialize(TriEditorMode.None); + } + + public void Dispose() + { + _propertyTree.Dispose(); + } + + protected override void Validate(ValidationResult result) + { + _propertyTree.Update(); + _propertyTree.RunValidation(); + _propertyTree.CopyValidationResultsTo(result); + } + } +} \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinFieldValidator.cs.meta b/Editor.Integrations/Odin/OdinFieldValidator.cs.meta new file mode 100644 index 0000000..6cc9a7d --- /dev/null +++ b/Editor.Integrations/Odin/OdinFieldValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50b3a32d5da945fbbdf9a981a9de5f86 +timeCreated: 1653119957 \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinObjectDrawer.cs b/Editor.Integrations/Odin/OdinObjectDrawer.cs new file mode 100644 index 0000000..827e53d --- /dev/null +++ b/Editor.Integrations/Odin/OdinObjectDrawer.cs @@ -0,0 +1,54 @@ +using System; +using Sirenix.OdinInspector.Editor; +using UnityEngine; + +namespace TriInspector.Editor.Integrations.Odin +{ + [DrawerPriority(0.0, 10000.0, 1.0)] + public class OdinObjectDrawer : OdinAttributeDrawer, IDisposable + where T : UnityEngine.Object + { + private TriPropertyTree _propertyTree; + + protected override bool CanDrawAttributeValueProperty(InspectorProperty property) + { + if (!property.IsTreeRoot) + { + return false; + } + + if (property.Tree.UnitySerializedObject == null) + { + return false; + } + + return true; + } + + protected override void Initialize() + { + base.Initialize(); + + var serializedObject = Property.Tree.UnitySerializedObject; + _propertyTree = new TriPropertyTreeForSerializedObject(serializedObject); + _propertyTree.Initialize(TriEditorMode.None); + } + + public void Dispose() + { + _propertyTree?.Dispose(); + } + + protected override void DrawPropertyLayout(GUIContent label) + { + _propertyTree.Update(); + + if (_propertyTree.ValidationRequired) + { + _propertyTree.RunValidation(); + } + + _propertyTree.Draw(); + } + } +} \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinObjectDrawer.cs.meta b/Editor.Integrations/Odin/OdinObjectDrawer.cs.meta new file mode 100644 index 0000000..4f957c8 --- /dev/null +++ b/Editor.Integrations/Odin/OdinObjectDrawer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a211e55ec804aa4b8554563e98d9b04 +timeCreated: 1653129453 \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinObjectValidator.cs b/Editor.Integrations/Odin/OdinObjectValidator.cs new file mode 100644 index 0000000..11a6327 --- /dev/null +++ b/Editor.Integrations/Odin/OdinObjectValidator.cs @@ -0,0 +1,52 @@ +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Validation; +using TriInspector.Editor.Integrations.Odin; +using UnityEditor; + +[assembly: RegisterValidator(typeof(OdinObjectValidator<>))] + +namespace TriInspector.Editor.Integrations.Odin +{ + public class OdinObjectValidator : AttributeValidator, IDisposable + where T : UnityEngine.Object + { + private TriPropertyTreeForSerializedObject _propertyTree; + private SerializedObject _serializedObject; + + public override RevalidationCriteria RevalidationCriteria { get; } + = RevalidationCriteria.OnValueChangeOrChildValueChange; + + public override bool CanValidateProperty(InspectorProperty property) + { + if (!property.IsTreeRoot) + { + return false; + } + + return true; + } + + protected override void Initialize() + { + _serializedObject = new SerializedObject(ValueEntry.SmartValue); + _propertyTree = new TriPropertyTreeForSerializedObject(_serializedObject); + _propertyTree.Initialize(TriEditorMode.None); + } + + public void Dispose() + { + _propertyTree.Dispose(); + _serializedObject.Dispose(); + } + + protected override void Validate(ValidationResult result) + { + _serializedObject.Update(); + + _propertyTree.Update(); + _propertyTree.RunValidation(); + _propertyTree.CopyValidationResultsTo(result); + } + } +} \ No newline at end of file diff --git a/Editor.Integrations/Odin/OdinObjectValidator.cs.meta b/Editor.Integrations/Odin/OdinObjectValidator.cs.meta new file mode 100644 index 0000000..a9044de --- /dev/null +++ b/Editor.Integrations/Odin/OdinObjectValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bbb0d53e419644038a32323fb3083fb3 +timeCreated: 1653123315 \ No newline at end of file diff --git a/Editor.Integrations/Odin/TriOdinExtensions.cs b/Editor.Integrations/Odin/TriOdinExtensions.cs new file mode 100644 index 0000000..93ac1c9 --- /dev/null +++ b/Editor.Integrations/Odin/TriOdinExtensions.cs @@ -0,0 +1,33 @@ +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor.Validation; + +namespace TriInspector.Editor.Integrations.Odin +{ + internal static class TriOdinExtensions + { + public static void CopyValidationResultsTo(this TriPropertyTree tree, ValidationResult result) + { + tree.EnumerateValidationResults((property, triResult) => result.Add(triResult)); + } + + private static void Add(this ValidationResult result, TriValidationResult src) + { + var severity = src.GetOdinValidatorSeverity(); + result.Add(severity, src.Message); + } + + private static ValidatorSeverity GetOdinValidatorSeverity(this TriValidationResult result) + { + switch (result.MessageType) + { + case TriMessageType.Error: + return ValidatorSeverity.Error; + + case TriMessageType.Warning: + return ValidatorSeverity.Warning; + } + + return ValidatorSeverity.Ignore; + } + } +} \ No newline at end of file diff --git a/Editor.Integrations/Odin/TriOdinExtensions.cs.meta b/Editor.Integrations/Odin/TriOdinExtensions.cs.meta new file mode 100644 index 0000000..d82ea1b --- /dev/null +++ b/Editor.Integrations/Odin/TriOdinExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c13b27eaa04b477db49de9a0582608b5 +timeCreated: 1653124004 \ No newline at end of file diff --git a/Editor.Integrations/Odin/TriPropertyTreeForOdin.cs b/Editor.Integrations/Odin/TriPropertyTreeForOdin.cs index e68159d..7e294c9 100644 --- a/Editor.Integrations/Odin/TriPropertyTreeForOdin.cs +++ b/Editor.Integrations/Odin/TriPropertyTreeForOdin.cs @@ -1,41 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using Sirenix.OdinInspector.Editor; -using Sirenix.Utilities; using Sirenix.Utilities.Editor; -using TriInspector.Elements; using UnityEditor; -using UnityEngine; using Object = UnityEngine.Object; namespace TriInspector.Editor.Integrations.Odin { - public class TriPropertyTreeForOdin : ITriPropertyParent, ITriPropertyTree + public class TriPropertyTreeForOdin : TriPropertyTree { private readonly IPropertyValueEntry _odinValueEntry; private readonly InspectorProperty _odinProperty; - private readonly IReadOnlyList _triProperties; - private readonly TriInspectorElement _triInspectorElement; private readonly SerializedProperty _serializedProperty; - private bool _validationRequired; - public TriPropertyTreeForOdin(IPropertyValueEntry odinValueEntry) { _odinValueEntry = odinValueEntry; _odinProperty = odinValueEntry.Property; + _serializedProperty = _odinProperty.Tree.GetUnityPropertyForPath(_odinProperty.Path, out _); TargetObjectType = _odinProperty.Tree.TargetType; TargetsCount = _odinProperty.Tree.WeakTargets.Count; TargetIsPersistent = _odinProperty.Tree.WeakTargets[0] is Object obj && obj != null && EditorUtility.IsPersistent(obj); - _serializedProperty = _odinProperty.Tree.GetUnityPropertyForPath(_odinProperty.Path, out _); + UpdateEmittedScriptableObject(); - UpdateAfterValueModification(); + _serializedProperty.serializedObject.SetIsDifferentCacheDirty(); + _serializedProperty.serializedObject.Update(); - _triProperties = TriTypeDefinition.GetCached(odinValueEntry.TypeOfValue) + _odinProperty.Update(); + + Properties = TriTypeDefinition.GetCached(odinValueEntry.TypeOfValue) .Properties .Select((propertyDefinition, index) => { @@ -43,49 +38,22 @@ namespace TriInspector.Editor.Integrations.Odin return new TriProperty(this, this, propertyDefinition, index, serializedProperty); }) .ToList(); - - _triInspectorElement = new TriInspectorElement(odinValueEntry.TypeOfValue, _triProperties); - _triInspectorElement.AttachInternal(); - - _triProperties.ForEach(it => it.Update()); - _triProperties.ForEach(it => it.RunValidation()); } - public void Draw() + public override void Update() { UpdateEmittedScriptableObject(); - _triProperties.ForEach(it => it.Update()); - if (_validationRequired) - { - _validationRequired = false; - - _triProperties.ForEach(it => it.RunValidation()); - } - - _triInspectorElement.Update(); - var width = EditorGUIUtility.currentViewWidth; - var height = _triInspectorElement.GetHeight(width); - var rect = GUILayoutUtility.GetRect(width, height); - _triInspectorElement.OnGUI(rect); + base.Update(); } - public Type TargetObjectType { get; } - public int TargetsCount { get; } - public bool TargetIsPersistent { get; } - - public void Dispose() - { - _triInspectorElement?.DetachInternal(); - } - - public void ForceCreateUndoGroup() + public override void ForceCreateUndoGroup() { _odinProperty.RecordForUndo(forceCompleteObjectUndo: true); Undo.FlushUndoRecordObjects(); } - public void PrepareForValueModification() + public override void PrepareForValueModification() { var dirty = false; dirty |= _odinValueEntry.ApplyChanges(); @@ -94,12 +62,12 @@ namespace TriInspector.Editor.Integrations.Odin if (dirty) { - _validationRequired = true; - GUIHelper.RequestRepaint(); + RequestValidation(); + RequestRepaint(); } } - public void UpdateAfterValueModification() + public override void UpdateAfterValueModification() { UpdateEmittedScriptableObject(); @@ -109,21 +77,25 @@ namespace TriInspector.Editor.Integrations.Odin _odinProperty.Update(); } - public void RequestRepaint() + public override void RequestRepaint() { + base.RequestRepaint(); + GUIHelper.RequestRepaint(); } - public object GetValue(int targetIndex) + public override object GetValue(int targetIndex) { return _odinValueEntry.Values[targetIndex]; } - public void NotifyValueChanged(TriProperty property) + public override void NotifyValueChanged(TriProperty property) { ApplyEmittedScriptableObject(); _odinValueEntry.Values.ForceMarkDirty(); + + RequestValidation(); } private void UpdateEmittedScriptableObject() diff --git a/Editor/Editors/TriMonoBehaviourEditor.cs b/Editor/Editors/TriMonoBehaviourEditor.cs deleted file mode 100644 index d1daf47..0000000 --- a/Editor/Editors/TriMonoBehaviourEditor.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UnityEditor; - -namespace TriInspector.Editors -{ - [CanEditMultipleObjects] - [CustomEditor(typeof(TriMonoBehaviour), true, isFallback = true)] - internal sealed class TriMonoBehaviourEditor : TriEditor - { - - } -} \ No newline at end of file diff --git a/Editor/Editors/TriMonoBehaviourEditor.cs.meta b/Editor/Editors/TriMonoBehaviourEditor.cs.meta deleted file mode 100644 index 7528754..0000000 --- a/Editor/Editors/TriMonoBehaviourEditor.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 54df0926814e4714b27ffbe0191516f5 -timeCreated: 1652774156 \ No newline at end of file diff --git a/Editor/Editors/TriScriptableObjectEditor.cs b/Editor/Editors/TriScriptableObjectEditor.cs deleted file mode 100644 index ff70fb0..0000000 --- a/Editor/Editors/TriScriptableObjectEditor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UnityEditor; - -namespace TriInspector.Editors -{ - [CanEditMultipleObjects] - [CustomEditor(typeof(TriScriptableObject), true, isFallback = true)] - internal sealed class TriScriptableObjectEditor : TriEditor - { - } -} \ No newline at end of file diff --git a/Editor/Editors/TriScriptableObjectEditor.cs.meta b/Editor/Editors/TriScriptableObjectEditor.cs.meta deleted file mode 100644 index 3fa301a..0000000 --- a/Editor/Editors/TriScriptableObjectEditor.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: a26587373eeb4f0fb8f99540b0ac352d -timeCreated: 1652774174 \ No newline at end of file diff --git a/Editor/TriEditor.cs b/Editor/TriEditor.cs index c6ec779..fc465e4 100644 --- a/Editor/TriEditor.cs +++ b/Editor/TriEditor.cs @@ -9,7 +9,7 @@ namespace TriInspector { private static readonly Stack EditorStack = new Stack(); - private TriPropertyTree _inspector; + private TriPropertyTreeForSerializedObject _inspector; private TriEditorMode _editorMode; @@ -39,7 +39,14 @@ namespace TriInspector return; } - _inspector = TriPropertyTree.Create(serializedObject, _editorMode); + if (serializedObject.targetObject == null) + { + EditorGUILayout.HelpBox("Script is missing", MessageType.Warning); + return; + } + + _inspector = new TriPropertyTreeForSerializedObject(serializedObject); + _inspector.Initialize(_editorMode); } serializedObject.UpdateIfRequiredOrScript(); @@ -59,8 +66,6 @@ namespace TriInspector { if (_inspector.ValidationRequired) { - _inspector.ValidationRequired = false; - _inspector.RunValidation(); } } @@ -73,7 +78,7 @@ namespace TriInspector Profiler.BeginSample("TriInspector.DoLayout()"); try { - _inspector.DoLayout(); + _inspector.Draw(); } finally { @@ -88,8 +93,6 @@ namespace TriInspector if (_inspector.RepaintRequired) { - _inspector.RepaintRequired = false; - Repaint(); } } diff --git a/Editor/TriProperty.cs b/Editor/TriProperty.cs index 5ef7bbe..8fdc6cd 100644 --- a/Editor/TriProperty.cs +++ b/Editor/TriProperty.cs @@ -27,7 +27,7 @@ namespace TriInspector private string _isExpandedPrefsKey; internal TriProperty( - ITriPropertyTree propertyTree, + TriPropertyTree propertyTree, ITriPropertyParent parent, TriPropertyDefinition definition, int propertyIndex, @@ -219,7 +219,7 @@ namespace TriInspector : throw new InvalidOperationException("Cannot read ArrayElementProperties for " + PropertyType); [PublicAPI] - public ITriPropertyTree PropertyTree { get; } + public TriPropertyTree PropertyTree { get; } [PublicAPI] [CanBeNull] @@ -388,6 +388,25 @@ namespace TriInspector } } + internal void EnumerateValidationResults(Action call) + { + if (_validationResults != null) + { + foreach (var result in _validationResults) + { + call.Invoke(this, result); + } + } + + if (_childrenProperties != null) + { + foreach (var childrenProperty in _childrenProperties) + { + childrenProperty.EnumerateValidationResults(call); + } + } + } + [PublicAPI] public bool TryGetSerializedProperty(out SerializedProperty serializedProperty) { diff --git a/Editor/TriPropertyTree.cs b/Editor/TriPropertyTree.cs index 636e17f..b45fac4 100644 --- a/Editor/TriPropertyTree.cs +++ b/Editor/TriPropertyTree.cs @@ -1,34 +1,29 @@ using System; using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; using TriInspector.Elements; using UnityEditor; using UnityEngine; -using Object = UnityEngine.Object; namespace TriInspector { - public sealed class TriPropertyTree : ITriPropertyParent, ITriPropertyTree + public abstract class TriPropertyTree : ITriPropertyParent { - private readonly TriEditorMode _mode; - private readonly TriInspectorElement _inspectorElement; + private TriInspectorElement _inspectorElement; + private TriEditorMode _mode; - private TriPropertyTree([NotNull] SerializedObject serializedObject, TriEditorMode mode) + public IReadOnlyList Properties { get; protected set; } + + public bool IsInlineEditor => (_mode & TriEditorMode.InlineEditor) != 0; + + public Type TargetObjectType { get; protected set; } + public int TargetsCount { get; protected set; } + public bool TargetIsPersistent { get; protected set; } + + public bool ValidationRequired { get; private set; } + public bool RepaintRequired { get; private set; } + + public void Initialize(TriEditorMode mode) { - SerializedObject = serializedObject ?? throw new ArgumentNullException(nameof(serializedObject)); - TargetObjects = serializedObject.targetObjects; - TargetObjectType = TargetObjects[0].GetType(); - - Properties = TriTypeDefinition.GetCached(TargetObjectType) - .Properties - .Select((propertyDefinition, index) => - { - var serializedProperty = serializedObject.FindProperty(propertyDefinition.Name); - return new TriProperty(this, this, propertyDefinition, index, serializedProperty); - }) - .ToList(); - _mode = mode; _inspectorElement = new TriInspectorElement(TargetObjectType, Properties); _inspectorElement.AttachInternal(); @@ -37,37 +32,7 @@ namespace TriInspector RunValidation(); } - [PublicAPI] - public IReadOnlyList Properties { get; } - - [PublicAPI] - public Object[] TargetObjects { get; } - - [PublicAPI] - public Type TargetObjectType { get; } - - [PublicAPI] - public int TargetsCount => TargetObjects.Length; - - private SerializedObject SerializedObject { get; } - - public bool IsInlineEditor => (_mode & TriEditorMode.InlineEditor) != 0; - - public bool TargetIsPersistent => TargetObjects[0] is var targetObject && - targetObject != null && !EditorUtility.IsPersistent(targetObject); - - internal bool RepaintRequired { get; set; } - internal bool ValidationRequired { get; set; } - - object ITriPropertyParent.GetValue(int targetIndex) => TargetObjects[targetIndex]; - - internal static TriPropertyTree Create(SerializedObject scriptableObject, - TriEditorMode mode = TriEditorMode.None) - { - return new TriPropertyTree(scriptableObject, mode); - } - - public void Dispose() + public virtual void Dispose() { if (!_inspectorElement.IsAttached) { @@ -77,7 +42,7 @@ namespace TriInspector _inspectorElement.DetachInternal(); } - internal void Update() + public virtual void Update() { foreach (var property in Properties) { @@ -85,8 +50,10 @@ namespace TriInspector } } - internal void RunValidation() + public void RunValidation() { + ValidationRequired = false; + foreach (var property in Properties) { property.RunValidation(); @@ -95,8 +62,10 @@ namespace TriInspector RequestRepaint(); } - internal void DoLayout() + public void Draw() { + RepaintRequired = false; + _inspectorElement.Update(); var width = EditorGUIUtility.currentViewWidth; var height = _inspectorElement.GetHeight(width); @@ -104,38 +73,15 @@ namespace TriInspector _inspectorElement.OnGUI(rect); } - public void ForceCreateUndoGroup() + public void EnumerateValidationResults(Action call) { - Undo.RegisterCompleteObjectUndo(TargetObjects, "Inspector"); - Undo.FlushUndoRecordObjects(); - } - - public void PrepareForValueModification() - { - if (SerializedObject.ApplyModifiedProperties()) + foreach (var triProperty in Properties) { - RequestValidation(); - RequestRepaint(); + triProperty.EnumerateValidationResults(call); } } - public void UpdateAfterValueModification() - { - SerializedObject.SetIsDifferentCacheDirty(); - SerializedObject.Update(); - } - - public void NotifyValueChanged(TriProperty property) - { - foreach (var targetObject in TargetObjects) - { - EditorUtility.SetDirty(targetObject); - } - - RequestValidation(); - } - - public void RequestRepaint() + public virtual void RequestRepaint() { RepaintRequired = true; } @@ -143,19 +89,15 @@ namespace TriInspector public void RequestValidation() { ValidationRequired = true; + + RequestRepaint(); } - } - public interface ITriPropertyTree : IDisposable - { - Type TargetObjectType { get; } - int TargetsCount { get; } - bool TargetIsPersistent { get; } - - void ForceCreateUndoGroup(); - void PrepareForValueModification(); - void UpdateAfterValueModification(); - void RequestRepaint(); + public abstract object GetValue(int targetIndex); + public abstract void NotifyValueChanged(TriProperty property); + public abstract void ForceCreateUndoGroup(); + public abstract void PrepareForValueModification(); + public abstract void UpdateAfterValueModification(); } [Flags] diff --git a/Editor/TriPropertyTree.cs.meta b/Editor/TriPropertyTree.cs.meta index 20102fd..1c1e978 100644 --- a/Editor/TriPropertyTree.cs.meta +++ b/Editor/TriPropertyTree.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 468b264c3e164c0681760c3d98451466 -timeCreated: 1638857169 \ No newline at end of file +guid: 9bb1d443163b4a26908eb26ba297c1d6 +timeCreated: 1653126491 \ No newline at end of file diff --git a/Editor/TriPropertyTreeForSerializedObject.cs b/Editor/TriPropertyTreeForSerializedObject.cs new file mode 100644 index 0000000..a196f3a --- /dev/null +++ b/Editor/TriPropertyTreeForSerializedObject.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using JetBrains.Annotations; +using UnityEditor; + +namespace TriInspector +{ + public sealed class TriPropertyTreeForSerializedObject : TriPropertyTree + { + private readonly SerializedObject _serializedObject; + + public TriPropertyTreeForSerializedObject([NotNull] SerializedObject serializedObject) + { + _serializedObject = serializedObject ?? throw new ArgumentNullException(nameof(serializedObject)); + + TargetObjectType = _serializedObject.targetObject.GetType(); + TargetsCount = _serializedObject.targetObjects.Length; + TargetIsPersistent = _serializedObject.targetObject is var targetObject && + targetObject != null && !EditorUtility.IsPersistent(targetObject); + + Properties = TriTypeDefinition.GetCached(TargetObjectType) + .Properties + .Select((propertyDefinition, index) => + { + var serializedProperty = serializedObject.FindProperty(propertyDefinition.Name); + return new TriProperty(this, this, propertyDefinition, index, serializedProperty); + }) + .ToList(); + } + + public override object GetValue(int targetIndex) + { + return _serializedObject.targetObjects[targetIndex]; + } + + public override void ForceCreateUndoGroup() + { + Undo.RegisterCompleteObjectUndo(_serializedObject.targetObjects, "Inspector"); + Undo.FlushUndoRecordObjects(); + } + + public override void PrepareForValueModification() + { + if (_serializedObject.ApplyModifiedProperties()) + { + RequestValidation(); + RequestRepaint(); + } + } + + public override void UpdateAfterValueModification() + { + _serializedObject.SetIsDifferentCacheDirty(); + _serializedObject.Update(); + } + + public override void NotifyValueChanged(TriProperty property) + { + foreach (var targetObject in _serializedObject.targetObjects) + { + EditorUtility.SetDirty(targetObject); + } + + RequestValidation(); + } + } +} \ No newline at end of file diff --git a/Editor/TriPropertyTreeForSerializedObject.cs.meta b/Editor/TriPropertyTreeForSerializedObject.cs.meta new file mode 100644 index 0000000..20102fd --- /dev/null +++ b/Editor/TriPropertyTreeForSerializedObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 468b264c3e164c0681760c3d98451466 +timeCreated: 1638857169 \ No newline at end of file diff --git a/README.md b/README.md index 4130fff..5d6c906 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ _Advanced inspector attributes for Unity_ - [Property Processors](#Property-Processors) - [Integrations](#Integrations) - [Odin Inspector](#Odin-Inspector) + - [Odin Validator](#Odin-Validator) - [License](#License) ## How to Install @@ -644,65 +645,16 @@ Tri Inspector is able to work in compatibility mode with Odin Inspector. In this mode, the primary interface will be drawn by the Odin Inspector. However, parts of the interface can be rendered by the Tri Inspector. -In order for the interface to be rendered by Tri instead of Odin, it is necessary to: -- Use TriMonoBehaviour instead of MonoBehaviour -- Use TriScriptableObject instead of ScriptableObject -- Mark serializable classes with DrawWithTriInspector +In order for the interface to be rendered by Tri instead of Odin, +it is necessary to mark classes with `[DrawWithTriInspector]` attribute. -```csharp -public class ThisBehaviourDrawnByOdin : MonoBehaviour -{ - [Sirenix.OdinInspector.InfoBox("Will be drawn by Odin Inspector as usual")] - [Sirenix.OdinInspector.BoxGroup("box", LabelText = "Box Group")] - public Texture tex; +### Odin Validator - [Sirenix.OdinInspector.InfoBox("Will be drawn by Odin Inspector as usual")] - public ThisClassDrawnByOdin odin; +Tri Inspector is integrated with the Odin Validator +so all validation results from Tri attributes will be shown +in the Odin Validator window. - [Sirenix.OdinInspector.InfoBox("This class marked with [DrawWithTriInspector] " + - "so will be drawn by Tri Inspector")] - public ThisClassDrawnByTri tri; -} - -[DeclareBoxGroup("box", Title = "Box Group")] -public class ThisBehaviourDrawnByTri : TriMonoBehaviour -{ - [TriInspector.InfoBox("Parent class inherits from TriMonoBehaviour " + - "so all fields will be drawn by Tri Inspector")] - [TriInspector.Group("box")] - public Texture tex; - - [TriInspector.InfoBox("It is impossible to draw Odin inside Tri, " + - "so this field will be drawn by Tri too")] - public ThisClassDrawnByOdin wrong; - - [TriInspector.InfoBox("Will be drawn by Tri Inspector too")] - // This field drawn by Tri Inspector - public ThisClassDrawnByTri tri; -} - -[Serializable] -public class ThisClassDrawnByOdin -{ - [Sirenix.OdinInspector.LabelText("Float Field")] - public float f; - - [Sirenix.OdinInspector.ListDrawerSettings(Expanded = true)] - public List materials; -} - -[Serializable, DrawWithTriInspector] -public class ThisClassDrawnByTri -{ - [TriInspector.LabelText("Float Field")] - public float f; - - [TriInspector.ListDrawerSettings(AlwaysExpanded = true)] - public List materials; -} -``` - -![Odin-Inspector-Integration](https://user-images.githubusercontent.com/26966368/169118626-dc5e5f10-2274-4232-967b-2811fb7fd34b.png) +![Odin-Validator-Integration](https://user-images.githubusercontent.com/26966368/169645537-d8f0b50f-46af-4804-95e8-337ff3b5ae83.png) ## License diff --git a/Runtime/TriMonoBehaviour.cs b/Runtime/TriMonoBehaviour.cs deleted file mode 100644 index eca8483..0000000 --- a/Runtime/TriMonoBehaviour.cs +++ /dev/null @@ -1,8 +0,0 @@ -using UnityEngine; - -namespace TriInspector -{ - public abstract class TriMonoBehaviour : MonoBehaviour - { - } -} \ No newline at end of file diff --git a/Runtime/TriMonoBehaviour.cs.meta b/Runtime/TriMonoBehaviour.cs.meta deleted file mode 100644 index 3ef9c92..0000000 --- a/Runtime/TriMonoBehaviour.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: f57eec47cca7485680d88d6daf43db8d -timeCreated: 1652774059 \ No newline at end of file diff --git a/Runtime/TriScriptableObject.cs b/Runtime/TriScriptableObject.cs deleted file mode 100644 index d200b1b..0000000 --- a/Runtime/TriScriptableObject.cs +++ /dev/null @@ -1,8 +0,0 @@ -using UnityEngine; - -namespace TriInspector -{ - public abstract class TriScriptableObject : ScriptableObject - { - } -} \ No newline at end of file diff --git a/Runtime/TriScriptableObject.cs.meta b/Runtime/TriScriptableObject.cs.meta deleted file mode 100644 index 902e658..0000000 --- a/Runtime/TriScriptableObject.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: a07f45c37367452280cda2001e97816e -timeCreated: 1652774082 \ No newline at end of file diff --git a/package.json b/package.json index fd989fc..4f19109 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "com.codewriter.triinspector", "displayName": "Tri Inspector", "description": "Better inspector and validator for Unity", - "version": "1.0.1", + "version": "1.0.2", "unity": "2020.3", "author": "CodeWriter (https://github.com/orgs/codewriter-packages)", "homepage": "https://github.com/codewriter-packages/Tri-Inspector#readme",