Skip to content
Snippets Groups Projects
ChangesetTreeView.cs 10.2 KiB
Newer Older
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
    class ChangesetTreeView : Subview
        private const string BasePathLabel = "{0}";
        private const string NoChangesLabel = "No changes found";
        [SerializeField] private List<GitStatusEntry> entries = new List<GitStatusEntry>();
        [SerializeField] private List<GitCommitTarget> entryCommitTargets = new List<GitCommitTarget>();
        [SerializeField] private List<string> foldedTreeEntries = new List<string>();
        [NonSerialized] private FileTreeNode tree;
        public override void OnGUI()
        {
            GUILayout.BeginVertical();
            {
                // The file tree (when available)
                if (tree != null && entries.Any())
                {
                    // Base path label
                    if (!string.IsNullOrEmpty(tree.Path))
                    {
                        GUILayout.BeginHorizontal();
                        {
                            var iconRect = GUILayoutUtility.GetRect(Styles.CommitIconSize, Styles.CommitIconSize, GUILayout.ExpandWidth(false));
                            iconRect.y += 2;
                            iconRect.x += 2;

                            GUI.DrawTexture(iconRect, Styles.FolderIcon, ScaleMode.ScaleToFit);

                            GUILayout.Label(string.Format(BasePathLabel, tree.Path));
                        }
                        GUILayout.EndHorizontal();
                    }

                    GUILayout.BeginHorizontal();
                    {
                        GUILayout.Space(Styles.TreeIndentation + Styles.TreeRootIndentation);
                        GUILayout.BeginVertical();
                        {
                            // Root nodes
                            foreach (var node in tree.Children)
                            {
                                TreeNode(node);
                            }
                        }
                        GUILayout.EndVertical();
                    }

                    GUILayout.EndHorizontal();

                    // If we have no minimum height calculated, do that now and repaint so it can be used
                    if (Height == 0f && Event.current.type == EventType.Repaint)
                    {
                        Height = GUILayoutUtility.GetLastRect().yMax + Styles.MinCommitTreePadding;
                    }

                    GUILayout.FlexibleSpace();
                }
                else
                {
                    GUILayout.FlexibleSpace();
                    GUILayout.BeginHorizontal();
                    {
                        GUILayout.FlexibleSpace();
                        GUILayout.Label(NoChangesLabel);
                        GUILayout.FlexibleSpace();
                    }
                    GUILayout.EndHorizontal();
                    GUILayout.FlexibleSpace();
                }
            }

            GUILayout.EndVertical();
        }

        private void OnCommitTreeChange()
        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);
        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;
                    {
                        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);
                        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)
                            var statusIcon = Styles.GetFileStatusIcon(entries[idx].Status, false);
                            if (statusIcon != null)
                                GUI.DrawTexture(statusRect, statusIcon);
                    GUILayout.Space(Styles.CommitIconHorizontalPadding);
                // 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();

        public float Height { get; protected set; }
        public bool Readonly { get; set; }

        public IList<GitStatusEntry> Entries
        {
            get { return entries; }
        }

        public IList<GitCommitTarget> CommitTargets
        {
            get { return entryCommitTargets; }
        }