unityEditor Note 编辑器笔记本

cs 复制代码
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Security.Cryptography;

public class UnityNotepad : EditorWindow
{
    // 数据模型
    [System.Serializable]
    private class NoteTab
    {
        public string title = "新笔记";
        public string content = "";
        public Vector2 scrollPosition;
        public bool isModified = false;
        public string filePath = "";
        public DateTime lastModified;
        public string lastSavedHash = "";
        public DateTime lastChangeTime;

        public NoteTab()
        {
            lastModified = DateTime.Now;
            lastChangeTime = DateTime.Now;
        }
    }

    // 备份文件信息
    private class BackupFileInfo
    {
        public string filePath;
        public string fileName;
        public string originalName;
        public DateTime backupTime;
        public long fileSize;
        public string relativePath;
    }

    // 主页模式
    private enum MainPageMode { Home, Backup }

    // 序列化存储
    [System.Serializable]
    private class NotepadData
    {
        public List<NoteTab> tabs = new List<NoteTab>();
        public int selectedTabIndex = 0;
        public bool autoSaveEnabled = true;
        public string notesFolder = "Assets/Editor/Notes/";
        public string backupFolder = "Assets/Editor/Notes/Backup/";
        public int fontSize = 14;
        public bool wordWrap = true;
        public bool showNoteList = true;
        public int mainPageTabIndex = -1; // 主页标签页索引
        public MainPageMode mainPageMode = MainPageMode.Home; // 主页当前显示模式
    }

    // 编辑器状态
    private NotepadData data;
    private Vector2 tabsScrollPos;
    private bool showSettings = false;
    private GUIStyle textAreaStyle;
    private string searchText = "";
    private List<int> searchResults = new List<int>();
    private int currentSearchIndex = 0;
    private int lastSelectedTabIndex = -1;

    private const string DATA_KEY = "UnityNotepad_Data_Fixed";
    private const int AUTO_SAVE_INTERVAL = 30;

    private DateTime lastAutoSaveTime;
    private Dictionary<int, double> scheduledSaves = new Dictionary<int, double>();
    private HashSet<string> openedFiles = new HashSet<string>();

    // 焦点管理 - 完全禁用自动焦点
    private string textAreaControlName = "NoteTextArea";
    private bool neverAutoFocus = true; // 完全禁用自动焦点

    // 笔记列表管理
    private List<string> noteFiles = new List<string>();
    private Vector2 noteListScrollPos;
    private string[] textExtensions = { ".txt", ".md", ".json", ".cs", ".xml", ".yaml", ".yml", ".html", ".css", ".js" };
    private DateTime lastNoteListRefresh = DateTime.MinValue;
    private const double NOTE_LIST_REFRESH_INTERVAL = 2.0;

    // 备份文件管理
    private List<BackupFileInfo> backupFiles = new List<BackupFileInfo>();
    private Vector2 backupListScrollPos;
    private DateTime lastBackupListRefresh = DateTime.MinValue;
    private BackupFileInfo selectedBackupForPreview = null;
    
    // 初始化窗口
    [MenuItem("Tools/Unity Notepad %#m")]
    public static void ShowWindow()
    {
        var window = GetWindow<UnityNotepad>();
        window.titleContent = new GUIContent("Unity Notepad", EditorGUIUtility.IconContent("TextAsset Icon").image);
        window.minSize = new Vector2(500, 400);
        window.Show();
    }

    private void OnEnable()
    {
        LoadData();
        CreateTextAreaStyle();
        lastAutoSaveTime = DateTime.Now;
        EditorApplication.update += OnEditorUpdate;

        UpdateOpenedFiles();
        RefreshNoteList();
        RefreshBackupList();

        // 确保有主页标签页
        EnsureMainPageTab();
    }

    private void OnDisable()
    {
        SaveAllModifiedTabs();
        SaveData();
        EditorApplication.update -= OnEditorUpdate;
    }

    private void OnFocus()
    {
        RefreshNoteList();
        RefreshBackupList();
    }

    private void UpdateOpenedFiles()
    {
        openedFiles.Clear();
        if (data != null && data.tabs != null)
        {
            foreach (var tab in data.tabs)
            {
                if (!string.IsNullOrEmpty(tab.filePath))
                {
                    openedFiles.Add(Path.GetFullPath(tab.filePath));
                }
            }
        }
    }

    private void OnGUI()
    {
        if (data == null)
        {
            data = new NotepadData();
        }

        // 处理键盘快捷键
        HandleKeyboardShortcuts();

        // 绘制界面
        DrawToolbar();
        DrawTabsHeader();
        DrawContentArea();
        DrawStatusBar();
    }

