/// <summary> /// Checks if inspected contains any targets that equal null, and if so, /// attempts to recover references to them. Any targets that remain null /// after these attempts get removed from the inspected array. /// </summary> /// <returns> True if there were no null targets, or any null targets could be recovered. Returns false if there were unrecoverable null targets. </returns> public bool TryRecoverAnyNullUnityObjects() { bool noUnfixableNullsFound = true; for (int n = inspected.Length - 1; n >= 0; n--) { var target = inspected[n]; if (target == null) { if (UnityObjectExtensions.TryToFixNull(ref target)) { #if DEV_MODE Debug.LogWarning("InspectorState.inspected[" + n + "] (\"" + target.name + "\") was null, but was able to recover it using InstanceID."); #endif continue; } #if DEV_MODE Debug.LogWarning("InspectorState.inspected[" + n + "] (\"" + (ReferenceEquals(target, null) ? "null" : target.name) + "\") was null and unrecoverable. Removing from array"); #endif // if could not recover target, remove null entry from array inspected = inspected.RemoveAt(n); noUnfixableNullsFound = false; } } return(noUnfixableNullsFound); }
/// <summary> /// Invokes the Method, messages the user about it and updates results member if method has a return value. /// </summary> /// <param name="pingIfUnityObject"> True if should "ping" the return value, if the method has one, and it's of type UnityEngine.Object or UnityEngine.Object[], and not null or empty. </param> /// <param name="displayDialogIfNotUnityObject"> True if should display a popup dialog to the user about the results, if method has a return type, and it's not of type UnityEngine.Object or UnityEngine.Object[]. </param> /// <param name="copyToClipboardIfNotPingedOrDialogShown"> True if should copy the method return value to clipboard, if it has one, and if value was not pinged and popup was not displayed. </param> /// <param name="pingIfUnityObject"> True if should select the return value(s), if the method has one, and it's of type UnityEngine.Object or UnityEngine.Object[], and not null or empty. </param> private void Invoke(bool pingIfUnityObject, bool displayDialogIfNotUnityObject, bool copyToClipboardIfNotPingedOrDialogShown, bool selectIfUnityObject) { Inspector.RefreshView(); // make sure repaint gets called immediately string error; bool suppressMessages = pingIfUnityObject || displayDialogIfNotUnityObject || copyToClipboardIfNotPingedOrDialogShown || selectIfUnityObject; Invoke(out error, suppressMessages); if (!hasReturnValue) { #if DEV_MODE && PI_ASSERTATIONS Debug.Assert(!suppressMessages); #endif return; } if (pingIfUnityObject) { var list = new List <Object>(0); if (UnityObjectExtensions.TryExtractObjectReferences(results, ref list)) { DrawGUI.Ping(list.ToArray()); } } if (selectIfUnityObject) { var list = new List <Object>(0); if (UnityObjectExtensions.TryExtractObjectReferences(results, ref list)) { Inspector.Select(list.ToArray()); } } if (displayDialogIfNotUnityObject && !isCoroutine) { TryDisplayDialogForResult(); } else if (copyToClipboardIfNotPingedOrDialogShown) { Clipboard.Copy(result); } if (Event.current != null) { ExitGUIUtility.ExitGUI(); // avoid ArgumentException: Getting control 1's position in a group with only 1 controls when doing repaint } }
/// <inheritdoc/> public void OnProjectOrHierarchyChanged(OnChangedEventSubject changed, ref bool hasNullReferences) { if (changed != OnChangedEventSubject.Project && changed != OnChangedEventSubject.Undefined) { return; } for (int n = targets.Length - 1; n >= 0; n--) { var target = targets[n]; if (target == null) { if (UnityObjectExtensions.TryToFixNull(ref target)) { #if DEV_MODE Debug.LogWarning(ToString() + ".OnHierarchyChanged fixed targets[" + n + "] (\"" + target.name + "\") being null."); #endif continue; } #if DEV_MODE Debug.Log(ToString() + ".OnHierarchyChanged targets[" + n + "] was null and could not be fixed."); #endif hasNullReferences = true; } } #if UNITY_EDITOR if (editor != null && Editors.DisposeIfInvalid(ref editor)) { hasNullReferences = true; // set to true so that drawers get rebuilt, in case e.g. asset paths have changed. } if (assetEditor != null && Editors.DisposeIfInvalid(ref assetEditor)) { hasNullReferences = true; // set to true so that drawers get rebuilt, in case e.g. asset paths have changed. } #endif FetchAssetEditor(); UpdateEditor(); }
private bool TryPingResult() { if (!hasReturnValue) { return(false); } if (!hasResult) { Invoke(); } var list = new List <Object>(0); if (UnityObjectExtensions.TryExtractObjectReferences(results, ref list)) { DrawGUI.Ping(list.ToArray()); } return(true); }
public void RemoveUnloadedAndInvalidTargets() { #if DEV_MODE && DEBUG_REMOVE_INVALID Debug.Log("SelectionHistory.RemoveReferencesToUnloadedOrInvalidScenes()"); #endif int count = history.Count; for (int n = count - 1; n >= 0; n--) { var objs = history[n]; for (int o = objs.Length - 1; o >= 0; o--) { var obj = objs[o]; if (obj == null) { if (!UnityObjectExtensions.TryToFixNull(ref obj)) { #if DEV_MODE && DEBUG_REMOVE_INVALID Debug.Log("Removing history[" + n + "][" + o + "]: because target no longer exists"); #endif objs = objs.RemoveAt(o); } } } if (objs.Length == 0) { history.RemoveAt(n); if (currentIndex > n) { currentIndex--; } #if DEV_MODE && DEBUG_REMOVE_INVALID Debug.Log("Removed history[" + n + "] because none of the targets existed any longer. Index now " + index + " nad history.Count=" + history.Count); #endif } } }
/// <inheritdoc/> public override bool DrawPrefix(Rect position) { if (Event.current.type == EventType.Repaint) { //hide prefix resizer line behind the field DrawGUI.Active.ColorRect(lastDrawPosition, DrawGUI.Active.InspectorBackgroundColor); if (backgroundRect.width > 0f) { GUI.Label(backgroundRect, "", InspectorPreferences.Styles.MethodBackground); } } bool drawBackgroundBehindFoldoutsWas = DrawGUI.drawBackgroundBehindFoldouts; DrawGUI.drawBackgroundBehindFoldouts = false; var prefixDrawRect = PrefixLabelPosition; var clipLabelInsideRect = prefixDrawRect; clipLabelInsideRect.width += clipLabelInsideRect.x; clipLabelInsideRect.x = 0f; prefixDrawRect.y = 0f; //use BeginArea to prevent foldout text clipping past the Button GUILayout.BeginArea(clipLabelInsideRect); bool dirty = base.DrawPrefix(prefixDrawRect); GUILayout.EndArea(); DrawGUI.drawBackgroundBehindFoldouts = drawBackgroundBehindFoldoutsWas; bool selected = Selected; var guiColorWas = GUI.color; bool isInvoking; if (isCoroutine && Application.isPlaying) { if (monoBehaviour != null) { isInvoking = monoBehaviour.IsInvoking(memberInfo.MethodInfo.Name); } else { isInvoking = StaticCoroutine.IsInvoking(memberInfo.MethodInfo.Name); } } else { isInvoking = false; } if (isInvoking) { GUI.color = Color.green; } else if (selected) { var col = preferences.theme.ButtonSelected; GUI.color = col; } if (GUI.Button(buttonRect, buttonLabel, Style)) { DrawGUI.UseEvent(); if (Event.current.button == 0) { dirty = true; Invoke(); Select(ReasonSelectionChanged.ControlClicked); } else if (Event.current.button == 1) { var menu = Menu.Create(); menu.Add(Menu.Item("Invoke", () => Invoke(false, false, false, false))); if (hasReturnValue) { #if !POWER_INSPECTOR_LITE menu.AddSeparator(); menu.Add(Menu.Item("Copy Return Value", () => Invoke(false, false, true, false))); #endif if (UnityObjectExtensions.IsUnityObjectOrUnityObjectCollectionType(Type)) { menu.Add(Menu.Item("Ping Return Value", () => Invoke(true, false, false, false))); menu.Add(Menu.Item("Select Return Value", () => Invoke(false, false, false, true))); } } if (isCoroutine) { menu.AddSeparator(); var monoBehaviour = UnityObject as MonoBehaviour; string methodName = MethodInfo.Name; if (monoBehaviour != null && Application.isPlaying) { menu.Add("Invoke Repeating/Every Second", () => monoBehaviour.InvokeRepeating(MethodInfo.Name, 1f, 1f)); menu.Add("Invoke Repeating/Every 5 Seconds", () => monoBehaviour.InvokeRepeating(MethodInfo.Name, 5f, 5f)); menu.Add("Invoke Repeating/Every 10 Seconds", () => monoBehaviour.InvokeRepeating(MethodInfo.Name, 10f, 10f)); if (monoBehaviour.IsInvoking(methodName)) { menu.Add("Stop Coroutine", () => monoBehaviour.StopCoroutine(MethodInfo.Name)); menu.Add("Cancel Invoke", () => monoBehaviour.CancelInvoke(MethodInfo.Name)); } } } ContextMenuUtility.Open(menu, this); } else if (Event.current.button == 2) { Invoke(true, false, true, false); } } GUI.color = guiColorWas; return(dirty); }