public void UpdateEntries(IList <GitStatusEntry> newEntries) { // Handle the empty list scenario if (!newEntries.Any()) { entries.Clear(); entryCommitTargets.Clear(); tree = null; foldedTreeEntries.Clear(); OnCommitTreeChange(); return; } tree = TreeBuilder.BuildTreeRoot(newEntries, entries, entryCommitTargets, foldedTreeEntries, AssetDatabase.GetCachedIcon); OnCommitTreeChange(); }
private void TreeNode(FileTreeNode node) { GUILayout.Space(Styles.TreeVerticalSpacing); var target = node.Target; var isFolder = node.Children.Any(); var isFolderForMeta = false; if (node.Children.Count() == 1) { isFolderForMeta = node.Children.First().Label.Substring(node.Label.Length).Equals(".meta"); } GUILayout.BeginHorizontal(); { if (!Readonly) { // Commit inclusion toggle var state = node.State; var toggled = state == CommitState.All; EditorGUI.BeginChangeCheck(); { toggled = GUILayout.Toggle(toggled, "", state == CommitState.Some ? Styles.ToggleMixedStyle : GUI.skin.toggle, GUILayout.ExpandWidth(false)); } if (EditorGUI.EndChangeCheck()) { node.State = toggled ? CommitState.All : CommitState.None; } } // Foldout if (isFolder) { Rect foldoutRect; if (Readonly) { foldoutRect = GUILayoutUtility.GetRect(1, 1); foldoutRect.Set(foldoutRect.x - 7f, foldoutRect.y + 3f, 0f, EditorGUIUtility.singleLineHeight); } else { foldoutRect = GUILayoutUtility.GetLastRect(); } foldoutRect.Set(foldoutRect.x - Styles.FoldoutWidth + Styles.FoldoutIndentation, foldoutRect.y, Styles.FoldoutWidth, foldoutRect.height); EditorGUI.BeginChangeCheck(); { node.Open = GUI.Toggle(foldoutRect, node.Open, "", EditorStyles.foldout); } if (EditorGUI.EndChangeCheck()) { if (!node.Open && !foldedTreeEntries.Contains(node.RepositoryPath)) { foldedTreeEntries.Add(node.RepositoryPath); } else if (node.Open) { foldedTreeEntries.Remove(node.RepositoryPath); } OnCommitTreeChange(); } } GitFileStatus?status = null; // Node icon and label GUILayout.BeginHorizontal(); { GUILayout.Space(Styles.CommitIconHorizontalPadding); var iconRect = GUILayoutUtility.GetRect(Styles.CommitIconSize, Styles.CommitIconSize, GUILayout.ExpandWidth(false)); iconRect.y += 2; iconRect.x -= 2; if (Event.current.type == EventType.Repaint) { var icon = (Texture)node.Icon; if (icon == null) { if (isFolderForMeta || !isFolder) { icon = Styles.DefaultAssetIcon; } else { icon = Styles.FolderIcon; } } if (icon != null) { GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit); } } var statusRect = new Rect( iconRect.xMax - 9, iconRect.yMax - 7, 9, 9); // Current status (if any) if (target != null) { var idx = entryCommitTargets.IndexOf(target); if (idx > -1) { status = entries[idx].Status; var statusIcon = Styles.GetFileStatusIcon(entries[idx].Status, false); if (statusIcon != null) { GUI.DrawTexture(statusRect, statusIcon); } } } GUILayout.Space(Styles.CommitIconHorizontalPadding); } GUILayout.EndHorizontal(); // Make the text gray and strikethrough if the file is deleted if (status == GitFileStatus.Deleted) { GUILayout.Label(new GUIContent(node.Label, node.RepositoryPath), Styles.DeletedFileLabel, GUILayout.ExpandWidth(true)); var labelRect = GUILayoutUtility.GetLastRect(); var strikeRect = new Rect(labelRect.xMin, labelRect.center.y, labelRect.width, 1); EditorGUI.DrawRect(strikeRect, Color.gray); } else { GUILayout.Label(new GUIContent(node.Label, node.RepositoryPath), GUILayout.ExpandWidth(true)); } GUILayout.FlexibleSpace(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); { // Render children (if any and folded out) if (isFolder && node.Open) { GUILayout.Space(Styles.TreeIndentation); GUILayout.BeginVertical(); { foreach (var child in node.Children) { TreeNode(child); } } GUILayout.EndVertical(); } } GUILayout.EndHorizontal(); }
public void UpdateEntries(IList <GitStatusEntry> newEntries) { // Handle the empty list scenario if (!newEntries.Any()) { entries.Clear(); entryCommitTargets.Clear(); tree = null; foldedTreeEntries.Clear(); OnCommitTreeChange(); return; } // Remove what got nuked for (var index = 0; index < entries.Count;) { if (!newEntries.Contains(entries[index])) { entries.RemoveAt(index); entryCommitTargets.RemoveAt(index); } else { index++; } } // Remove folding state of nuked items for (var index = 0; index < foldedTreeEntries.Count;) { if (!newEntries.Any(e => e.Path.IndexOf(foldedTreeEntries[index]) == 0)) { foldedTreeEntries.RemoveAt(index); } else { index++; } } // Add new stuff for (var index = 0; index < newEntries.Count; ++index) { var entry = newEntries[index]; if (!entries.Contains(entry)) { entries.Add(entry); entryCommitTargets.Add(new GitCommitTarget()); } } // TODO: Filter .meta files - consider adding them as children of the asset or folder they're supporting // TODO: In stead of completely rebuilding the tree structure, figure out a way to migrate open/closed states from the old tree to the new // Build tree structure tree = new FileTreeNode(FileSystemHelpers.FindCommonPath(entries.Select(e => e.Path)), stateChangeCallback); tree.RepositoryPath = tree.Path; for (var index = 0; index < entries.Count; index++) { var entryPath = entries[index].Path.ToNPath(); if (entryPath.IsChildOf(tree.Path)) { entryPath = entryPath.RelativeTo(tree.Path.ToNPath()); } var node = new FileTreeNode(entryPath, stateChangeCallback) { Target = entryCommitTargets[index] }; if (!string.IsNullOrEmpty(entries[index].ProjectPath)) { node.Icon = AssetDatabase.GetCachedIcon(entries[index].ProjectPath); } BuildTree(tree, node); } OnCommitTreeChange(); }
public FileTreeNode Add(FileTreeNode child) { children.Add(child); return(child); }
private void BuildTree(FileTreeNode parent, FileTreeNode node) { if (string.IsNullOrEmpty(node.Label)) { // TODO: We should probably reassign this target onto the parent? Depends on how we want to handle .meta files for folders return; } node.RepositoryPath = parent.RepositoryPath.ToNPath().Combine(node.Label); parent.Open = !foldedTreeEntries.Contains(parent.RepositoryPath); // Is this node inside a folder? var nodePath = node.Label.ToNPath(); if (nodePath.Elements.Count() > 1) { // Figure out what the root folder is and chop it from the path var root = nodePath.Elements.First(); node.Label = new NPath("").Combine(nodePath.Elements.Skip(1).ToArray()); // Look for a branch matching our root in the existing children var found = false; foreach (var child in parent.Children) { // If we found the branch, continue building from that branch if (child.Label.Equals(root)) { found = true; BuildTree(child, node); break; } } // No existing branch - we will have to add a new one to build from if (!found) { var p = parent.RepositoryPath.ToNPath().Combine(root); BuildTree(parent.Add(new FileTreeNode(root, stateChangeCallback) { RepositoryPath = p }), node); } } else if (nodePath.ExtensionWithDot == ".meta") { // Look for a branch matching our root in the existing children var found = false; foreach (var child in parent.Children) { // If we found the branch, continue building from that branch if (child.Label.Equals(nodePath.Parent.Combine(nodePath.FileNameWithoutExtension))) { found = true; BuildTree(child, node); break; } } if (!found) { parent.Add(node); } } // Not inside a folder - just add this node right here else { parent.Add(node); } }
internal static FileTreeNode BuildTreeRoot(IList <GitStatusEntry> newEntries, List <GitStatusEntry> gitStatusEntries, List <GitCommitTarget> gitCommitTargets, List <string> foldedTreeEntries, Func <string, object> iconLoaderFunc = null) { Guard.ArgumentNotNullOrEmpty(newEntries, "newEntries"); var newEntriesSetByPath = new HashSet <string>(newEntries.Select(entry => entry.Path)); var gitStatusEntriesSetByPath = new HashSet <string>(gitStatusEntries.Select(entry => entry.Path)); // Remove what got nuked for (var index = 0; index < gitStatusEntries.Count;) { if (!newEntriesSetByPath.Contains(gitStatusEntries[index].Path)) { gitStatusEntries.RemoveAt(index); gitCommitTargets.RemoveAt(index); } else { index++; } } // Remove folding state of nuked items for (var index = 0; index < foldedTreeEntries.Count;) { if (newEntries.All(e => e.Path.IndexOf(foldedTreeEntries[index], StringComparison.CurrentCulture) != 0)) { foldedTreeEntries.RemoveAt(index); } else { index++; } } var foldedTreeSet = new HashSet <string>(foldedTreeEntries); // Add new stuff for (var index = 0; index < newEntries.Count; ++index) { var entry = newEntries[index]; if (!gitStatusEntriesSetByPath.Contains(entry.Path)) { gitStatusEntries.Add(entry); gitCommitTargets.Add(new GitCommitTarget()); } } // TODO: In stead of completely rebuilding the tree structure, figure out a way to migrate open/closed states from the old tree to the new // Build tree structure var tree = new FileTreeNode(FileSystemHelpers.FindCommonPath(gitStatusEntries.Select(e => e.Path))); tree.RepositoryPath = tree.Path; for (var index1 = 0; index1 < gitStatusEntries.Count; index1++) { var gitStatusEntry = gitStatusEntries[index1]; var entryPath = gitStatusEntry.Path.ToNPath(); if (entryPath.IsChildOf(tree.Path)) { entryPath = entryPath.RelativeTo(tree.Path.ToNPath()); } var node = new FileTreeNode(entryPath) { Target = gitCommitTargets[index1] }; if (!String.IsNullOrEmpty(gitStatusEntry.ProjectPath)) { node.Icon = iconLoaderFunc?.Invoke(gitStatusEntry.ProjectPath); } BuildChildNode(tree, node, foldedTreeSet); } return(tree); }