    #region GUI 绘制方法
    private void DrawToolbar()
    {
        GUILayout.BeginHorizontal(EditorStyles.toolbar);

        // 文件操作按钮
        if (GUILayout.Button("新建", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            CreateNewTab();
        }

        if (GUILayout.Button("打开", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            OpenNoteFile();
        }

        if (GUILayout.Button("保存", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            SaveCurrentTab();
        }

        if (GUILayout.Button("另存为", EditorStyles.toolbarButton, GUILayout.Width(50)))
        {
            SaveAsCurrentTab();
        }

        GUILayout.Space(10);

        // 编辑操作按钮
        if (GUILayout.Button("复制", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            var currentTab = GetCurrentTab();
            if (currentTab != null && !string.IsNullOrEmpty(currentTab.content))
                EditorGUIUtility.systemCopyBuffer = currentTab.content;
        }

        if (GUILayout.Button("粘贴", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            AppendToCurrentTab(EditorGUIUtility.systemCopyBuffer);
        }

        GUILayout.Space(10);

        // 搜索
        GUILayout.Label("搜索:", EditorStyles.miniLabel);
        string newSearchText = GUILayout.TextField(searchText, GUILayout.Width(150));
        if (newSearchText != searchText)
        {
            searchText = newSearchText;
            PerformSearch();
        }

        if (searchResults.Count > 0)
        {
            GUILayout.Label($"{currentSearchIndex + 1}/{searchResults.Count}", EditorStyles.miniLabel);
            if (GUILayout.Button("↑", EditorStyles.toolbarButton, GUILayout.Width(20)))
                NavigateSearch(-1);
            if (GUILayout.Button("↓", EditorStyles.toolbarButton, GUILayout.Width(20)))
                NavigateSearch(1);
        }

        GUILayout.FlexibleSpace();

        // 设置按钮
        showSettings = GUILayout.Toggle(showSettings, "设置", EditorStyles.toolbarButton, GUILayout.Width(40));

        // 主页按钮
        if (GUILayout.Button("主页", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
            {
                data.selectedTabIndex = data.mainPageTabIndex;
                data.mainPageMode = MainPageMode.Home;
            }
        }

        // 备份按钮
        if (GUILayout.Button("备份", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
            {
                data.selectedTabIndex = data.mainPageTabIndex;
                data.mainPageMode = MainPageMode.Backup;
            }
        }

        // 关闭笔记本按钮
        if (GUILayout.Button("关闭", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            CloseNotepad();
        }

        GUILayout.EndHorizontal();

        if (showSettings)
            DrawSettingsPanel();
    }

    private void DrawTabsHeader()
    {
        // 确保有标签页(至少有一个主页标签)
        if (data.tabs.Count == 0)
        {
            CreateMainPageTab();
        }

        GUILayout.BeginHorizontal(EditorStyles.toolbar);

        // 显示所有普通笔记标签
        for (int i = 0; i < data.tabs.Count; i++)
        {
            // 跳过主页标签
            if (i == data.mainPageTabIndex) continue;
            
            var tab = data.tabs[i];
            string tabName = (string.IsNullOrEmpty(tab.title) ? "未命名" : tab.title) + (tab.isModified ? " *" : "");

            if (DrawTabButton(tabName, i == data.selectedTabIndex, 120, i))
            {
                data.selectedTabIndex = i;
                // 切换标签时清除焦点,避免全选
                GUI.FocusControl(null);
            }
        }

        GUILayout.FlexibleSpace();

        // 添加新标签按钮
        if (GUILayout.Button("+", EditorStyles.toolbarButton, GUILayout.Width(30)))
        {
            CreateNewTab();
        }

        // 关闭所有标签按钮
        if (GUILayout.Button("× 全部", EditorStyles.toolbarButton, GUILayout.Width(50)))
        {
            CloseAllTabs();
        }

        GUILayout.EndHorizontal();
    }

    private bool DrawTabButton(string label, bool isSelected, float width, int tabIndex)
    {
        bool clicked = false;
        Rect tabRect = GUILayoutUtility.GetRect(
            new GUIContent(label + "   ×"),
            EditorStyles.toolbarButton,
            GUILayout.Width(width),
            GUILayout.Height(20)
        );

        if (isSelected)
        {
            EditorGUI.DrawRect(tabRect, new Color(0.2f, 0.4f, 0.8f, 0.8f));
        }

        Rect closeButtonRect = new Rect(
            tabRect.xMax - 20,
            tabRect.y + (tabRect.height - 12) / 2,
            12,
            12
        );

        Rect textRect = new Rect(
            tabRect.x,
            tabRect.y,
            tabRect.width - 20,
            tabRect.height
        );

        Event currentEvent = Event.current;

        if (currentEvent.type == EventType.MouseDown && tabRect.Contains(currentEvent.mousePosition))
        {
            if (currentEvent.button == 0)
            {
                if (closeButtonRect.Contains(currentEvent.mousePosition))
                {
                    CloseTab(tabIndex);
                    currentEvent.Use();
                }
                else
                {
                    clicked = true;
                    currentEvent.Use();
                }
            }
        }

        GUIStyle textStyle = new GUIStyle(EditorStyles.toolbarButton)
        {
            alignment = TextAnchor.MiddleLeft,
            padding = new RectOffset(5, 0, 0, 0)
        };

        GUI.Label(textRect, label, textStyle);

        GUIStyle closeStyle = new GUIStyle(EditorStyles.miniLabel)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = 10,
            normal = { textColor = closeButtonRect.Contains(currentEvent.mousePosition) ? Color.red : Color.gray }
        };

        GUI.Label(closeButtonRect, "×", closeStyle);

        return clicked;
    }

    private void DrawContentArea()
    {
        if (data.tabs.Count == 0)
        {
            CreateMainPageTab();
            return;
        }

        var currentTab = GetCurrentTab();
        if (currentTab == null)
        {
            data.selectedTabIndex = Mathf.Max(0, data.tabs.Count - 1);
            return;
        }

        // 检查标签页是否切换
        if (lastSelectedTabIndex != data.selectedTabIndex)
        {
            lastSelectedTabIndex = data.selectedTabIndex;
            // 标签页切换时清除焦点
            GUI.FocusControl(null);
        }

        EditorGUI.BeginChangeCheck();

        currentTab.scrollPosition = EditorGUILayout.BeginScrollView(currentTab.scrollPosition);

        // 如果是主页标签,绘制主页页面
        if (data.selectedTabIndex == data.mainPageTabIndex)
        {
            DrawMainPage();
        }
        else
        {
            // 正常笔记标签
            textAreaStyle.fontSize = data.fontSize;
            textAreaStyle.wordWrap = data.wordWrap;

            // 使用TextArea但不自动聚焦 - 这里的关键是永远不要调用GUI.SetNextControlName
            string newContent = EditorGUILayout.TextArea(
                currentTab.content,
                textAreaStyle,
                GUILayout.ExpandHeight(true)
            );

            if (EditorGUI.EndChangeCheck() && newContent != currentTab.content)
            {
                currentTab.content = newContent;
                currentTab.isModified = true;
                currentTab.lastModified = DateTime.Now;
                currentTab.lastChangeTime = DateTime.Now;
                ScheduleAutoSave(currentTab);
            }
        }

        EditorGUILayout.EndScrollView();
    }

    private void DrawMainPage()
    {
        GUILayout.BeginVertical();

        // 顶部导航栏
        GUILayout.BeginHorizontal(EditorStyles.toolbar);
        
        bool homeSelected = data.mainPageMode == MainPageMode.Home;
        bool backupSelected = data.mainPageMode == MainPageMode.Backup;
        
        // 主页按钮
        if (GUILayout.Toggle(homeSelected, " 主页 ", EditorStyles.toolbarButton))
        {
            data.mainPageMode = MainPageMode.Home;
        }
        
        // 备份按钮
        if (GUILayout.Toggle(backupSelected, " 备份管理 ", EditorStyles.toolbarButton))
        {
            data.mainPageMode = MainPageMode.Backup;
        }
        
        GUILayout.FlexibleSpace();
        
        // 快速操作按钮
        if (data.mainPageMode == MainPageMode.Home)
        {
            if (GUILayout.Button("新建笔记", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                CreateNewTab();
            }
            if (GUILayout.Button("打开文件", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                OpenNoteFile();
            }
        }
        else if (data.mainPageMode == MainPageMode.Backup)
        {
            if (GUILayout.Button("备份所有", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                BackupAllNotes();
            }
            if (GUILayout.Button("清理旧备份", EditorStyles.toolbarButton, GUILayout.Width(80)))
            {
                CleanupOldBackups(7);
            }
        }
        
        GUILayout.EndHorizontal();
        
        GUILayout.Space(10);

        // 根据模式显示不同内容
        if (data.mainPageMode == MainPageMode.Home)
        {
            DrawHomeContent();
        }
        else
        {
            DrawBackupContent();
        }

        GUILayout.EndVertical();
    }

    private void DrawHomeContent()
    {
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        GUILayout.BeginVertical(GUILayout.Width(350));

        GUILayout.Label(EditorGUIUtility.IconContent("TextAsset Icon"), GUILayout.Width(48), GUILayout.Height(48));
        GUILayout.Space(10);

        GUIStyle titleStyle = new GUIStyle(EditorStyles.largeLabel)
        {
            fontSize = 18,
            alignment = TextAnchor.MiddleCenter,
            fontStyle = FontStyle.Bold
        };
        GUILayout.Label("Unity 记事本", titleStyle);
        GUILayout.Space(20);

        GUILayout.Label("使用说明:", EditorStyles.boldLabel);
        GUILayout.Space(8);

        EditorGUILayout.HelpBox(
            "• 点击「新建」按钮创建新笔记\n" +
            "• 点击「打开」按钮导入现有笔记\n" +
            "• 点击「保存」按钮保存当前笔记\n" +
            "• 使用「Ctrl+S」快捷键快速保存\n" +
            "• 使用「Ctrl+N」快捷键新建笔记\n" +
            "• 点击标签右侧的「×」关闭标签\n" +
            "• 支持自动保存和搜索功能\n" +
            "• 点击「备份」进入备份管理",
            MessageType.Info
        );

        GUILayout.Space(20);

        // 快速访问面板
        GUILayout.Label("快速访问", EditorStyles.boldLabel);
        GUILayout.Space(5);
        
        GUILayout.BeginHorizontal();
        if (GUILayout.Button(" 新建笔记 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            CreateNewTab();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 打开文件 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            OpenNoteFile();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 备份管理 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            data.mainPageMode = MainPageMode.Backup;
        }
        GUILayout.EndHorizontal();
        
        GUILayout.Space(10);
        
        // 当前笔记快速备份
        var currentTab = GetCurrentTab();
        if (currentTab != null && data.selectedTabIndex != data.mainPageTabIndex && 
            !string.IsNullOrEmpty(currentTab.filePath))
        {
            GUILayout.BeginVertical("Box");
            GUILayout.Label("当前笔记:", EditorStyles.miniBoldLabel);
            
            GUILayout.BeginHorizontal();
            GUILayout.Label(currentTab.title, EditorStyles.label, GUILayout.ExpandWidth(true));
            
            if (GUILayout.Button("快速备份", EditorStyles.miniButton, GUILayout.Width(80)))
            {
                BackupCurrentNote();
            }
            GUILayout.EndHorizontal();
            
            GUILayout.EndVertical();
        }

        GUILayout.EndVertical();

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.Space(20);

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
        GUILayout.Space(10);

        DrawNoteList();
    }

    private void DrawBackupContent()
    {
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        GUILayout.BeginVertical(GUILayout.Width(400));

        GUILayout.Label(EditorGUIUtility.IconContent("d_SaveAs").image, GUILayout.Width(48), GUILayout.Height(48));
        GUILayout.Space(10);

        GUIStyle titleStyle = new GUIStyle(EditorStyles.largeLabel)
        {
            fontSize = 18,
            alignment = TextAnchor.MiddleCenter,
            fontStyle = FontStyle.Bold
        };
        GUILayout.Label("备份管理", titleStyle);
        GUILayout.Space(20);

        EditorGUILayout.HelpBox(
            "• 这里显示所有备份的笔记文件\n" +
            "• 备份文件名格式:原文件名_备份时间.扩展名\n" +
            "• 点击备份文件名可以打开查看备份内容\n" +
            "• 可以删除不需要的备份文件\n" +
            "• 备份文件夹:" + data.backupFolder,
            MessageType.Info
        );

        GUILayout.Space(20);

        // 快速操作面板
        GUILayout.Label("快速操作", EditorStyles.boldLabel);
        GUILayout.Space(5);
        
        GUILayout.BeginHorizontal();
        
        // 备份当前笔记按钮(如果当前是笔记标签)
        if (data.selectedTabIndex != data.mainPageTabIndex)
        {
            var currentTab = GetCurrentTab();
            if (currentTab != null && !string.IsNullOrEmpty(currentTab.filePath))
            {
                if (GUILayout.Button(" 备份当前笔记 ", GUILayout.Height(35), GUILayout.Width(120)))
                {
                    BackupCurrentNote();
                }
                GUILayout.Space(10);
            }
        }

        if (GUILayout.Button(" 备份所有笔记 ", GUILayout.Height(35), GUILayout.Width(120)))
        {
            BackupAllNotes();
        }
        GUILayout.Space(10);

        if (GUILayout.Button(" 刷新列表 ", GUILayout.Height(35), GUILayout.Width(80)))
        {
            RefreshBackupList();
        }
        GUILayout.EndHorizontal();
        
        GUILayout.Space(10);
        
        GUILayout.BeginHorizontal();
        if (GUILayout.Button(" 打开备份文件夹 ", GUILayout.Height(35), GUILayout.Width(130)))
        {
            OpenBackupFolder();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 清理7天前备份 ", GUILayout.Height(35), GUILayout.Width(130)))
        {
            CleanupOldBackups(7);
        }
        GUILayout.EndHorizontal();

        GUILayout.EndVertical();

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.Space(20);

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
        GUILayout.Space(10);

        DrawBackupList();
    }

    private void DrawNoteList()
    {
        // 刷新笔记列表(如果需要)
        double timeSinceStartup = EditorApplication.timeSinceStartup;
        TimeSpan timeSinceLastRefresh = DateTime.Now - lastNoteListRefresh;

        if (timeSinceLastRefresh.TotalSeconds > NOTE_LIST_REFRESH_INTERVAL)
        {
            RefreshNoteList();
        }

        GUILayout.BeginHorizontal();
        GUILayout.Label("笔记文件夹: ", EditorStyles.boldLabel);
        GUILayout.Label(data.notesFolder, EditorStyles.wordWrappedLabel);
        GUILayout.FlexibleSpace();

        data.showNoteList = GUILayout.Toggle(data.showNoteList, "显示列表", EditorStyles.miniButton, GUILayout.Width(80));

        if (GUILayout.Button("刷新", EditorStyles.miniButton, GUILayout.Width(50)))
        {
            RefreshNoteList();
        }
        GUILayout.EndHorizontal();

        if (!data.showNoteList) return;

        GUILayout.Space(5);

        if (noteFiles.Count == 0)
        {
            EditorGUILayout.HelpBox($"笔记文件夹中没有找到文本文件。\n支持的格式: {string.Join(", ", textExtensions)}", MessageType.Info);
            return;
        }

        GUILayout.Label($"找到 {noteFiles.Count} 个笔记:", EditorStyles.miniBoldLabel);

        noteListScrollPos = GUILayout.BeginScrollView(noteListScrollPos, GUILayout.Height(200));

        foreach (string filePath in noteFiles)
        {
            try
            {
                string fileName = Path.GetFileName(filePath);
                string relativePath = GetRelativePath(filePath);
                DateTime lastModified = File.GetLastWriteTime(filePath);
                string fileInfo = $"{fileName} ({GetFileSize(filePath)}) - {lastModified:yyyy-MM-dd HH:mm}";

                GUILayout.BeginHorizontal(EditorStyles.helpBox);

                // 使用文件名为按钮,点击即可打开
                if (GUILayout.Button(fileName, EditorStyles.miniButton, GUILayout.ExpandWidth(true)))
                {
                    OpenNoteFile(filePath);
                }

                GUILayout.Space(5);

                // 显示更多信息
                GUILayout.BeginVertical(GUILayout.Width(100));
                GUILayout.Label(GetFileSize(filePath), EditorStyles.miniLabel);
                GUILayout.Label(lastModified.ToString("yyyy-MM-dd"), EditorStyles.miniLabel);
                GUILayout.EndVertical();

                // 显示是否已打开
                if (openedFiles.Contains(Path.GetFullPath(filePath)))
                {
                    GUILayout.Label("✓", EditorStyles.miniLabel, GUILayout.Width(20));
                }

                GUILayout.Space(5);

                // 添加删除按钮
                if (GUILayout.Button("×", EditorStyles.miniButton, GUILayout.Width(25)))
                {
                    DeleteNoteFile(filePath);
                }

                GUILayout.EndHorizontal();

                GUILayout.Space(2);
            }
            catch (Exception)
            {
                continue;
            }
        }

        GUILayout.EndScrollView();
    }

    private void DrawBackupList()
    {
        // 刷新备份列表(如果需要)
        TimeSpan timeSinceLastRefresh = DateTime.Now - lastBackupListRefresh;

        if (timeSinceLastRefresh.TotalSeconds > NOTE_LIST_REFRESH_INTERVAL)
        {
            RefreshBackupList();
        }

        GUILayout.BeginHorizontal();
        GUILayout.Label("备份文件夹: ", EditorStyles.boldLabel);
        GUILayout.Label(data.backupFolder, EditorStyles.wordWrappedLabel);
        GUILayout.FlexibleSpace();

        if (GUILayout.Button("打开文件夹", EditorStyles.miniButton, GUILayout.Width(80)))
        {
            OpenBackupFolder();
        }

        GUILayout.Space(5);

        if (GUILayout.Button("刷新", EditorStyles.miniButton, GUILayout.Width(50)))
        {
            RefreshBackupList();
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(5);

        if (backupFiles.Count == 0)
        {
            EditorGUILayout.HelpBox($"备份文件夹中没有找到备份文件。\n点击上面的按钮备份当前笔记或所有笔记。", MessageType.Info);
            return;
        }

        GUILayout.Label($"找到 {backupFiles.Count} 个备份文件:", EditorStyles.miniBoldLabel);

        backupListScrollPos = GUILayout.BeginScrollView(backupListScrollPos, GUILayout.ExpandHeight(true));

        // 按备份时间倒序排序
        var sortedBackups = backupFiles.OrderByDescending(b => b.backupTime).ToList();

        foreach (var backup in sortedBackups)
        {
            GUILayout.BeginVertical(EditorStyles.helpBox);

            // 备份文件信息行
            GUILayout.BeginHorizontal();

            GUILayout.BeginVertical(GUILayout.ExpandWidth(true));

            // 原始文件名
            GUILayout.Label(backup.originalName, EditorStyles.boldLabel);

            // 备份时间
            GUILayout.Label($"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}", EditorStyles.miniLabel);

            // 文件大小
            GUILayout.Label($"大小: {FormatFileSize(backup.fileSize)}", EditorStyles.miniLabel);

            GUILayout.EndVertical();

            // 按钮区域
            GUILayout.BeginVertical(GUILayout.Width(200));

            GUILayout.BeginHorizontal();

            // 打开备份按钮
            if (GUILayout.Button("预览", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                ShowBackupPreview(backup);
            }

            // 恢复备份按钮
            if (GUILayout.Button("恢复", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                RestoreBackup(backup);
            }

            // 删除备份按钮
            if (GUILayout.Button("删除", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                DeleteBackupFile(backup.filePath);
            }

            GUILayout.EndHorizontal();

            GUILayout.EndVertical();

            GUILayout.EndHorizontal();

            // 预览内容(展开时显示)
            if (selectedBackupForPreview == backup)
            {
                GUILayout.Space(5);
                DrawBackupPreview(backup);
            }

            GUILayout.EndVertical();

            GUILayout.Space(3);
        }

        GUILayout.EndScrollView();
    }
    
    private void ShowBackupPreview(BackupFileInfo backup)
    {
        try
        {
            string content = File.ReadAllText(backup.filePath, Encoding.UTF8);
            string preview = content.Length > 200 ? content.Substring(0, 200) + "..." : content;
            
            EditorUtility.DisplayDialog(
                $"预览: {backup.originalName}",
                $"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}\n\n" +
                $"预览内容:\n{preview}",
                "确定"
            );
        }
        catch (Exception e)
        {
            Debug.LogWarning($"无法预览备份文件: {e.Message}");
        }
    }
    
    private void DrawBackupPreview(BackupFileInfo backup)
    {
        try
        {
            string content = File.ReadAllText(backup.filePath, Encoding.UTF8);
            string preview = content.Length > 500 ? content.Substring(0, 500) + "..." : content;
            
            EditorGUILayout.HelpBox(
                $"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}\n\n" +
                $"预览内容:\n{preview}",
                MessageType.Info
            );
            
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("在编辑器中打开", EditorStyles.miniButton, GUILayout.Width(120)))
            {
                OpenBackupFile(backup.filePath);
            }
            GUILayout.EndHorizontal();
        }
        catch (Exception e)
        {
            EditorGUILayout.HelpBox($"无法预览备份文件: {e.Message}", MessageType.Error);
        }
    }

    private void DrawStatusBar()
    {
        GUILayout.BeginHorizontal("Box");

        var currentTab = GetCurrentTab();

        if (currentTab != null)
        {
            if (data.selectedTabIndex == data.mainPageTabIndex)
            {
                string modeText = data.mainPageMode == MainPageMode.Home ? "主页" : "备份管理";
                GUILayout.Label($"{modeText} - 共 {noteFiles.Count} 个笔记, {backupFiles.Count} 个备份", EditorStyles.miniLabel);
            }
            else
            {
                string status = $"字数: {currentTab.content.Length}";

                if (currentTab.isModified)
                    status += " | 已修改";

                if (!string.IsNullOrEmpty(currentTab.filePath))
                {
                    string fileName = Path.GetFileName(currentTab.filePath);
                    status += $" | 文件: {fileName}";
                }

                GUILayout.Label(status, EditorStyles.miniLabel);
            }
        }
        else
        {
            GUILayout.Label("没有打开的笔记", EditorStyles.miniLabel);
        }

        GUILayout.FlexibleSpace();

        GUILayout.Label(data.autoSaveEnabled ? "自动保存: 开启" : "自动保存: 关闭",
            EditorStyles.miniLabel);

        GUILayout.EndHorizontal();
    }

    private void DrawSettingsPanel()
    {
        GUILayout.BeginVertical("Box");

        EditorGUILayout.LabelField("设置", EditorStyles.boldLabel);

        data.autoSaveEnabled = EditorGUILayout.Toggle("启用自动保存", data.autoSaveEnabled);
        data.fontSize = EditorGUILayout.IntSlider("字体大小", data.fontSize, 10, 24);
        data.wordWrap = EditorGUILayout.Toggle("自动换行", data.wordWrap);
        data.showNoteList = EditorGUILayout.Toggle("显示笔记列表", data.showNoteList);

        EditorGUILayout.Space();

        GUILayout.Label("笔记文件夹:", EditorStyles.miniBoldLabel);
        GUILayout.BeginHorizontal();
        GUILayout.Label(data.notesFolder, EditorStyles.textField);
        if (GUILayout.Button("浏览", GUILayout.Width(60)))
        {
            string newPath = EditorUtility.OpenFolderPanel("选择笔记文件夹", Application.dataPath, "");
            if (!string.IsNullOrEmpty(newPath))
            {
                string relativePath = "Assets" + newPath.Replace(Application.dataPath, "");
                if (relativePath != data.notesFolder)
                {
                    data.notesFolder = relativePath;
                    RefreshNoteList();
                }
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.Label("备份文件夹:", EditorStyles.miniBoldLabel);
        GUILayout.BeginHorizontal();
        GUILayout.Label(data.backupFolder, EditorStyles.textField);
        if (GUILayout.Button("浏览", GUILayout.Width(60)))
        {
            string newPath = EditorUtility.OpenFolderPanel("选择备份文件夹", Application.dataPath, "");
            if (!string.IsNullOrEmpty(newPath))
            {
                string relativePath = "Assets" + newPath.Replace(Application.dataPath, "");
                if (relativePath != data.backupFolder)
                {
                    data.backupFolder = relativePath;
                    RefreshBackupList();
                }
            }
        }
        GUILayout.EndHorizontal();

        EditorGUILayout.Space();

        if (GUILayout.Button("导出所有笔记", GUILayout.Height(25)))
        {
            ExportAllNotes();
        }

        GUILayout.EndVertical();
    }
    #endregion

    #region 笔记列表功能
    private void RefreshNoteList()
    {
        noteFiles.Clear();

        try
        {
            string fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.notesFolder));

            if (Directory.Exists(fullPath))
            {
                foreach (string extension in textExtensions)
                {
                    try
                    {
                        // 只搜索笔记文件夹,不搜索子文件夹
                        string[] files = Directory.GetFiles(fullPath, "*" + extension, SearchOption.TopDirectoryOnly);

                        foreach (string filePath in files)
                        {
                            // 检查是否是备份文件(文件名中包含"备份"字样)
                            string fileName = Path.GetFileName(filePath);
                            if (!fileName.Contains("_备份_"))
                            {
                                noteFiles.Add(filePath);
                            }
                        }
                    }
                    catch (Exception) { }
                }

                noteFiles.Sort((a, b) => File.GetLastWriteTime(b).CompareTo(File.GetLastWriteTime(a)));
            }
        }
        catch (Exception e)
        {
            Debug.LogWarning($"刷新笔记列表失败: {e.Message}");
        }

        lastNoteListRefresh = DateTime.Now;
    }

    private string GetRelativePath(string fullPath)
    {
        try
        {
            string projectPath = Path.GetFullPath(Application.dataPath + "/..");
            if (fullPath.StartsWith(projectPath))
            {
                return fullPath.Substring(projectPath.Length + 1);
            }
        }
        catch { }

        return fullPath;
    }

    private string GetFileSize(string filePath)
    {
        try
        {
            FileInfo fileInfo = new FileInfo(filePath);
            long size = fileInfo.Length;

            if (size < 1024) return $"{size} B";
            else if (size < 1024 * 1024) return $"{(size / 1024.0):0.0} KB";
            else return $"{(size / (1024.0 * 1024.0)):0.0} MB";
        }
        catch
        {
            return "未知大小";
        }
    }

    private void DeleteNoteFile(string filePath)
    {
        try
        {
            // 检查文件是否已经打开
            string fullPath = Path.GetFullPath(filePath);
            bool isFileOpened = openedFiles.Contains(fullPath);

            string message;
            if (isFileOpened)
            {
                message = $"文件正在编辑中,删除后未保存的内容将丢失!\n\n确定要删除文件吗?\n{Path.GetFileName(filePath)}";
            }
            else
            {
                message = $"确定要删除文件吗?\n{Path.GetFileName(filePath)}";
            }

            // 确认对话框
            if (EditorUtility.DisplayDialog("删除文件", message, "删除", "取消"))
            {
                // 如果文件已经打开,先关闭对应的标签页
                if (isFileOpened)
                {
                    for (int i = 0; i < data.tabs.Count; i++)
                    {
                        if (i != data.mainPageTabIndex &&
                            !string.IsNullOrEmpty(data.tabs[i].filePath) &&
                            Path.GetFullPath(data.tabs[i].filePath) == fullPath)
                        {
                            // 先强制关闭标签页,不保存
                            var tab = data.tabs[i];
                            if (!string.IsNullOrEmpty(tab.filePath))
                            {
                                openedFiles.Remove(Path.GetFullPath(tab.filePath));
                            }
                            data.tabs.RemoveAt(i);

                            // 更新特殊标签的索引
                            if (i < data.mainPageTabIndex) data.mainPageTabIndex--;

                            // 调整选中索引
                            if (data.selectedTabIndex >= i && data.selectedTabIndex > 0)
                            {
                                data.selectedTabIndex--;
                            }
                            else if (data.selectedTabIndex >= data.tabs.Count)
                            {
                                data.selectedTabIndex = data.tabs.Count - 1;
                            }

                            break;
                        }
                    }
                }

                // 删除物理文件
                File.Delete(filePath);

                // 刷新列表
                RefreshNoteList();

                // 更新界面
                Repaint();

                // 显示删除成功的提示
                EditorUtility.DisplayDialog("删除成功", $"文件已删除:{Path.GetFileName(filePath)}", "确定");
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("删除失败", $"无法删除文件:{e.Message}", "确定");
        }
    }
    #endregion

    #region 备份管理功能
    private void RefreshBackupList()
    {
        backupFiles.Clear();

        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                // 获取所有备份文件
                foreach (string extension in textExtensions)
                {
                    try
                    {
                        string[] files = Directory.GetFiles(backupFolderPath, "*" + extension, SearchOption.TopDirectoryOnly);

                        foreach (string filePath in files)
                        {
                            try
                            {
                                FileInfo fileInfo = new FileInfo(filePath);
                                string fileName = fileInfo.Name;

                                // 只显示备份文件(文件名中包含"备份"字样)
                                if (fileName.Contains("_备份_"))
                                {
                                    // 解析原始文件名(去掉备份时间戳部分)
                                    int backupIndex = fileName.IndexOf("_备份_");
                                    string originalName = fileName.Substring(0, backupIndex) + fileInfo.Extension;

                                    BackupFileInfo backupInfo = new BackupFileInfo
                                    {
                                        filePath = filePath,
                                        fileName = fileName,
                                        originalName = originalName,
                                        backupTime = fileInfo.LastWriteTime,
                                        fileSize = fileInfo.Length,
                                        relativePath = GetRelativePath(filePath)
                                    };

                                    backupFiles.Add(backupInfo);
                                }
                            }
                            catch (Exception) { }
                        }
                    }
                    catch (Exception) { }
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogWarning($"刷新备份列表失败: {e.Message}");
        }

        lastBackupListRefresh = DateTime.Now;
    }

    private string FormatFileSize(long size)
    {
        if (size < 1024) return $"{size} B";
        else if (size < 1024 * 1024) return $"{(size / 1024.0):0.0} KB";
        else return $"{(size / (1024.0 * 1024.0)):0.0} MB";
    }

    private void OpenBackupFile(string filePath)
    {
        try
        {
            string content = File.ReadAllText(filePath, Encoding.UTF8);
            string fileName = Path.GetFileName(filePath);

            // 创建新标签页打开备份文件
            var newTab = new NoteTab();
            newTab.title = "[备份] " + Path.GetFileNameWithoutExtension(fileName);
            newTab.content = content;
            newTab.filePath = filePath;
            newTab.isModified = false; // 备份文件默认只读,不能修改

            data.tabs.Add(newTab);
            data.selectedTabIndex = data.tabs.Count - 1;
            lastSelectedTabIndex = data.selectedTabIndex;

            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开备份文件: {e.Message}", "确定");
        }
    }

    private void DeleteBackupFile(string filePath)
    {
        try
        {
            string fileName = Path.GetFileName(filePath);

            if (EditorUtility.DisplayDialog("删除备份", $"确定要删除备份文件吗?\n{fileName}", "删除", "取消"))
            {
                File.Delete(filePath);
                RefreshBackupList();

                // 提示用户
                EditorUtility.DisplayDialog("删除成功", $"已删除备份文件:{fileName}", "确定");

                // 刷新显示
                Repaint();
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("删除失败", $"无法删除备份文件:{e.Message}", "确定");
        }
    }

    private void RestoreBackup(BackupFileInfo backup)
    {
        try
        {
            // 询问恢复位置
            string defaultName = backup.originalName;
            string restorePath = EditorUtility.SaveFilePanel("恢复备份", data.notesFolder, defaultName, "txt");

            if (!string.IsNullOrEmpty(restorePath))
            {
                // 复制备份文件到指定位置
                File.Copy(backup.filePath, restorePath, true);

                // 刷新笔记列表
                RefreshNoteList();

                // 提示用户
                EditorUtility.DisplayDialog("恢复成功",
                    $"已恢复备份文件:{backup.fileName}\n恢复到:{restorePath}", "确定");

                // 打开恢复的文件
                OpenNoteFile(restorePath);
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("恢复失败", $"无法恢复备份:{e.Message}", "确定");
        }
    }

    private void OpenBackupFolder()
    {
        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                // 在文件资源管理器中打开文件夹
                EditorUtility.RevealInFinder(backupFolderPath);
            }
            else
            {
                EditorUtility.DisplayDialog("提示", "备份文件夹不存在,将创建新文件夹", "确定");
                Directory.CreateDirectory(backupFolderPath);
                EditorUtility.RevealInFinder(backupFolderPath);
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开备份文件夹:{e.Message}", "确定");
        }
    }

    private void CleanupOldBackups(int days)
    {
        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                DateTime cutoffDate = DateTime.Now.AddDays(-days);
                int deletedCount = 0;

                foreach (var extension in textExtensions)
                {
                    string[] files = Directory.GetFiles(backupFolderPath, "*" + extension, SearchOption.TopDirectoryOnly);

                    foreach (string filePath in files)
                    {
                        try
                        {
                            FileInfo fileInfo = new FileInfo(filePath);
                            if (fileInfo.LastWriteTime < cutoffDate)
                            {
                                File.Delete(filePath);
                                deletedCount++;
                            }
                        }
                        catch (Exception) { }
                    }
                }

                // 刷新备份列表
                RefreshBackupList();

                // 提示用户
                if (deletedCount > 0)
                {
                    EditorUtility.DisplayDialog("清理完成",
                        $"已清理 {deletedCount} 个 {days} 天前的备份文件", "确定");
                }
                else
                {
                    EditorUtility.DisplayDialog("清理完成",
                        $"没有找到 {days} 天前的备份文件", "确定");
                }

                // 刷新显示
                Repaint();
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("清理失败", $"清理备份文件失败:{e.Message}", "确定");
        }
    }

    // 备份当前笔记
    private void BackupCurrentNote()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || string.IsNullOrEmpty(currentTab.filePath))
        {
            EditorUtility.DisplayDialog("备份失败", "当前笔记没有保存到文件,请先保存", "确定");
            return;
        }

        try
        {
            // 确保备份文件夹存在
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));
            if (!Directory.Exists(backupFolderPath))
            {
                Directory.CreateDirectory(backupFolderPath);
            }

            // 获取文件信息
            FileInfo fileInfo = new FileInfo(currentTab.filePath);
            string fileName = fileInfo.Name;
            string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
            string fileExt = fileInfo.Extension;

            // 生成备份文件名(添加时间戳)
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string backupFileName = $"{fileNameWithoutExt}_备份_{timestamp}{fileExt}";
            string backupFilePath = Path.Combine(backupFolderPath, backupFileName);

            // 复制文件
            File.Copy(currentTab.filePath, backupFilePath, true);

            // 刷新备份列表
            RefreshBackupList();

            // 提示用户
            EditorUtility.DisplayDialog("备份成功",
                $"已成功备份当前笔记:{fileName}\n备份到:{backupFileName}", "确定");

            // 刷新显示
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("备份失败", $"无法备份文件:{e.Message}", "确定");
        }
    }

    // 备份所有笔记
    private void BackupAllNotes()
    {
        try
        {
            // 确保备份文件夹存在
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));
            if (!Directory.Exists(backupFolderPath))
            {
                Directory.CreateDirectory(backupFolderPath);
            }

            int backupCount = 0;
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");

            // 备份所有已打开的且有文件路径的笔记
            foreach (var tab in data.tabs)
            {
                if (!string.IsNullOrEmpty(tab.filePath) && tab.title != "主页")
                {
                    try
                    {
                        FileInfo fileInfo = new FileInfo(tab.filePath);
                        string fileName = fileInfo.Name;
                        string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
                        string fileExt = fileInfo.Extension;

                        string backupFileName = $"{fileNameWithoutExt}_备份_{timestamp}{fileExt}";
                        string backupFilePath = Path.Combine(backupFolderPath, backupFileName);

                        File.Copy(tab.filePath, backupFilePath, true);
                        backupCount++;
                    }
                    catch (Exception) { }
                }
            }

            // 刷新备份列表
            RefreshBackupList();

            // 提示用户
            if (backupCount > 0)
            {
                EditorUtility.DisplayDialog("备份成功",
                    $"已成功备份 {backupCount} 个笔记文件\n备份时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}", "确定");
            }
            else
            {
                EditorUtility.DisplayDialog("备份失败", "没有找到可以备份的笔记文件", "确定");
            }

            // 刷新显示
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("备份失败", $"无法备份文件:{e.Message}", "确定");
        }
    }
    #endregion

    #region 标签管理
    private NoteTab GetCurrentTab()
    {
        if (data == null || data.tabs.Count == 0) return null;

        if (data.selectedTabIndex < 0)
            data.selectedTabIndex = 0;
        else if (data.selectedTabIndex >= data.tabs.Count)
            data.selectedTabIndex = data.tabs.Count - 1;

        return data.tabs[data.selectedTabIndex];
    }

    private void EnsureMainPageTab()
    {
        // 如果没有主页标签,创建一个
        if (data.mainPageTabIndex < 0 || data.mainPageTabIndex >= data.tabs.Count || data.tabs[data.mainPageTabIndex].title != "主页")
        {
            CreateMainPageTab();
        }
    }

    private void CreateMainPageTab()
    {
        // 检查是否已经有主页标签
        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (data.tabs[i].title == "主页")
            {
                data.mainPageTabIndex = i;
                return;
            }
        }

        // 创建新的主页标签
        var mainTab = new NoteTab();
        mainTab.title = "主页";
        mainTab.content = ""; // 主页页面不需要内容
        data.tabs.Add(mainTab);
        data.mainPageTabIndex = data.tabs.Count - 1;
        
        // 如果这是第一个标签,选中它
        if (data.tabs.Count == 1)
        {
            data.selectedTabIndex = 0;
        }
    }

    private void CreateNewTab()
    {
        if (data == null) data = new NotepadData();

        var newTab = new NoteTab();
        newTab.title = "笔记 " + (data.tabs.Count);
        data.tabs.Add(newTab);
        data.selectedTabIndex = data.tabs.Count - 1;
        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }

    private void CloseTab(int index)
    {
        if (data == null || index < 0 || index >= data.tabs.Count) return;

        var tab = data.tabs[index];

        // 不允许关闭主页标签
        if (index == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("提示", "主页标签不能关闭", "确定");
            return;
        }

        if (tab.isModified)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭笔记",
                $"'{tab.title}' 有未保存的更改。",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                SaveTab(index);
            }
            else if (result == 2)
            {
                return;
            }
        }

        if (!string.IsNullOrEmpty(tab.filePath))
        {
            openedFiles.Remove(Path.GetFullPath(tab.filePath));
        }

        // 移除标签
        data.tabs.RemoveAt(index);

        // 更新特殊标签的索引
        if (index < data.mainPageTabIndex)
        {
            data.mainPageTabIndex--;
        }

        // 如果关闭后没有普通标签,切换到主页标签
        if (data.tabs.Count == 1 && data.mainPageTabIndex >= 0)
        {
            data.selectedTabIndex = data.mainPageTabIndex;
        }
        else if (data.tabs.Count > 0)
        {
            if (data.selectedTabIndex >= index && data.selectedTabIndex > 0)
            {
                data.selectedTabIndex--;
            }
            else if (data.selectedTabIndex >= data.tabs.Count)
            {
                data.selectedTabIndex = data.tabs.Count - 1;
            }
        }

        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }

    private void CloseAllTabs()
    {
        if (data == null || data.tabs.Count <= 1) return; // 至少有主页标签

        // 只关闭普通标签,保留主页标签
        List<int> tabsToClose = new List<int>();

        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i != data.mainPageTabIndex)
            {
                tabsToClose.Add(i);
            }
        }

        bool hasUnsavedChanges = tabsToClose.Any(i => data.tabs[i].isModified);

        if (hasUnsavedChanges)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭所有标签",
                "有未保存的笔记。是否保存更改?",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                foreach (int i in tabsToClose)
                {
                    if (data.tabs[i].isModified)
                    {
                        SaveTab(i);
                    }
                }
            }
            else if (result == 2)
            {
                return;
            }
        }

        // 关闭所有普通标签
        for (int i = tabsToClose.Count - 1; i >= 0; i--)
        {
            int tabIndex = tabsToClose[i];
            if (!string.IsNullOrEmpty(data.tabs[tabIndex].filePath))
            {
                openedFiles.Remove(Path.GetFullPath(data.tabs[tabIndex].filePath));
            }
            data.tabs.RemoveAt(tabIndex);
        }

        // 更新特殊标签索引
        data.mainPageTabIndex = 0;
        data.selectedTabIndex = data.mainPageTabIndex;
        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }
    #endregion

    #region 文件操作
    private void OpenNoteFile()
    {
        // 修改这里:使用笔记文件夹路径作为默认打开路径
        string defaultPath = Path.Combine(Application.dataPath, "Editor", "Notes");

        // 如果文件夹不存在则创建
        if (!Directory.Exists(defaultPath))
        {
            Directory.CreateDirectory(defaultPath);
        }

        string path = EditorUtility.OpenFilePanel("打开笔记文件", defaultPath, "txt,md,json,cs");

        if (!string.IsNullOrEmpty(path))
        {
            OpenNoteFile(path);
        }
    }

    private void OpenNoteFile(string filePath)
    {
        try
        {
            string fullPath = Path.GetFullPath(filePath);

            // 检查文件是否已经打开
            if (openedFiles.Contains(fullPath))
            {
                for (int i = 0; i < data.tabs.Count; i++)
                {
                    if (i != data.mainPageTabIndex &&
                        !string.IsNullOrEmpty(data.tabs[i].filePath) &&
                        Path.GetFullPath(data.tabs[i].filePath) == fullPath)
                    {
                        data.selectedTabIndex = i;
                        lastSelectedTabIndex = data.selectedTabIndex;
                        Repaint();
                        return;
                    }
                }
            }

            string content = File.ReadAllText(filePath, Encoding.UTF8);
            var newTab = new NoteTab();
            newTab.title = Path.GetFileNameWithoutExtension(filePath);
            newTab.content = content;
            newTab.filePath = filePath;
            newTab.isModified = false;

            data.tabs.Add(newTab);
            data.selectedTabIndex = data.tabs.Count - 1;
            lastSelectedTabIndex = data.selectedTabIndex;

            openedFiles.Add(fullPath);

            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开文件: {e.Message}", "确定");
        }
    }

    private void SaveCurrentTab()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || data.selectedTabIndex == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("保存", "没有活动的笔记可以保存", "确定");
            return;
        }

        SaveTab(data.selectedTabIndex);
    }

    private void SaveTab(int index)
    {
        if (data == null || index < 0 || index >= data.tabs.Count ||
            index == data.mainPageTabIndex) return;

        var tab = data.tabs[index];

        if (string.IsNullOrEmpty(tab.filePath))
        {
            SaveAsCurrentTab();
            return;
        }

        try
        {
            string directory = Path.GetDirectoryName(tab.filePath);
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            File.WriteAllText(tab.filePath, tab.content, Encoding.UTF8);
            tab.isModified = false;
            tab.lastModified = DateTime.Now;
            tab.lastSavedHash = CalculateContentHash(tab.content);

            AssetDatabase.Refresh();
            RefreshNoteList();
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"保存失败: {e.Message}", "确定");
        }
    }

