Exemplo n.º 1
0
            //PUBLIC

            /// <summary>
            /// Find the height of a persistent callback object
            /// </summary>
            /// <param name="property">The property that is to have the height calculated for</param>
            /// <returns>Returns the height in pixels that should be reserved for this element</returns>
            public static float GetElementHeight(SerializedProperty property)
            {
                //Get the target objects of the property
                PersistentCallback[] callbacks;
                property.GetPropertyValues(out callbacks);

                //Check that there are values to check
                if (callbacks.Length == 0)
                {
                    return(0f);
                }

                //Get the height that is used for the object/method selection elements
                float height = EditorGUIUtility.singleLineHeight * 2 + (BUFFER_SPACE * 2f);

                //Get the method that is in use by the primary (first) callback
                PersistentMethod primary = PersistentOptionsUtility.GetPersistentMethodFromCallback(callbacks[0]);

                //Check if this method is the same across all selections
                bool isDifferent = false;

                for (int i = 1; i < callbacks.Length; i++)
                {
                    if (primary != PersistentOptionsUtility.GetPersistentMethodFromCallback(callbacks[i]))
                    {
                        isDifferent = true;
                        break;
                    }
                }

                //If the methods are the same then the parameters can be shown
                if (!isDifferent && !callbacks[0].IsDynamic && primary != null)
                {
                    //Calculate the height required for each drawer
                    int ind = 0;
                    foreach (var param in primary.Parameters)
                    {
                        //Get the drawer for the type
                        AParameterDrawer drawer = ParameterDrawers.GetDrawer(primary.ParameterSignature[ind], param.Attribute);

                        //Collate the parameter cache's that are needed for this callback
                        PersistentParameterCache[] current = new PersistentParameterCache[callbacks.Length];
                        for (int i = 0; i < callbacks.Length; i++)
                        {
                            current[i] = callbacks[i].ParameterInfo[ind].ParameterCache;
                        }

                        //Retrieve the height for the drawer
                        height += drawer.GetDrawerHeight(current, param.DisplayLabel) + BUFFER_SPACE;

                        //Increment the progress
                        ++ind;
                    }
                }

                //Return the final completed height
                return(height);
            }
