Last active
July 19, 2023 12:33
-
-
Save gjroelofs/e46deeba8296f617a3d0e9dc7ec1390c to your computer and use it in GitHub Desktop.
Revisions
-
gjroelofs revised this gist
May 23, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -25,7 +25,7 @@ namespace Utility /// /// [HideLabel, ShowIf("@Target != null")] /// public Name { /// get => Target?.Name; /// set => { /// if(Target != null) Target.Name = value; /// } -
gjroelofs revised this gist
May 23, 2020 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -26,7 +26,9 @@ namespace Utility /// [HideLabel, ShowIf("@Target != null")] /// public Name { /// get => Target.Name; /// set => { /// if(Target != null) Target.Name = value; /// } /// } /// /// } -
gjroelofs revised this gist
May 23, 2020 . 1 changed file with 20 additions and 11 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -39,23 +39,23 @@ namespace Utility /// <summary> Contains a mapping of the targeted Type to the concrete Facade. </summary> public Lazy<Dictionary< /* Foo */ Type, /* FooFacade (: Facade<Foo>) */ Type>> Facades = new Lazy<Dictionary<Type, Type>>(() => { return TypeCache.GetTypesDerivedFrom(typeof(OdinFacade<>)) .Where(t => !t.IsGenericTypeDefinition) .Select(t => Tuple.Create( t.GetInterfaces() .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOdinFacade<>)) .GenericTypeArguments.First(), t)) .ToDictionary(t => t.Item1, t => t.Item2); }); /// <summary> Creates a getter setter that forwards the property's getter setter onto the Facade, with concrete type. </summary> private Lazy<MethodInfo> GetterSetter = new Lazy<MethodInfo>(() => typeof(FacadePropertyResolver).GetMethod("CreateWrapper", BindingFlags.Public | BindingFlags.Static)); public override void ProcessMemberProperties(List<InspectorPropertyInfo> propertyInfos) { // TODO: Support inheritance. // TODO: Cache the translation instead of doing it on every call. // Go through and replace all types for which we have a registered facade. for (int i = 0; i < propertyInfos.Count; i++) { var info = propertyInfos[i]; @@ -76,27 +76,37 @@ namespace Utility } public static GetterSetter<TParent, TFacade> CreateWrapper<TParent, TWrapped, TFacade>(InspectorPropertyInfo property) where TFacade : OdinFacade<TWrapped>, new() { var facade = new TFacade(); return new GetterSetter<TParent, TFacade>( (ref TParent instance) => { facade.Target = (TWrapped) property.GetGetterSetter().GetValue(instance); return facade; }, (ref TParent instance, TFacade value) => property.GetGetterSetter().SetValue(instance, value.Target) ); } } public interface IOdinFacade { object UntypedTarget { get; set; } } public interface IOdinFacade<T> : IOdinFacade { /// <summary> Strongly typed target we are editing. </summary> T Target { get; set; } } /// <summary> /// Allows using a different type to construct an editor for a target type. /// I.e.: Given type ComplexObject, use ComplexObjectFacade : OdinFacade{ComplexObject} to define the variables & attributes to be used instead of those defined on ComplexObject. /// </summary> /// <typeparam name="T">The type of object we want to edit.</typeparam> [HideLabel, HideReferenceObjectPicker, InlineProperty] public class OdinFacade<T> : IOdinFacade<T> { public T Target { get; set; } @@ -106,5 +116,4 @@ namespace Utility } } } -
gjroelofs revised this gist
May 23, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -106,4 +106,5 @@ namespace Utility } } } } -
gjroelofs revised this gist
May 23, 2020 . 1 changed file with 0 additions and 2 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -50,8 +50,6 @@ namespace Utility /// <summary> Creates a getter setter that forwards the property's getter setter onto the Facade, with concrete type. </summary> public Lazy<MethodInfo> GetterSetter = new Lazy<MethodInfo>(() => typeof(FacadePropertyResolver).GetMethod("CreateWrapper", BindingFlags.Public | BindingFlags.Static)); public override void ProcessMemberProperties(List<InspectorPropertyInfo> propertyInfos) { -
gjroelofs created this gist
May 23, 2020 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,111 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using UnityEngine; using TypeCache = UnityEditor.TypeCache; namespace Utility { /// <summary> /// Allows creating a type that will be used to render the editor in place of the given type. /// This allows you to use attributes / normal editor design workflow instead of having to write a custom value drawer. /// /// E.g.: Given type `ComplexObject`, create a `ComplexObjectFacade : Facade{ComplexObject}` which has all variables you want to expose and appropriate OdinInspector attributes. /// The `ComplexObject` will then be accessible through the `Target` variable. /// /// E.g.: /// <code> /// // The below code will only show the name of the ComplexObject, without label and if the CO is not null. /// public class ComplexObjectFacade : Facade{ComplexObject} { /// /// [HideLabel, ShowIf("@Target != null")] /// public Name { /// get => Target.Name; /// set => Target.Name = value; /// } /// /// } /// </code> /// /// </summary> public class FacadePropertyResolver : OdinPropertyProcessor { /// <summary> Contains a mapping of the targeted Type to the concrete Facade. </summary> public Lazy<Dictionary< /* Foo */ Type, /* FooFacade (: Facade<Foo>) */ Type>> Facades = new Lazy<Dictionary<Type, Type>>(() => { return TypeCache.GetTypesDerivedFrom(typeof(Facade<>)) .Where(t => !t.IsGenericTypeDefinition) .Select(t => Tuple.Create( t.GetInterfaces() .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFacade<>)) .GenericTypeArguments.First(), t)) .ToDictionary(t => t.Item1, t => t.Item2); }); /// <summary> Creates a getter setter that forwards the property's getter setter onto the Facade, with concrete type. </summary> public Lazy<MethodInfo> GetterSetter = new Lazy<MethodInfo>(() => typeof(FacadePropertyResolver).GetMethod("CreateWrapper", BindingFlags.Public | BindingFlags.Static)); private IFacade _temp; public override void ProcessMemberProperties(List<InspectorPropertyInfo> propertyInfos) { // TODO: Cache the translation instead of doing it on every call. // TODO: Don't recreate the Facade every draw, but keep it alive on the Resolver. for (int i = 0; i < propertyInfos.Count; i++) { var info = propertyInfos[i]; if (info.TypeOfValue == null) continue; if (!Facades.Value.ContainsKey(info.TypeOfValue)) continue; // Create a function that will given a propertyInfo, forward value get/set onto the Facade. var creator = GetterSetter.Value.MakeGenericMethod(info.TypeOfOwner, info.TypeOfValue, Facades.Value[info.TypeOfValue]); var getterSetter = (IValueGetterSetter) creator.Invoke(null, new object[]{info}); // Draw the facade without a wrapping label. var newPI = InspectorPropertyInfo.CreateValue(info.PropertyName, info.Order, info.SerializationBackend, getterSetter); propertyInfos.RemoveAt(i); propertyInfos.Insert(i, newPI); } } public static GetterSetter<TParent, TFacade> CreateWrapper<TParent, TWrapped, TFacade>(InspectorPropertyInfo property) where TFacade : Facade<TWrapped>, new() { return new GetterSetter<TParent, TFacade>( (ref TParent instance) => new TFacade{Target = (TWrapped) property.GetGetterSetter().GetValue(instance)}, (ref TParent instance, TFacade value) => property.GetGetterSetter().SetValue(instance, value.Target) ); } } public interface IFacade { object UntypedTarget { get; set; } } public interface IFacade<T> : IFacade { T Target { get; set; } } [HideLabel, HideReferenceObjectPicker, InlineProperty] public class Facade<T> : IFacade<T> { public T Target { get; set; } public object UntypedTarget { get => Target; set => Target = (T) value; } } }