    private void SaveAsCurrentTab()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || data.selectedTabIndex == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("另存为", "没有活动的笔记可以另存为", "确定");
            return;
        }

        // 修改这里:使用笔记文件夹路径作为默认保存路径
        string defaultPath = Path.Combine(Application.dataPath, "Editor", "Notes");

        // 如果文件夹不存在则创建
        if (!Directory.Exists(defaultPath))
        {
            Directory.CreateDirectory(defaultPath);
        }

        string defaultName = string.IsNullOrEmpty(currentTab.title) ? "NewNote.txt" : currentTab.title + ".txt";
        string defaultFilePath = Path.Combine(defaultPath, defaultName);

        string notesPath = Path.Combine(Application.dataPath, "Editor", "Notes");
        if (!Directory.Exists(notesPath))
        {
            Directory.CreateDirectory(notesPath);
        }
        string path = EditorUtility.SaveFilePanel("另存为", notesPath, defaultName, "txt");

        if (!string.IsNullOrEmpty(path))
        {
            if (!string.IsNullOrEmpty(currentTab.filePath))
            {
                openedFiles.Remove(Path.GetFullPath(currentTab.filePath));
            }

            currentTab.filePath = path;
            currentTab.title = Path.GetFileNameWithoutExtension(path);

            openedFiles.Add(Path.GetFullPath(path));

            SaveCurrentTab();
        }
    }
    #endregion

    #region 自动保存系统
    private void ScheduleAutoSave(NoteTab tab)
    {
        int tabIndex = data.tabs.IndexOf(tab);
        if (tabIndex == -1) return;

        scheduledSaves[tabIndex] = EditorApplication.timeSinceStartup + 2.0;
    }

    private void OnEditorUpdate()
    {
        CheckScheduledSaves();

        if (data != null && data.autoSaveEnabled && (DateTime.Now - lastAutoSaveTime).TotalSeconds >= AUTO_SAVE_INTERVAL)
        {
            AutoSaveAllModifiedTabs();
            lastAutoSaveTime = DateTime.Now;
        }
    }

    private void CheckScheduledSaves()
    {
        if (scheduledSaves.Count == 0) return;

        List<int> toRemove = new List<int>();
        double currentTime = EditorApplication.timeSinceStartup;

        foreach (var kvp in scheduledSaves)
        {
            if (currentTime >= kvp.Value)
            {
                int tabIndex = kvp.Key;
                if (tabIndex < data.tabs.Count)
                {
                    var tab = data.tabs[tabIndex];
                    if (tab.isModified)
                    {
                        string currentHash = CalculateContentHash(tab.content);
                        if (currentHash != tab.lastSavedHash)
                        {
                            PerformAutoSave(tab, tabIndex);
                            tab.lastSavedHash = currentHash;
                        }
                    }
                }
                toRemove.Add(tabIndex);
            }
        }

        foreach (int index in toRemove)
        {
            scheduledSaves.Remove(index);
        }
    }

    private void AutoSaveAllModifiedTabs()
    {
        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i == data.mainPageTabIndex) continue; // 跳过主页标签

            var tab = data.tabs[i];
            if (tab.isModified)
            {
                PerformAutoSave(tab, i);
            }
        }
    }

    private void PerformAutoSave(NoteTab tab, int tabIndex)
    {
        if (string.IsNullOrEmpty(tab.filePath))
        {
            SaveToTempFile(tab);
        }
        else
        {
            SaveTabSilently(tabIndex);
        }
    }

    private void SaveTabSilently(int index)
    {
        if (index < 0 || index >= data.tabs.Count ||
            index == data.mainPageTabIndex) return;

        var tab = data.tabs[index];

        try
        {
            string directory = Path.GetDirectoryName(tab.filePath);
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            File.WriteAllText(tab.filePath, tab.content, Encoding.UTF8);
            tab.isModified = false;
            tab.lastModified = DateTime.Now;
            tab.lastSavedHash = CalculateContentHash(tab.content);
        }
        catch (Exception e)
        {
            Debug.LogWarning($"自动保存失败: {e.Message}");
        }
    }

    private void SaveToTempFile(NoteTab tab)
    {
        try
        {
            string tempFolder = Path.Combine(Application.temporaryCachePath, "UnityNotepad/Autosave");
            if (!Directory.Exists(tempFolder))
                Directory.CreateDirectory(tempFolder);

            string safeTitle = string.IsNullOrEmpty(tab.title) ?
                "Untitled" :
                System.Text.RegularExpressions.Regex.Replace(tab.title, @"[^\w]", "_");

            string fileName = $"{safeTitle}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
            string tempFile = Path.Combine(tempFolder, fileName);

            File.WriteAllText(tempFile, tab.content, Encoding.UTF8);
            CleanupOldTempFiles(tempFolder, 10);
        }
        catch { }
    }

    private void SaveAllModifiedTabs()
    {
        if (data == null) return;

        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i == data.mainPageTabIndex) continue;

            var tab = data.tabs[i];
            if (tab.isModified)
            {
                SaveTabSilently(i);
            }
        }
    }
    #endregion

    #region 辅助方法
    private void CloseNotepad()
    {
        bool hasUnsavedChanges = data?.tabs?.Any(t => t.isModified) ?? false;

        if (hasUnsavedChanges)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭笔记本",
                "有未保存的笔记。是否保存更改?",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                SaveAllModifiedTabs();
                Close();
            }
            else if (result == 1)
            {
                Close();
            }
        }
        else
        {
            Close();
        }
    }

    private string CalculateContentHash(string content)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] bytes = Encoding.UTF8.GetBytes(content);
            byte[] hash = sha256.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }
    }

    private void CleanupOldTempFiles(string folder, int keepCount)
    {
        try
        {
            var files = Directory.GetFiles(folder, "*.txt")
                .Select(f => new FileInfo(f))
                .OrderByDescending(f => f.LastWriteTime)
                .ToList();

            for (int i = keepCount; i < files.Count; i++)
            {
                files[i].Delete();
            }
        }
        catch { }
    }

    private void CreateTextAreaStyle()
    {
        textAreaStyle = new GUIStyle(EditorStyles.textArea)
        {
            wordWrap = true,
            fontSize = 14,
            padding = new RectOffset(10, 10, 10, 10),
            margin = new RectOffset(5, 5, 5, 5)
        };
    }

    private void AppendToCurrentTab(string text)
    {
        var currentTab = GetCurrentTab();
        if (currentTab != null && data.selectedTabIndex != data.mainPageTabIndex && 
            !string.IsNullOrEmpty(text))
        {
            currentTab.content += text;
            currentTab.isModified = true;
            ScheduleAutoSave(currentTab);
            Repaint();
        }
    }

    private void LoadData()
    {
        string json = EditorPrefs.GetString(DATA_KEY, "");

        if (!string.IsNullOrEmpty(json))
        {
            try
            {
                data = JsonUtility.FromJson<NotepadData>(json);
                UpdateOpenedFiles();
            }
            catch (Exception e)
            {
                Debug.LogError($"加载数据失败: {e.Message}");
                data = new NotepadData();
            }
        }
        else
        {
            data = new NotepadData();

            // 修改这里:设置默认文件夹
            data.notesFolder = "Assets/Editor/Notes/";
            data.backupFolder = "Assets/Editor/Notes/Backup/";
        }

        // 确保文件夹存在
        if (!string.IsNullOrEmpty(data.notesFolder))
        {
            string notesFullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.notesFolder));
            if (!Directory.Exists(notesFullPath))
            {
                Directory.CreateDirectory(notesFullPath);
            }
        }

        if (!string.IsNullOrEmpty(data.backupFolder))
        {
            string backupFullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));
            if (!Directory.Exists(backupFullPath))
            {
                Directory.CreateDirectory(backupFullPath);
            }
        }
    }

    private void SaveData()
    {
        if (data == null) return;

        try
        {
            string json = JsonUtility.ToJson(data, true);
            EditorPrefs.SetString(DATA_KEY, json);
        }
        catch (Exception e)
        {
            Debug.LogError($"保存数据失败: {e.Message}");
        }
    }
    #endregion

    #region 搜索功能
    private void PerformSearch()
    {
        searchResults.Clear();
        currentSearchIndex = 0;

        if (string.IsNullOrEmpty(searchText) ||
            data.selectedTabIndex == data.mainPageTabIndex) return;

        var currentTab = GetCurrentTab();
        if (currentTab == null || string.IsNullOrEmpty(currentTab.content)) return;

        string content = currentTab.content.ToLower();
        string searchLower = searchText.ToLower();

        int index = 0;
        while ((index = content.IndexOf(searchLower, index)) != -1)
        {
            searchResults.Add(index);
            index += searchLower.Length;
        }

        if (searchResults.Count > 0)
        {
            ScrollToSearchResult(0);
        }
    }

    private void NavigateSearch(int direction)
    {
        if (searchResults.Count == 0) return;

        currentSearchIndex = (currentSearchIndex + direction + searchResults.Count) % searchResults.Count;
        ScrollToSearchResult(currentSearchIndex);
    }

    private void ScrollToSearchResult(int resultIndex)
    {
        if (resultIndex < 0 || resultIndex >= searchResults.Count) return;

        var currentTab = GetCurrentTab();
        if (currentTab == null) return;

        int charPosition = searchResults[resultIndex];
        int linesBefore = currentTab.content.Substring(0, Math.Min(charPosition, currentTab.content.Length)).Split('\n').Length - 1;
        float lineHeight = data.fontSize * 1.5f;
        float targetY = linesBefore * lineHeight;

        currentTab.scrollPosition.y = Mathf.Max(0, targetY - position.height / 3);
        Repaint();
    }
    #endregion

    #region 快捷键
    private void HandleKeyboardShortcuts()
    {
        Event e = Event.current;

        if (e.type == EventType.KeyDown)
        {
            bool control = e.control || e.command;

            if (control)
            {
                switch (e.keyCode)
                {
                    case KeyCode.S:
                        SaveCurrentTab();
                        e.Use();
                        break;
                    case KeyCode.N:
                        CreateNewTab();
                        e.Use();
                        break;
                    case KeyCode.W:
                        if (data.tabs.Count > 0 && data.selectedTabIndex != data.mainPageTabIndex)
                        {
                            CloseTab(data.selectedTabIndex);
                            e.Use();
                        }
                        break;
                    case KeyCode.Tab:
                        if (e.shift)
                        {
                            if (data.tabs.Count > 1)
                            {
                                data.selectedTabIndex = (data.selectedTabIndex - 1 + data.tabs.Count) % data.tabs.Count;
                                e.Use();
                            }
                        }
                        else
                        {
                            if (data.tabs.Count > 1)
                            {
                                data.selectedTabIndex = (data.selectedTabIndex + 1) % data.tabs.Count;
                                e.Use();
                            }
                        }
                        break;
                    case KeyCode.F:
                        if (e.shift)
                        {
                            EditorGUI.FocusTextInControl("SearchField");
                            e.Use();
                        }
                        break;
                    case KeyCode.H:
                        if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
                        {
                            data.selectedTabIndex = data.mainPageTabIndex;
                            data.mainPageMode = MainPageMode.Home;
                            e.Use();
                        }
                        break;
                    case KeyCode.B:
                        if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
                        {
                            data.selectedTabIndex = data.mainPageTabIndex;
                            data.mainPageMode = MainPageMode.Backup;
                            e.Use();
                        }
                        break;
                }
            }
        }
    }
    #endregion

    #region 导入导出
    private void ExportAllNotes()
    {
        if (data == null || data.tabs.Count <= 1) // 只有主页标签
        {
            EditorUtility.DisplayDialog("导出", "没有可导出的笔记", "确定");
            return;
        }

        string exportFolder = EditorUtility.SaveFolderPanel("导出笔记", Application.dataPath, "NotesExport");

        if (!string.IsNullOrEmpty(exportFolder))
        {
            try
            {
                int exported = 0;
                foreach (var tab in data.tabs)
                {
                    if (!string.IsNullOrEmpty(tab.content) && tab.title != "主页")
                    {
                        string safeTitle = System.Text.RegularExpressions.Regex.Replace(tab.title, @"[^\w\u4e00-\u9fa5]", "_");
                        string fileName = $"{safeTitle}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
                        string filePath = Path.Combine(exportFolder, fileName);
                        File.WriteAllText(filePath, tab.content, Encoding.UTF8);
                        exported++;
                    }
                }

                EditorUtility.DisplayDialog("导出完成", $"已导出 {exported} 个笔记到:\n{exportFolder}", "确定");
            }
            catch (Exception e)
            {
                EditorUtility.DisplayDialog("导出失败", e.Message, "确定");
            }
        }
    }
    #endregion
}

