/// <summary>
 /// Recursive!
 /// </summary>
 /// <param name="sb"></param>
 /// <param name="node"></param>
 public static void DescribeChildren(ref StringBuilder sb, Node node, bool richText)
 {
     foreach (Node childNode in node.ChildNodes)
     {
         string name = null == childNode.Transform ? "New" : childNode.Transform.name;
         sb.AppendLine(StringUtil.Indent(childNode.Depth/* + 1*/, string.Format("{0} [A:{1}]", name, childNode.AdapterId)));
         DescribeChildren(ref sb, childNode, richText);
     }
 }
Example #2
0
        public static void Fix(Node node)
        {
            /**
             * 1. Just in case it got stucked to value other then 0 (not related)
             * */
            EditorSettings.ReadyToProcessHierarchyChanges = 0;

            /**
             * 2. Fix hierarchy
             * */
            StringBuilder sb = new StringBuilder();
            DoFix(ref sb, node);

            if (!string.IsNullOrEmpty(sb.ToString()))
            {
                Debug.Log(string.Format(@"### FIXED ### 
{0}", sb));
            }

            // TEMP:
            OrderDisplay.Instance.Refresh();
        }
Example #3
0
        /// <summary>
        /// Recursive!
        /// </summary>
        /// <param name="sb"></param>
        /// <param name="node"></param>
        private static void DoFix(ref StringBuilder sb, Node node)
        {
            bool isRoot = (null == node.Adapter);
            //if (!isRoot)
            //    Debug.Log(StringUtil.Indent(node.Depth, "Fixing " + node.Adapter));

            var childNodes = node.ChildNodes;

            var unprocessedNodes = ListUtil<Node>.Clone(node.ChildNodes);
            
            var adapter = node.Adapter;

            //Debug.Log(@"adapter: " + adapter);
            var containerAdapter = adapter as GroupAdapter;
            //Debug.Log(@"containerAdapter: " + containerAdapter);

            // Note: the ROOT node has no adapter no groups defined!

            if (null != containerAdapter) {
                ChildGroupPack pack = ChildGroupPack.Read(containerAdapter);
                // Debug.Log(@"pack: 
                //" + pack);
                foreach (ChildGroup childGroup in pack.Groups)
                {
                    //sb.AppendLine(StringUtil.Indent(node.Depth + 1, string.Format("== {0} ==", childGroup.GroupName)));
                    //foreach (ComponentAdapter componentAdapter in childGroup.Adapters)
                    for (int i = childGroup.Adapters.Count - 1; i >= 0; i--)
                    {
                        ComponentAdapter componentAdapter = childGroup.Adapters[i];

                        bool doRemove = false;

                        /**
                         * 1. Handle null
                         * */
                        if (null == componentAdapter)
                        {
                            //sb.AppendLine(StringUtil.Indent(node.Depth + 1, "*** Not found ***"));
                            // adapter is not child of this container, remove it from the list
                            //toRemove.Add(componentAdapter);
                            doRemove = true;
                        }
                        /**
                         * 2. Not null. Handle the adapter
                         * */
                        else
                        {
                            var childNode = GetNode(childNodes, componentAdapter);
                            if (null == childNode)
                            {
                                //sb.AppendLine(StringUtil.Indent(node.Depth + 1, "*** Not found ***"));
                                // adapter is not child of this container, remove it from the list
                                //toRemove.Add(componentAdapter);
                                doRemove = true;
                            }
                            else
                            {
                                unprocessedNodes.Remove(childNode);
                                DoFix(ref sb, childNode);
                            }
                        }

                        if (doRemove)
                        {
                            //Debug.Log("list 1: " + ComponentAdapterUtil.DescribeAdapterList(childGroup.Adapters));
                            childGroup.Adapters.RemoveAt(i);
                            //childGroup.Adapters.Remove(adapterToRemove);
                            if (null != componentAdapter)
                                sb.AppendLine(string.Format("{0}: {1} [A:{2}] removed", GuiLookup.PathToString(containerAdapter.transform, "->"), componentAdapter, componentAdapter.GetInstanceID()));
                            else
                                sb.AppendLine(string.Format("{0}: Adapter at position {1} removed", GuiLookup.PathToString(containerAdapter.transform, "->"), i));
                            //Debug.Log("list 2: " + ComponentAdapterUtil.DescribeAdapterList(childGroup.Adapters));
                        }
                    }
                }
            }

            // orphans
            foreach (Node childNode in unprocessedNodes)
            {
                if (!isRoot)
                {
                    if (null != containerAdapter)
                    {
                        ChildGroupPack pack = ChildGroupPack.Read(containerAdapter);
                        if (pack.Groups.Count > 0)
                        {
                            pack.Groups[0].Adapters.Add(childNode.Adapter);
                            sb.AppendLine(string.Format("{0}: [{1}] added to the first group", GuiLookup.PathToString(containerAdapter.transform, "->"), childNode.Adapter));    
                        }
                    }
                }
                DoFix(ref sb, childNode);
            }
        }
        public static void DescribeChildrenWithGroups(ref StringBuilder sb, Node node, bool richText)
        {
            var childNodes = node.ChildNodes;

            var unprocessedNodes = ListUtil<Node>.Clone(node.ChildNodes);

            var adapter = node.Adapter;

            //Debug.Log(@"adapter: " + adapter);
            var containerAdapter = adapter as GroupAdapter;

            // Note: the ROOT node has no adapter nor groups defined!
            if (null != containerAdapter)
            {
                ChildGroupPack pack = ChildGroupPack.Read(containerAdapter);
                //                Debug.Log(@"pack: 
                //" + pack);
                foreach (ChildGroup childGroup in pack.Groups)
                {
                    sb.AppendLine(StringUtil.Indent(node.Depth + 1, string.Format("== {0} ==", childGroup.GroupName)));
                    foreach (ComponentAdapter componentAdapter in childGroup.Adapters)
                    {
                        var childNode = GetNode(childNodes, componentAdapter);
                        if (null == childNode)
                        {
                            var line = StringUtil.Indent(node.Depth + 1, string.Format("*** Not found ***"));
                            if (richText)
                                line = (StringUtil.WrapColor(StringUtil.WrapTag(line, "b"), EditorSettings.UseDarkSkin ? "yellow" : "blue"));
                            sb.AppendLine(line);
                        }
                        else
                        {
                            unprocessedNodes.Remove(childNode);
                            string name = null == childNode.Transform ? "New" : childNode.Transform.name;

                            var isSelected = Selection.activeGameObject == componentAdapter.gameObject;
                            string selected = isSelected ? "*" : string.Empty;
                            string changed = ParentChildLinker.Instance.IsChanged(childNode.AdapterId) ? "[changed]" : string.Empty;

                            var line = StringUtil.Indent(childNode.Depth /* + 1*/,
                                string.Format("{2}{0} [A:{1}]{3}", name, childNode.AdapterId, selected, changed));

                            if (richText && isSelected)
                                line = StringUtil.WrapTag(line, "b");

                            sb.AppendLine(line);
                            DescribeChildrenWithGroups(ref sb, childNode, richText);
                        }
                    }
                }
            }

            if (unprocessedNodes.Count > 0 && null != node.ParentTransform)
            {
                sb.AppendLine(StringUtil.Indent(node.Depth + 1, string.Format("== Orphans ==")));
            }

            // orphans
            foreach (Node childNode in unprocessedNodes)
            {
                string name = null == childNode.Transform ? "New" : childNode.Transform.name;
                var isSelected = (Selection.activeGameObject == childNode.Adapter.gameObject);
                string selected = isSelected ? "*" : string.Empty;
                string changed = ParentChildLinker.Instance.IsChanged(childNode.AdapterId) ? "[changed]" : string.Empty;

                bool isOrphan = !(childNode.Adapter is StageAdapter);
                string orphan = isOrphan ? "[orphan] " : string.Empty;
                //string orphan = StringUtil.WrapColor((childNode.Adapter is StageAdapter) ? string.Empty : "[orphan] ",
                    //EditorSettings.UseDarkSkin ? "yellow" : "blue");

                var line = StringUtil.Indent(childNode.Depth/* + 1*/, string.Format("{4}{2}{0} [A:{1}]{3}", name, childNode.AdapterId, selected, changed, orphan));
                if (isOrphan)
                    line = StringUtil.WrapColor(StringUtil.WrapTag(line, "b"), EditorSettings.UseDarkSkin ? "yellow" : "blue");

                if (richText && isSelected)
                    line = StringUtil.WrapTag(line, "b");

                sb.AppendLine(line);
                DescribeChildrenWithGroups(ref sb, childNode, richText);
            }
        }
        /// <summary>
        /// Processes changes
        /// </summary>
        private void ProcessChanges(bool inPlayMode)
        {
            /* Calculating the single step change */
            _stepDelta = HierarchyComparer.Compare(
                _oldHierarchy,
                _newHierarchy
            );

#if DEBUG
            if (DebugMode)
            {
                Debug.Log(_stepDelta);
            }
#endif
            //Debug.Log(delta);

            /**
             * 2. Handle additions
             * a) Instantiate component
             * b) Record transform addition
             * */

            if (inPlayMode)
            {
                /**
                 * 1. PLAY mode
                 * Leaving the processing to persistence logic
                 * */

                /**
                 * In play mode we need a full delta, so we will be able to re-apply it
                 * after the play mode is stopped
                 * Tracking full delta with each change, because we don't know when
                 * will be the last chance to track it before stopping the play mode
                 * */
                _fullDelta = HierarchyComparer.Compare(
                    _originalHierarchy,
                    _newHierarchy
                );

                /**
                 * IMPORTANT:
                 * This was the source of the quite performance killing recursive bug
                 * If this processing take place in play mode, OnHierarchyChange fires in each consequent frame, without ever stopping
                 * TODO: do a special logic for reordering items in play mode
                 * */
                AdditionProcessor.Process(_stepDelta.TopLevelAdditions, _stepDelta.Additions);
                RemovalProcessor.Process(_stepDelta.Removals);
                MovesProcessor.Process(_stepDelta.Moves);

                /**
                 * Accumulate delta
                 * */
                PersistenceManager.Instance.Delta = _fullDelta; //.Reset(); // TEMP -> create a direct Delta setter
                //PersistenceManager.Instance.Delta.Accumulate(_fullDelta);
            }
            else
            {
                /**
                * 2. EDIT mode
                * In edit mode, so handling the ordering information immediatelly
                * Note: the ordering information is all that has to be processed when adding a new component 
                * or a prefab in Edit mode - other things (parent-child relationship etc.) is handled by Unity
                * */
                EditModeAdditionsProcessor.Process(_stepDelta.TopLevelAdditions);
                EditModeRemovalProcessor.Process(_stepDelta.TopLevelRemovals);
                EditModeMovesProcessor.Process(_stepDelta.Moves);
            }

            _stepDelta.Reset();

            // save the current hierarchy
            _oldHierarchy = _newHierarchy;

            EditorState.Instance.Adapter = ComponentRegistry.Instance.Get(EditorState.Instance.AdapterId, true) as ComponentAdapter;

            ChangesProcessedSignal.Emit();
        }
        public void Reset()
        {
            if (null != _fullDelta)
                _fullDelta.Reset();
            if (null != _stepDelta)
                _stepDelta.Reset();

            _originalHierarchy = new Node(null);
            _oldHierarchy = new Node(null);
            _newHierarchy = null;
        }
        /// <summary>
        /// Called the moment the play mode starts
        /// Used for serializing the starting state
        /// </summary>
        /// <returns></returns>
        public void InitialSnapshot()
        {
            //LogUtil.PrintCurrentMethod();
            /**
             * 1. Take the hierarchy snapshot
             * */
            _originalHierarchy = HierarchyBuilder.Instance.BuildHierarchy();

            //Debug.Log(_originalHierarchy.DescribeHierarchy());
        }
        public void HierarchyChangeSlot(object[] parameters)
        {
#if DEBUG
            if (DebugMode)
            {
                Debug.Log("HierarchyChangeProcessor->HierarchyChangeSlot");
            }
#endif
            
            if (!Application.isPlaying && !EditorSettings.WatchChanges)
                return; // not watching for changes in play mode, return

            /**
             * 1. Take the hierarchy snapshot
             * */
            _newHierarchy = HierarchyBuilder.Instance.BuildHierarchy();
            
            /*/**
             * 2. Return for the first time (if the flag is OFF)
             * (first hierarchy change happens the moment the project is loaded)
             * #1#
            if (!_initialHierarchyChangeDone)
            {
                _initialHierarchyChangeDone = true;
                _oldHierarchy = _newHierarchy;
                return;
            }*/

            /**
             * 3. If second level loaded, go no further - do not process any more changes
             * */
            if (_levelLoaded)
            {
                return;
            }

            /**
             * 4. Process changes
             * If the application is playing, we are processing changes
             * If not playing, we have to be sure that this is not inside a few frames after the Play mode is stopped
             * This is because the PersistenceManager only should submit changes after the play mode is stopped,
             * or else we might end in edit mode change processing (like we added new components by hand)
             * */
            if (Application.isPlaying || 
                (!Application.isPlaying && EditorSettings.ReadyToProcessHierarchyChanges == 0))
            {
                ProcessChanges(Application.isPlaying);
                /**
                 * 5. Process the selection the newly created component
                 * */
                SelectionChangeProcessor.Instance.SelectCreatedComponent();
            }

            /**
             * After the play mode stopped, and if the hierarchy has not changed yet (flag not set), set it:
             * */
            if (EditorSettings.ReadyToProcessHierarchyChanges > 0/* && !Application.isPlaying*/)
            {
                EditorSettings.ReadyToProcessHierarchyChanges -= 1;
            }
            
            /**
             * 7. Rescan hierarchy tree icons
             * Important when expanding a part of the tree
             * */
            HierarchyViewDecorator.Instance.ReScan();

            if (null == Selection.activeTransform && null != DesignerOverlay.Instance) // DesignerOverlay.Instance might be null
                DesignerOverlay.Instance.Deselect();
        }
Example #9
0
        /// <summary>
        /// Compares 2 hierarchies for changes:<br/>
        /// 1. Additions<br/>
        /// 2. Removals<br/>
        /// 3. TopLevelRemovals<br/>
        /// 4. Moves<br/>
        /// 5. Renamings<br/>
        /// <br/>
        /// Detailed:<br/>
        /// 1. Additions: When something is added to a hierarchy while in play mode, we have to add it again after the play mode stopped<br/>
        /// We have to do through each added component in chield hierarchy and add it (TODO: what to do with prefabs?)<br/>
        /// 3. Removals: we have to remove persisted data from the list IMMEDIATELLY while still in play mode<br/>
        /// 2. TopLevelRemovals: We have to remove only the TOP components or containers when play mode STOPPED.<br/>
        /// Meaning, if we removed the panel with children while in play mode it will be recreated <br/>
        /// after the play mode is stopped, including all the children. We should remove only the parent.<br/>
        /// </summary>
        /// <param name="oldNode"></param>
        /// <param name="newNode"></param>
        /// <returns></returns>
        public static Delta Compare(Node oldNode, Node newNode)
        {
            Delta delta = new Delta();
            //Process(oldNode, newNode, ref delta);

            var oldList = oldNode.ToList();
            var newList = newNode.ToList();

            /**
             * 1. Raw removals = all removed transforms (including children)
             * */
            var removals = FindMissing(oldList, newList);
            //Debug.Log("### Removals: " + removals.Count);

            /**
             * 2. Top level removals
             * Ignore children if parent in the list -> this is giving us top containers being removed
             * */
            var topLevelRemovals = removals.FindAll(delegate(Node node) // all nodes NOT having the parent node in the list
            {
                //return true; // TEMP
                return !removals.Contains(node.ParentNode);
            });
            
            // Important: we want to process removals bottom-up (from children to parent)
            removals.Reverse();

            /**
             * 3. Delta
             * */
            delta.Removals = removals;
            delta.TopLevelRemovals = topLevelRemovals;

            /**
             * ############ ADDITIONS ############
             * */

            /**
             * 1. Raw additions = all added transforms (including children)
             * */
            var additions = FindMissing(newList, oldList);
            
            /**
             * 2. Top level additions
             * Ignore children if parent in the list -> this is giving us top containers being added
             * */
            var topLevelAdditions = additions.FindAll(delegate(Node node) // all nodes NOT having the parent node in the list
            {
                /**
                 * Note: there was an error before the 20130612
                 * Instead of only the top level additions, all the additions were processed
                 * So it turned out that each sub-collection of the added prefab is being re-scanned, thus overwriting the order saved with a prefab
                 * Only the top-level additions needs to be processed
                 */

                //return true; // TEMP
                return !additions.Contains(node.ParentNode); // 20130612
            });

            /**
             * 3. Delta
             * */
            delta.Additions = additions;
            delta.TopLevelAdditions = topLevelAdditions;

            /**
             * ############ MOVES ############
             * */

            /**
             * 1. Building the list of old list candidates (all nodes in the old list that are NOT REMOVED)
             * */
            var candidatesInTheOldList = oldList.FindAll(delegate(Node node)
                                  {
                                      return !removals.Contains(node);
                                  });

            /**
             * 2. Building the list of new list candidates (all the nodes in the new list that are NOT ADD)
             * */
            var moves = newList.FindAll(delegate(Node node)
            {
                Node oldSnapshot = candidatesInTheOldList.Find(delegate(Node on)
                                                               {
                                                                   return on.Transform == node.Transform;
                                                               });

                var changed = !additions.Contains(node) && node.HasChangedParent(oldSnapshot);

                if (changed)
                {
                    node.OldParentTransform = oldSnapshot.ParentTransform;
                }

                return changed;
            });

            /**
             * 3. Delta
             * */
            delta.Moves = moves;

            /**
             * ############ RENAMINGS ############
             * */

            delta.Renamings = FindRenamed(oldList, newList);

            return delta;
        }
Example #10
0
        public Node BuildHierarchy()
        {
            /**
             * 1. Fetch all objects
             * */
            List<Transform> topTransforms = 
                (ClassMode == LevelLookupMode.GuiStagesOnly) ? FetchStages() : FetchTopTransforms();
#if DEBUG
            if (DebugMode)
            {
                StringBuilder sb = new StringBuilder(@"Root game objects in the hierarchy:
"); foreach (Transform go in topTransforms)
                {
                    sb.AppendLine(string.Format("    {0}", go.name));
                }

                Debug.Log(sb);
            }
#endif

            /**
             * 3. Create node for each root transform
             * Build each node and add the to root nodes collection
             * */
            _rootNode = new Node(null);

            foreach (Transform transform in topTransforms)
            {
                var node = new Node(transform);
                ComponentAdapter adapter = node.Transform.gameObject.GetComponent<ComponentAdapter>();

                if (null != adapter)
                {
                    node.Build();
                    _rootNode.Add(node);
                }
            }

            return _rootNode;
        }
Example #11
0
        public bool HasChangedParent(Node oldSnapshot)
        {
            //if (null == Transform)
            //    return false; // the ROOT case

            return oldSnapshot.ParentTransform != ParentTransform;
        }
Example #12
0
 private void TraverseDelegate(Node node)
 {
     _nodeList.Add(node);
 }
Example #13
0
        // 2 nodes are equal if their transforms are equal

        public bool Equals(Node other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other._transform, _transform);
        }
Example #14
0
        /// <summary>
        /// Builds the node and its children using the transform relation (recursivelly!)
        /// </summary>
        public void Build()
        {
            foreach (Transform transform in Transform)
            {
                var childNode = new Node(transform) {Depth = Depth + 1, _parentNode = this};

                //ComponentAdapter adapter = childNode.Transform.gameObject.GetComponent<ComponentAdapter>();
                //if (null != adapter)
                //{
                    _childNodes.Add(childNode);
                    childNode.Build();
                //}
            }

            //PersistOrder();
        }
Example #15
0
 public void Add(Node node)
 {
     _childNodes.Add(node);
     node._parentNode = this;
 }