/// <summary> /// Show dropdown for selecting a UnityEvent to bind to. /// </summary> protected void ShowEventMenu( AbstractMemberBinding targetScript, BindableEvent[] events, Action <string> propertyValueSetter, string curPropertyValue ) { var eventNames = events .Select(evt => evt.ComponentType.Name + "." + evt.Name) .ToArray(); var selectedIndex = Array.IndexOf(eventNames, curPropertyValue); var content = events .Select(evt => new GUIContent(evt.ComponentType.Name + "." + evt.Name)) .ToArray(); var newSelectedIndex = EditorGUILayout.Popup( new GUIContent("View event", "Event on the view to bind to."), selectedIndex, content ); if (newSelectedIndex != selectedIndex) { var selectedEvent = events[newSelectedIndex]; UpdateProperty( propertyValueSetter, curPropertyValue, selectedEvent.ComponentType.Name + "." + selectedEvent.Name ); } }
/// <summary> /// Display a popup menu for selecting a property from a view-model. /// </summary> protected void ShowViewModelPropertyMenu( string label, AbstractMemberBinding target, PropertyInfo[] bindableProperties, Action <string> propertyValueSetter, string curPropertyValue, Func <PropertyInfo, bool> menuEnabled ) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(label); var dropdownPosition = GUILayoutUtility.GetLastRect(); dropdownPosition.x += dropdownPosition.width; if (GUILayout.Button(new GUIContent(curPropertyValue), EditorStyles.popup)) { InspectorUtils.ShowMenu <PropertyInfo>( property => property.ReflectedType + "/" + property.Name + " : " + property.PropertyType.Name, menuEnabled, property => property.ReflectedType.Name + "." + property.Name == curPropertyValue, property => UpdateProperty( propertyValueSetter, curPropertyValue, property.ReflectedType.Name + "." + property.Name ), bindableProperties, dropdownPosition ); } EditorGUILayout.EndHorizontal(); }
/// <summary> /// Find bindable properties in available view models. /// </summary> public static PropertyInfo[] FindBindableProperties(AbstractMemberBinding target) { return(FindAvailableViewModelTypes(target) .SelectMany(type => type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) .Where(property => property .GetCustomAttributes(false) .Any(attribute => attribute is BindingAttribute) // Filter out properties that don't have [Binding]. ) .ToArray()); }
/// <summary> /// Scan up the hierarchy and find all the types that can be bound to /// a specified MemberBinding. /// </summary> private static IEnumerable <Type> FindAvailableViewModelTypes(AbstractMemberBinding memberBinding) { var foundAtLeastOneBinding = false; var trans = memberBinding.transform; while (trans != null) { var components = trans.GetComponents <MonoBehaviour>(); foreach (var component in components) { // Can't bind to self or null if (component == null || component == memberBinding) { continue; } // Case where a ViewModelBinding is used to bind a non-MonoBehaviour class. var viewModelBinding = component as IViewModelProvider; if (viewModelBinding != null) { var viewModelTypeName = viewModelBinding.GetViewModelTypeName(); // Ignore view model bindings that haven't been set up yet. if (string.IsNullOrEmpty(viewModelTypeName)) { continue; } foundAtLeastOneBinding = true; yield return(GetViewModelType(viewModelBinding.GetViewModelTypeName())); } else if (component.GetType().GetCustomAttributes(typeof(BindingAttribute), false).Any()) { // Case where we are binding to an existing MonoBehaviour. foundAtLeastOneBinding = true; yield return(component.GetType()); } } trans = trans.parent; } if (!foundAtLeastOneBinding) { Debug.LogError("UI binding " + memberBinding.gameObject.name + " must be placed underneath at least one bindable component.", memberBinding); } }
/// <summary> /// Scan up the hierarchy and find all the types that can be bound to /// a specified MemberBinding. /// </summary> public static IEnumerable <Type> GetAvailableViewModelTypes(this AbstractMemberBinding memberBinding) { bool foundAtLeastOneBinding = false; var trans = memberBinding.transform; while (trans != null) { var viewModels = trans.GetComponents <MonoBehaviour>(); foreach (var viewModel in viewModels) { // Can't bind to self if (viewModel == memberBinding) { continue; } // Case where a ViewModelBinding is used to bind a non-MonoBehaviour class. var viewModelBinding = viewModel as IViewModelBinding; if (viewModelBinding != null) { foundAtLeastOneBinding = true; yield return(GetBoundViewType(viewModelBinding)); } else if (viewModel.GetType().GetCustomAttributes(typeof(BindingAttribute), false).Any()) { // Case where we are binding to an existing MonoBehaviour. foundAtLeastOneBinding = true; yield return(viewModel.GetType()); } } // Stop at the top level if (trans.GetComponent <BindingRoot>() != null) { break; } trans = trans.parent; } if (!foundAtLeastOneBinding) { Debug.LogError("UI binding " + memberBinding.gameObject.name + " must be placed underneath at least one bindable component.", memberBinding); } }
/// <summary> /// Shows a dropdown for selecting a property in the UI to bind to. /// </summary> public void ShowViewPropertyMenu( GUIContent label, AbstractMemberBinding targetScript, BindablePropertyInfo[] properties, Action <string> propertyValueSetter, string curPropertyValue, out Type selectedPropertyType ) { var propertyNames = properties .Select(prop => prop.PropertyInfo.ReflectedType.Name + "." + prop.PropertyInfo.Name) .ToArray(); var selectedIndex = Array.IndexOf(propertyNames, curPropertyValue); var content = properties.Select(prop => new GUIContent( prop.PropertyInfo.ReflectedType.Name + "/" + prop.PropertyInfo.Name + " : " + prop.PropertyInfo.PropertyType.Name )) .ToArray(); var newSelectedIndex = EditorGUILayout.Popup(label, selectedIndex, content); if (newSelectedIndex != selectedIndex) { var newSelectedProperty = properties[newSelectedIndex]; UpdateProperty( propertyValueSetter, curPropertyValue, newSelectedProperty.PropertyInfo.ReflectedType.Name + "." + newSelectedProperty.PropertyInfo.Name ); selectedPropertyType = newSelectedProperty.PropertyInfo.PropertyType; } else { if (selectedIndex < 0) { selectedPropertyType = null; return; } selectedPropertyType = properties[selectedIndex].PropertyInfo.PropertyType; } }
/// <summary> /// Scan up the hierarchy and find all the types that can be bound to /// a specified MemberBinding. /// </summary> private static IEnumerable <Type> FindAvailableViewModelTypes(AbstractMemberBinding memberBinding) { var foundAtLeastOneBinding = false; var trans = memberBinding.transform; while (trans != null) { var components = trans.GetComponents <MonoBehaviour>(); foreach (var component in components) { // Can't bind to self or null if (component == null || component == memberBinding) { continue; } // Case where a ViewModelBinding is used to bind a non-MonoBehaviour class. var viewModelBinding = component as IViewModelProvider; if (viewModelBinding != null) { var viewModelTypeName = viewModelBinding.GetViewModelTypeName(); // Ignore view model bindings that haven't been set up yet. if (string.IsNullOrEmpty(viewModelTypeName)) { continue; } var viewModelType = GetViewModelType(viewModelBinding.GetViewModelTypeName()); if (viewModelType == null) { viewModelType = GetViewModelType_Hotfix(viewModelBinding.GetViewModelTypeName()); } if (viewModelType == null) { continue; } foundAtLeastOneBinding = true; yield return(viewModelType); } else if (component.GetType().FullName.Equals("Framework.Hotfix.HotfixMBContainer")) { // Case where a ViewModelBinding is used to bind hotfix class var prop = component.GetType().GetProperty("HotfixName", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public); if (prop == null) { continue; } string viewModelTypeName = (string)prop.GetValue(component); if (string.IsNullOrEmpty(viewModelTypeName)) { continue; } Type viewModelType = GetViewModelType_Hotfix(viewModelTypeName); if (viewModelType == null) { continue; } foundAtLeastOneBinding = true; yield return(viewModelType); } else if (component.GetType().GetCustomAttributes(typeof(BindingAttribute), false).Any()) { // Case where we are binding to an existing MonoBehaviour. foundAtLeastOneBinding = true; yield return(component.GetType()); } } trans = trans.parent; } if (!foundAtLeastOneBinding) { Debug.LogError("UI binding " + memberBinding.gameObject.name + " must be placed underneath at least one bindable component.", memberBinding); } }