Ejemplo n.º 1
0
        private Tuple <PropertyWrapper, CableProperties.Direction, object> OnPropertyGUI(CableProperties.Direction dir,
                                                                                         CableProperties properties)
        {
            Tuple <PropertyWrapper, CableProperties.Direction, object> changed = null;
            var data = EditorData.Instance.GetData(properties, "CableProperty" + dir.ToString());

            if (InspectorGUI.Foldout(data, GUI.MakeLabel(dir.ToString())))
            {
                using (InspectorGUI.IndentScope.Single) {
                    var wrappers = PropertyWrapper.FindProperties <CableProperty>(System.Reflection.BindingFlags.Instance |
                                                                                  System.Reflection.BindingFlags.Public);
                    foreach (var wrapper in wrappers)
                    {
                        if (wrapper.GetContainingType() == typeof(float) && InspectorEditor.ShouldBeShownInInspector(wrapper.Member))
                        {
                            var value = EditorGUILayout.FloatField(InspectorGUI.MakeLabel(wrapper.Member),
                                                                   wrapper.Get <float>(properties[dir]));
                            if (UnityEngine.GUI.changed)
                            {
                                changed = new Tuple <PropertyWrapper, CableProperties.Direction, object>(wrapper, dir, value);
                                UnityEngine.GUI.changed = false;
                            }
                        }
                    }
                }
            }
            return(changed);
        }
Ejemplo n.º 2
0
        public override void OnPostTargetMembersGUI()
        {
            if (IsMultiSelect)
            {
                return;
            }

            var shapeVisual = ShapeVisual.Find(Shape);

            if (shapeVisual == null)
            {
                return;
            }

            var materials = shapeVisual.GetMaterials();

            if (materials.Length > 1)
            {
                var names = (from renderer in shapeVisual.GetComponentsInChildren <MeshRenderer>()
                             from material in renderer.sharedMaterials
                             select renderer.name).ToArray();

                var distinctMaterials = materials.Distinct().ToArray();
                var isExtended        = false;
                if (distinctMaterials.Length == 1)
                {
                    isExtended = ShapeVisualMaterialGUI("Common Render Material",
                                                        distinctMaterials[0],
                                                        newMaterial => shapeVisual.SetMaterial(newMaterial));
                }
                else
                {
                    isExtended = InspectorGUI.Foldout(EditorData.Instance.GetData(Shape, "Render Materials"),
                                                      GUI.MakeLabel("Render Materials"));
                }

                if (isExtended)
                {
                    using (InspectorGUI.IndentScope.Single)
                        for (int i = 0; i < materials.Length; ++i)
                        {
                            ShapeVisualMaterialGUI(names[i],
                                                   materials[i],
                                                   newMaterial => shapeVisual.ReplaceMaterial(i, newMaterial));
                        }
                }
            }
            else if (materials.Length == 1)
            {
                ShapeVisualMaterialGUI("Render Material",
                                       materials[0],
                                       newMaterial => shapeVisual.ReplaceMaterial(0, newMaterial));
            }
        }