版本2

cs 复制代码
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Security.Cryptography;

public class UnityNotepad : EditorWindow
{
    // 数据模型
    [System.Serializable]
    private class NoteTab
    {
        public string title = "新笔记";
        public string content = "";
        public Vector2 scrollPosition;
        public bool isModified = false;
        public string filePath = "";
        public DateTime lastModified;
        public string lastSavedHash = "";
        public DateTime lastChangeTime;

        public NoteTab()
        {
            lastModified = DateTime.Now;
            lastChangeTime = DateTime.Now;
        }
    }

    // 备份文件信息
    private class BackupFileInfo
    {
        public string filePath;
        public string fileName;
        public string originalName;
        public DateTime backupTime;
        public long fileSize;
        public string relativePath;
    }

    // 主页模式
    private enum MainPageMode { Home, Backup }

    // 序列化存储
    [System.Serializable]
    private class NotepadData
    {
        public List<NoteTab> tabs = new List<NoteTab>();
        public int selectedTabIndex = 0;
        public bool autoSaveEnabled = true;
        public string notesFolder = "Assets/Editor/Notes/";
        public string backupFolder = "Assets/Editor/Notes/Backup/";
        public int fontSize = 14;
        public bool wordWrap = true;
        public bool showNoteList = true;
        public int mainPageTabIndex = -1; // 主页标签页索引
        public MainPageMode mainPageMode = MainPageMode.Home; // 主页当前显示模式
    }