Exemplo n.º 2
0
            /// <summary>
            /// Display the elements of the property within the designated area on the inspector area
            /// </summary>
            /// <param name="position">The position within the inspector that the property should be drawn to</param>
            /// <param name="property">The property that is to be displayed within the inspector</param>
            /// <param name="dynamicTypes">Defines the types that are designated as dynamic types for operation</param>
            /// <param name="forceDirty">An action that can be raised from the generic menus callback to force a dirty of the current elements</param>
            /// <returns>Returns true if an event occurred that caused changes that need to be saved</returns>
            public static bool DrawLayoutElements(Rect position, SerializedProperty property, Type[] dynamicTypes, Action forceDirty)
            {
                //Get the target objects of the property
                PersistentCallback[] callbacks;

                //Check that there are values to check
                if (!property.GetPropertyValues(out callbacks) || callbacks.Length == 0)
                {
                    return(false);
                }

                //Flag if the height of this object needs to be recalculated
                bool elementsModified = false;

                {
                    //Check if the event state is different for the contained callbacks
                    bool isDifferent = false;
                    for (int i = 1; i < callbacks.Length; i++)
                    {
                        if (callbacks[0].EventState != callbacks[i].EventState)
                        {
                            isDifferent = true;
                            break;
                        }
                    }

                    //Display an option to change the event state of the options
                    using (GUIMixer.PushSegment(isDifferent)) {
                        //Begin checking for UI changes
                        EditorGUI.BeginChangeCheck();

                        //Present the option for changing the event state
                        ERuntimeEventState newState = (ERuntimeEventState)EditorGUI.EnumPopup(GetLineOffsetPosition(position, 0, 1f), GUIContent.none, callbacks[0].EventState);

                        //If the state has changed, apply it to all of the callbacks
                        if (EditorGUI.EndChangeCheck())
                        {
                            for (int i = 0; i < callbacks.Length; i++)
                            {
                                callbacks[i].EventState = newState;
                            }
                        }
                    }
                }

                {
                    //Check if the target object is different for the contained callbacks
                    bool isDifferent = false;

                    //Check there are multiple objects to modify
                    if (callbacks.Length > 1)
                    {
                        //Get the target of the initial object
                        UnityEngine.Object baseTarget = GetBaseTarget(callbacks[0].Target);

                        //Compare the base target against the others
                        for (int i = 1; i < callbacks.Length; i++)
                        {
                            if (baseTarget != GetBaseTarget(callbacks[i].Target))
                            {
                                isDifferent = true;
                                break;
                            }
                        }
                    }

                    //Display a target field for the callback operations
                    using (GUIMixer.PushSegment(isDifferent)) {
                        //Begin checking for UI changes
                        EditorGUI.BeginChangeCheck();

                        //Present the option for changing the target object
                        UnityEngine.Object newTarget = EditorGUI.ObjectField(GetLineOffsetPosition(position, 1, .4f), GUIContent.none, callbacks[0].Target, typeof(UnityEngine.Object), true);

                        //If the target changed
                        if (EditorGUI.EndChangeCheck())
                        {
                            //Apply the new target to all contained elements
                            for (int i = 0; i < callbacks.Length; i++)
                            {
                                callbacks[i].Target = newTarget;
                            }

                            //That can cause massive changes
                            elementsModified = true;
                        }
                    }
                }

                //Store the persistent method that is assigned to the primary
                PersistentMethod primaryMethod = PersistentOptionsUtility.GetPersistentMethodFromCallback(callbacks[0]);

                //Store a flag that indicates if the callbacks have different methods
                bool methodsAreDifferent = false;

                //Allow for the modification of selected function if there is a target set
                using (GUILocker.PushSegment(callbacks[0].Target)) {
                    //Retrieve the Content that will be displayed for the function selection selected option
                    GUIContent selectedDisplay;

                    //If a method match could be found, use its display label
                    if (primaryMethod != null)
                    {
                        selectedDisplay = primaryMethod.DisplayLabel;
                    }

                    //Otherwise, method may be missing
                    else
                    {
                        //Try to retrieve the label for the callback
                        string generatedLabel = PersistentOptionsUtility.GetPersistentMethodLabelFromCallback(callbacks[0]);

                        //If the string is empty, no function has been set yet
                        selectedDisplay = (string.IsNullOrEmpty(generatedLabel) ?
                                           NO_SELECTION_LABEL :
                                           new GUIContent(generatedLabel + " " + MISSING_LABEL)
                                           );
                    }

                    //Check there are multiple callbacks to compare against
                    if (callbacks.Length > 1)
                    {
                        //Check if the selected callbacks for the other callbacks is different
                        for (int i = 1; i < callbacks.Length; i++)
                        {
                            if (primaryMethod != PersistentOptionsUtility.GetPersistentMethodFromCallback(callbacks[i]))
                            {
                                methodsAreDifferent = true;
                                break;
                            }
                        }
                    }

                    //Display the dropdown button for selecting the active callback
                    using (GUIMixer.PushSegment(methodsAreDifferent)) {
                        if (EditorGUI.DropdownButton(GetLineOffsetPosition(position, 1, .6f, .4f), selectedDisplay, FocusType.Passive))
                        {
                            //Retrieve the constructed menu
                            GenericMenu optionsMenu = CreateOptionsSelectionMenu(callbacks[0].Target, callbacks[0].IsValid, callbacks[0].IsDynamic, primaryMethod, dynamicTypes,
                                                                                 //Reset function
                                                                                 () => {
                                //Loop through and reset all of the callback methods
                                for (int i = 0; i < callbacks.Length; i++)
                                {
                                    callbacks[i].ResetMethod();
                                }

                                //Reset the primary method
                                primaryMethod       = null;
                                methodsAreDifferent = false;

                                //Values modified, force the calling object to re-calculate this element
                                forceDirty();
                            },
                                                                                 //Assign method function
                                                                                 (target, method, isDynamic) => {
                                //Apply the new method to the option
                                AssignPersistentCallback(callbacks, target, method, isDynamic);

                                //Methods are now the same
                                primaryMethod       = method;
                                methodsAreDifferent = false;

                                //Values modified, force the calling object to re-calculate this element
                                forceDirty();
                            });

                            //Display the various options
                            optionsMenu.DropDown(GetLineOffsetPosition(position, 1, .6f, .4f));
                        }
                    }
                }

                //If the primary method is consistent across the multiple objects, display the values
                if (!methodsAreDifferent && !callbacks[0].IsDynamic && primaryMethod != null)
                {
                    //Store the rect of the last point
                    Rect displayRect = GetLineOffsetPosition(position, 1, 1f);

                    //Loop through all of the parameters for this method
                    int ind = 0;
                    foreach (var param in primaryMethod.Parameters)
                    {
                        //Retrieve the drawer for this parameter
                        AParameterDrawer drawer = ParameterDrawers.GetDrawer(primaryMethod.ParameterSignature[ind], param.Attribute);

                        //Collate the parameter cache's that are needed for this callback
                        PersistentParameterCache[] current = new PersistentParameterCache[callbacks.Length];
                        for (int i = 0; i < callbacks.Length; i++)
                        {
                            current[i] = callbacks[i].ParameterInfo[ind].ParameterCache;
                        }

                        //Setup the display rect for displaying values correctly given the values
                        displayRect.y     += displayRect.height + BUFFER_SPACE;
                        displayRect.height = drawer.GetDrawerHeight(current, param.DisplayLabel);

                        //Draw the elements to the inspector
                        if (drawer.DisplayParameterValue(displayRect, current, param.DisplayLabel) && !elementsModified)
                        {
                            elementsModified = true;
                        }

                        //Increment the current progress through the parameters
                        ++ind;
                    }
                }

                //Return the re-calculate flag
                return(elementsModified);
            }