Ejemplo n.º 3
0
        private void MeshStatisticsGUI()
        {
            if (IsMultiSelect || Mesh.PrecomputedCollisionMeshes.Length == 0)
            {
                return;
            }

            EditorGUILayout.Space();

            InspectorGUI.BrandSeparator();

            var numCollisionMeshes = Mesh.PrecomputedCollisionMeshes.Length;
            var totNumVertices     = Mesh.PrecomputedCollisionMeshes.Select(collisionMesh => collisionMesh.Vertices.Length).Sum();
            var totNumTriangles    = Mesh.PrecomputedCollisionMeshes.Select(collisionMesh => collisionMesh.Indices.Length).Sum() / 3;
            var meshPlural         = numCollisionMeshes > 1 ? "es" : string.Empty;
            var summaryString      = $"Summary ({numCollisionMeshes} mesh{meshPlural}, {totNumTriangles} triangles, {totNumVertices} vertices)";

            if (InspectorGUI.Foldout(GetMeshStatisticsEditorData(Mesh),
                                     GUI.MakeLabel(summaryString)))
            {
                InspectorGUI.Separator();

                EditorGUILayout.LabelField(GUI.MakeLabel("Number of meshes"),
                                           GUI.MakeLabel(Mesh.PrecomputedCollisionMeshes.Length.ToString(), Color.green),
                                           InspectorEditor.Skin.TextField);
                using (InspectorGUI.IndentScope.Single) {
                    InspectorGUI.Separator();
                    for (int i = 0; i < Mesh.PrecomputedCollisionMeshes.Length; ++i)
                    {
                        var numVertices  = Mesh.PrecomputedCollisionMeshes[i].Vertices.Length;
                        var numTriangles = Mesh.PrecomputedCollisionMeshes[i].Indices.Length / 3;
                        EditorGUILayout.LabelField(GUI.MakeLabel($"[{i}] Number of triangles (vertices)"),
                                                   GUI.MakeLabel($"{numTriangles.ToString().Color( InspectorGUISkin.BrandColorBlue )} ({numVertices.ToString()})"),
                                                   InspectorEditor.Skin.TextField);
                    }
                    InspectorGUI.Separator();
                }
                var totNumTrianglesString  = totNumTriangles.ToString().Color(InspectorGUISkin.BrandColorBlue);
                var hasReducedNumTriangles = Mesh.Options != null &&
                                             (Mesh.Options.Mode != AGXUnity.Collide.CollisionMeshOptions.MeshMode.Trimesh ||
                                              Mesh.Options.ReductionEnabled);
                if (hasReducedNumTriangles)
                {
                    totNumTrianglesString += $" (originally: {Mesh.SourceObjects.Select( source => source.triangles.Length / 3 ).Sum().ToString().Color( Color.red )})";
                }
                EditorGUILayout.LabelField(GUI.MakeLabel("Total number of triangles"),
                                           GUI.MakeLabel(totNumTrianglesString),
                                           InspectorEditor.Skin.TextField);
            }
        }
Ejemplo n.º 4
0
        private NodeFoldoutState NodeFoldout(Route <NodeT> .ValidatedNode validatedNode)
        {
            if (s_invalidNodeStyle == null)
            {
                s_invalidNodeStyle = new GUIStyle(InspectorEditor.Skin.Label);
                s_invalidNodeStyle.normal.background = GUI.CreateColoredTexture(1,
                                                                                1,
                                                                                Color.Lerp(UnityEngine.GUI.color,
                                                                                           Color.red,
                                                                                           0.75f));
            }

            var state = new NodeFoldoutState();
            var node  = validatedNode.Node;

            var verticalScope = !validatedNode.Valid ?
                                new EditorGUILayout.VerticalScope(s_invalidNodeStyle) :
                                null;
            var horizontalScope = node == Selected ?
                                  new EditorGUILayout.HorizontalScope(InspectorEditor.Skin.Label) :
                                  new EditorGUILayout.HorizontalScope(InspectorEditor.Skin.TextArea);

            state.Foldout = InspectorGUI.Foldout(GetFoldoutData(node),
                                                 GUI.MakeLabel(GetNodeTypeString(node) + ' ' +
                                                               SelectGameObjectDropdownMenuTool.GetGUIContent(node.Parent).text,
                                                               !validatedNode.Valid,
                                                               validatedNode.ErrorString),
                                                 newState =>
            {
                Selected = newState ? node : null;
                EditorUtility.SetDirty(Parent);
            });

            state.InsertBefore = InspectorGUI.Button(MiscIcon.EntryInsertBefore,
                                                     true,
                                                     "Insert a new node before this node.",
                                                     GUILayout.Width(18));
            state.InsertAfter = InspectorGUI.Button(MiscIcon.EntryInsertAfter,
                                                    true,
                                                    "Insert a new node after this node.",
                                                    GUILayout.Width(18));
            state.Erase = InspectorGUI.Button(MiscIcon.EntryRemove,
                                              true,
                                              "Remove this node from the route.",
                                              GUILayout.Width(18));
            horizontalScope?.Dispose();
            verticalScope?.Dispose();

            return(state);
        }