    // 编辑器状态
    private NotepadData data;
    private Vector2 tabsScrollPos;
    private bool showSettings = false;
    private GUIStyle textAreaStyle;
    private string searchText = "";
    private List<int> searchResults = new List<int>();
    private int currentSearchIndex = 0;
    private int lastSelectedTabIndex = -1;

    private const string DATA_KEY = "UnityNotepad_Data_Fixed";
    private const int AUTO_SAVE_INTERVAL = 30;

    private DateTime lastAutoSaveTime;
    private Dictionary<int, double> scheduledSaves = new Dictionary<int, double>();
    private HashSet<string> openedFiles = new HashSet<string>();

    // 焦点管理 - 完全禁用自动焦点
    private string textAreaControlName = "NoteTextArea";
    private bool neverAutoFocus = true; // 完全禁用自动焦点

    // 笔记列表管理
    private List<string> noteFiles = new List<string>();
    private Vector2 noteListScrollPos;
    private string[] textExtensions = { ".txt", ".md", ".json", ".cs", ".xml", ".yaml", ".yml", ".html", ".css", ".js" };
    private DateTime lastNoteListRefresh = DateTime.MinValue;
    private const double NOTE_LIST_REFRESH_INTERVAL = 2.0;

    // 备份文件管理
    private List<BackupFileInfo> backupFiles = new List<BackupFileInfo>();
    private Vector2 backupListScrollPos;
    private DateTime lastBackupListRefresh = DateTime.MinValue;
    private BackupFileInfo selectedBackupForPreview = null;

    // 初始化窗口
    [MenuItem("Tools/Unity Notepad %#m")]
    public static void ShowWindow()
    {
        var window = GetWindow<UnityNotepad>();
        window.titleContent = new GUIContent("Unity Notepad", EditorGUIUtility.IconContent("TextAsset Icon").image);
        window.minSize = new Vector2(500, 400);
        window.Show();
    }

    private void OnEnable()
    {
        LoadData();
        CreateTextAreaStyle();
        lastAutoSaveTime = DateTime.Now;
        EditorApplication.update += OnEditorUpdate;

        UpdateOpenedFiles();
        RefreshNoteList();
        RefreshBackupList();

        // 确保有主页标签页
        EnsureMainPageTab();
    }

    private void OnDisable()
    {
        SaveAllModifiedTabs();
        SaveData();
        EditorApplication.update -= OnEditorUpdate;
    }

    private void OnFocus()
    {
        RefreshNoteList();
        RefreshBackupList();
    }

    private void UpdateOpenedFiles()
    {
        openedFiles.Clear();
        if (data != null && data.tabs != null)
        {
            foreach (var tab in data.tabs)
            {
                if (!string.IsNullOrEmpty(tab.filePath))
                {
                    openedFiles.Add(Path.GetFullPath(tab.filePath));
                }
            }
        }
    }

    private void OnGUI()
    {
        if (data == null)
        {
            data = new NotepadData();
        }

        // 处理键盘快捷键
        HandleKeyboardShortcuts();

        // 绘制界面
        DrawToolbar();
        DrawTabsHeader();
        DrawContentArea();
        DrawStatusBar();
    }

