#if UNITY_EDITOR
using System;
using System.Linq;
using AibisDream.UI;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace AibisDream.DeveloperMode.Editor
{
/// 只重建 DeveloperModePanel 的测试存档页,避免覆盖其他页的人工调整。
public static class DeveloperModeTestSavePanelBuilder
{
private const string PrefabPath =
"Assets/GameContent/Feature_MainUI/Prefabs/DeveloperModePanel.prefab";
private const string FontAssetPath =
"Assets/Font/Assets/WenQuanYi Bitmap Song 14px SDF.asset";
private static readonly Color Surface = new(0.055f, 0.085f, 0.11f, 0.96f);
private static readonly Color Accent = new(0.16f, 0.75f, 0.88f, 1f);
private static readonly Color TextColor = new(0.88f, 0.96f, 1f, 1f);
[MenuItem("Tools/AIBIS/Developer Mode/Rebuild Test Save Page")]
public static void BuildFromMenu()
{
Build();
Selection.activeObject = AssetDatabase.LoadAssetAtPath(PrefabPath);
}
public static void BuildFromCommandLine()
{
try
{
Build();
Debug.Log("[DeveloperModeTestSavePanelBuilder] Build completed.");
}
catch (Exception ex)
{
Debug.LogException(ex);
EditorApplication.Exit(1);
}
}
private static void Build()
{
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
try
{
var panel = root.GetComponent()
?? throw new InvalidOperationException("DeveloperModePanel component not found.");
var serialized = new SerializedObject(panel);
var pages = serialized.FindProperty("tabPages")
?? throw new MissingFieldException(nameof(DeveloperModePanel), "tabPages");
if (pages.arraySize < 4)
{
throw new InvalidOperationException("DeveloperModePanel requires five tab pages.");
}
var savePage = pages.GetArrayElementAtIndex(3).objectReferenceValue as GameObject
?? throw new InvalidOperationException("Save page reference is missing.");
ClearChildren(savePage.transform);
ConfigurePageLayout(savePage);
BuildSavePage(savePage.transform, serialized);
RenameSaveTab(serialized);
var font = AssetDatabase.LoadAssetAtPath(FontAssetPath)
?? throw new InvalidOperationException($"Font not found: {FontAssetPath}");
foreach (var text in root.GetComponentsInChildren(true))
{
text.font = font;
}
serialized.ApplyModifiedPropertiesWithoutUndo();
PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
}
finally
{
PrefabUtility.UnloadPrefabContents(root);
}
AssetDatabase.SaveAssets();
}
private static void BuildSavePage(Transform page, SerializedObject serialized)
{
var recording = CreateHorizontal("Recording Toolbar", page, 6f);
AddLayout(recording, 38f);
var recordingToggle = CreateToggle("Record Test Saves", recording.transform, "录制测试存档", 145f);
var recordingState = CreateText("Recording State", recording.transform, "○ OFF", 14f, FontStyles.Bold);
AddLayout(recordingState.gameObject, 34f);
recordingState.rectTransform.sizeDelta = new Vector2(105f, 0f);
var search = CreateInput("Test Save Search", recording.transform, "搜索章节、节点或场景...", 250f);
var reload = CreateButton("Reload Test Saves", recording.transform, "重新扫描", 92f, 34f);
var open = CreateButton("Open Test Saves", recording.transform, "打开目录", 92f, 34f);
var columns = CreateHorizontal("Library Columns", page, 7f);
var columnsLayout = columns.AddComponent();
columnsLayout.flexibleHeight = 1f;
columnsLayout.minHeight = 420f;
var chapters = CreateVertical("Chapter Column", columns.transform, 4f);
var chapterLayout = chapters.AddComponent();
chapterLayout.preferredWidth = 245f;
chapterLayout.flexibleWidth = 0f;
chapterLayout.flexibleHeight = 1f;
var chapterTitle = CreateText("Title", chapters.transform, "章节 / 覆盖率", 16f, FontStyles.Bold);
AddLayout(chapterTitle.gameObject, 28f);
CreateList(
"Chapter List",
chapters.transform,
out var chapterContent,
out var chapterTemplate);
var entries = CreateVertical("Entry Column", columns.transform, 4f);
var entryLayout = entries.AddComponent();
entryLayout.flexibleWidth = 1f;
entryLayout.flexibleHeight = 1f;
var entryTitle = CreateText(
"Title",
entries.transform,
"存档点(● 已录制 / ○ 缺失 / ✕ 无效)",
16f,
FontStyles.Bold);
AddLayout(entryTitle.gameObject, 28f);
CreateList(
"Entry List",
entries.transform,
out var entryContent,
out var entryTemplate);
var actions = CreateHorizontal("Actions", page, 6f);
AddLayout(actions, 36f);
var jump = CreateButton("Jump", actions.transform, "跳转到选中", 118f, 32f);
var deleteSelected = CreateButton("Delete Selected", actions.transform, "删除选中", 105f, 32f);
var deleteInvalid = CreateButton("Delete Invalid", actions.transform, "清理无效", 105f, 32f);
var clear = CreateButton("Clear Library", actions.transform, "清空存档库", 118f, 32f);
var status = CreateText(
"Save Status",
page,
"测试存档录制默认关闭;开启后仅复制正式节点自动档。",
14f);
AddLayout(status.gameObject, 46f);
status.color = new Color(0.45f, 1f, 0.65f);
status.alignment = TextAlignmentOptions.TopLeft;
Set(serialized, "testSaveRecordingToggle", recordingToggle);
Set(serialized, "testSaveRecordingStateText", recordingState);
Set(serialized, "testSaveSearchInput", search);
Set(serialized, "reloadSavesButton", reload.GetComponent