diff --git a/UnityWeld/Binding/Adapters/StringArrayToListAdapter.cs b/UnityWeld/Binding/Adapters/StringArrayToListAdapter.cs new file mode 100644 index 0000000..8654a08 --- /dev/null +++ b/UnityWeld/Binding/Adapters/StringArrayToListAdapter.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace UnityWeld.Binding.Adapters +{ + /// + /// Adapter for converting from a string array to a string list. + /// + [Adapter(typeof(string[]), typeof(List), null)] + public class StringArrayToListAdapter : IAdapter + { + public object Convert(object valueIn, AdapterOptions options) + { + List output = new List(); + + if(valueIn == null) + { + return output; + } + return new List((string[])valueIn); + } + } +} \ No newline at end of file diff --git a/UnityWeld/Binding/Adapters/StringListToArrayAdapter.cs b/UnityWeld/Binding/Adapters/StringListToArrayAdapter.cs new file mode 100644 index 0000000..01e6cbf --- /dev/null +++ b/UnityWeld/Binding/Adapters/StringListToArrayAdapter.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace UnityWeld.Binding.Adapters +{ + /// + /// Adapter for converting from a string list to a string array. + /// + [Adapter(typeof(List), typeof(string[]), null)] + public class StringListToArrayAdapter : IAdapter + { + public object Convert(object valueIn, AdapterOptions options) + { + + if (valueIn == null) + { + return new string[0]; + } + + return ((List)valueIn).ToArray(); + } + } +} \ No newline at end of file diff --git a/UnityWeld/Binding/DropdownBinding.cs b/UnityWeld/Binding/DropdownBinding.cs index 264a7c5..521e108 100644 --- a/UnityWeld/Binding/DropdownBinding.cs +++ b/UnityWeld/Binding/DropdownBinding.cs @@ -14,12 +14,14 @@ public class DropdownBinding : AbstractMemberBinding /// /// Name of the property in the view model to bind for the current selection. /// - public string viewModelSelectionPropertyName; + [SerializeField] + private string viewModelSelectionPropertyName; /// /// Name of the property in the view model to bind for the list of options. /// - public string viewModelOptionsPropertyName; + [SerializeField] + private string viewModelOptionsPropertyName; /// /// The name of the property to assign an exception to when adapter/validation fails. @@ -57,7 +59,14 @@ public class DropdownBinding : AbstractMemberBinding /// Adapter for converting the options list in the view model /// to the correct format to display in the UI. /// - public string optionsAdapter; + [SerializeField] + public string optionsViewAdapterTypeName; + + /// + /// Option data to use for the options adapter + /// + [SerializeField] + public AdapterOptions optionsViewAdapterOptions; /// /// Cached drop down component. @@ -68,8 +77,8 @@ public override void Connect() { dropdown = GetComponent(); - var selectionPropertyEndPoint = MakeViewModelEndPoint(viewModelSelectionPropertyName, selectionUIToViewModelAdapter, null); - + var selectionPropertyEndPoint = MakeViewModelEndPoint(ViewModelSelectionPropertyName, selectionUIToViewModelAdapter, null); + var selectionPropertySync = new PropertySync( // Source selectionPropertyEndPoint, @@ -101,20 +110,20 @@ public override void Connect() "onValueChanged", () => { - selectedOption = Options[dropdown.value]; // Copy value back from dropdown. + selectedOption = Options.Length > 0 ? Options[dropdown.value] : null; // Copy value back from dropdown. selectionPropertySync.SyncFromDest(); } ); var optionsPropertySync = new PropertySync( // Source - MakeViewModelEndPoint(viewModelOptionsPropertyName, null, null), + MakeViewModelEndPoint(ViewModelOptionsPropertyName, null, null), // Dest new PropertyEndPoint( this, "Options", - CreateAdapter(optionsAdapter), + CreateAdapter(optionsViewAdapterTypeName), null, "view", this @@ -201,6 +210,24 @@ public string SelectedOption } } + public string ViewModelOptionsPropertyName + { + get { return viewModelOptionsPropertyName; } + set + { + viewModelOptionsPropertyName = value; + } + } + + public string ViewModelSelectionPropertyName + { + get { return viewModelSelectionPropertyName; } + set + { + viewModelSelectionPropertyName = value; + } + } + /// /// Update the options. /// @@ -209,6 +236,8 @@ private void UpdateOptions() dropdown.options = options .Select(option => new Dropdown.OptionData(option)) .ToList(); + + UpdateSelectedOption(); } /// diff --git a/UnityWeld_Editor/DropdownBindingEditor.cs b/UnityWeld_Editor/DropdownBindingEditor.cs new file mode 100644 index 0000000..0e0738a --- /dev/null +++ b/UnityWeld_Editor/DropdownBindingEditor.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.AnimatedValues; +using UnityEngine; +using UnityWeld.Binding; +using UnityWeld.Binding.Internal; + +namespace UnityWeld_Editor +{ + [CustomEditor(typeof(DropdownBinding))] + public class DropdownBindingEditor : BaseBindingEditor + { + private DropdownBinding targetScript; + + private readonly bool viewPropertyPrefabModified; + private AnimBool optionsAdapterOptionsFade; + + private void OnEnable() + { + // Initialise reference to target script + targetScript = (DropdownBinding)target; + + Type adapterType; + + optionsAdapterOptionsFade = new AnimBool( + ShouldShowAdapterOptions(targetScript.optionsViewAdapterTypeName, out adapterType) + ); + + optionsAdapterOptionsFade.valueChanged.AddListener(Repaint); + } + + private void OnDisable() + { + optionsAdapterOptionsFade.valueChanged.RemoveListener(Repaint); + } + + public override void OnInspectorGUI() + { + UpdatePrefabModifiedProperties(); + + var defaultLabelStyle = EditorStyles.label.fontStyle; + EditorStyles.label.fontStyle = viewPropertyPrefabModified + ? FontStyle.Bold + : defaultLabelStyle; + + ShowViewModelPropertyMenu( + new GUIContent( + "Options: View-model property", + "Property on the view-model to bind Options to." + ), + TypeResolver.FindBindableProperties(targetScript), + updatedValue => targetScript.ViewModelOptionsPropertyName = updatedValue, + targetScript.ViewModelOptionsPropertyName, + property => property.PropertyType == typeof(string[]) || property.PropertyType == typeof(System.Collections.Generic.List) + ); + + Type viewPropertyType = typeof(string[]); + + var viewAdapterTypeNames = GetAdapterTypeNames( + type => + { + return viewPropertyType == null || + TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType; + }); + + var guiPreviouslyEnabled = GUI.enabled; + + ShowAdapterMenu( + new GUIContent( + "View adapter", + "Adapter that converts values sent from the view-model to the view." + ), + viewAdapterTypeNames, + targetScript.optionsViewAdapterTypeName, + newValue => + { + // Get rid of old adapter options if we changed the type of the adapter. + if (newValue != targetScript.optionsViewAdapterTypeName) + { + Undo.RecordObject(targetScript, "Set view adapter options"); + targetScript.optionsViewAdapterTypeName = null; + } + + UpdateProperty( + updatedValue => targetScript.optionsViewAdapterTypeName = updatedValue, + targetScript.optionsViewAdapterTypeName, + newValue, + "Set view adapter" + ); + } + ); + + Type adapterType; + optionsAdapterOptionsFade.target = ShouldShowAdapterOptions( + targetScript.optionsViewAdapterTypeName, + out adapterType + ); + + ShowAdapterOptionsMenu( + "View adapter options", + adapterType, + options => targetScript.optionsViewAdapterOptions = options, + targetScript.optionsViewAdapterOptions, + optionsAdapterOptionsFade.faded + ); + + + + ShowViewModelPropertyMenu( + new GUIContent( + "Selected Item : View-model property", + "Property on the view-model to bind the selected item to." + ), + TypeResolver.FindBindableProperties(targetScript), + updatedValue => targetScript.ViewModelSelectionPropertyName = updatedValue, + targetScript.ViewModelSelectionPropertyName, + property => property.PropertyType == typeof(string) + ); + + GUI.enabled = guiPreviouslyEnabled; + + EditorStyles.label.fontStyle = defaultLabelStyle; + } + + /// + /// Check whether each of the properties on the object have been changed + /// from the value in the prefab. + /// + 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) + { + + default: + //Debug.Log(property.name); + break; + } + } + while (property.Next(false)); + } + } +} \ No newline at end of file