    #region GUI 绘制方法
    private void DrawToolbar()
    {
        GUILayout.BeginHorizontal(EditorStyles.toolbar);

        // 文件操作按钮
        if (GUILayout.Button("新建", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            CreateNewTab();
        }

        if (GUILayout.Button("打开", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            OpenNoteFile();
        }

        if (GUILayout.Button("保存", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            SaveCurrentTab();
        }

        if (GUILayout.Button("另存为", EditorStyles.toolbarButton, GUILayout.Width(50)))
        {
            SaveAsCurrentTab();
        }

        GUILayout.Space(10);

        // 编辑操作按钮
        if (GUILayout.Button("复制", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            var currentTab = GetCurrentTab();
            if (currentTab != null && !string.IsNullOrEmpty(currentTab.content))
                EditorGUIUtility.systemCopyBuffer = currentTab.content;
        }

        if (GUILayout.Button("粘贴", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            AppendToCurrentTab(EditorGUIUtility.systemCopyBuffer);
        }

        GUILayout.Space(10);

        // 搜索
        GUILayout.Label("搜索:", EditorStyles.miniLabel);
        string newSearchText = GUILayout.TextField(searchText, GUILayout.Width(150));
        if (newSearchText != searchText)
        {
            searchText = newSearchText;
            PerformSearch();
        }

        if (searchResults.Count > 0)
        {
            GUILayout.Label($"{currentSearchIndex + 1}/{searchResults.Count}", EditorStyles.miniLabel);
            if (GUILayout.Button("↑", EditorStyles.toolbarButton, GUILayout.Width(20)))
                NavigateSearch(-1);
            if (GUILayout.Button("↓", EditorStyles.toolbarButton, GUILayout.Width(20)))
                NavigateSearch(1);
        }

        GUILayout.FlexibleSpace();

        // 设置按钮
        showSettings = GUILayout.Toggle(showSettings, "设置", EditorStyles.toolbarButton, GUILayout.Width(40));

        // 主页按钮
        if (GUILayout.Button("主页", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
            {
                data.selectedTabIndex = data.mainPageTabIndex;
                data.mainPageMode = MainPageMode.Home;
            }
        }

        // 备份按钮
        if (GUILayout.Button("备份", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
            {
                data.selectedTabIndex = data.mainPageTabIndex;
                data.mainPageMode = MainPageMode.Backup;
            }
        }

        // 关闭笔记本按钮
        if (GUILayout.Button("关闭", EditorStyles.toolbarButton, GUILayout.Width(40)))
        {
            CloseNotepad();
        }

        GUILayout.EndHorizontal();

        if (showSettings)
            DrawSettingsPanel();
    }

    private void DrawTabsHeader()
    {
        // 确保有标签页(至少有一个主页标签)
        if (data.tabs.Count == 0)
        {
            CreateMainPageTab();
        }

        GUILayout.BeginHorizontal(EditorStyles.toolbar);

        // 显示所有普通笔记标签
        for (int i = 0; i < data.tabs.Count; i++)
        {
            // 跳过主页标签
            if (i == data.mainPageTabIndex) continue;

            var tab = data.tabs[i];
            string tabName = (string.IsNullOrEmpty(tab.title) ? "未命名" : tab.title) + (tab.isModified ? " *" : "");

            if (DrawTabButton(tabName, i == data.selectedTabIndex, 120, i))
            {
                data.selectedTabIndex = i;
                // 切换标签时清除焦点,避免全选
                GUI.FocusControl(null);
            }
        }

        GUILayout.FlexibleSpace();

        // 添加新标签按钮
        if (GUILayout.Button("+", EditorStyles.toolbarButton, GUILayout.Width(30)))
        {
            CreateNewTab();
        }

        // 关闭所有标签按钮
        if (GUILayout.Button("× 全部", EditorStyles.toolbarButton, GUILayout.Width(50)))
        {
            CloseAllTabs();
        }

        GUILayout.EndHorizontal();
    }

    private bool DrawTabButton(string label, bool isSelected, float width, int tabIndex)
    {
        bool clicked = false;
        Rect tabRect = GUILayoutUtility.GetRect(
            new GUIContent(label + "   ×"),
            EditorStyles.toolbarButton,
            GUILayout.Width(width),
            GUILayout.Height(20)
        );

        if (isSelected)
        {
            EditorGUI.DrawRect(tabRect, new Color(0.2f, 0.4f, 0.8f, 0.8f));
        }

        Rect closeButtonRect = new Rect(
            tabRect.xMax - 20,
            tabRect.y + (tabRect.height - 12) / 2,
            12,
            12
        );

        Rect textRect = new Rect(
            tabRect.x,
            tabRect.y,
            tabRect.width - 20,
            tabRect.height
        );

        Event currentEvent = Event.current;

        if (currentEvent.type == EventType.MouseDown && tabRect.Contains(currentEvent.mousePosition))
        {
            if (currentEvent.button == 0)
            {
                if (closeButtonRect.Contains(currentEvent.mousePosition))
                {
                    CloseTab(tabIndex);
                    currentEvent.Use();
                }
                else
                {
                    clicked = true;
                    currentEvent.Use();
                }
            }
        }

        GUIStyle textStyle = new GUIStyle(EditorStyles.toolbarButton)
        {
            alignment = TextAnchor.MiddleLeft,
            padding = new RectOffset(5, 0, 0, 0)
        };

        GUI.Label(textRect, label, textStyle);

        GUIStyle closeStyle = new GUIStyle(EditorStyles.miniLabel)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = 10,
            normal = { textColor = closeButtonRect.Contains(currentEvent.mousePosition) ? Color.red : Color.gray }
        };

        GUI.Label(closeButtonRect, "×", closeStyle);

        return clicked;
    }

    private void DrawContentArea()
    {
        if (data.tabs.Count == 0)
        {
            CreateMainPageTab();
            return;
        }

        var currentTab = GetCurrentTab();
        if (currentTab == null)
        {
            data.selectedTabIndex = Mathf.Max(0, data.tabs.Count - 1);
            return;
        }

        // 检查标签页是否切换
        if (lastSelectedTabIndex != data.selectedTabIndex)
        {
            lastSelectedTabIndex = data.selectedTabIndex;
            // 标签页切换时清除焦点
            GUI.FocusControl(null);
        }

        EditorGUI.BeginChangeCheck();

        currentTab.scrollPosition = EditorGUILayout.BeginScrollView(currentTab.scrollPosition);

        // 如果是主页标签,绘制主页页面
        if (data.selectedTabIndex == data.mainPageTabIndex)
        {
            DrawMainPage();
        }
        else
        {
            // 正常笔记标签
            textAreaStyle.fontSize = data.fontSize;
            textAreaStyle.wordWrap = data.wordWrap;

            // 使用TextArea但不自动聚焦 - 这里的关键是永远不要调用GUI.SetNextControlName
            string newContent = EditorGUILayout.TextArea(
                currentTab.content,
                textAreaStyle,
                GUILayout.ExpandHeight(true)
            );

            if (EditorGUI.EndChangeCheck() && newContent != currentTab.content)
            {
                currentTab.content = newContent;
                currentTab.isModified = true;
                currentTab.lastModified = DateTime.Now;
                currentTab.lastChangeTime = DateTime.Now;
                ScheduleAutoSave(currentTab);
            }
        }

        EditorGUILayout.EndScrollView();
    }

    private void DrawMainPage()
    {
        GUILayout.BeginVertical();

        // 顶部导航栏
        GUILayout.BeginHorizontal(EditorStyles.toolbar);

        bool homeSelected = data.mainPageMode == MainPageMode.Home;
        bool backupSelected = data.mainPageMode == MainPageMode.Backup;

        // 主页按钮
        if (GUILayout.Toggle(homeSelected, " 主页 ", EditorStyles.toolbarButton))
        {
            data.mainPageMode = MainPageMode.Home;
        }

        // 备份按钮
        if (GUILayout.Toggle(backupSelected, " 备份管理 ", EditorStyles.toolbarButton))
        {
            data.mainPageMode = MainPageMode.Backup;
        }

        GUILayout.FlexibleSpace();

        // 快速操作按钮
        if (data.mainPageMode == MainPageMode.Home)
        {
            if (GUILayout.Button("新建笔记", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                CreateNewTab();
            }
            if (GUILayout.Button("打开文件", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                OpenNoteFile();
            }
        }
        else if (data.mainPageMode == MainPageMode.Backup)
        {
            if (GUILayout.Button("备份所有", EditorStyles.toolbarButton, GUILayout.Width(70)))
            {
                BackupAllNotes();
            }
            if (GUILayout.Button("清理旧备份", EditorStyles.toolbarButton, GUILayout.Width(80)))
            {
                CleanupOldBackups(7);
            }
        }

        GUILayout.EndHorizontal();

        GUILayout.Space(10);

        // 根据模式显示不同内容
        if (data.mainPageMode == MainPageMode.Home)
        {
            DrawHomeContent();
        }
        else
        {
            DrawBackupContent();
        }

        GUILayout.EndVertical();
    }

    private void DrawHomeContent()
    {
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        GUILayout.BeginVertical(GUILayout.Width(350));

        GUILayout.Label(EditorGUIUtility.IconContent("TextAsset Icon"), GUILayout.Width(48), GUILayout.Height(48));
        GUILayout.Space(10);

        GUIStyle titleStyle = new GUIStyle(EditorStyles.largeLabel)
        {
            fontSize = 18,
            alignment = TextAnchor.MiddleCenter,
            fontStyle = FontStyle.Bold
        };
        GUILayout.Label("Unity 记事本", titleStyle);
        GUILayout.Space(20);

        GUILayout.Label("使用说明:", EditorStyles.boldLabel);
        GUILayout.Space(8);

        EditorGUILayout.HelpBox(
            "• 点击「新建」按钮创建新笔记\n" +
            "• 点击「打开」按钮导入现有笔记\n" +
            "• 点击「保存」按钮保存当前笔记\n" +
            "• 使用「Ctrl+S」快捷键快速保存\n" +
            "• 使用「Ctrl+N」快捷键新建笔记\n" +
            "• 点击标签右侧的「×」关闭标签\n" +
            "• 支持自动保存和搜索功能\n" +
            "• 点击「备份」进入备份管理",
            MessageType.Info
        );

        GUILayout.Space(20);

        // 快速访问面板
        GUILayout.Label("快速访问", EditorStyles.boldLabel);
        GUILayout.Space(5);

        GUILayout.BeginHorizontal();
        if (GUILayout.Button(" 新建笔记 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            CreateNewTab();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 打开文件 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            OpenNoteFile();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 备份管理 ", GUILayout.Height(35), GUILayout.Width(100)))
        {
            data.mainPageMode = MainPageMode.Backup;
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(10);

        // 当前笔记快速备份
        var currentTab = GetCurrentTab();
        if (currentTab != null && data.selectedTabIndex != data.mainPageTabIndex &&
            !string.IsNullOrEmpty(currentTab.filePath))
        {
            GUILayout.BeginVertical("Box");
            GUILayout.Label("当前笔记:", EditorStyles.miniBoldLabel);

            GUILayout.BeginHorizontal();
            GUILayout.Label(currentTab.title, EditorStyles.label, GUILayout.ExpandWidth(true));

            if (GUILayout.Button("快速备份", EditorStyles.miniButton, GUILayout.Width(80)))
            {
                BackupCurrentNote();
            }
            GUILayout.EndHorizontal();

            GUILayout.EndVertical();
        }

        GUILayout.EndVertical();

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.Space(20);

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
        GUILayout.Space(10);

        DrawNoteList();
    }

    private void DrawBackupContent()
    {
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        GUILayout.BeginVertical(GUILayout.Width(400));

        GUILayout.Label(EditorGUIUtility.IconContent("d_SaveAs").image, GUILayout.Width(48), GUILayout.Height(48));
        GUILayout.Space(10);

        GUIStyle titleStyle = new GUIStyle(EditorStyles.largeLabel)
        {
            fontSize = 18,
            alignment = TextAnchor.MiddleCenter,
            fontStyle = FontStyle.Bold
        };
        GUILayout.Label("备份管理", titleStyle);
        GUILayout.Space(20);

        EditorGUILayout.HelpBox(
            "• 这里显示所有备份的笔记文件\n" +
            "• 备份文件名格式:原文件名_备份时间.扩展名\n" +
            "• 点击备份文件名可以打开查看备份内容\n" +
            "• 可以删除不需要的备份文件\n" +
            "• 备份文件夹:" + data.backupFolder,
            MessageType.Info
        );

        GUILayout.Space(20);

        // 快速操作面板
        GUILayout.Label("快速操作", EditorStyles.boldLabel);
        GUILayout.Space(5);

        GUILayout.BeginHorizontal();

        // 备份当前笔记按钮(如果当前是笔记标签)
        if (data.selectedTabIndex != data.mainPageTabIndex)
        {
            var currentTab = GetCurrentTab();
            if (currentTab != null && !string.IsNullOrEmpty(currentTab.filePath))
            {
                if (GUILayout.Button(" 备份当前笔记 ", GUILayout.Height(35), GUILayout.Width(120)))
                {
                    BackupCurrentNote();
                }
                GUILayout.Space(10);
            }
        }

        if (GUILayout.Button(" 备份所有笔记 ", GUILayout.Height(35), GUILayout.Width(120)))
        {
            BackupAllNotes();
        }
        GUILayout.Space(10);

        if (GUILayout.Button(" 刷新列表 ", GUILayout.Height(35), GUILayout.Width(80)))
        {
            RefreshBackupList();
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        if (GUILayout.Button(" 打开备份文件夹 ", GUILayout.Height(35), GUILayout.Width(130)))
        {
            OpenBackupFolder();
        }
        GUILayout.Space(10);
        if (GUILayout.Button(" 清理7天前备份 ", GUILayout.Height(35), GUILayout.Width(130)))
        {
            CleanupOldBackups(7);
        }
        GUILayout.EndHorizontal();

        GUILayout.EndVertical();

        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();

        GUILayout.Space(20);

        EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
        GUILayout.Space(10);

        DrawBackupList();
    }

    private void DrawNoteList()
    {
        // 刷新笔记列表(如果需要)
        double timeSinceStartup = EditorApplication.timeSinceStartup;
        TimeSpan timeSinceLastRefresh = DateTime.Now - lastNoteListRefresh;

        if (timeSinceLastRefresh.TotalSeconds > NOTE_LIST_REFRESH_INTERVAL)
        {
            RefreshNoteList();
        }

        GUILayout.BeginHorizontal();
        GUILayout.Label("笔记文件夹: ", EditorStyles.boldLabel);
        GUILayout.Label(data.notesFolder, EditorStyles.wordWrappedLabel);
        GUILayout.FlexibleSpace();

        data.showNoteList = GUILayout.Toggle(data.showNoteList, "显示列表", EditorStyles.miniButton, GUILayout.Width(80));

        if (GUILayout.Button("刷新", EditorStyles.miniButton, GUILayout.Width(50)))
        {
            RefreshNoteList();
        }
        GUILayout.EndHorizontal();

        if (!data.showNoteList) return;

        GUILayout.Space(5);

        if (noteFiles.Count == 0)
        {
            EditorGUILayout.HelpBox($"笔记文件夹中没有找到文本文件。\n支持的格式: {string.Join(", ", textExtensions)}", MessageType.Info);
            return;
        }

        GUILayout.Label($"找到 {noteFiles.Count} 个笔记:", EditorStyles.miniBoldLabel);

        noteListScrollPos = GUILayout.BeginScrollView(noteListScrollPos, GUILayout.Height(200));

        foreach (string filePath in noteFiles)
        {
            try
            {
                string fileName = Path.GetFileName(filePath);
                string relativePath = GetRelativePath(filePath);
                DateTime lastModified = File.GetLastWriteTime(filePath);
                string fileInfo = $"{fileName} ({GetFileSize(filePath)}) - {lastModified:yyyy-MM-dd HH:mm}";

                GUILayout.BeginHorizontal(EditorStyles.helpBox);

                // 使用文件名为按钮,点击即可打开
                if (GUILayout.Button(fileName, EditorStyles.miniButton, GUILayout.ExpandWidth(true)))
                {
                    OpenNoteFile(filePath);
                }

                GUILayout.Space(5);

                // 显示更多信息
                GUILayout.BeginVertical(GUILayout.Width(100));
                GUILayout.Label(GetFileSize(filePath), EditorStyles.miniLabel);
                GUILayout.Label(lastModified.ToString("yyyy-MM-dd"), EditorStyles.miniLabel);
                GUILayout.EndVertical();

                // 显示是否已打开
                if (openedFiles.Contains(Path.GetFullPath(filePath)))
                {
                    GUILayout.Label("✓", EditorStyles.miniLabel, GUILayout.Width(20));
                }

                GUILayout.Space(5);

                // 添加删除按钮
                if (GUILayout.Button("×", EditorStyles.miniButton, GUILayout.Width(25)))
                {
                    DeleteNoteFile(filePath);
                }

                GUILayout.EndHorizontal();

                GUILayout.Space(2);
            }
            catch (Exception)
            {
                continue;
            }
        }

        GUILayout.EndScrollView();
    }

    private void DrawBackupList()
    {
        // 刷新备份列表(如果需要)
        TimeSpan timeSinceLastRefresh = DateTime.Now - lastBackupListRefresh;

        if (timeSinceLastRefresh.TotalSeconds > NOTE_LIST_REFRESH_INTERVAL)
        {
            RefreshBackupList();
        }

        GUILayout.BeginHorizontal();
        GUILayout.Label("备份文件夹: ", EditorStyles.boldLabel);
        GUILayout.Label(data.backupFolder, EditorStyles.wordWrappedLabel);
        GUILayout.FlexibleSpace();

        if (GUILayout.Button("打开文件夹", EditorStyles.miniButton, GUILayout.Width(80)))
        {
            OpenBackupFolder();
        }

        GUILayout.Space(5);

        if (GUILayout.Button("刷新", EditorStyles.miniButton, GUILayout.Width(50)))
        {
            RefreshBackupList();
        }
        GUILayout.EndHorizontal();

        GUILayout.Space(5);

        if (backupFiles.Count == 0)
        {
            EditorGUILayout.HelpBox($"备份文件夹中没有找到备份文件。\n点击上面的按钮备份当前笔记或所有笔记。", MessageType.Info);
            return;
        }

        GUILayout.Label($"找到 {backupFiles.Count} 个备份文件:", EditorStyles.miniBoldLabel);

        backupListScrollPos = GUILayout.BeginScrollView(backupListScrollPos, GUILayout.ExpandHeight(true));

        // 按备份时间倒序排序
        var sortedBackups = backupFiles.OrderByDescending(b => b.backupTime).ToList();

        foreach (var backup in sortedBackups)
        {
            GUILayout.BeginVertical(EditorStyles.helpBox);

            // 备份文件信息行
            GUILayout.BeginHorizontal();

            GUILayout.BeginVertical(GUILayout.ExpandWidth(true));

            // 原始文件名
            GUILayout.Label(backup.originalName, EditorStyles.boldLabel);

            // 备份时间
            GUILayout.Label($"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}", EditorStyles.miniLabel);

            // 文件大小
            GUILayout.Label($"大小: {FormatFileSize(backup.fileSize)}", EditorStyles.miniLabel);

            GUILayout.EndVertical();

            // 按钮区域
            GUILayout.BeginVertical(GUILayout.Width(200));

            GUILayout.BeginHorizontal();

            // 打开备份按钮
            if (GUILayout.Button("预览", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                ShowBackupPreview(backup);
            }

            // 恢复备份按钮
            if (GUILayout.Button("恢复", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                RestoreBackup(backup);
            }

            // 删除备份按钮
            if (GUILayout.Button("删除", EditorStyles.miniButton, GUILayout.Width(60)))
            {
                DeleteBackupFile(backup.filePath);
            }

            GUILayout.EndHorizontal();

            GUILayout.EndVertical();

            GUILayout.EndHorizontal();

            // 预览内容(展开时显示)
            if (selectedBackupForPreview == backup)
            {
                GUILayout.Space(5);
                DrawBackupPreview(backup);
            }

            GUILayout.EndVertical();

            GUILayout.Space(3);
        }

        GUILayout.EndScrollView();
    }

    private void ShowBackupPreview(BackupFileInfo backup)
    {
        try
        {
            string content = File.ReadAllText(backup.filePath, Encoding.UTF8);
            string preview = content.Length > 200 ? content.Substring(0, 200) + "..." : content;

            EditorUtility.DisplayDialog(
                $"预览: {backup.originalName}",
                $"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}\n\n" +
                $"预览内容:\n{preview}",
                "确定"
            );
        }
        catch (Exception e)
        {
            Debug.LogWarning($"无法预览备份文件: {e.Message}");
        }
    }

    private void DrawBackupPreview(BackupFileInfo backup)
    {
        try
        {
            string content = File.ReadAllText(backup.filePath, Encoding.UTF8);
            string preview = content.Length > 500 ? content.Substring(0, 500) + "..." : content;

            EditorGUILayout.HelpBox(
                $"备份时间: {backup.backupTime:yyyy-MM-dd HH:mm:ss}\n\n" +
                $"预览内容:\n{preview}",
                MessageType.Info
            );

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("在编辑器中打开", EditorStyles.miniButton, GUILayout.Width(120)))
            {
                OpenBackupFile(backup.filePath);
            }
            GUILayout.EndHorizontal();
        }
        catch (Exception e)
        {
            EditorGUILayout.HelpBox($"无法预览备份文件: {e.Message}", MessageType.Error);
        }
    }

    private void DrawStatusBar()
    {
        GUILayout.BeginHorizontal("Box");

        var currentTab = GetCurrentTab();

        if (currentTab != null)
        {
            if (data.selectedTabIndex == data.mainPageTabIndex)
            {
                string modeText = data.mainPageMode == MainPageMode.Home ? "主页" : "备份管理";
                GUILayout.Label($"{modeText} - 共 {noteFiles.Count} 个笔记, {backupFiles.Count} 个备份", EditorStyles.miniLabel);
            }
            else
            {
                string status = $"字数: {currentTab.content.Length}";

                if (currentTab.isModified)
                    status += " | 已修改";

                if (!string.IsNullOrEmpty(currentTab.filePath))
                {
                    string fileName = Path.GetFileName(currentTab.filePath);
                    status += $" | 文件: {fileName}";
                }

                GUILayout.Label(status, EditorStyles.miniLabel);
            }
        }
        else
        {
            GUILayout.Label("没有打开的笔记", EditorStyles.miniLabel);
        }

        GUILayout.FlexibleSpace();

        GUILayout.Label(data.autoSaveEnabled ? "自动保存: 开启" : "自动保存: 关闭",
            EditorStyles.miniLabel);

        GUILayout.EndHorizontal();
    }

    private void DrawSettingsPanel()
    {
        GUILayout.BeginVertical("Box");

        EditorGUILayout.LabelField("设置", EditorStyles.boldLabel);

        data.autoSaveEnabled = EditorGUILayout.Toggle("启用自动保存", data.autoSaveEnabled);
        data.fontSize = EditorGUILayout.IntSlider("字体大小", data.fontSize, 10, 24);
        data.wordWrap = EditorGUILayout.Toggle("自动换行", data.wordWrap);
        data.showNoteList = EditorGUILayout.Toggle("显示笔记列表", data.showNoteList);

        EditorGUILayout.Space();

        GUILayout.Label("笔记文件夹:", EditorStyles.miniBoldLabel);
        GUILayout.BeginHorizontal();
        GUILayout.Label(data.notesFolder, EditorStyles.textField);
        if (GUILayout.Button("浏览", GUILayout.Width(60)))
        {
            string newPath = EditorUtility.OpenFolderPanel("选择笔记文件夹", Application.dataPath, "");
            if (!string.IsNullOrEmpty(newPath))
            {
                string relativePath = "Assets" + newPath.Replace(Application.dataPath, "");
                if (relativePath != data.notesFolder)
                {
                    data.notesFolder = relativePath;
                    RefreshNoteList();
                }
            }
        }
        GUILayout.EndHorizontal();

        GUILayout.Label("备份文件夹:", EditorStyles.miniBoldLabel);
        GUILayout.BeginHorizontal();
        GUILayout.Label(data.backupFolder, EditorStyles.textField);
        if (GUILayout.Button("浏览", GUILayout.Width(60)))
        {
            string newPath = EditorUtility.OpenFolderPanel("选择备份文件夹", Application.dataPath, "");
            if (!string.IsNullOrEmpty(newPath))
            {
                string relativePath = "Assets" + newPath.Replace(Application.dataPath, "");
                if (relativePath != data.backupFolder)
                {
                    data.backupFolder = relativePath;
                    RefreshBackupList();
                }
            }
        }
        GUILayout.EndHorizontal();

        EditorGUILayout.Space();

        if (GUILayout.Button("导出所有笔记", GUILayout.Height(25)))
        {
            ExportAllNotes();
        }

        GUILayout.EndVertical();
    }
    #endregion

    #region 笔记列表功能
    private void RefreshNoteList()
    {
        noteFiles.Clear();

        try
        {
            string fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.notesFolder));

            if (Directory.Exists(fullPath))
            {
                foreach (string extension in textExtensions)
                {
                    try
                    {
                        // 只搜索笔记文件夹,不搜索子文件夹
                        string[] files = Directory.GetFiles(fullPath, "*" + extension, SearchOption.TopDirectoryOnly);

                        foreach (string filePath in files)
                        {
                            // 检查是否是备份文件(文件名中包含"备份"字样)
                            string fileName = Path.GetFileName(filePath);
                            if (!fileName.Contains("_备份_"))
                            {
                                noteFiles.Add(filePath);
                            }
                        }
                    }
                    catch (Exception) { }
                }

                noteFiles.Sort((a, b) => File.GetLastWriteTime(b).CompareTo(File.GetLastWriteTime(a)));
            }
        }
        catch (Exception e)
        {
            Debug.LogWarning($"刷新笔记列表失败: {e.Message}");
        }

        lastNoteListRefresh = DateTime.Now;
    }

    private string GetRelativePath(string fullPath)
    {
        try
        {
            string projectPath = Path.GetFullPath(Application.dataPath + "/..");
            if (fullPath.StartsWith(projectPath))
            {
                return fullPath.Substring(projectPath.Length + 1);
            }
        }
        catch { }

        return fullPath;
    }

    private string GetFileSize(string filePath)
    {
        try
        {
            FileInfo fileInfo = new FileInfo(filePath);
            long size = fileInfo.Length;

            if (size < 1024) return $"{size} B";
            else if (size < 1024 * 1024) return $"{(size / 1024.0):0.0} KB";
            else return $"{(size / (1024.0 * 1024.0)):0.0} MB";
        }
        catch
        {
            return "未知大小";
        }
    }

    private void DeleteNoteFile(string filePath)
    {
        try
        {
            // 检查文件是否已经打开
            string fullPath = Path.GetFullPath(filePath);
            bool isFileOpened = openedFiles.Contains(fullPath);

            string message;
            if (isFileOpened)
            {
                message = $"文件正在编辑中,删除后未保存的内容将丢失!\n\n确定要删除文件吗?\n{Path.GetFileName(filePath)}";
            }
            else
            {
                message = $"确定要删除文件吗?\n{Path.GetFileName(filePath)}";
            }

            // 确认对话框
            if (EditorUtility.DisplayDialog("删除文件", message, "删除", "取消"))
            {
                // 如果文件已经打开,先关闭对应的标签页
                if (isFileOpened)
                {
                    for (int i = 0; i < data.tabs.Count; i++)
                    {
                        if (i != data.mainPageTabIndex &&
                            !string.IsNullOrEmpty(data.tabs[i].filePath) &&
                            Path.GetFullPath(data.tabs[i].filePath) == fullPath)
                        {
                            // 先强制关闭标签页,不保存
                            var tab = data.tabs[i];
                            if (!string.IsNullOrEmpty(tab.filePath))
                            {
                                openedFiles.Remove(Path.GetFullPath(tab.filePath));
                            }
                            data.tabs.RemoveAt(i);

                            // 更新特殊标签的索引
                            if (i < data.mainPageTabIndex) data.mainPageTabIndex--;

                            // 调整选中索引
                            if (data.selectedTabIndex >= i && data.selectedTabIndex > 0)
                            {
                                data.selectedTabIndex--;
                            }
                            else if (data.selectedTabIndex >= data.tabs.Count)
                            {
                                data.selectedTabIndex = data.tabs.Count - 1;
                            }

                            break;
                        }
                    }
                }

                // 删除物理文件
                File.Delete(filePath);

                // 刷新列表
                RefreshNoteList();

                // 更新界面
                Repaint();

                // 显示删除成功的提示
                EditorUtility.DisplayDialog("删除成功", $"文件已删除:{Path.GetFileName(filePath)}", "确定");
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("删除失败", $"无法删除文件:{e.Message}", "确定");
        }
    }
    #endregion

    #region 备份管理功能
    private void RefreshBackupList()
    {
        backupFiles.Clear();

        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                // 获取所有备份文件
                foreach (string extension in textExtensions)
                {
                    try
                    {
                        string[] files = Directory.GetFiles(backupFolderPath, "*" + extension, SearchOption.TopDirectoryOnly);

                        foreach (string filePath in files)
                        {
                            try
                            {
                                FileInfo fileInfo = new FileInfo(filePath);
                                string fileName = fileInfo.Name;

                                // 只显示备份文件(文件名中包含"备份"字样)
                                if (fileName.Contains("_备份_"))
                                {
                                    // 解析原始文件名(去掉备份时间戳部分)
                                    int backupIndex = fileName.IndexOf("_备份_");
                                    string originalName = fileName.Substring(0, backupIndex) + fileInfo.Extension;

                                    BackupFileInfo backupInfo = new BackupFileInfo
                                    {
                                        filePath = filePath,
                                        fileName = fileName,
                                        originalName = originalName,
                                        backupTime = fileInfo.LastWriteTime,
                                        fileSize = fileInfo.Length,
                                        relativePath = GetRelativePath(filePath)
                                    };

                                    backupFiles.Add(backupInfo);
                                }
                            }
                            catch (Exception) { }
                        }
                    }
                    catch (Exception) { }
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogWarning($"刷新备份列表失败: {e.Message}");
        }

        lastBackupListRefresh = DateTime.Now;
    }

    private string FormatFileSize(long size)
    {
        if (size < 1024) return $"{size} B";
        else if (size < 1024 * 1024) return $"{(size / 1024.0):0.0} KB";
        else return $"{(size / (1024.0 * 1024.0)):0.0} MB";
    }

    private void OpenBackupFile(string filePath)
    {
        try
        {
            string content = File.ReadAllText(filePath, Encoding.UTF8);
            string fileName = Path.GetFileName(filePath);

            // 创建新标签页打开备份文件
            var newTab = new NoteTab();
            newTab.title = "[备份] " + Path.GetFileNameWithoutExtension(fileName);
            newTab.content = content;
            newTab.filePath = filePath;
            newTab.isModified = false; // 备份文件默认只读,不能修改

            data.tabs.Add(newTab);
            data.selectedTabIndex = data.tabs.Count - 1;
            lastSelectedTabIndex = data.selectedTabIndex;

            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开备份文件: {e.Message}", "确定");
        }
    }

    private void DeleteBackupFile(string filePath)
    {
        try
        {
            string fileName = Path.GetFileName(filePath);

            if (EditorUtility.DisplayDialog("删除备份", $"确定要删除备份文件吗?\n{fileName}", "删除", "取消"))
            {
                File.Delete(filePath);
                RefreshBackupList();

                // 提示用户
                EditorUtility.DisplayDialog("删除成功", $"已删除备份文件:{fileName}", "确定");

                // 刷新显示
                Repaint();
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("删除失败", $"无法删除备份文件:{e.Message}", "确定");
        }
    }

    private void RestoreBackup(BackupFileInfo backup)
    {
        try
        {
            // 询问恢复位置
            string defaultName = backup.originalName;
            string restorePath = EditorUtility.SaveFilePanel("恢复备份", data.notesFolder, defaultName, "txt");

            if (!string.IsNullOrEmpty(restorePath))
            {
                // 复制备份文件到指定位置
                File.Copy(backup.filePath, restorePath, true);

                // 刷新笔记列表
                RefreshNoteList();

                // 提示用户
                EditorUtility.DisplayDialog("恢复成功",
                    $"已恢复备份文件:{backup.fileName}\n恢复到:{restorePath}", "确定");

                // 打开恢复的文件
                OpenNoteFile(restorePath);
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("恢复失败", $"无法恢复备份:{e.Message}", "确定");
        }
    }

    private void OpenBackupFolder()
    {
        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                // 在文件资源管理器中打开文件夹
                EditorUtility.RevealInFinder(backupFolderPath);
            }
            else
            {
                EditorUtility.DisplayDialog("提示", "备份文件夹不存在,将创建新文件夹", "确定");
                Directory.CreateDirectory(backupFolderPath);
                EditorUtility.RevealInFinder(backupFolderPath);
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开备份文件夹:{e.Message}", "确定");
        }
    }

    private void CleanupOldBackups(int days)
    {
        try
        {
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));

            if (Directory.Exists(backupFolderPath))
            {
                DateTime cutoffDate = DateTime.Now.AddDays(-days);
                int deletedCount = 0;

                foreach (var extension in textExtensions)
                {
                    string[] files = Directory.GetFiles(backupFolderPath, "*" + extension, SearchOption.TopDirectoryOnly);

                    foreach (string filePath in files)
                    {
                        try
                        {
                            FileInfo fileInfo = new FileInfo(filePath);
                            if (fileInfo.LastWriteTime < cutoffDate)
                            {
                                File.Delete(filePath);
                                deletedCount++;
                            }
                        }
                        catch (Exception) { }
                    }
                }

                // 刷新备份列表
                RefreshBackupList();

                // 提示用户
                if (deletedCount > 0)
                {
                    EditorUtility.DisplayDialog("清理完成",
                        $"已清理 {deletedCount} 个 {days} 天前的备份文件", "确定");
                }
                else
                {
                    EditorUtility.DisplayDialog("清理完成",
                        $"没有找到 {days} 天前的备份文件", "确定");
                }

                // 刷新显示
                Repaint();
            }
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("清理失败", $"清理备份文件失败:{e.Message}", "确定");
        }
    }

    // 备份当前笔记
    private void BackupCurrentNote()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || string.IsNullOrEmpty(currentTab.filePath))
        {
            EditorUtility.DisplayDialog("备份失败", "当前笔记没有保存到文件,请先保存", "确定");
            return;
        }

        try
        {
            // 确保备份文件夹存在
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));
            if (!Directory.Exists(backupFolderPath))
            {
                Directory.CreateDirectory(backupFolderPath);
            }

            // 获取文件信息
            FileInfo fileInfo = new FileInfo(currentTab.filePath);
            string fileName = fileInfo.Name;
            string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
            string fileExt = fileInfo.Extension;

            // 生成备份文件名(添加时间戳)
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string backupFileName = $"{fileNameWithoutExt}_备份_{timestamp}{fileExt}";
            string backupFilePath = Path.Combine(backupFolderPath, backupFileName);

            // 复制文件
            File.Copy(currentTab.filePath, backupFilePath, true);

            // 刷新备份列表
            RefreshBackupList();

            // 提示用户
            EditorUtility.DisplayDialog("备份成功",
                $"已成功备份当前笔记:{fileName}\n备份到:{backupFileName}", "确定");

            // 刷新显示
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("备份失败", $"无法备份文件:{e.Message}", "确定");
        }
    }

    // 备份所有笔记
    private void BackupAllNotes()
    {
        try
        {
            // 确保备份文件夹存在
            string backupFolderPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", data.backupFolder));
            if (!Directory.Exists(backupFolderPath))
            {
                Directory.CreateDirectory(backupFolderPath);
            }

            int backupCount = 0;
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");

            // 备份所有已打开的且有文件路径的笔记
            foreach (var tab in data.tabs)
            {
                if (!string.IsNullOrEmpty(tab.filePath) && tab.title != "主页")
                {
                    try
                    {
                        FileInfo fileInfo = new FileInfo(tab.filePath);
                        string fileName = fileInfo.Name;
                        string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
                        string fileExt = fileInfo.Extension;

                        string backupFileName = $"{fileNameWithoutExt}_备份_{timestamp}{fileExt}";
                        string backupFilePath = Path.Combine(backupFolderPath, backupFileName);

                        File.Copy(tab.filePath, backupFilePath, true);
                        backupCount++;
                    }
                    catch (Exception) { }
                }
            }

            // 刷新备份列表
            RefreshBackupList();

            // 提示用户
            if (backupCount > 0)
            {
                EditorUtility.DisplayDialog("备份成功",
                    $"已成功备份 {backupCount} 个笔记文件\n备份时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}", "确定");
            }
            else
            {
                EditorUtility.DisplayDialog("备份失败", "没有找到可以备份的笔记文件", "确定");
            }

            // 刷新显示
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("备份失败", $"无法备份文件:{e.Message}", "确定");
        }
    }
    #endregion

    #region 标签管理
    private NoteTab GetCurrentTab()
    {
        if (data == null || data.tabs.Count == 0) return null;

        if (data.selectedTabIndex < 0)
            data.selectedTabIndex = 0;
        else if (data.selectedTabIndex >= data.tabs.Count)
            data.selectedTabIndex = data.tabs.Count - 1;

        return data.tabs[data.selectedTabIndex];
    }

    private void EnsureMainPageTab()
    {
        // 如果没有主页标签,创建一个
        if (data.mainPageTabIndex < 0 || data.mainPageTabIndex >= data.tabs.Count || data.tabs[data.mainPageTabIndex].title != "主页")
        {
            CreateMainPageTab();
        }
    }

    private void CreateMainPageTab()
    {
        // 检查是否已经有主页标签
        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (data.tabs[i].title == "主页")
            {
                data.mainPageTabIndex = i;
                return;
            }
        }

        // 创建新的主页标签
        var mainTab = new NoteTab();
        mainTab.title = "主页";
        mainTab.content = ""; // 主页页面不需要内容
        data.tabs.Add(mainTab);
        data.mainPageTabIndex = data.tabs.Count - 1;

        // 如果这是第一个标签,选中它
        if (data.tabs.Count == 1)
        {
            data.selectedTabIndex = 0;
        }
    }

    private void CreateNewTab()
    {
        if (data == null) data = new NotepadData();

        var newTab = new NoteTab();
        newTab.title = "笔记 " + (data.tabs.Count);
        data.tabs.Add(newTab);
        data.selectedTabIndex = data.tabs.Count - 1;
        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }

    private void CloseTab(int index)
    {
        if (data == null || index < 0 || index >= data.tabs.Count) return;

        var tab = data.tabs[index];

        // 不允许关闭主页标签
        if (index == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("提示", "主页标签不能关闭", "确定");
            return;
        }

        if (tab.isModified)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭笔记",
                $"'{tab.title}' 有未保存的更改。",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                SaveTab(index);
            }
            else if (result == 2)
            {
                return;
            }
        }

        if (!string.IsNullOrEmpty(tab.filePath))
        {
            openedFiles.Remove(Path.GetFullPath(tab.filePath));
        }

        // 移除标签
        data.tabs.RemoveAt(index);

        // 更新特殊标签的索引
        if (index < data.mainPageTabIndex)
        {
            data.mainPageTabIndex--;
        }

        // 如果关闭后没有普通标签,切换到主页标签
        if (data.tabs.Count == 1 && data.mainPageTabIndex >= 0)
        {
            data.selectedTabIndex = data.mainPageTabIndex;
        }
        else if (data.tabs.Count > 0)
        {
            if (data.selectedTabIndex >= index && data.selectedTabIndex > 0)
            {
                data.selectedTabIndex--;
            }
            else if (data.selectedTabIndex >= data.tabs.Count)
            {
                data.selectedTabIndex = data.tabs.Count - 1;
            }
        }

        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }

    private void CloseAllTabs()
    {
        if (data == null || data.tabs.Count <= 1) return; // 至少有主页标签

        // 只关闭普通标签,保留主页标签
        List<int> tabsToClose = new List<int>();

        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i != data.mainPageTabIndex)
            {
                tabsToClose.Add(i);
            }
        }

        bool hasUnsavedChanges = tabsToClose.Any(i => data.tabs[i].isModified);

        if (hasUnsavedChanges)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭所有标签",
                "有未保存的笔记。是否保存更改?",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                foreach (int i in tabsToClose)
                {
                    if (data.tabs[i].isModified)
                    {
                        SaveTab(i);
                    }
                }
            }
            else if (result == 2)
            {
                return;
            }
        }

        // 关闭所有普通标签
        for (int i = tabsToClose.Count - 1; i >= 0; i--)
        {
            int tabIndex = tabsToClose[i];
            if (!string.IsNullOrEmpty(data.tabs[tabIndex].filePath))
            {
                openedFiles.Remove(Path.GetFullPath(data.tabs[tabIndex].filePath));
            }
            data.tabs.RemoveAt(tabIndex);
        }

        // 更新特殊标签索引
        data.mainPageTabIndex = 0;
        data.selectedTabIndex = data.mainPageTabIndex;
        lastSelectedTabIndex = data.selectedTabIndex;

        Repaint();
    }
    #endregion

    #region 文件操作
    private void OpenNoteFile()
    {
        string path = EditorUtility.OpenFilePanel("打开笔记文件", Application.dataPath, "txt,md,json,cs");

        if (!string.IsNullOrEmpty(path))
        {
            OpenNoteFile(path);
        }
    }

    private void OpenNoteFile(string filePath)
    {
        try
        {
            string fullPath = Path.GetFullPath(filePath);

            // 检查文件是否已经打开
            if (openedFiles.Contains(fullPath))
            {
                for (int i = 0; i < data.tabs.Count; i++)
                {
                    if (i != data.mainPageTabIndex &&
                        !string.IsNullOrEmpty(data.tabs[i].filePath) &&
                        Path.GetFullPath(data.tabs[i].filePath) == fullPath)
                    {
                        data.selectedTabIndex = i;
                        lastSelectedTabIndex = data.selectedTabIndex;
                        Repaint();
                        return;
                    }
                }
            }

            string content = File.ReadAllText(filePath, Encoding.UTF8);
            var newTab = new NoteTab();
            newTab.title = Path.GetFileNameWithoutExtension(filePath);
            newTab.content = content;
            newTab.filePath = filePath;
            newTab.isModified = false;

            data.tabs.Add(newTab);
            data.selectedTabIndex = data.tabs.Count - 1;
            lastSelectedTabIndex = data.selectedTabIndex;

            openedFiles.Add(fullPath);

            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"无法打开文件: {e.Message}", "确定");
        }
    }

    private void SaveCurrentTab()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || data.selectedTabIndex == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("保存", "没有活动的笔记可以保存", "确定");
            return;
        }

        SaveTab(data.selectedTabIndex);
    }

    private void SaveTab(int index)
    {
        if (data == null || index < 0 || index >= data.tabs.Count ||
            index == data.mainPageTabIndex) return;

        var tab = data.tabs[index];

        if (string.IsNullOrEmpty(tab.filePath))
        {
            SaveAsCurrentTab();
            return;
        }

        try
        {
            string directory = Path.GetDirectoryName(tab.filePath);
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            File.WriteAllText(tab.filePath, tab.content, Encoding.UTF8);
            tab.isModified = false;
            tab.lastModified = DateTime.Now;
            tab.lastSavedHash = CalculateContentHash(tab.content);

            AssetDatabase.Refresh();
            RefreshNoteList();
            Repaint();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", $"保存失败: {e.Message}", "确定");
        }
    }

    private void SaveAsCurrentTab()
    {
        var currentTab = GetCurrentTab();
        if (currentTab == null || data.selectedTabIndex == data.mainPageTabIndex)
        {
            EditorUtility.DisplayDialog("另存为", "没有活动的笔记可以另存为", "确定");
            return;
        }

        string defaultName = string.IsNullOrEmpty(currentTab.title) ? "NewNote.txt" : currentTab.title + ".txt";
        string path = EditorUtility.SaveFilePanel("另存为", Application.dataPath, defaultName, "txt");

        if (!string.IsNullOrEmpty(path))
        {
            if (!string.IsNullOrEmpty(currentTab.filePath))
            {
                openedFiles.Remove(Path.GetFullPath(currentTab.filePath));
            }

            currentTab.filePath = path;
            currentTab.title = Path.GetFileNameWithoutExtension(path);

            openedFiles.Add(Path.GetFullPath(path));

            SaveCurrentTab();
        }
    }
    #endregion

    #region 自动保存系统
    private void ScheduleAutoSave(NoteTab tab)
    {
        int tabIndex = data.tabs.IndexOf(tab);
        if (tabIndex == -1) return;

        scheduledSaves[tabIndex] = EditorApplication.timeSinceStartup + 2.0;
    }

    private void OnEditorUpdate()
    {
        CheckScheduledSaves();

        if (data != null && data.autoSaveEnabled && (DateTime.Now - lastAutoSaveTime).TotalSeconds >= AUTO_SAVE_INTERVAL)
        {
            AutoSaveAllModifiedTabs();
            lastAutoSaveTime = DateTime.Now;
        }
    }

    private void CheckScheduledSaves()
    {
        if (scheduledSaves.Count == 0) return;

        List<int> toRemove = new List<int>();
        double currentTime = EditorApplication.timeSinceStartup;

        foreach (var kvp in scheduledSaves)
        {
            if (currentTime >= kvp.Value)
            {
                int tabIndex = kvp.Key;
                if (tabIndex < data.tabs.Count)
                {
                    var tab = data.tabs[tabIndex];
                    if (tab.isModified)
                    {
                        string currentHash = CalculateContentHash(tab.content);
                        if (currentHash != tab.lastSavedHash)
                        {
                            PerformAutoSave(tab, tabIndex);
                            tab.lastSavedHash = currentHash;
                        }
                    }
                }
                toRemove.Add(tabIndex);
            }
        }

        foreach (int index in toRemove)
        {
            scheduledSaves.Remove(index);
        }
    }

    private void AutoSaveAllModifiedTabs()
    {
        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i == data.mainPageTabIndex) continue; // 跳过主页标签

            var tab = data.tabs[i];
            if (tab.isModified)
            {
                PerformAutoSave(tab, i);
            }
        }
    }

    private void PerformAutoSave(NoteTab tab, int tabIndex)
    {
        if (string.IsNullOrEmpty(tab.filePath))
        {
            SaveToTempFile(tab);
        }
        else
        {
            SaveTabSilently(tabIndex);
        }
    }

    private void SaveTabSilently(int index)
    {
        if (index < 0 || index >= data.tabs.Count ||
            index == data.mainPageTabIndex) return;

        var tab = data.tabs[index];

        try
        {
            string directory = Path.GetDirectoryName(tab.filePath);
            if (!Directory.Exists(directory))
                Directory.CreateDirectory(directory);

            File.WriteAllText(tab.filePath, tab.content, Encoding.UTF8);
            tab.isModified = false;
            tab.lastModified = DateTime.Now;
            tab.lastSavedHash = CalculateContentHash(tab.content);
        }
        catch (Exception e)
        {
            Debug.LogWarning($"自动保存失败: {e.Message}");
        }
    }

    private void SaveToTempFile(NoteTab tab)
    {
        try
        {
            string tempFolder = Path.Combine(Application.temporaryCachePath, "UnityNotepad/Autosave");
            if (!Directory.Exists(tempFolder))
                Directory.CreateDirectory(tempFolder);

            string safeTitle = string.IsNullOrEmpty(tab.title) ?
                "Untitled" :
                System.Text.RegularExpressions.Regex.Replace(tab.title, @"[^\w]", "_");

            string fileName = $"{safeTitle}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
            string tempFile = Path.Combine(tempFolder, fileName);

            File.WriteAllText(tempFile, tab.content, Encoding.UTF8);
            CleanupOldTempFiles(tempFolder, 10);
        }
        catch { }
    }

    private void SaveAllModifiedTabs()
    {
        if (data == null) return;

        for (int i = 0; i < data.tabs.Count; i++)
        {
            if (i == data.mainPageTabIndex) continue;

            var tab = data.tabs[i];
            if (tab.isModified)
            {
                SaveTabSilently(i);
            }
        }
    }
    #endregion

    #region 辅助方法
    private void CloseNotepad()
    {
        bool hasUnsavedChanges = data?.tabs?.Any(t => t.isModified) ?? false;

        if (hasUnsavedChanges)
        {
            int result = EditorUtility.DisplayDialogComplex(
                "关闭笔记本",
                "有未保存的笔记。是否保存更改?",
                "保存并关闭",
                "直接关闭",
                "取消"
            );

            if (result == 0)
            {
                SaveAllModifiedTabs();
                Close();
            }
            else if (result == 1)
            {
                Close();
            }
        }
        else
        {
            Close();
        }
    }

    private string CalculateContentHash(string content)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] bytes = Encoding.UTF8.GetBytes(content);
            byte[] hash = sha256.ComputeHash(bytes);
            return Convert.ToBase64String(hash);
        }
    }

    private void CleanupOldTempFiles(string folder, int keepCount)
    {
        try
        {
            var files = Directory.GetFiles(folder, "*.txt")
                .Select(f => new FileInfo(f))
                .OrderByDescending(f => f.LastWriteTime)
                .ToList();

            for (int i = keepCount; i < files.Count; i++)
            {
                files[i].Delete();
            }
        }
        catch { }
    }

    private void CreateTextAreaStyle()
    {
        textAreaStyle = new GUIStyle(EditorStyles.textArea)
        {
            wordWrap = true,
            fontSize = 14,
            padding = new RectOffset(10, 10, 10, 10),
            margin = new RectOffset(5, 5, 5, 5)
        };
    }

    private void AppendToCurrentTab(string text)
    {
        var currentTab = GetCurrentTab();
        if (currentTab != null && data.selectedTabIndex != data.mainPageTabIndex &&
            !string.IsNullOrEmpty(text))
        {
            currentTab.content += text;
            currentTab.isModified = true;
            ScheduleAutoSave(currentTab);
            Repaint();
        }
    }

    private void LoadData()
    {
        string json = EditorPrefs.GetString(DATA_KEY, "");

        if (!string.IsNullOrEmpty(json))
        {
            try
            {
                data = JsonUtility.FromJson<NotepadData>(json);
                UpdateOpenedFiles();
            }
            catch (Exception e)
            {
                Debug.LogError($"加载数据失败: {e.Message}");
                data = new NotepadData();
            }
        }
        else
        {
            data = new NotepadData();
        }

        // 确保文件夹存在
        if (!string.IsNullOrEmpty(data.notesFolder) && !Directory.Exists(data.notesFolder))
        {
            Directory.CreateDirectory(data.notesFolder);
        }

        if (!string.IsNullOrEmpty(data.backupFolder) && !Directory.Exists(data.backupFolder))
        {
            Directory.CreateDirectory(data.backupFolder);
        }
    }

    private void SaveData()
    {
        if (data == null) return;

        try
        {
            string json = JsonUtility.ToJson(data, true);
            EditorPrefs.SetString(DATA_KEY, json);
        }
        catch (Exception e)
        {
            Debug.LogError($"保存数据失败: {e.Message}");
        }
    }
    #endregion

    #region 搜索功能
    private void PerformSearch()
    {
        searchResults.Clear();
        currentSearchIndex = 0;

        if (string.IsNullOrEmpty(searchText) ||
            data.selectedTabIndex == data.mainPageTabIndex) return;

        var currentTab = GetCurrentTab();
        if (currentTab == null || string.IsNullOrEmpty(currentTab.content)) return;

        string content = currentTab.content.ToLower();
        string searchLower = searchText.ToLower();

        int index = 0;
        while ((index = content.IndexOf(searchLower, index)) != -1)
        {
            searchResults.Add(index);
            index += searchLower.Length;
        }

        if (searchResults.Count > 0)
        {
            ScrollToSearchResult(0);
        }
    }

    private void NavigateSearch(int direction)
    {
        if (searchResults.Count == 0) return;

        currentSearchIndex = (currentSearchIndex + direction + searchResults.Count) % searchResults.Count;
        ScrollToSearchResult(currentSearchIndex);
    }

    private void ScrollToSearchResult(int resultIndex)
    {
        if (resultIndex < 0 || resultIndex >= searchResults.Count) return;

        var currentTab = GetCurrentTab();
        if (currentTab == null) return;

        int charPosition = searchResults[resultIndex];
        int linesBefore = currentTab.content.Substring(0, Math.Min(charPosition, currentTab.content.Length)).Split('\n').Length - 1;
        float lineHeight = data.fontSize * 1.5f;
        float targetY = linesBefore * lineHeight;

        currentTab.scrollPosition.y = Mathf.Max(0, targetY - position.height / 3);
        Repaint();
    }
    #endregion

    #region 快捷键
    private void HandleKeyboardShortcuts()
    {
        Event e = Event.current;

        if (e.type == EventType.KeyDown)
        {
            bool control = e.control || e.command;

            if (control)
            {
                switch (e.keyCode)
                {
                    case KeyCode.S:
                        SaveCurrentTab();
                        e.Use();
                        break;
                    case KeyCode.N:
                        CreateNewTab();
                        e.Use();
                        break;
                    case KeyCode.W:
                        if (data.tabs.Count > 0 && data.selectedTabIndex != data.mainPageTabIndex)
                        {
                            CloseTab(data.selectedTabIndex);
                            e.Use();
                        }
                        break;
                    case KeyCode.Tab:
                        if (e.shift)
                        {
                            if (data.tabs.Count > 1)
                            {
                                data.selectedTabIndex = (data.selectedTabIndex - 1 + data.tabs.Count) % data.tabs.Count;
                                e.Use();
                            }
                        }
                        else
                        {
                            if (data.tabs.Count > 1)
                            {
                                data.selectedTabIndex = (data.selectedTabIndex + 1) % data.tabs.Count;
                                e.Use();
                            }
                        }
                        break;
                    case KeyCode.F:
                        if (e.shift)
                        {
                            EditorGUI.FocusTextInControl("SearchField");
                            e.Use();
                        }
                        break;
                    case KeyCode.H:
                        if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
                        {
                            data.selectedTabIndex = data.mainPageTabIndex;
                            data.mainPageMode = MainPageMode.Home;
                            e.Use();
                        }
                        break;
                    case KeyCode.B:
                        if (data.mainPageTabIndex >= 0 && data.mainPageTabIndex < data.tabs.Count)
                        {
                            data.selectedTabIndex = data.mainPageTabIndex;
                            data.mainPageMode = MainPageMode.Backup;
                            e.Use();
                        }
                        break;
                }
            }
        }
    }
    #endregion

    #region 导入导出
    private void ExportAllNotes()
    {
        if (data == null || data.tabs.Count <= 1) // 只有主页标签
        {
            EditorUtility.DisplayDialog("导出", "没有可导出的笔记", "确定");
            return;
        }

        string exportFolder = EditorUtility.SaveFolderPanel("导出笔记", Application.dataPath, "NotesExport");

        if (!string.IsNullOrEmpty(exportFolder))
        {
            try
            {
                int exported = 0;
                foreach (var tab in data.tabs)
                {
                    if (!string.IsNullOrEmpty(tab.content) && tab.title != "主页")
                    {
                        string safeTitle = System.Text.RegularExpressions.Regex.Replace(tab.title, @"[^\w\u4e00-\u9fa5]", "_");
                        string fileName = $"{safeTitle}_{DateTime.Now:yyyyMMdd_HHmmss}.txt";
                        string filePath = Path.Combine(exportFolder, fileName);
                        File.WriteAllText(filePath, tab.content, Encoding.UTF8);
                        exported++;
                    }
                }

                EditorUtility.DisplayDialog("导出完成", $"已导出 {exported} 个笔记到:\n{exportFolder}", "确定");
            }
            catch (Exception e)
            {
                EditorUtility.DisplayDialog("导出失败", e.Message, "确定");
            }
        }
    }
    #endregion
}

