From dff9de630fbae7cb6eadda3844e0c20a6edaface Mon Sep 17 00:00:00 2001 From: UlyssesWu Date: Mon, 24 Sep 2018 11:34:26 +0800 Subject: [PATCH 1/2] Add ToggleSelfBinding --- UnityWeld/Binding/AbstractMemberBinding.cs | 8 +- UnityWeld/Binding/ToggleSelfBinding.cs | 115 +++++++++++++++ UnityWeld/UnityWeld.csproj | 2 + UnityWeld/WeldContext.cs | 33 +++++ UnityWeld_Editor/ToggleSelfBindingEditor.cs | 153 ++++++++++++++++++++ UnityWeld_Editor/UnityWeld_Editor.csproj | 1 + 6 files changed, 308 insertions(+), 4 deletions(-) create mode 100644 UnityWeld/Binding/ToggleSelfBinding.cs create mode 100644 UnityWeld/WeldContext.cs create mode 100644 UnityWeld_Editor/ToggleSelfBindingEditor.cs diff --git a/UnityWeld/Binding/AbstractMemberBinding.cs b/UnityWeld/Binding/AbstractMemberBinding.cs index 11afd27..005891e 100644 --- a/UnityWeld/Binding/AbstractMemberBinding.cs +++ b/UnityWeld/Binding/AbstractMemberBinding.cs @@ -175,10 +175,10 @@ protected void ParseViewEndPointReference(string endPointReference, out string m /// Standard MonoBehaviour awake message, do not call this explicitly. /// Initialises the binding. /// - protected void Awake() - { - Init(); - } + //protected void Awake() + //{ + // Init(); + //} /// /// Clean up when the game object is destroyed. diff --git a/UnityWeld/Binding/ToggleSelfBinding.cs b/UnityWeld/Binding/ToggleSelfBinding.cs new file mode 100644 index 0000000..161df8d --- /dev/null +++ b/UnityWeld/Binding/ToggleSelfBinding.cs @@ -0,0 +1,115 @@ +using UnityEngine; +using UnityWeld.Binding.Internal; + +namespace UnityWeld.Binding +{ + /// + /// Bind to a boolean property on the view model and turn itself on + /// or off based on its value. + /// + [AddComponentMenu("Unity Weld/ToggleSelf Binding")] + [HelpURL("https://github.com/Real-Serious-Games/Unity-Weld")] + public class ToggleSelfBinding : AbstractMemberBinding + { + /// + /// Type of the adapter we're using to adapt between the view model property + /// and view property. + /// + public string ViewAdapterTypeName + { + get { return viewAdapterTypeName; } + set { viewAdapterTypeName = value; } + } + + [SerializeField] + private string viewAdapterTypeName; + + /// + /// Options for adapting from the view model to the view property. + /// + public AdapterOptions ViewAdapterOptions + { + get { return viewAdapterOptions; } + set { viewAdapterOptions = value; } + } + + [SerializeField] + private AdapterOptions viewAdapterOptions; + + /// + /// Name of the property in the view model to bind. + /// + public string ViewModelPropertyName + { + get { return viewModelPropertyName; } + set { viewModelPropertyName = value; } + } + + [SerializeField] + private string viewModelPropertyName; + + /// + /// Watcher the view-model for changes that must be propagated to the view. + /// + private PropertyWatcher viewModelWatcher; + + /// + /// Property for the propertySync to set in order to activate and deactivate all children + /// + public bool SelfActive + { + set + { + SetActive(value); + } + } + + /// + public override void Connect() + { + var viewModelEndPoint = MakeViewModelEndPoint(viewModelPropertyName, null, null); + + var propertySync = new PropertySync( + // Source + viewModelEndPoint, + + // Dest + new PropertyEndPoint( + this, + "SelfActive", + CreateAdapter(viewAdapterTypeName), + viewAdapterOptions, + "view", + this + ), + + // Errors, exceptions and validation. + null, // Validation not needed + + this + ); + + viewModelWatcher = viewModelEndPoint.Watch( + () => propertySync.SyncFromSource() + ); + + // Copy the initial value over from the view-model. + propertySync.SyncFromSource(); + } + + /// + public override void Disconnect() + { + if (viewModelWatcher != null) + { + viewModelWatcher.Dispose(); + viewModelWatcher = null; + } + } + + private void SetActive(bool active) + { + transform.gameObject.SetActive(active); + } + } +} diff --git a/UnityWeld/UnityWeld.csproj b/UnityWeld/UnityWeld.csproj index af50b61..c6abf18 100644 --- a/UnityWeld/UnityWeld.csproj +++ b/UnityWeld/UnityWeld.csproj @@ -108,6 +108,7 @@ + @@ -120,6 +121,7 @@ + diff --git a/UnityWeld/WeldContext.cs b/UnityWeld/WeldContext.cs new file mode 100644 index 0000000..a1022d1 --- /dev/null +++ b/UnityWeld/WeldContext.cs @@ -0,0 +1,33 @@ +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityWeld.Binding; + +namespace UnityWeld +{ + /// + /// Initialize all bindings when scene loaded + /// + public static class WeldContext + { + /// + /// Automatically called before scene load + /// + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + public static void WeldContextStartup() + { + SceneManager.sceneLoaded += SceneManager_SceneLoaded; + } + + static void SceneManager_SceneLoaded(Scene scene, LoadSceneMode mode) + { + foreach (var parentT in scene.GetRootGameObjects()) + { + //Call Init() on all bindings + foreach (var binding in parentT.GetComponentsInChildren(true)) //including inactive GameObjects + { + binding.Init(); + } + } + } + } +} diff --git a/UnityWeld_Editor/ToggleSelfBindingEditor.cs b/UnityWeld_Editor/ToggleSelfBindingEditor.cs new file mode 100644 index 0000000..9daea21 --- /dev/null +++ b/UnityWeld_Editor/ToggleSelfBindingEditor.cs @@ -0,0 +1,153 @@ +using System; +using UnityEditor; +using UnityEditor.AnimatedValues; +using UnityEngine; +using UnityWeld.Binding; +using UnityWeld.Binding.Internal; + +namespace UnityWeld_Editor +{ + [CustomEditor(typeof(ToggleSelfBinding))] + public class ToggleSelfBindingEditor : BaseBindingEditor + { + private ToggleSelfBinding targetScript; + + private AnimBool viewAdapterOptionsFade; + + private bool viewAdapterPrefabModified; + private bool viewAdapterOptionsPrefabModified; + private bool viewModelPropertyPrefabModified; + + private void OnEnable() + { + targetScript = (ToggleSelfBinding)target; + + Type adapterType; + + viewAdapterOptionsFade = new AnimBool( + ShouldShowAdapterOptions(targetScript.ViewAdapterTypeName, out adapterType) + ); + + viewAdapterOptionsFade.valueChanged.AddListener(Repaint); + } + + private void OnDisable() + { + viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint); + } + + public override void OnInspectorGUI() + { + if (CannotModifyInPlayMode()) + { + GUI.enabled = false; + } + + UpdatePrefabModifiedProperties(); + + var defaultLabelStyle = EditorStyles.label.fontStyle; + + var viewPropertyType = typeof(bool); + + var viewAdapterTypeNames = GetAdapterTypeNames( + type => TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType + ); + + EditorStyles.label.fontStyle = viewAdapterPrefabModified + ? FontStyle.Bold + : defaultLabelStyle; + + ShowAdapterMenu( + new GUIContent( + "View adapter", + "Adapter that converts values sent from the view-model to the view." + ), + viewAdapterTypeNames, + targetScript.ViewAdapterTypeName, + newValue => + { + // Get rid of old adapter options if we changed the type of the adapter. + if (newValue != targetScript.ViewAdapterTypeName) + { + Undo.RecordObject(targetScript, "Set view adapter options"); + targetScript.ViewAdapterOptions = null; + } + + UpdateProperty( + updatedValue => targetScript.ViewAdapterTypeName = updatedValue, + targetScript.ViewAdapterTypeName, + newValue, + "Set view adapter" + ); + } + ); + + Type adapterType; + viewAdapterOptionsFade.target = ShouldShowAdapterOptions( + targetScript.ViewAdapterTypeName, + out adapterType + ); + + EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified + ? FontStyle.Bold + : defaultLabelStyle; + + ShowAdapterOptionsMenu( + "View adapter options", + adapterType, + options => targetScript.ViewAdapterOptions = options, + targetScript.ViewAdapterOptions, + viewAdapterOptionsFade.faded + ); + + EditorGUILayout.Space(); + + EditorStyles.label.fontStyle = viewModelPropertyPrefabModified + ? FontStyle.Bold + : defaultLabelStyle; + + var adaptedViewPropertyType = AdaptTypeBackward( + viewPropertyType, + targetScript.ViewAdapterTypeName + ); + ShowViewModelPropertyMenu( + new GUIContent( + "View-model property", + "Property on the view-model to bind to." + ), + TypeResolver.FindBindableProperties(targetScript), + updatedValue => targetScript.ViewModelPropertyName = updatedValue, + targetScript.ViewModelPropertyName, + property => property.PropertyType == adaptedViewPropertyType + ); + + EditorStyles.label.fontStyle = defaultLabelStyle; + } + + private void UpdatePrefabModifiedProperties() + { + var property = serializedObject.GetIterator(); + // Need to call Next(true) to get the first child. Once we have it, Next(false) + // will iterate through the properties. + property.Next(true); + do + { + switch (property.name) + { + case "viewAdapterTypeName": + viewAdapterPrefabModified = property.prefabOverride; + break; + + case "viewAdapterOptions": + viewAdapterOptionsPrefabModified = property.prefabOverride; + break; + + case "viewModelPropertyName": + viewModelPropertyPrefabModified = property.prefabOverride; + break; + } + } + while (property.Next(false)); + } + } +} diff --git a/UnityWeld_Editor/UnityWeld_Editor.csproj b/UnityWeld_Editor/UnityWeld_Editor.csproj index 8669124..d6d131c 100644 --- a/UnityWeld_Editor/UnityWeld_Editor.csproj +++ b/UnityWeld_Editor/UnityWeld_Editor.csproj @@ -53,6 +53,7 @@ + From f262d2af7fb660571f51b53f6d8b33ed3003bd18 Mon Sep 17 00:00:00 2001 From: UlyssesWu Date: Tue, 25 Sep 2018 21:26:35 +0800 Subject: [PATCH 2/2] Remove unused Awake() method --- UnityWeld/Binding/AbstractMemberBinding.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/UnityWeld/Binding/AbstractMemberBinding.cs b/UnityWeld/Binding/AbstractMemberBinding.cs index 005891e..3f77066 100644 --- a/UnityWeld/Binding/AbstractMemberBinding.cs +++ b/UnityWeld/Binding/AbstractMemberBinding.cs @@ -28,11 +28,13 @@ public virtual void Init() private object FindViewModel(string viewModelName) { var trans = transform; + while (trans != null) { var components = trans.GetComponents(); var monoBehaviourViewModel = components .FirstOrDefault(component => component.GetType().ToString() == viewModelName); + if (monoBehaviourViewModel != null) { return monoBehaviourViewModel; @@ -42,8 +44,8 @@ private object FindViewModel(string viewModelName) .Select(component => component as IViewModelProvider) .Where(component => component != null) .FirstOrDefault( - viewModelBinding => viewModelBinding.GetViewModelTypeName() == viewModelName && -#pragma warning disable 252,253 // Warning says unintended reference comparison, but we do want to compare references + viewModelBinding => viewModelBinding.GetViewModelTypeName() == viewModelName && +#pragma warning disable 252, 253 // Warning says unintended reference comparison, but we do want to compare references (object)viewModelBinding != this #pragma warning restore 252,253 ); @@ -73,6 +75,7 @@ protected static IAdapter CreateAdapter(string adapterTypeName) } var adapterType = TypeResolver.FindAdapterType(adapterTypeName); + if (adapterType == null) { throw new NoSuchAdapterException(adapterTypeName); @@ -106,6 +109,7 @@ protected PropertyEndPoint MakeViewModelEndPoint(string viewModelPropertyName, s protected static void ParseEndPointReference(string endPointReference, out string memberName, out string typeName) { var lastPeriodIndex = endPointReference.LastIndexOf('.'); + if (lastPeriodIndex == -1) { throw new InvalidEndPointException( @@ -171,15 +175,6 @@ protected void ParseViewEndPointReference(string endPointReference, out string m /// public abstract void Disconnect(); - /// - /// Standard MonoBehaviour awake message, do not call this explicitly. - /// Initialises the binding. - /// - //protected void Awake() - //{ - // Init(); - //} - /// /// Clean up when the game object is destroyed. /// @@ -188,4 +183,4 @@ public void OnDestroy() Disconnect(); } } -} +} \ No newline at end of file