unity editor文件数(支持勾选框)
使用的时候new一个box即可
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class TGFileTreeViewBox<T> where T : OdinMenuEditorWindow
{
    public TGFileTreeView fileTreeView;
    private Vector2 scrollPosition;
    public TGFileTreeViewBox(bool ShowCheckboxes = true)
    {
        fileTreeView = new TGFileTreeView(new List<string>(), ThisRepaint);
        fileTreeView.ShowCheckboxes = ShowCheckboxes;
        //fileTreeView = new TGFileTreeView(GetFilePathsTest(), ThisRepaint);
        fileTreeView.SelectAll();
    }
    public TGFileTreeViewBox(List<string> paths, bool ShowCheckboxes = true)
    {
        //for (int i = 0; i < paths.Count; i++)
        //{
        //    Logger.Log($"文件夹树视图:{paths[i]}");
        //}
        fileTreeView = new TGFileTreeView(paths, ThisRepaint);
        fileTreeView.ShowCheckboxes = ShowCheckboxes;
        //fileTreeView = new TGFileTreeView(GetFilePathsTest(), ThisRepaint);
        //fileTreeView.ToggleExpandAll(true);
        fileTreeView.SelectAll();
    }
    [OnInspectorGUI]
    private void DrawCustomInspector()
    {
        float contentHeight = fileTreeView.GetHeight();
        float maxHeight = Mathf.Min(contentHeight, 250f);
        scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Height(maxHeight));
        fileTreeView.DrawTreeView();
        //if (GUILayout.Button("打印选中路径"))
        //{
        //    foreach (var path in fileTreeView.CheckedPaths)
        //    {
        //        Logger.Log($"选中了--{path}");
        //    }
        //}
        //if (GUILayout.Button("打印未选中路径"))
        //{
        //    foreach (var path in fileTreeView.UncheckedPaths)
        //    {
        //        Logger.Log($"未选中--{path}");
        //    }
        //}
        //if (GUILayout.Button("打印选中文件路径"))
        //{
        //    foreach (var path in fileTreeView.CheckedFilePaths)
        //    {
        //        Logger.Log($"选中文件了--{path}");
        //    }
        //}
        GUILayout.EndScrollView();
    }
    // 刷新界面的方法
    public static void ThisRepaint()
    {
        var window = UnityEditor.EditorWindow.GetWindow<T>();
        window?.Repaint();
    }
    private static List<string> GetFilePathsTest()
    {
        // 直接定义路径数据
        return new List<string>
        {
            "Assets/_Test/sedan.prefab",
            "Assets/_Test/mat/New Material.mat",
            "Assets/_Test/mat/New Material 1.mat",
            "Assets/_Test/mat/New Material 2.mat",
            "Assets/_Test/mat/New Material 3.mat",
            "Assets/_Test/New Material 1.mat",
            "Assets/_Test/New Material 2.mat",
            "Assets/_Test/New Material 3.mat",
            "Assets/_Test/New Material 4.mat",
            "Assets/_Test/New Material.mat",
            "Assets/_Test/source/sedan.fbx",
            "Assets/_Test/source/sedan 1.fbx",
            "Assets/_Test/TestNull",
            "Assets/_Test/textures/internal_ground_ao_texture.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 1.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 2.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 3.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 4.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 5.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 6.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 7.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 8.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 9.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture 10.jpeg",
            "Assets/_Test/textures/internal_ground_ao_texture.tga"
        };
    }
}
using Sirenix.OdinInspector;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class TGFileTreeView
{
    private List<string> allfilePaths = new List<string>(); // 传入的路径
    private List<string> filePaths = new List<string>(); // 当前有效路径,经过过滤
    private List<string> checkedPaths = new List<string>(); // 勾选的路径
    private List<string> uncheckedPaths = new List<string>(); // 未勾选的路径
    private List<string> checkedFilePaths = new List<string>(); // 选中的文件路径列表
    private Dictionary<string, bool> foldoutStates = new Dictionary<string, bool>();
    private Dictionary<string, bool> checkboxStates = new Dictionary<string, bool>();
    private string selectedFilePath = "";
    private Action repaintAction;
    private int treeHeight;
    private bool isExpandAll = true;
    public List<string> CheckedPaths => checkedPaths;
    public List<string> UncheckedPaths => uncheckedPaths;
    public List<string> AllPaths => allfilePaths; // 传入的路径列表
    public List<string> CurrentValidPaths => filePaths; // 当前有效路径
    public List<string> CheckedFilePaths //选中的文件路径列表
    {
        get
        {
            checkedFilePaths.Clear();
            foreach (var item in checkedPaths)
            {
                if (Path.HasExtension(item))
                {
                    checkedFilePaths.Add(item);
                }
            }
            return checkedFilePaths;
        }
    }
    public Action<string, bool> OnCheckStateChanged; // path 和状态
    public bool ShowCheckboxes = true; // 全局控制是否显示勾选框
    public TGFileTreeView(List<string> paths, System.Action repaintAction)
    {
        allfilePaths = paths;
        this.repaintAction = repaintAction;
        InitializePaths();
    }
    // 初始化时计算有效路径,并根据勾选状态更新 CheckedPaths 和 UncheckedPaths
    private void InitializePaths()
    {
        // 过滤出有效路径
        filePaths = allfilePaths.Where(path => File.Exists(path) || Directory.Exists(path)).ToList();
        // 初始化勾选与取消勾选的路径
        checkedPaths = new List<string>();
        uncheckedPaths = new List<string>();
        foreach (var path in filePaths)
        {
            checkboxStates[path] = false;
            uncheckedPaths.Add(path);
        }
    }
    public void DrawTreeView()
    {
        CheckForDeletedFiles();
        if (filePaths.Count == 0)
        {
            EditorGUILayout.LabelField("没有可用的文件路径!!");
            return;
        }
        DrawFileTree(filePaths);
    }
    private void CheckForDeletedFiles()
    {
        filePaths.Clear();
        foreach (var path in allfilePaths)
        {
            if (File.Exists(path) || Directory.Exists(path))
            {
                filePaths.Add(path);
            }
        }
    }
    private void DrawFileTree(List<string> paths)
    {
        TGTreeNode root = BuildTree(paths);
        treeHeight = GetTotalHeight(root);
        DrawNode(root, 0);
    }
    private void DrawNode(TGTreeNode node, int indentLevel)
    {
        EditorGUILayout.BeginHorizontal();
        if (!foldoutStates.ContainsKey(node.Path))
            foldoutStates[node.Path] = isExpandAll;
        if (!checkboxStates.ContainsKey(node.Path))
        {
            checkboxStates[node.Path] = false;
            UpdateCheckboxLists(node.Path, false);
        }
        float lineHeight = 20f;
        float indentX = indentLevel * 20f;
        float foldoutWidth = 14f;
        float spacingA = 20f; // 箭头和勾选框之间
        float toggleWidth = 18f;
        float spacingB = 4f; // 勾选框和图标之间
        float iconWidth = 20f;
        float spacingC = 4f; // 图标和文件名之间
        Rect lineRect = GUILayoutUtility.GetRect(0, lineHeight, GUILayout.ExpandWidth(true));
        float x = lineRect.x + indentX;
        // 折叠箭头
        if (node.IsFolder && node.Children.Count > 0)
        {
            Rect foldoutRect = new Rect(x, lineRect.y + 3, foldoutWidth, 14f);
            foldoutStates[node.Path] = EditorGUI.Foldout(foldoutRect, foldoutStates[node.Path], GUIContent.none, false);
            x += foldoutWidth + spacingA;
        }
        else
        {
            x += foldoutWidth + spacingA;
        }
        // 勾选框
        if (ShowCheckboxes)
        {
            // 勾选框
            Rect toggleRect = new Rect(x, lineRect.y + 1, toggleWidth, 18f);
            bool oldChecked = checkboxStates[node.Path];
            bool newChecked = GUI.Toggle(toggleRect, oldChecked, GUIContent.none);
            if (oldChecked != newChecked)
            {
                checkboxStates[node.Path] = newChecked;
                UpdateCheckboxLists(node.Path, newChecked);
            }
            x += toggleWidth + spacingB;
        }
        else
        {
            x += spacingB; // 保留缩进对齐
        }
        // 图标
        Texture icon = GetIconForPath(node.Path, node.IsFolder);
        if (icon != null)
        {
            Rect iconRect = new Rect(x, lineRect.y, iconWidth, 20f);
            GUI.DrawTexture(iconRect, icon);
            x += iconWidth + spacingC;
        }
        // 文件名
        Rect labelRect = new Rect(x, lineRect.y, lineRect.width - (x - lineRect.x), lineHeight);
        GUIContent labelContent = new GUIContent(GetFileName(node.Path));
        if (selectedFilePath == node.Path)
            EditorGUI.DrawRect(lineRect, new Color(0.24f, 0.49f, 0.90f, 0.3f));
        HandleClick(labelRect, node.Path);
        GUI.Label(labelRect, labelContent);
        EditorGUILayout.EndHorizontal();
        if (node.IsFolder && foldoutStates[node.Path])
        {
            foreach (var child in node.Children)
            {
                DrawNode(child, indentLevel + 1);
            }
        }
    }
    private void UpdateCheckboxLists(string path, bool isChecked)
    {
        checkboxStates[path] = isChecked;
        if (isChecked)
        {
            if (!checkedPaths.Contains(path)) checkedPaths.Add(path);
            uncheckedPaths.Remove(path);
        }
        else
        {
            if (!uncheckedPaths.Contains(path)) uncheckedPaths.Add(path);
            checkedPaths.Remove(path);
        }
        // 更新子项状态
        var keys = checkboxStates.Keys.ToList();
        foreach (var kvp in keys)
        {
            if (kvp != path && kvp.StartsWith(path + "/"))
            {
                checkboxStates[kvp] = isChecked;
                if (isChecked)
                {
                    if (!checkedPaths.Contains(kvp)) checkedPaths.Add(kvp);
                    uncheckedPaths.Remove(kvp);
                }
                else
                {
                    if (!uncheckedPaths.Contains(kvp)) uncheckedPaths.Add(kvp);
                    checkedPaths.Remove(kvp);
                }
            }
        }
        OnCheckStateChanged?.Invoke(path, isChecked);
        UpdateParentCheckboxStates(path);
    }
    private void UpdateParentCheckboxStates(string path)
    {
        string parentPath = GetParentPath(path);
        if (string.IsNullOrEmpty(parentPath)) return;
        var childPaths = checkboxStates.Keys
            .Where(k => GetParentPath(k) == parentPath)
            .ToList();
        bool anyChildChecked = childPaths.Any(k => checkboxStates.ContainsKey(k) && checkboxStates[k]);
        checkboxStates[parentPath] = anyChildChecked;
        if (anyChildChecked)
        {
            if (!checkedPaths.Contains(parentPath)) checkedPaths.Add(parentPath);
            uncheckedPaths.Remove(parentPath);
        }
        else
        {
            if (!uncheckedPaths.Contains(parentPath)) uncheckedPaths.Add(parentPath);
            checkedPaths.Remove(parentPath);
        }
        UpdateParentCheckboxStates(parentPath);
    }
    private string GetParentPath(string path)
    {
        if (string.IsNullOrEmpty(path)) return null;
        int lastSlashIndex = path.LastIndexOf('/');
        if (lastSlashIndex <= 0) return null;
        return path.Substring(0, lastSlashIndex);
    }
    private void HandleClick(Rect rect, string path)
    {
        Event e = Event.current;
        if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition))
        {
            if (e.clickCount == 1)
            {
                selectedFilePath = path;
                repaintAction?.Invoke();
            }
            else if (e.clickCount == 2)
            {
                if (File.Exists(path) || Directory.Exists(path))
                {
                    EditorUtility.FocusProjectWindow();
                    UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);
                    if (asset != null)
                    {
                        EditorGUIUtility.PingObject(asset);
                        Selection.activeObject = asset;
                    }
                }
            }
            e.Use();
        }
    }
    private TGTreeNode BuildTree(List<string> paths)
    {
        TGTreeNode root = new TGTreeNode("Assets", true);
        foreach (string path in paths)
        {
            string[] parts = path.Split('/');
            TGTreeNode current = root;
            for (int i = 1; i < parts.Length; i++)
            {
                string part = parts[i];
                string fullPath = string.Join("/", parts, 0, i + 1);
                bool isFile = (i == parts.Length - 1) && Path.HasExtension(part);
                if (!current.HasChild(part))
                {
                    current.Children.Add(new TGTreeNode(fullPath, !isFile));
                }
                current = current.GetChild(part);
            }
        }
        return root;
    }
    private string GetFileName(string path)
    {
        return Path.GetFileName(path);
    }
    private int GetTotalHeight(TGTreeNode node)
    {
        int totalHeight = 25;
        bool isFolded = !foldoutStates.ContainsKey(node.Path) || !foldoutStates[node.Path];
        if (node.IsFolder && !isFolded)
        {
            foreach (var child in node.Children)
            {
                totalHeight += GetTotalHeight(child);
            }
        }
        return totalHeight;
    }
    public int GetHeight()
    {
        return treeHeight;
    }
    public void ToggleExpandAll(bool _isExpandAll)
    {
        isExpandAll = _isExpandAll;
        foldoutStates.Clear();
        repaintAction?.Invoke();
    }
    // 全选
    public void SelectAll()
    {
        foreach (var path in filePaths)
        {
            if (!checkboxStates.ContainsKey(path)) continue;
            if (!checkboxStates[path]) // 只有未勾选的才修改
            {
                checkboxStates[path] = true;
                UpdateCheckboxLists(path, true);
            }
        }
        repaintAction?.Invoke(); // 更新界面
    }
    // 全不选
    public void DeselectAll()
    {
        foreach (var path in filePaths)
        {
            if (!checkboxStates.ContainsKey(path)) continue;
            if (checkboxStates[path]) // 只有勾选的才修改
            {
                checkboxStates[path] = false;
                UpdateCheckboxLists(path, false);
            }
        }
        repaintAction?.Invoke(); // 更新界面
    }
    private Texture GetIconForPath(string path, bool isFolder)
    {
        if (isFolder) return EditorGUIUtility.IconContent("Folder Icon").image;
        string extension = Path.GetExtension(path).ToLower();
        switch (extension)
        {
            case ".prefab": return EditorGUIUtility.IconContent("Prefab Icon").image;
            case ".fbx":
            case ".obj":
            case ".blend": return EditorGUIUtility.IconContent("Mesh Icon").image;
            case ".mat": return EditorGUIUtility.IconContent("Material Icon").image;
            case ".shader":
            case ".compute": return EditorGUIUtility.IconContent("Shader Icon").image;
            case ".png":
            case ".jpg": return EditorGUIUtility.IconContent("Texture Icon").image;
            case ".anim": return EditorGUIUtility.IconContent("Animation Icon").image;
            case ".controller": return EditorGUIUtility.IconContent("AnimatorController Icon").image;
            case ".unity": return EditorGUIUtility.IconContent("SceneAsset Icon").image;
            case ".ttf":
            case ".otf": return EditorGUIUtility.IconContent("Font Icon").image;
            default: return EditorGUIUtility.IconContent("DefaultAsset Icon").image;
        }
    }
}
public class TGTreeNode
{
    public string Path { get; private set; }
    public bool IsFolder { get; private set; }
    public List<TGTreeNode> Children { get; private set; }
    public TGTreeNode(string path, bool isFolder)
    {
        Path = path;
        IsFolder = isFolder;
        Children = new List<TGTreeNode>();
    }
    public bool HasChild(string path)
    {
        return Children.Exists(child => child.Path.EndsWith(path));
    }
    public TGTreeNode GetChild(string path)
    {
        return Children.Find(child => child.Path.EndsWith(path));
    }
}


