Ejemplo n.º 5
0
        private void HandleLineToolInspectorGUI(LineTool lineTool, string name)
        {
            // If visible, the vertical maker starts under the foldout, otherwise
            // render the marker through the fouldout label.
            var isVisible = GetLineToggleData(name).Bool;
            var color     = Color.Lerp(lineTool.Color, InspectorGUI.BackgroundColor, 0.25f);

            using (new InspectorGUI.VerticalScopeMarker(color)) {
                if (!InspectorGUI.Foldout(GetLineToggleData(name),
                                          GUI.MakeLabel(name, true)))
                {
                    return;
                }
                //using ( new InspectorGUI.VerticalScopeMarker( color ) )
                using (InspectorGUI.IndentScope.Single)
                    lineTool.OnInspectorGUI();
            }
        }
Ejemplo n.º 6
0
        public void OnInspectorGUI()
        {
            if (Mode == ToolMode.Direction)
            {
                StartFrameNameId = "Frame";
            }

            StartFrameToolEnable = Line.Valid;
            EndFrameToolEnable   = Line.Valid;

            bool toggleCreateEdge    = false;
            bool toggleFlipDirection = false;
            bool toggleRenderAsArrow = false;
            bool showTools           = !EditorApplication.isPlaying;

            if (showTools)
            {
                var toolButtonData = new List <InspectorGUI.ToolButtonData>();
                toolButtonData.Add(InspectorGUI.ToolButtonData.Create(ToolIcon.VisualizeLineDirection,
                                                                      RenderAsArrow,
                                                                      "Visualize line direction.",
                                                                      () => toggleRenderAsArrow = true,
                                                                      Mode != ToolMode.Direction));
                toolButtonData.Add(InspectorGUI.ToolButtonData.Create(ToolIcon.FlipDirection,
                                                                      false,
                                                                      "Flip direction.",
                                                                      () => toggleFlipDirection = true,
                                                                      Line.Valid));
                toolButtonData.Add(InspectorGUI.ToolButtonData.Create(ToolIcon.FindTransformGivenEdge,
                                                                      EdgeDetectionToolEnable,
                                                                      "Find line given edge.",
                                                                      () => toggleCreateEdge = true));
                InspectorGUI.ToolButtons(toolButtonData.ToArray());
            }

            if (toggleCreateEdge)
            {
                EdgeDetectionToolEnable = !EdgeDetectionToolEnable;
            }

            if (toggleFlipDirection && EditorUtility.DisplayDialog("Line direction",
                                                                   "Flip direction of " + Name + "?",
                                                                   "Yes",
                                                                   "No"))
            {
                StartFrameToolEnable = false;

                if (Mode == ToolMode.Direction)
                {
                    Line.End.Position   = Line.End.Position - 2.0f * Line.Length * Line.Direction;
                    Line.Start.Rotation = Quaternion.Euler(-Line.Start.Rotation.eulerAngles);
                    Line.End.Rotation   = Quaternion.Euler(-Line.End.Rotation.eulerAngles);
                }
                else
                {
                    var tmp = Line.Start;
                    Line.Start = Line.End;
                    Line.End   = tmp;
                }
            }
            if (toggleRenderAsArrow)
            {
                RenderAsArrow = !RenderAsArrow;
            }

            if (!Line.Valid)
            {
                InspectorGUI.WarningLabel(Name + " isn't created - use Tools to configure.");
            }

            if (StartFrameToolEnable)
            {
                if (InspectorGUI.Foldout(GetToggleData(StartFrameNameId),
                                         GUI.MakeLabel(StartFrameNameId, true)))
                {
                    StartFrameTool.ForceDisableTransformHandle = EditorApplication.isPlaying;
                    using (new GUI.EnabledBlock(!EditorApplication.isPlaying))
                        InspectorGUI.HandleFrame(StartFrameTool.Frame, 1);
                }
            }
            if (EndFrameToolEnable)
            {
                if (InspectorGUI.Foldout(GetToggleData(EndFrameNameId),
                                         GUI.MakeLabel(EndFrameNameId, true)))
                {
                    EndFrameTool.ForceDisableTransformHandle = EditorApplication.isPlaying;
                    using (new GUI.EnabledBlock(!EditorApplication.isPlaying))
                        InspectorGUI.HandleFrame(EndFrameTool.Frame, 1);
                }
            }

            Synchronize();
        }