Exemplo n.º 3
0
            /// <summary>
            /// Populate a Generic Menu with the available persistent methods on the supplied target
            /// </summary>
            /// <param name="target">The target object that should be scanned for raisable methods</param>
            /// <param name="currentIsValid">Flags if the current callback is valid</param>
            /// <param name="currentIsDynamic">Flags if the current persistent callback is a dynamic one</param>
            /// <param name="selectedMethod">The previous method that has been selected for use</param>
            /// <param name="dynamicTypes">The types that are able to be dynamic in their raising of the callback</param>
            /// <param name="resetCallback">A callback that is raised when the no function option is selected for callback(s)</param>
            /// <param name="assignCallback">The callback that will be used to assign the callback values to the callback object</param>
            /// <returns>Returns a Generic Menu that can be displayed to list the available options</returns>
            private static GenericMenu CreateOptionsSelectionMenu(UnityEngine.Object target, bool currentIsValid, bool currentIsDynamic, PersistentMethod selectedMethod, Type[] dynamicTypes, GenericMenu.MenuFunction resetCallback, AssignCallbackDel assignCallback)
            {
                //Create a generic menu that can be used to display possible options
                GenericMenu optionsMenu = new GenericMenu();

                #if UNITY_2018_2_OR_NEWER
                //Toggle the option to allow for the method to be raised on specific components if there are multiple of the same type
                optionsMenu.allowDuplicateNames = true;
                #endif

                //Store a list of the elements that are to have their values displayed
                List <UnityEngine.Object> searchTargets = new List <UnityEngine.Object>(1);

                //Store a reference to the Game Object to show options for
                GameObject searchObject = GetBaseTarget(target) as GameObject;

                //If there is a search object, use that
                if (searchObject != null)
                {
                    //Add the search object itself
                    searchTargets.Add(searchObject);

                    //Grab all of the components attached to the object
                    searchTargets.AddRange(searchObject.GetComponents <Component>());
                }

                //Otherwise, just use the target (Scriptable Assets etc.)
                else
                {
                    searchTargets.Add(target);
                }

                //Add a label for the target object
                optionsMenu.AddDisabledItem(new GUIContent(searchTargets[0].name + " "));
                optionsMenu.AddSeparator(string.Empty);

                //Add the clear option to the menu
                optionsMenu.AddItem(NO_SELECTION_LABEL, !currentIsValid, resetCallback);
                optionsMenu.AddSeparator(string.Empty);

                //Store the signature string for dynamic elements
                string dynamicSignature = (dynamicTypes.Length > 0 ? PersistentOptionsUtility.GenerateSignatureLabelString(dynamicTypes) : string.Empty);

                //Process all of the elements to be searched
                foreach (UnityEngine.Object search in searchTargets)
                {
                    //Retrieve the methods that can be used at this level
                    PersistentMethod[] methods = PersistentOptionsUtility.RetrieveObjectMethods(search);

                    //If there are no methods, skip
                    if (methods.Length == 0)
                    {
                        continue;
                    }

                    //Sort the methods based on their display labels
                    Array.Sort(methods, (left, right) => {
                        //If the property flag differs, sort based on that
                        if (left.IsProperty != right.IsProperty)
                        {
                            return(right.IsProperty.CompareTo(left.IsProperty));
                        }

                        //Otherwise, go alphabetical
                        return(left.DisplayLabel.text.CompareTo(right.DisplayLabel.text));
                    });

                    //Store the starting directory label that will be used for creating the sub directories of options
                    string labelPrefix = search.GetType().Name + "/";

                    //Use a string builder to construct the display names for the different method options
                    StringBuilder sb = new StringBuilder();

                    //Store the target that will be operated on if this option is selected
                    UnityEngine.Object objTarget = search;

                    //Loop through the options to add the dynamic methods
                    if (dynamicTypes.Length > 0)
                    {
                        //Flag if an option was found
                        bool foundOne = false;

                        //Check if any of the options can be used as dynamic types
                        for (int i = 0; i < methods.Length; i++)
                        {
                            //Check the parameter length is the same
                            if (methods[i].ParameterSignature.Length != dynamicTypes.Length)
                            {
                                continue;
                            }

                            //Flag if this is a valid candidate
                            bool valid = true;
                            for (int j = 0; j < dynamicTypes.Length; j++)
                            {
                                if (methods[i].ParameterSignature[j] != dynamicTypes[j])
                                {
                                    valid = false;
                                    break;
                                }
                            }

                            //If the method isn't valid, don't bother
                            if (!valid)
                            {
                                continue;
                            }

                            //Check if this is the first valid option found
                            if (!foundOne)
                            {
                                //Add the initial header element
                                optionsMenu.AddDisabledItem(new GUIContent(labelPrefix + "Dynamic Methods " + dynamicSignature));

                                //Flag one as found
                                foundOne = true;
                            }

                            //Store a collective index for the lambda
                            int ind = i;

                            //Add the option to the menu
                            optionsMenu.AddItem(
                                new GUIContent(
                                    labelPrefix + (methods[i].IsProperty ?
                                                   methods[i].MethodName.Substring(PersistentOptionsUtility.SETTER_PROPERTY_PREFIX.Length) :
                                                   methods[i].MethodName) +
                                    " ",
                                    methods[i].DisplayLabel.tooltip
                                    ),
                                currentIsDynamic && methods[i] == selectedMethod,
                                () => assignCallback(objTarget, methods[ind], true)
                                );
                        }

                        //If an option was found add the ending buffer elements
                        if (foundOne)
                        {
                            optionsMenu.AddDisabledItem(new GUIContent(labelPrefix + " "));
                            optionsMenu.AddDisabledItem(new GUIContent(labelPrefix + "Constant Methods"));
                        }
                    }

                    //Loop through the options to add the persistent options
                    for (int i = 0; i < methods.Length; i++)
                    {
                        //Store a collectible index for the lambda
                        int ind = i;

                        //Clear out the previous string value
                        sb.Length = 0;

                        //Add the current prefix to the entry
                        sb.Append(labelPrefix);

                        //Add the display text to the entry
                        sb.Append(methods[i].DisplayLabel.text);

                        //Check to see if all of the contained entries have drawers
                        bool found = false;

                        //Check if a missing drawer was found
                        for (int j = 0; j < methods[i].Parameters.Length; j++)
                        {
                            if (!ParameterDrawers.HasDrawer(methods[i].ParameterSignature[j], methods[i].Parameters[j].Attribute))
                            {
                                found = true;
                                break;
                            }
                        }

                        //Check for a missing drawer
                        if (found)
                        {
                            sb.Append("\t*");
                        }

                        //Add the option to the menu
                        optionsMenu.AddItem(
                            new GUIContent(sb.ToString(), methods[i].DisplayLabel.tooltip),
                            !currentIsDynamic && methods[i] == selectedMethod,
                            () => assignCallback(objTarget, methods[ind], false)
                            );
                    }
                }

                //Return the constructed menu
                return(optionsMenu);
            }