4. 使用说明

  1. 安装

    • 将脚本放在 Assets/Editor/ 文件夹中

    • 菜单栏选择 Tools/Unity Notepad 或按 Ctrl+Shift+N 打开

  2. 主要功能

    • 多标签编辑

    • 自动保存(每30秒)

    • 文件导入/导出

    • 文本搜索

    • 自定义字体大小和换行

  3. 快捷键

    • Ctrl/Cmd + S:保存

    • Ctrl/Cmd + N:新建标签

    • Ctrl/Cmd + W:关闭标签

    • Ctrl/Cmd + Tab:切换标签

    • Ctrl/Cmd + F:搜索

5. 扩展建议

如需增强功能,可考虑:

  1. 语法高亮:为不同文件类型(C#、JSON、Markdown)添加颜色

  2. 云端同步:集成Google Drive或OneDrive

  3. 版本控制:集成Git,记录修改历史

  4. Markdown预览:添加实时预览面板

  5. 插件系统:支持自定义扩展

这个记事本完全运行在Unity Editor中,适合记录开发笔记、TODO列表、临时代码片段等。数据会自动保存到EditorPrefs中,不会影响项目文件。

相关推荐
我送炭你添花1 小时前
Pelco KBD300A 模拟器:07+1.宏脚本编辑器与模板库管理实现细节
python·自动化·编辑器·运维开发
智源研究院官方账号20 小时前
众智FlagOS 1.6发布,以统一架构推动AI硬件、软件技术生态创新发展
数据库·人工智能·算法·架构·编辑器·硬件工程·开源软件
咬人喵喵1 天前
SVG 答题类互动模板汇总(共 16 种/来自 E2 编辑器)
编辑器·svg·e2 编辑器
咬人喵喵1 天前
16 类春节核心 SVG 交互方案拆解(E2 编辑器实战)
前端·css·编辑器·交互·svg
lixzest1 天前
Vim 快捷键速查表
linux·编辑器·vim
环黄金线HHJX.1 天前
MCP: Tuan编程 + Qt架构 + QoS - 量子-经典混合计算管理控制平台
开发语言·qt·算法·编辑器·量子计算
全干工程师—2 天前
在liunx下使用VScode检测到 #include 错误。请更新 includePath问题的解决方法
ide·vscode·编辑器
神码编程2 天前
【Unity】 HTFramework框架(六十九)Log在编辑器日志中自定义点击事件
unity·编辑器·游戏引擎