Ejemplo n.º 7
0
        public override void OnPreTargetMembersGUI()
        {
            var skin           = InspectorEditor.Skin;
            var constraints    = GetTargets <Constraint>().ToArray();
            var refConstraint  = constraints[0];
            var differentTypes = false;
            var anyUnknownType = constraints.Any(c => c.Type == ConstraintType.Unknown);

            for (int i = 1; i < constraints.Length; ++i)
            {
                differentTypes = differentTypes || refConstraint.Type != constraints[i].Type;
            }

            // Render AttachmentPair GUI.
            ConstraintAttachmentFrameTool.OnPreTargetMembersGUI();

            Undo.RecordObjects(constraints, "ConstraintTool");

            UnityEngine.GUI.changed = false;

            // The constraint type is Unknown when, e.g., go.AddComponent<Constraint>()
            // or when the constraint has been reset. If any of the selected constraints
            // is of type Unknown, we exit the GUI here.
            if (!ConstraintTypeGUI(constraints, differentTypes))
            {
                return;
            }

            EditorGUI.showMixedValue = constraints.Any(constraint => refConstraint.CollisionsState != constraint.CollisionsState);
            var collisionsState = ConstraintCollisionsStateGUI(refConstraint.CollisionsState);

            EditorGUI.showMixedValue = false;

            if (UnityEngine.GUI.changed)
            {
                foreach (var constraint in constraints)
                {
                    constraint.CollisionsState = collisionsState;
                }
                UnityEngine.GUI.changed = false;
            }

            EditorGUI.showMixedValue = constraints.Any(constraint => refConstraint.SolveType != constraint.SolveType);
            var solveType = ConstraintSolveTypeGUI(refConstraint.SolveType);

            EditorGUI.showMixedValue = false;

            if (UnityEngine.GUI.changed)
            {
                foreach (var constraint in constraints)
                {
                    constraint.SolveType = solveType;
                }
                UnityEngine.GUI.changed = false;
            }

            EditorGUI.showMixedValue = constraints.Any(constraint => refConstraint.ConnectedFrameNativeSyncEnabled != constraint.ConnectedFrameNativeSyncEnabled);
            var frameNativeSync = ConstraintConnectedFrameSyncGUI(refConstraint.ConnectedFrameNativeSyncEnabled);

            EditorGUI.showMixedValue = false;

            if (UnityEngine.GUI.changed)
            {
                foreach (var constraint in constraints)
                {
                    constraint.ConnectedFrameNativeSyncEnabled = frameNativeSync;
                }
                UnityEngine.GUI.changed = false;
            }

            if (differentTypes)
            {
                InspectorGUI.WarningLabel("Constraints are of different types.\nRow data editing not supported.");
                return;
            }

            Func <string, EditorDataEntry> selected = (id) =>
            {
                return(EditorData.Instance.GetData(refConstraint, id, entry => entry.Bool = false));
            };

            var constraintsParser = (from constraint
                                     in constraints
                                     select ConstraintUtils.ConstraintRowParser.Create(constraint)).ToArray();
            var allElementaryConstraints = constraints.SelectMany(constraint => constraint.GetOrdinaryElementaryConstraints()).ToArray();

            Undo.RecordObjects(allElementaryConstraints, "ConstraintTool");

            var ecRowDataWrappers = InvokeWrapper.FindFieldsAndProperties <ElementaryConstraintRowData>();

            foreach (ConstraintUtils.ConstraintRowParser.RowType rowType in Enum.GetValues(typeof(ConstraintUtils.ConstraintRowParser.RowType)))
            {
                if (!InspectorGUI.Foldout(selected("ec_" + rowType.ToString()),
                                          GUI.MakeLabel(rowType.ToString() + " properties", true)))
                {
                    continue;
                }

                using (InspectorGUI.IndentScope.Single) {
                    var refTransOrRotRowData = constraintsParser[0][rowType];
                    foreach (var wrapper in ecRowDataWrappers)
                    {
                        if (!InspectorEditor.ShouldBeShownInInspector(wrapper.Member))
                        {
                            continue;
                        }

                        for (int i = 0; i < 3; ++i)
                        {
                            var rowDataInstances = (from constraintParser
                                                    in constraintsParser
                                                    where constraintParser[rowType][i] != null
                                                    select constraintParser[rowType][i].RowData).ToArray();

                            using (new GUI.EnabledBlock(refTransOrRotRowData[i] != null)) {
                                var labelContent = i == 0 ? InspectorGUI.MakeLabel(wrapper.Member) : null;
                                var fieldContent = GUI.MakeLabel(RowLabels[i], RowColors[i]);
                                if (wrapper.IsType <float>())
                                {
                                    EditorGUI.showMixedValue = !wrapper.AreValuesEqual(rowDataInstances);
                                    var value = InspectorGUI.CustomFloatField(labelContent,
                                                                              fieldContent,
                                                                              wrapper.Get <float>(refTransOrRotRowData[i]?.RowData));
                                    if (UnityEngine.GUI.changed)
                                    {
                                        foreach (var constraintParser in constraintsParser)
                                        {
                                            wrapper.ConditionalSet(constraintParser[rowType][i]?.RowData, value);
                                        }
                                    }
                                }
                                else if (wrapper.IsType <RangeReal>())
                                {
                                    EditorGUI.showMixedValue = rowDataInstances.Any(rowData => !Equals(wrapper.Get <RangeReal>(refTransOrRotRowData[i]?.RowData).Min,
                                                                                                       wrapper.Get <RangeReal>(rowData).Min)) ||
                                                               rowDataInstances.Any(rowData => !Equals(wrapper.Get <RangeReal>(refTransOrRotRowData[i]?.RowData).Max,
                                                                                                       wrapper.Get <RangeReal>(rowData).Max));
                                    var rangeChangeData = InspectorGUI.RangeRealField(labelContent,
                                                                                      wrapper.Get <RangeReal>(refTransOrRotRowData[i]?.RowData),
                                                                                      GUI.MakeLabel(RowLabels[i], RowColors[i]));
                                    if (rangeChangeData.MinChanged || rangeChangeData.MaxChanged)
                                    {
                                        foreach (var constraintParser in constraintsParser)
                                        {
                                            var range = wrapper.Get <RangeReal>(constraintParser[rowType][i].RowData);
                                            if (rangeChangeData.MinChanged)
                                            {
                                                range.Min = rangeChangeData.Min;
                                            }
                                            if (rangeChangeData.MaxChanged)
                                            {
                                                range.Max = rangeChangeData.Max;
                                            }

                                            // Validation of Min > Max has to go somewhere else because if e.g.,
                                            // Min = 50 and the user wants to type Max = 200 we're receiving
                                            // Max = 2 as the user types.

                                            wrapper.ConditionalSet(constraintParser[rowType][i].RowData, range);
                                        }
                                    }
                                }
                            }

                            UnityEngine.GUI.changed  = false;
                            EditorGUI.showMixedValue = false;
                        }
                    } // For type wrappers.
                }     // Indentation.
            }         // For Translational, Rotational.

            var ecControllers = refConstraint.GetElementaryConstraintControllers();

            if (ecControllers.Length > 0 &&
                InspectorGUI.Foldout(selected("controllers"),
                                     GUI.MakeLabel("Controllers", true)))
            {
                using (InspectorGUI.IndentScope.Single) {
                    foreach (var refController in ecControllers)
                    {
                        var controllerType    = refController.GetControllerType();
                        var controllerTypeTag = controllerType.ToString()[0].ToString();
                        var controllerName    = ConstraintUtils.FindName(refController);
                        if (controllerName.EndsWith(" Controller"))
                        {
                            controllerName = controllerName.Remove(controllerName.LastIndexOf(" Controller"));
                        }
                        var controllerLabel = GUI.MakeLabel((controllerType == Constraint.ControllerType.Rotational ?
                                                             GUI.Symbols.CircleArrowAcw.ToString() + " " :
                                                             GUI.Symbols.ArrowRight.ToString() + " ") + controllerName, true);
                        if (!InspectorGUI.Foldout(selected(controllerTypeTag + controllerName),
                                                  controllerLabel))
                        {
                            continue;
                        }

                        var controllers = (from constraint
                                           in constraints
                                           from controller
                                           in constraint.GetElementaryConstraintControllers()
                                           where controller.GetType() == refController.GetType() &&
                                           controller.GetControllerType() == refController.GetControllerType()
                                           select controller).ToArray();
                        using (InspectorGUI.IndentScope.Single) {
                            InspectorEditor.DrawMembersGUI(controllers);
                            InspectorEditor.DrawMembersGUI(controllers, controller => (controller as ElementaryConstraint).RowData[0]);
                        }
                    }
                }
            }
        }
