/// <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); } }
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(); }
/// <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(); }
/// <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; }
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; }
public bool HasChangedParent(Node oldSnapshot) { //if (null == Transform) // return false; // the ROOT case return oldSnapshot.ParentTransform != ParentTransform; }
private void TraverseDelegate(Node node) { _nodeList.Add(node); }
// 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); }
/// <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(); }
public void Add(Node node) { _childNodes.Add(node); node._parentNode = this; }