/// <inheritdoc /> public override object GetValue(int index) { #if DEV_MODE if (hasParameters) { var vDrawer = IndexParameterDrawer.Value; var vMemberInfo = memberInfo.IndexParameters; Debug.Assert(vDrawer.ContentsMatch(vMemberInfo), "IndexParameterDrawer.Value " + StringUtils.ToString(vDrawer) + " != memberInfo.IndexParameters " + StringUtils.ToString(vMemberInfo)); } #endif #if DEV_MODE && DEBUG_INVOKE Debug.Log("Invoking " + memberInfo + " with " + (hasParameters ? "params " + StringUtils.ToString((object)IndexParameterDrawer.Value) : "0 parameters")); #endif if (memberInfo != null && memberInfo.CanRead && !getValueCausedException) { using (var logCatcher = new LogCatcher()) { try { var result = memberInfo.GetValue(index); if (logCatcher.HasMessage && logCatcher.LogType != LogType.Log) { OnErrorOrWarningWhenCallingGetter(logCatcher.Message, logCatcher.LogType); } return(result); } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { throw; } OnExceptionWhenCallingGetter(e); Debug.LogError(ToString() + ".GetValue " + e); return(base.Value); } } } #if DEV_MODE Debug.LogWarning(ToString() + ".GetValue(" + index + ") called but memberInfo.CanRead=" + StringUtils.False); #endif return(base.Value); }
/// <inheritdoc cref="IDrawer.ApplyInVisibleChildren" /> public override void ApplyInVisibleChildren(Action <IDrawer> action) { for (int n = visibleMembers.Length - 1; n >= 0; n--) { try { visibleMembers[n].ApplyInVisibleChildren(action); } catch (Exception e) { #if DEV_MODE Debug.LogError(ToString() + " ApplyInVisibleChildren - visibleMembers[" + n + "] " + e); #endif if (ExitGUIUtility.ShouldRethrowException(e)) { throw; } } } action(this); }
/// <inheritdoc cref="IDrawer.Draw" /> public override bool Draw(Rect position) { #if SAFE_MODE || DEV_MODE var targets = memberInfo.UnityObjects; int targetCount = targets.Length; if (targetCount == 0) { #if DEV_MODE Debug.LogWarning(ToString() + ".Draw() - target count was zero, rebuilding drawer"); #endif InspectorUtility.ActiveInspector.RebuildDrawersIfTargetsChanged(); return(false); } if (targets.ContainsNullObjects()) { #if DEV_MODE Debug.LogWarning(ToString() + ".Draw() - target was null, rebuilding drawer"); #endif InspectorUtility.ActiveInspector.RebuildDrawersIfTargetsChanged(); return(false); } #endif var positionWithoutMargins = position; bool dirty = false; GenerateControlId(); if (Event.current.type == EventType.Layout) { OnLayoutEvent(position); } #if !DRAW_LABEL_RESIZE_CONTROL DrawGUI.Active.ColorRect(position, DrawGUI.Active.InspectorBackgroundColor); #endif position.height -= 2f; position.y += 1f; position.width -= DrawGUI.RightPadding; float labelWidthWas = EditorGUIUtility.labelWidth; float fieldWidthWas = EditorGUIUtility.fieldWidth; float leftPadding = DrawGUI.LeftPadding; int labelRightPadding = (int)(DrawGUI.MiddlePadding + DrawGUI.MiddlePadding); position.x += leftPadding; position.width -= leftPadding; //always use wide mode for properties because it works better with the prefix width control #if ALWAYS_USE_WIDEMODE bool wideModeWas = EditorGUIUtility.wideMode; EditorGUIUtility.wideMode = true; #endif EditorStyles.label.padding.right = labelRightPadding; GUILayout.BeginArea(positionWithoutMargins); { position.y -= positionWithoutMargins.y; position.x -= positionWithoutMargins.x; EditorGUI.BeginChangeCheck(); { DrawerUtility.BeginInputField(this, controlId, ref editField, ref focusField, memberInfo == null ? false : memberInfo.MixedContent); { var serializedProperty = SerializedProperty; if (serializedProperty == null) { // NOTE: This can happen during Remove Component for some reason #if DEV_MODE Debug.LogError(ToString() + ".Draw - SerializedProperty was null (parent=" + StringUtils.ToString(parent) + ") so can't use EditorGUI.PropertyField"); #endif EditorGUI.PrefixLabel(position, label); } else { bool editingTextFieldWas; EventType eventType; KeyCode keyCode; CustomEditorUtility.BeginPropertyDrawer(out editingTextFieldWas, out eventType, out keyCode); { // fix needed or foldouts will be drawn at incorrect positions var leftMarginWas = EditorStyles.foldout.margin.left; EditorStyles.foldout.margin.left = -12; try { EditorGUI.PropertyField(position, serializedProperty, label, serializedProperty.isExpanded); } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { EditorStyles.foldout.margin.left = leftMarginWas; throw; } #if DEV_MODE Debug.LogWarning(ToString() + " " + e); #endif } EditorStyles.foldout.margin.left = leftMarginWas; } CustomEditorUtility.EndPropertyDrawer(editingTextFieldWas, eventType, keyCode); bool editingTextFieldIs = EditorGUIUtility.editingTextField; if (editingTextFieldIs != editingTextFieldWas) { DrawGUI.EditingTextField = editingTextFieldIs; } } } DrawerUtility.EndInputField(); } if (EditorGUI.EndChangeCheck()) { GUI.changed = true; SerializedProperty.serializedObject.ApplyModifiedProperties(); dirty = true; } } GUILayout.EndArea(); #if ALWAYS_USE_WIDEMODE EditorGUIUtility.wideMode = wideModeWas; #endif EditorStyles.label.padding.right = 2; EditorGUIUtility.labelWidth = labelWidthWas; EditorGUIUtility.fieldWidth = fieldWidthWas; return(dirty); }
/// <summary> This should be called by the IInspectorDrawer of the inspector during every OnGUI event. </summary> /// <param name="inspectorDimensions"> The position and bounds for where the inspecto should be drawn. </param> /// <param name="anyInspectorPartMouseovered"> True if any inspector part is currently mouseovered. </param> public override void OnGUI(Rect inspectorDimensions, bool anyInspectorPartMouseovered) { UnityEngine.Profiling.Profiler.BeginSample("OnGUI"); #if DEV_MODE && DEBUG_CLICK var e = Event.current; if (e.rawType == EventType.MouseDown) { Debug.Log(StringUtils.ToColorizedString(ToString() + " Event=", e, ", e.type=", e.type, ", button=", e.button, ", mousePos=", e.mousePosition, ", GUIUtility.hotControl=", GUIUtility.hotControl)); } #endif //this can happen e.g. if the preferences file gets reimported due to being altered outside of Unity if (Preferences == null) { Preferences = GetPreferences(); } #if DEV_MODE && DEBUG_MOUSEOVERED_PART if (State.drawer.VisibleMembers.Length > 0 && DrawGUI.IsUnityObjectDrag) { Debug.Log(StringUtils.ToColorizedString(ToString(), ".OnGUI with mouseoveredPart=", MouseoveredPart, ", Event=" + StringUtils.ToString(Event.current), ", ignoreAllMouseInputs=", InspectorDrawer.Manager.IgnoreAllMouseInputs, "´, ObjectPickerIsOpen=", ObjectPicker.IsOpen, ", anyInspectorPartMouseovered=", anyInspectorPartMouseovered, ", InspectorDrawer.MouseIsOver=", InspectorDrawer.MouseIsOver, ", DrawGUI.CanRequestMousePosition=", Cursor.CanRequestLocalPosition)); } #endif InspectorUtility.BeginInspector(this, ref anyInspectorPartMouseovered); Rect toolbarRect; Rect viewportRect; Rect previewAreaRect; GetDrawPositions(inspectorDimensions, out toolbarRect, out viewportRect, out previewAreaRect); // trying to fix a bug where the default inspector layout gets wacky if both it and this window are open // by making sure all values that could affect it are restored back to normal // var indentLevelWas = EditorGUI.indentLevel; #if UNITY_EDITOR var labelWidthWas = EditorGUIUtility.labelWidth; #endif var matrixWas = GUI.matrix; var currentEvent = Event.current; switch (currentEvent.type) { case EventType.Layout: State.nextUpdateCachedValues--; if (State.nextUpdateCachedValues <= 0) { UpdateCachedValuesFromFields(); } OnCursorPositionOrLayoutChanged(); break; case EventType.MouseMove: case EventType.MouseDrag: case EventType.DragUpdated: if (IgnoreViewportMouseInputs()) { #if DEV_MODE //Debug.Log("ignoring "+ currentEvent.type+"..."); #endif break; } OnCursorPositionOrLayoutChanged(); InspectorDrawer.RefreshView(); break; } bool dirty; try { dirty = DrawViewport(viewportRect); } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { NowDrawingPart = InspectorPart.None; DrawGUI.IndentLevel = 0; #if UNITY_EDITOR EditorGUIUtility.labelWidth = labelWidthWas; #endif GUI.skin = null; GUI.matrix = matrixWas; throw; } #if DEV_MODE Debug.LogWarning(ToString() + " " + e); #endif dirty = true; } #if !POWER_INSPECTOR_LITE NowDrawingPart = InspectorPart.Toolbar; { Toolbar.Draw(toolbarRect); } #endif NowDrawingPart = InspectorPart.Other; //TO DO: Move to EndInspector if these are needed? //trying to fix a bug where the default inspector layout gets wacky if both it and this window are open //by making sure all values that could affect it are restored back to normal DrawGUI.IndentLevel = 0; #if UNITY_EDITOR EditorGUIUtility.labelWidth = labelWidthWas; #endif GUI.skin = null; GUI.matrix = matrixWas; if (dirty) { InspectorDrawer.RefreshView(); } InspectorUtility.EndInspector(this); UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> This should be called by the IInspectorDrawer of the inspector during every OnGUI event. </summary> /// <param name="inspectorDimensions"> The position and bounds for where the inspecto should be drawn. </param> /// <param name="anyInspectorPartMouseovered"> True if any inspector part is currently mouseovered. </param> public override void OnGUI(Rect inspectorDimensions, bool anyInspectorPartMouseovered) { UnityEngine.Profiling.Profiler.BeginSample("OnGUI"); #if DEV_MODE && DEBUG_CLICK var ev = Event.current; if (ev.rawType == EventType.MouseDown) { Debug.Log(StringUtils.ToColorizedString(ToString() + " Event=", ev, ", e.type=", ev.type, ", button=", ev.button, ", mousePos=", ev.mousePosition, ", GUIUtility.hotControl=", GUIUtility.hotControl)); } #endif #if DEV_MODE && PI_ASSERTATIONS if (inspectorDimensions.width <= 0f) { Debug.LogError(GetType().Name + ".OnGUI inspectorDimensions.width <= 0f: " + inspectorDimensions); } #endif //this can happen e.g. if the preferences file gets reimported due to being altered outside of Unity if (Preferences == null) { Preferences = GetPreferences(); } #if DEV_MODE && DEBUG_MOUSEOVERED_PART if (State.drawer.VisibleMembers.Length > 0 && DrawGUI.IsUnityObjectDrag) { Debug.Log(StringUtils.ToColorizedString(ToString(), ".OnGUI with mouseoveredPart=", MouseoveredPart, ", Event=" + StringUtils.ToString(Event.current), ", ignoreAllMouseInputs=", InspectorDrawer.Manager.IgnoreAllMouseInputs, "´, ObjectPickerIsOpen=", ObjectPicker.IsOpen, ", anyInspectorPartMouseovered=", anyInspectorPartMouseovered, ", InspectorDrawer.MouseIsOver=", InspectorDrawer.MouseIsOver, ", DrawGUI.CanRequestMousePosition=", Cursor.CanRequestLocalPosition)); } #endif InspectorUtility.BeginInspector(this, ref anyInspectorPartMouseovered); Rect toolbarRect; Rect viewportRect; Rect previewAreaRect; GetDrawPositions(inspectorDimensions, out toolbarRect, out viewportRect, out previewAreaRect); // trying to fix a bug where the default inspector layout gets wacky if both it and this window are open // by making sure all values that could affect it are restored back to normal // var indentLevelWas = EditorGUI.indentLevel; var labelWidthWas = EditorGUIUtility.labelWidth; var matrixWas = GUI.matrix; var currentEvent = Event.current; switch (currentEvent.type) { case EventType.Layout: State.nextUpdateCachedValues--; if (State.nextUpdateCachedValues <= 0) { UpdateCachedValuesFromFields(); } OnCursorPositionOrLayoutChanged(); break; case EventType.MouseMove: case EventType.MouseDrag: case EventType.DragUpdated: if (IgnoreViewportMouseInputs()) { #if DEV_MODE //Debug.Log("ignoring "+ currentEvent.type+"..."); #endif break; } OnCursorPositionOrLayoutChanged(); InspectorDrawer.RefreshView(); break; } bool dirty; try { dirty = DrawViewport(viewportRect); } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { NowDrawingPart = InspectorPart.None; DrawGUI.IndentLevel = 0; EditorGUIUtility.labelWidth = labelWidthWas; GUI.skin = null; GUI.matrix = matrixWas; throw; } #if DEV_MODE Debug.LogWarning(ToString() + " " + e); #endif // Always throw ExitGUI exception if exceptions were caught to avoid GUI Layout warnings. ExitGUIUtility.ExitGUI(); return; } #if !POWER_INSPECTOR_LITE NowDrawingPart = InspectorPart.Toolbar; { Toolbar.Draw(toolbarRect); #if UNITY_2019_3_OR_NEWER Color lineColor; if (DrawGUI.IsProSkin) { lineColor = Preferences.theme.ComponentSeparatorLine; } else { lineColor = new Color32(153, 153, 153, 255); } var lineRect = toolbarRect; lineRect.height = 1f; lineRect.y = toolbarRect.height; DrawGUI.DrawLine(lineRect, lineColor); #endif } #endif NowDrawingPart = InspectorPart.Other; try { if (DrawPreviewArea) { NowDrawingPart = InspectorPart.PreviewArea; { previewDrawer.Draw(previewAreaRect); } NowDrawingPart = InspectorPart.Other; } } #if DEV_MODE catch (ArgumentException e) // GUILayout: Mismatched LayoutGroup.repaint { Debug.LogError(StringUtils.ToString(Event.current) + " " + e + "\nEvent=" + StringUtils.ToString(Event.current)); #else catch (ArgumentException) { #endif // new test to avoid GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced. NowDrawingPart = InspectorPart.None; ExitGUIUtility.ExitGUI(); } //TO DO: Move to EndInspector if these are needed? //trying to fix a bug where the default inspector layout gets wacky if both it and this window are open //by making sure all values that could affect it are restored back to normal DrawGUI.IndentLevel = 0; EditorGUIUtility.labelWidth = labelWidthWas; GUI.skin = null; GUI.matrix = matrixWas; if (dirty) { InspectorDrawer.RefreshView(); } InspectorUtility.EndInspector(this); UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// Invokes the Method, returns method return value and outputs possible error descriptions encountered. /// </summary> /// <param name="index"> Index of target on which to invoke method. </param> /// <param name="error"> [out] True if should display message to user about invoking or possible exceptions. </param> protected object GetValue(int index, [NotNull] out string error) { // generics and parameter info is needed from members // so build them if they have not yet been built // (i.e. if MethodDrawer has yet to be unfolded) if (memberBuildState == MemberBuildState.BuildListGenerated) { BuildMembers(); } bool runAsCoroutine; MethodInfo methodInfo; if (isGeneric) { runAsCoroutine = false; try { var genericTypes = GenericsDrawer.Value; #if DEV_MODE && DEBUG_INVOKE Debug.Log("Making generic method of " + MethodInfo.Name + " from " + genericTypes.Length + " generic types: " + StringUtils.ToString(genericTypes)); #endif //needed? if (genericTypes.Length == 1) { methodInfo = MethodInfo.MakeGenericMethod(genericTypes[0]); } //needed? else if (genericTypes.Length == 2) { methodInfo = MethodInfo.MakeGenericMethod(genericTypes[0], genericTypes[1]); } else { methodInfo = MethodInfo.MakeGenericMethod(genericTypes); } } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { throw; } error = e.ToString(); return(DefaultValue()); } } else { try { methodInfo = MethodInfo; } catch (Exception e) { error = e.ToString(); return(DefaultValue()); } if (isCoroutine) { if (!Inspector.Preferences.askAboutStartingCoroutines) { runAsCoroutine = true; } else { switch (DrawGUI.Active.DisplayDialog("Invoke as coroutine?", "The method return type is IEnumerable. Would you like to start it as a coroutine?", "Start As Coroutine", "Invoke As Normal Method", "Cancel")) { case 0: runAsCoroutine = true; break; case 1: runAsCoroutine = false; break; case 2: ExitGUIUtility.ExitGUI(); error = ""; return(result); default: #if DEV_MODE throw new IndexOutOfRangeException(); #else runAsCoroutine = false; break; #endif } } } else { runAsCoroutine = false; } } object returnValue; var parameters = hasParameters ? ParameterDrawer.Value : null; #if DEV_MODE && PI_ASSERTATIONS if (hasParameters && (parameters == null || parameters.Length == 0)) { Debug.LogError("hasParameters was " + StringUtils.True + " but params was " + StringUtils.ToString(parameters)); } #endif try { #if DEV_MODE && DEBUG_INVOKE Debug.Log("Invoking method " + methodInfo.Name + " with " + (parameters == null ? "" : parameters.Length + " ") + "params=" + StringUtils.ToString(parameters) + ", hasParameters=" + StringUtils.ToColorizedString(hasParameters) + ", runAsCoroutine=" + runAsCoroutine); #endif // this can be null for static methods var fieldOwner = memberInfo.GetFieldOwner(index); //get return value by invoking method with current parameter values returnValue = methodInfo.Invoke(fieldOwner, parameters); if (runAsCoroutine) { #if UNITY_EDITOR if (!Application.isPlaying) { StaticCoroutine.StartCoroutineInEditMode((IEnumerator)returnValue); } else if (monoBehaviour != null) #else if (monoBehaviour != null) #endif { monoBehaviour.StartCoroutine((IEnumerator)returnValue); } else { StaticCoroutine.StartCoroutine((IEnumerator)returnValue); } } if (hasParameters) { //update parameter Drawer with values of parameters so that changes made to parameters //(e.g. via ref and out) get shown in the ParameterDrawer var paramMembers = ParameterDrawer.Members; int paramMembersCount = paramMembers.Length; for (int n = ParameterDrawer.parameterInfos.Length - 1; n >= 0; n--) { var paramInfo = ParameterDrawer.parameterInfos[n]; if (paramInfo.ParameterType.IsByRef && paramMembersCount > n) { var memb = ParameterDrawer.Members[n]; if (memb.Type == paramInfo.ParameterType.GetElementType()) { #if DEV_MODE && DEBUG_INVOKE Debug.Log("param #" + n + " \"" + memb.Name + "\" value: " + StringUtils.ToString(parameters[n])); #endif memb.SetValue(parameters[n]); } } } } } catch (Exception e) { if (Inspector.Preferences.messageDisplayMethod == MessageDisplayMethod.None) { Debug.LogError(e); } else { InspectorUtility.ActiveInspector.Message(e.ToString(), null, MessageType.Error); } returnValue = DefaultValue(); } #if CALL_ON_VALIDATE if (!methodInfo.IsStatic && methodInfo.GetCustomAttributes(typeof(PureAttribute), false).Length == 0) { OnValidateHandler.CallForTargets(UnityObjects); } #endif error = ""; return(returnValue); }
/// <inheritdoc cref="IDrawer.Draw" /> public override bool Draw(Rect position) { var positionWithoutMargins = position; if (Event.current.type == EventType.Layout) { OnLayoutEvent(position); } #if !DRAW_LABEL_RESIZE_CONTROL DrawGUI.Active.ColorRect(position, DrawGUI.Active.InspectorBackgroundColor); #endif position.height -= 2f; position.y += 1f; position.width -= DrawGUI.RightPadding; float labelWidthWas = EditorGUIUtility.labelWidth; float fieldWidthWas = EditorGUIUtility.fieldWidth; float leftPadding = DrawGUI.LeftPadding; int labelRightPadding = (int)(DrawGUI.MiddlePadding + DrawGUI.MiddlePadding); position.x += leftPadding; position.width -= leftPadding; //always use wide mode for properties because it works better with the prefix width control #if ALWAYS_USE_WIDEMODE bool wideModeWas = EditorGUIUtility.wideMode; EditorGUIUtility.wideMode = true; #endif EditorStyles.label.padding.right = labelRightPadding; GUILayout.BeginArea(positionWithoutMargins); { position.y = position.y - positionWithoutMargins.y; position.x = position.x - positionWithoutMargins.x; try { decoratorDrawer.OnGUI(position); } catch (Exception e) { if (ExitGUIUtility.ShouldRethrowException(e)) { throw; } #if DEV_MODE Debug.LogWarning(ToString() + " " + e); #endif } } GUILayout.EndArea(); #if ALWAYS_USE_WIDEMODE EditorGUIUtility.wideMode = wideModeWas; #endif EditorStyles.label.padding.right = 2; EditorGUIUtility.labelWidth = labelWidthWas; EditorGUIUtility.fieldWidth = fieldWidthWas; return(false); }
/// <inheritdoc/> public void OnLayout() { // Swap onNextLayoutDelayed with an empty list before invoking actions in onNextLayout or // onNextLayoutDelayed. This is to avoid possible infinite loops and other problems if an // invoked action should add new actions to onNextLayoutDelayed. var applyTargeted = onNextLayoutDelayed; onNextLayoutDelayed = onNextLayoutDelayedSwapTarget; onNextLayoutDelayedSwapTarget = applyTargeted; #if DEV_MODE && PI_ASSERTATIONS Debug.Assert(onNextLayoutDelayed.Count == 0); #endif if (onNextLayout != null) { #if DEV_MODE && DEBUG_ON_NEXT_LAYOUT_DETAILED Debug.Log("Applying OnNextLayout Action: " + StringUtils.ToString(onNextLayout)); #endif // Copy onNextLayout to method-level variable and set it to null // before invoking the action. This is to avoid possible infinite loops // and other problems if an invoked action should add new actions to onNextLayout. //var apply = onNextLayout; //onNextLayout = null; //apply(); var invocationList = onNextLayout.GetInvocationList(); onNextLayout = null; Exception exception = null; for (int n = 0, count = invocationList.Length; n < count; n++) { try { var invoke = invocationList[n] as Action; invoke(); } catch (Exception e) { #if DEV_MODE if (!ExitGUIUtility.IsExitGUIException(exception)) { Debug.LogError(e); } #endif exception = e; } } if (exception != null && ExitGUIUtility.ShouldRethrowException(exception)) { throw exception; } } int applyTargetedCount = applyTargeted.Count; if (applyTargetedCount > 0) { for (int n = 0; n < applyTargetedCount; n++) { var apply = applyTargeted.Dequeue(); #if DEV_MODE && DEBUG_ON_NEXT_LAYOUT_DETAILED Debug.Log("Applying OnNextLayout DelayedAction: " + StringUtils.ToString(apply)); #endif // Only invoke targeted action if target drawers instance still exist. apply.InvokeIfInstanceReferenceIsValid(); } #if DEV_MODE && PI_ASSERTATIONS Debug.Assert(applyTargeted.Count == 0); #endif } if (keyHeldDown != KeyCode.None) { GUI.changed = true; if (Platform.Time > sendNextHoldEventAt) { sendNextHoldEventAt = Platform.Time + KeyHoldSendEventInterval; OnKeyDown(Event.KeyboardEvent(keyHeldDownName)); } } }