Ejemplo n.º 8
0
        private void MeshOptionsGUI()
        {
            InspectorGUI.Separator();

            using (new GUI.EnabledBlock(!EditorApplication.isPlayingOrWillChangePlaymode)) {
                if (InspectorGUI.Foldout(GetEditorData(Mesh), GUI.MakeLabel("Options")))
                {
                    using (InspectorGUI.IndentScope.Single) {
                        InspectorEditor.DrawMembersGUI(Targets, t => (t as AGXUnity.Collide.Mesh).Options);
                        var applyResetResult = InspectorGUI.PositiveNegativeButtons(UnityEngine.GUI.enabled,
                                                                                    "Apply",
                                                                                    "Apply the changes",
                                                                                    "Reset",
                                                                                    "Delete collision meshes and reset mesh options values to default.");
                        if (applyResetResult == InspectorGUI.PositiveNegativeResult.Positive)
                        {
                            var meshes = GetTargets <AGXUnity.Collide.Mesh>().ToArray();
                            var collisionMeshGenerator = new AGXUnity.Collide.CollisionMeshGenerator();
                            var generatorStartTime     = EditorApplication.timeSinceStartup;
                            collisionMeshGenerator.GenerateAsync(meshes);
                            var isCanceled = false;
                            while (!isCanceled && collisionMeshGenerator.IsRunning)
                            {
                                var progressBarTitle = $"Generating collision meshes: {(int)( EditorApplication.timeSinceStartup - generatorStartTime )} s";
                                var progressBarInfo  = string.Empty;
                                var progress         = collisionMeshGenerator.Progress;
                                isCanceled = EditorUtility.DisplayCancelableProgressBar(progressBarTitle, progressBarInfo, progress);
                                if (!isCanceled)
                                {
                                    System.Threading.Thread.Sleep(50);
                                }
                            }

                            EditorUtility.ClearProgressBar();

                            if (isCanceled)
                            {
                                CanceledAsyncCollisionMeshGeneretors.RegisterCanceled(collisionMeshGenerator);
                            }
                            else
                            {
                                var results = collisionMeshGenerator.CollectResults();
                                using (new Utils.UndoCollapseBlock("Apply collision mesh data")) {
                                    foreach (var result in results)
                                    {
                                        Undo.RecordObject(result.Mesh, "Collision Meshes");
                                        result.Mesh.Options = result.Options;
                                        result.Mesh.PrecomputedCollisionMeshes = result.CollisionMeshes;
                                    }
                                }

                                var hasPrefabAssetBeenChanged = results.Any(result =>
                                                                            PrefabUtility.GetCorrespondingObjectFromOriginalSource(result.Mesh.gameObject) == null &&
                                                                            PrefabUtility.GetPrefabInstanceHandle(result.Mesh.gameObject) == null);
                                // Trying to dirty gizmos rendering of all affected prefab instances.
                                // We don't have to dirty them all but it's hard to determine where
                                // the instance is located in the hierarchy.
                                if (hasPrefabAssetBeenChanged)
                                {
                                    var allMeshes = Object.FindObjectsOfType <AGXUnity.Collide.Mesh>();
                                    foreach (var m in allMeshes)
                                    {
                                        m.OnPrecomputedCollisionMeshDataDirty();
                                    }
                                }
                            }

                            collisionMeshGenerator = null;

                            GUIUtility.ExitGUI();
                        }
                        else if (applyResetResult == InspectorGUI.PositiveNegativeResult.Negative &&
                                 EditorUtility.DisplayDialog("Reset collision meshes to default",
                                                             "Destroy collision meshes and reset mesh options to default?",
                                                             "Yes", "Cancel"))
                        {
                            var meshes = GetTargets <AGXUnity.Collide.Mesh>().ToArray();
                            using (new Utils.UndoCollapseBlock("Reset collision mesh data")) {
                                for (int i = 0; i < meshes.Length; ++i)
                                {
                                    var mesh = meshes[i];
                                    Undo.RecordObject(mesh, "Resetting collision mesh data");
                                    mesh.DestroyCollisionMeshes();
                                    if (mesh.Options != null)
                                    {
                                        Undo.RecordObject(mesh, "Resetting mesh options to default");
                                        mesh.Options.ResetToDesfault();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 9
0
        private void RouteGUI()
        {
            var addNewPressed       = false;
            var insertBeforePressed = false;
            var insertAfterPressed  = false;
            var erasePressed        = false;

            NodeT listOpNode = null;

            Undo.RecordObject(Route, "Route changed");

            if (InspectorGUI.Foldout(EditorData.Instance.GetData(Parent,
                                                                 "Route",
                                                                 entry => { entry.Bool = true; }),
                                     GUI.MakeLabel("Route")))
            {
                var validatedRoute = Route.GetValidated();
                foreach (var validatedNode in validatedRoute)
                {
                    var node = validatedNode.Node;
                    using (InspectorGUI.IndentScope.Single) {
                        var foldoutState = NodeFoldout(validatedNode);
                        if (foldoutState.Foldout)
                        {
                            OnPreFrameGUI(node);

                            InspectorGUI.HandleFrame(node, 1);

                            OnPostFrameGUI(node);
                        }

                        if (listOpNode == null && foldoutState.ButtonPressed)
                        {
                            listOpNode = node;
                        }

                        insertBeforePressed = insertBeforePressed || foldoutState.InsertBefore;
                        insertAfterPressed  = insertAfterPressed || foldoutState.InsertAfter;
                        erasePressed        = erasePressed || foldoutState.Erase;
                    }

                    if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition) &&
                        Event.current.type == EventType.MouseDown &&
                        Event.current.button == 0)
                    {
                        Selected = node;
                    }
                }

                GUILayout.BeginHorizontal();
                {
                    InspectorGUI.Separator(1, EditorGUIUtility.singleLineHeight);

                    addNewPressed = InspectorGUI.Button(MiscIcon.EntryAdd,
                                                        true,
                                                        "Add new node to the route.",
                                                        GUILayout.Width(18));

                    if (listOpNode == null && addNewPressed)
                    {
                        listOpNode = Route.LastOrDefault();
                    }
                }
                GUILayout.EndHorizontal();
            }
            else
            {
                InspectorGUI.Separator(1, 3);
            }

            if (addNewPressed || insertBeforePressed || insertAfterPressed)
            {
                NodeT newRouteNode = null;
                // Clicking "Add" will not copy data from last node.
                newRouteNode = listOpNode != null?
                               addNewPressed?
                               RouteNode.Create <NodeT>(null, listOpNode.Position, listOpNode.Rotation) :
                                   RouteNode.Create <NodeT>(listOpNode.Parent, listOpNode.LocalPosition, listOpNode.LocalRotation) :
                                       RouteNode.Create <NodeT>();

                OnNodeCreate(newRouteNode, listOpNode, addNewPressed);

                if (addNewPressed)
                {
                    Route.Add(newRouteNode);
                }
                if (insertBeforePressed)
                {
                    Route.InsertBefore(newRouteNode, listOpNode);
                }
                if (insertAfterPressed)
                {
                    Route.InsertAfter(newRouteNode, listOpNode);
                }

                if (newRouteNode != null)
                {
                    CreateRouteNodeTool(newRouteNode);
                    Selected = newRouteNode;
                }
            }
            else if (listOpNode != null && erasePressed)
            {
                Selected = null;
                Route.Remove(listOpNode);
            }
        }
        public override void OnPreTargetMembersGUI()
        {
            var  skin          = InspectorEditor.Skin;
            var  disabledPairs = Manager.DisabledPairs;
            bool clearPressed  = false;
            bool addPressed    = false;
            CollisionGroupEntryPair erasePair = null;

            GUILayout.Label(GUI.MakeLabel("Add pair",
                                          true),
                            skin.LabelMiddleCenter);

            GUILayout.BeginVertical(skin.TextArea);
            {
                HandleCollisionGroupEntryPair(m_groupEntryPairToAdd);

                var buttonState = InspectorGUI.PositiveNegativeButtons(m_groupEntryPairToAdd.First.Tag.Length > 0 ||
                                                                       m_groupEntryPairToAdd.Second.Tag.Length > 0,
                                                                       "Add",
                                                                       "Add pair to disabled pairs.",
                                                                       "Clear");
                addPressed   = buttonState == InspectorGUI.PositiveNegativeResult.Positive;
                clearPressed = buttonState == InspectorGUI.PositiveNegativeResult.Negative;
            }
            GUILayout.EndVertical();

            if (InspectorGUI.Foldout(FoldoutDataEntry, GUI.MakeLabel("Disabled Pairs [" + disabledPairs.Length + "]")))
            {
                using (InspectorGUI.IndentScope.Single) {
                    foreach (var disabledPair in disabledPairs)
                    {
                        GUILayout.BeginHorizontal();
                        {
                            InspectorGUI.Separator(1, EditorGUIUtility.singleLineHeight);

                            if (InspectorGUI.Button(MiscIcon.EntryRemove,
                                                    true,
                                                    "Remove pair from list.",
                                                    GUILayout.Width(18)))
                            {
                                erasePair = disabledPair;
                            }
                        }
                        GUILayout.EndHorizontal();

                        HandleCollisionGroupEntryPair(disabledPair);

                        // TODO GUI: Maybe unnecessary space with correct separator.
                        GUILayout.Space(6.0f);
                    }
                }
            }

            if (clearPressed)
            {
                m_groupEntryPairToAdd.First.Tag = m_groupEntryPairToAdd.Second.Tag = string.Empty;
            }
            if (addPressed)
            {
                Manager.SetEnablePair(m_groupEntryPairToAdd.First.Tag, m_groupEntryPairToAdd.Second.Tag, false);
                m_groupEntryPairToAdd.First.Tag = m_groupEntryPairToAdd.Second.Tag = string.Empty;
                FoldoutDataEntry.Bool           = true;
            }
            if (erasePair != null)
            {
                if (EditorUtility.DisplayDialog("Remove pair",
                                                "Erase disabled pair: " + erasePair.First.Tag + " and " + erasePair.Second.Tag + "?",
                                                "Yes",
                                                "No"))
                {
                    Manager.SetEnablePair(erasePair.First.Tag, erasePair.Second.Tag, true);
                }
            }
        }