void CreatePreset(Object target) { var preset = new Preset(target); var path = CreatePresetDialog(ref preset, target); if (!string.IsNullOrEmpty(path)) { // If the asset already exist, we need to make sure we keep the same PPtr valid in memory. // To ensure that, we use CopySerialized on the Asset instance instead of erasing the asset with CreateAsset. var oldPreset = AssetDatabase.LoadAssetAtPath <Preset>(path); if (oldPreset != null) { EditorUtility.CopySerialized(preset, oldPreset); // replace name because it was erased by the CopySerialized oldPreset.name = System.IO.Path.GetFileNameWithoutExtension(path); AssetDatabase.SaveAssetIfDirty(oldPreset); // If the preset is opened in any inspectors/property windows, rebuild them since the preset has been overwritten var propertyEditors = Resources.FindObjectsOfTypeAll <PropertyEditor>().Where(pe => { var editor = InspectorWindowUtils.GetFirstNonImportInspectorEditor(pe.tracker.activeEditors); return(editor != null && editor.targets.Any(o => o == oldPreset)); }); foreach (var pe in propertyEditors) { pe.tracker.ForceRebuild(); } } else { AssetDatabase.CreateAsset(preset, path); } } GUIUtility.ExitGUI(); }
private VisualElement CreateIMGUIInspectorFromEditor(SerializedObject serializedObject, Editor editor, bool reuseIMGUIContainer) { if ((mode & (Mode.IMGUICustom | Mode.IMGUIDefault)) == 0) { return(null); } if ((mode & Mode.IMGUICustom) > 0 && (mode & Mode.IMGUIDefault) == 0 && editor is GenericInspector) { return(null); } if ((mode & Mode.IMGUICustom) == 0 && (mode & Mode.IMGUIDefault) > 0 && !(editor is GenericInspector)) { editor = ScriptableObject.CreateInstance <GenericInspector>(); editor.hideFlags = HideFlags.HideAndDontSave; editor.InternalSetTargets(new[] { serializedObject.targetObject }); } if (editor is GenericInspector) { AddToClassList(iMGUIDefaultVariantUssClassName); if ((mode & Mode.DebugMod) > 0) { AddToClassList(debugVariantUssClassName); editor.inspectorMode = InspectorMode.Debug; } else if ((mode & Mode.DebugInternalMod) > 0) { AddToClassList(debugInternalVariantUssClassName); editor.inspectorMode = InspectorMode.DebugInternal; } } else { AddToClassList(iMGUICustomVariantUssClassName); } IMGUIContainer inspector; // Reusing the existing IMGUIContainer allows us to re-use the existing gui state, when we are drawing the same inspector this will let us keep the same control ids if (reuseIMGUIContainer && m_IMGUIContainer != null) { inspector = m_IMGUIContainer; } else { inspector = new IMGUIContainer(); } m_IgnoreOnInspectorGUIErrors = false; inspector.onGUIHandler = () => { // It's possible to run 2-3 frames after the tracker of this inspector window has // been recreated, and with it the Editor and its SerializedObject. One example of // when this happens is when the Preview window is detached from a *second* instance // of an InspectorWindow and re-attached. // // This is only a problem for the *second* (or third, forth, etc) instance of // the InspectorWindow because only the first instance can use the // ActiveEditorTracker.sharedTracker in InspectorWindow.CreateTracker(). The // other instances have to create a new tracker...each time. // // Not an ideal solution, but basically we temporarily hold the printing to console // for errors for which GUIUtility.ShouldRethrowException(e) returns false. // The errors that may occur during this brief "bad state" are SerializedProperty // errors. If the custom Editor created and remembered references to some // SerializedProperties during its OnEnable(), those references will be invalid // when the tracker is refreshed, until this GUIHandler is reassigned. This fix // just ignores those errors. // // We don't simply early return here because that can break some tests that // rely heavily on yields and timing of UI redraws. Yes.. // // case 1119612 if (editor.m_SerializedObject == null) { editor.Repaint(); m_IgnoreOnInspectorGUIErrors = true; } if ((editor.target == null && !GenericInspector.ObjectIsMonoBehaviourOrScriptableObject(editor.target)) || !editor.serializedObject.isValid) { return; } EditorGUIUtility.ResetGUIState(); using (new EditorGUI.DisabledScope(!editor.IsEnabled())) { var genericEditor = editor as GenericInspector; if (genericEditor != null) { switch (mode) { case Mode.Normal: genericEditor.inspectorMode = InspectorMode.Normal; break; case Mode.Default: genericEditor.inspectorMode = InspectorMode.Debug; break; case Mode.Custom: genericEditor.inspectorMode = InspectorMode.DebugInternal; break; case Mode.IMGUI: break; } } //set the current PropertyHandlerCache to the current editor ScriptAttributeUtility.propertyHandlerCache = editor.propertyHandlerCache; var originalHierarchyMode = EditorGUIUtility.hierarchyMode; EditorGUIUtility.hierarchyMode = true; var originalWideMode = SetWideModeForWidth(inspector); GUIStyle editorWrapper = (editor.UseDefaultMargins() ? EditorStyles.inspectorDefaultMargins : GUIStyle.none); try { GUI.changed = false; using (new InspectorWindowUtils.LayoutGroupChecker()) { EditorGUILayout.BeginVertical(editorWrapper); { // we have no guarantees regarding what happens in the try/catch block below, // so we need to save state here and restore it afterwards, // the natural thing to do would be using SavedGUIState, // but it implicitly resets keyboards bindings and it breaks functionality. // We have identified issues with layout so we just save that for the time being. var layoutCache = new GUILayoutUtility.LayoutCache(GUILayoutUtility.current); try { var rebuildOptimizedGUIBlocks = GetRebuildOptimizedGUIBlocks(editor.target); rebuildOptimizedGUIBlocks |= editor.isInspectorDirty; float height; if (editor.GetOptimizedGUIBlock(rebuildOptimizedGUIBlocks, visible, out height)) { var contentRect = GUILayoutUtility.GetRect(0, visible ? height : 0); // Layout events are ignored in the optimized code path // The exception is when we are drawing a GenericInspector, they always use the optimized path and must therefore run at least one layout calculation in it if (Event.current.type == EventType.Layout && !(editor is GenericInspector)) { return; } InspectorWindowUtils.DrawAddedComponentBackground(contentRect, editor.targets); // Draw content if (visible) { GUI.changed = false; editor.OnOptimizedInspectorGUI(contentRect); } } else { InspectorWindowUtils.DrawAddedComponentBackground(contentRect, editor.targets); editor.OnInspectorGUI(); } } catch (Exception e) { if (GUIUtility.ShouldRethrowException(e)) { throw; } if (!m_IgnoreOnInspectorGUIErrors) { Debug.LogException(e); } } finally { GUILayoutUtility.current = layoutCache; } } EditorGUILayout.EndVertical(); } } finally { if (GUI.changed) { // This forces a relayout of all imguicontainers in this inspector window. // fixes part of case 1148706 var element = inspector.GetFirstAncestorOfType <EditorElement>(); if (element != null) { EditorElement.InvalidateIMGUILayouts(element.parent); } } EditorGUIUtility.wideMode = originalWideMode; EditorGUIUtility.hierarchyMode = originalHierarchyMode; } } }; inspector.style.overflow = Overflow.Visible; m_IMGUIContainer = inspector; if (!(editor is GenericInspector)) { inspector.AddToClassList(customInspectorUssClassName); } inspector.AddToClassList(iMGUIContainerUssClassName); AddToClassList(iMGUIInspectorVariantUssClassName); return(inspector); }
void HeaderOnGUI() { var editors = PopulateCache(); if (!IsEditorValid()) { SetElementVisible(m_InspectorElement, false); return; } var target = editor.target; if ((target.hideFlags & HideFlags.HideInInspector) == HideFlags.HideInInspector) { return; } // Avoid drawing editor if native target object is not alive, unless it's a MonoBehaviour/ScriptableObject // We want to draw the generic editor with a warning about missing/invalid script // Case 891450: // - ActiveEditorTracker will automatically create editors for materials of components on tracked game objects // - UnityEngine.UI.Mask will destroy this material in OnDisable (e.g. disabling it with the checkbox) causing problems when drawing the material editor if (target == null && !NativeClassExtensionUtilities.ExtendsANativeType(target)) { SetElementVisible(m_InspectorElement, false); return; } m_WasVisible = inspectorWindow.WasEditorVisible(editors, m_EditorIndex, target); GUIUtility.GetControlID(target.GetInstanceID(), FocusType.Passive); EditorGUIUtility.ResetGUIState(); if (editor.target is AssetImporter) { inspectorWindow.editorsWithImportedObjectLabel.Add(m_EditorIndex + 1); } //set the current PropertyHandlerCache to the current editor ScriptAttributeUtility.propertyHandlerCache = editor.propertyHandlerCache; using (new InspectorWindowUtils.LayoutGroupChecker()) { m_DragRect = DrawEditorHeader(editors, target, ref m_WasVisible); } if (GUI.changed) { // If the header changed something, we must trigger a layout calculating on imgui children // Fixes Material editor toggling layout issues (case 1148706) InvalidateIMGUILayouts(this); } if (m_WasVisible != IsElementVisible(m_InspectorElement)) { SetElementVisible(m_InspectorElement, m_WasVisible); } UpdateInspectorVisibility(); var multiEditingSupported = inspectorWindow.IsMultiEditingSupported(editor, target); if (!multiEditingSupported && m_WasVisible) { GUILayout.Label("Multi-object editing not supported.", EditorStyles.helpBox); return; } InspectorWindowUtils.DisplayDeprecationMessageIfNecessary(editor); // Reset dirtiness when repainting if (Event.current.type == EventType.Repaint) { editor.isInspectorDirty = false; } bool excludedClass = InspectorWindowUtils.IsExcludedClass(target); if (excludedClass) { EditorGUILayout.HelpBox( "The module which implements this component type has been force excluded in player settings. This object will be removed in play mode and from any builds you make.", MessageType.Warning); } if (IsElementVisible(m_InspectorElement)) { m_ContentRect = m_InspectorElement.layout; } else { Rect r = m_Header.layout; r.y = r.y + r.height - 1; r.height = kFooterDefaultHeight; m_ContentRect = r; } }