AssetArtScannerWindow.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. #if UNITY_2019_4_OR_NEWER
  2. using System.IO;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using UnityEditor.UIElements;
  8. using UnityEngine.UIElements;
  9. namespace YooAsset.Editor
  10. {
  11. public class AssetArtScannerWindow : EditorWindow
  12. {
  13. [MenuItem("YooAsset/AssetArt Scanner", false, 301)]
  14. public static void OpenWindow()
  15. {
  16. AssetArtScannerWindow window = GetWindow<AssetArtScannerWindow>("AssetArt Scanner", true, WindowsDefine.DockedWindowTypes);
  17. window.minSize = new Vector2(800, 600);
  18. }
  19. private Button _saveButton;
  20. private ListView _scannerListView;
  21. private ToolbarSearchField _scannerSearchField;
  22. private VisualElement _scannerContentContainer;
  23. private VisualElement _inspectorContainer;
  24. private Label _schemaGuideTxt;
  25. private TextField _scannerNameTxt;
  26. private TextField _scannerDescTxt;
  27. private ObjectField _scannerSchemaField;
  28. private ObjectField _outputFolderField;
  29. private ScrollView _collectorScrollView;
  30. private int _lastModifyScannerIndex = 0;
  31. public void CreateGUI()
  32. {
  33. Undo.undoRedoPerformed -= RefreshWindow;
  34. Undo.undoRedoPerformed += RefreshWindow;
  35. try
  36. {
  37. VisualElement root = this.rootVisualElement;
  38. // 加载布局文件
  39. var visualAsset = UxmlLoader.LoadWindowUXML<AssetArtScannerWindow>();
  40. if (visualAsset == null)
  41. return;
  42. visualAsset.CloneTree(root);
  43. // 导入导出按钮
  44. var exportBtn = root.Q<Button>("ExportButton");
  45. exportBtn.clicked += ExportBtn_clicked;
  46. var importBtn = root.Q<Button>("ImportButton");
  47. importBtn.clicked += ImportBtn_clicked;
  48. // 配置保存按钮
  49. _saveButton = root.Q<Button>("SaveButton");
  50. _saveButton.clicked += SaveBtn_clicked;
  51. // 扫描按钮
  52. var scanAllBtn = root.Q<Button>("ScanAllButton");
  53. scanAllBtn.clicked += ScanAllBtn_clicked;
  54. var scanBtn = root.Q<Button>("ScanBtn");
  55. scanBtn.clicked += ScanBtn_clicked;
  56. // 扫描列表相关
  57. _scannerListView = root.Q<ListView>("ScannerListView");
  58. _scannerListView.makeItem = MakeScannerListViewItem;
  59. _scannerListView.bindItem = BindScannerListViewItem;
  60. #if UNITY_2022_3_OR_NEWER
  61. _scannerListView.selectionChanged += ScannerListView_onSelectionChange;
  62. #elif UNITY_2020_1_OR_NEWER
  63. _scannerListView.onSelectionChange += ScannerListView_onSelectionChange;
  64. #else
  65. _scannerListView.onSelectionChanged += ScannerListView_onSelectionChange;
  66. #endif
  67. // 扫描列表过滤
  68. _scannerSearchField = root.Q<ToolbarSearchField>("ScannerSearchField");
  69. _scannerSearchField.RegisterValueChangedCallback(OnSearchKeyWordChange);
  70. // 扫描器添加删除按钮
  71. var scannerAddContainer = root.Q("ScannerAddContainer");
  72. {
  73. var addBtn = scannerAddContainer.Q<Button>("AddBtn");
  74. addBtn.clicked += AddScannerBtn_clicked;
  75. var removeBtn = scannerAddContainer.Q<Button>("RemoveBtn");
  76. removeBtn.clicked += RemoveScannerBtn_clicked;
  77. }
  78. // 扫描器容器
  79. _scannerContentContainer = root.Q("ScannerContentContainer");
  80. // 检视界面容器
  81. _inspectorContainer = root.Q("InspectorContainer");
  82. // 扫描器指南
  83. _schemaGuideTxt = root.Q<Label>("SchemaUserGuide");
  84. // 扫描器名称
  85. _scannerNameTxt = root.Q<TextField>("ScannerName");
  86. _scannerNameTxt.RegisterValueChangedCallback(evt =>
  87. {
  88. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  89. if (selectScanner != null)
  90. {
  91. selectScanner.ScannerName = evt.newValue;
  92. AssetArtScannerSettingData.ModifyScanner(selectScanner);
  93. FillScannerListViewData();
  94. }
  95. });
  96. // 扫描器备注
  97. _scannerDescTxt = root.Q<TextField>("ScannerDesc");
  98. _scannerDescTxt.RegisterValueChangedCallback(evt =>
  99. {
  100. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  101. if (selectScanner != null)
  102. {
  103. selectScanner.ScannerDesc = evt.newValue;
  104. AssetArtScannerSettingData.ModifyScanner(selectScanner);
  105. FillScannerListViewData();
  106. }
  107. });
  108. // 扫描模式
  109. _scannerSchemaField = root.Q<ObjectField>("ScanSchema");
  110. _scannerSchemaField.RegisterValueChangedCallback(evt =>
  111. {
  112. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  113. if (selectScanner != null)
  114. {
  115. string assetPath = AssetDatabase.GetAssetPath(evt.newValue);
  116. selectScanner.ScannerSchema = AssetDatabase.AssetPathToGUID(assetPath);
  117. AssetArtScannerSettingData.ModifyScanner(selectScanner);
  118. }
  119. });
  120. // 存储目录
  121. _outputFolderField = root.Q<ObjectField>("OutputFolder");
  122. _outputFolderField.RegisterValueChangedCallback(evt =>
  123. {
  124. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  125. if (selectScanner != null)
  126. {
  127. if (evt.newValue == null)
  128. {
  129. selectScanner.SaveDirectory = string.Empty;
  130. AssetArtScannerSettingData.ModifyScanner(selectScanner);
  131. }
  132. else
  133. {
  134. string assetPath = AssetDatabase.GetAssetPath(evt.newValue);
  135. if (AssetDatabase.IsValidFolder(assetPath))
  136. {
  137. selectScanner.SaveDirectory = assetPath;
  138. AssetArtScannerSettingData.ModifyScanner(selectScanner);
  139. }
  140. else
  141. {
  142. Debug.LogWarning($"Select asset object not folder ! {assetPath}");
  143. }
  144. }
  145. }
  146. });
  147. // 收集列表相关
  148. _collectorScrollView = root.Q<ScrollView>("CollectorScrollView");
  149. _collectorScrollView.style.height = new Length(100, LengthUnit.Percent);
  150. _collectorScrollView.viewDataKey = "scrollView";
  151. // 收集器创建按钮
  152. var collectorAddContainer = root.Q("CollectorAddContainer");
  153. {
  154. var addBtn = collectorAddContainer.Q<Button>("AddBtn");
  155. addBtn.clicked += AddCollectorBtn_clicked;
  156. }
  157. // 刷新窗体
  158. RefreshWindow();
  159. }
  160. catch (System.Exception e)
  161. {
  162. Debug.LogError(e.ToString());
  163. }
  164. }
  165. public void OnDestroy()
  166. {
  167. // 注意:清空所有撤销操作
  168. Undo.ClearAll();
  169. if (AssetArtScannerSettingData.IsDirty)
  170. AssetArtScannerSettingData.SaveFile();
  171. }
  172. public void Update()
  173. {
  174. if (_saveButton != null)
  175. {
  176. if (AssetArtScannerSettingData.IsDirty)
  177. {
  178. if (_saveButton.enabledSelf == false)
  179. _saveButton.SetEnabled(true);
  180. }
  181. else
  182. {
  183. if (_saveButton.enabledSelf)
  184. _saveButton.SetEnabled(false);
  185. }
  186. }
  187. }
  188. private void RefreshWindow()
  189. {
  190. _scannerContentContainer.visible = false;
  191. FillScannerListViewData();
  192. }
  193. private void ExportBtn_clicked()
  194. {
  195. string resultPath = EditorTools.OpenFolderPanel("Export JSON", "Assets/");
  196. if (resultPath != null)
  197. {
  198. AssetArtScannerConfig.ExportJsonConfig($"{resultPath}/AssetArtScannerConfig.json");
  199. }
  200. }
  201. private void ImportBtn_clicked()
  202. {
  203. string resultPath = EditorTools.OpenFilePath("Import JSON", "Assets/", "json");
  204. if (resultPath != null)
  205. {
  206. AssetArtScannerConfig.ImportJsonConfig(resultPath);
  207. RefreshWindow();
  208. }
  209. }
  210. private void SaveBtn_clicked()
  211. {
  212. AssetArtScannerSettingData.SaveFile();
  213. }
  214. private void ScanAllBtn_clicked()
  215. {
  216. if (EditorUtility.DisplayDialog("提示", $"开始全面扫描!", "Yes", "No"))
  217. {
  218. string searchKeyWord = _scannerSearchField.value;
  219. AssetArtScannerSettingData.ScanAll(searchKeyWord);
  220. AssetDatabase.Refresh();
  221. }
  222. else
  223. {
  224. Debug.LogWarning("全面扫描已经取消");
  225. }
  226. }
  227. private void ScanBtn_clicked()
  228. {
  229. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  230. if (selectScanner == null)
  231. return;
  232. ScannerResult scannerResult = AssetArtScannerSettingData.Scan(selectScanner.ScannerGUID);
  233. if (scannerResult.Succeed)
  234. {
  235. // 自动打开报告界面
  236. scannerResult.OpenReportWindow();
  237. AssetDatabase.Refresh();
  238. }
  239. }
  240. private void OnSearchKeyWordChange(ChangeEvent<string> e)
  241. {
  242. _lastModifyScannerIndex = 0;
  243. RefreshWindow();
  244. }
  245. // 分组列表相关
  246. private void FillScannerListViewData()
  247. {
  248. _scannerListView.Clear();
  249. _scannerListView.ClearSelection();
  250. var filterItems = FilterScanners();
  251. if (AssetArtScannerSettingData.Setting.Scanners.Count == filterItems.Count)
  252. {
  253. #if UNITY_2020_3_OR_NEWER
  254. _scannerListView.reorderable = true;
  255. #endif
  256. _scannerListView.itemsSource = AssetArtScannerSettingData.Setting.Scanners;
  257. _scannerListView.Rebuild();
  258. }
  259. else
  260. {
  261. #if UNITY_2020_3_OR_NEWER
  262. _scannerListView.reorderable = false;
  263. #endif
  264. _scannerListView.itemsSource = filterItems;
  265. _scannerListView.Rebuild();
  266. }
  267. }
  268. private List<AssetArtScanner> FilterScanners()
  269. {
  270. string searchKeyWord = _scannerSearchField.value;
  271. List<AssetArtScanner> result = new List<AssetArtScanner>(AssetArtScannerSettingData.Setting.Scanners.Count);
  272. // 过滤列表
  273. foreach (var scanner in AssetArtScannerSettingData.Setting.Scanners)
  274. {
  275. if (string.IsNullOrEmpty(searchKeyWord) == false)
  276. {
  277. if (scanner.CheckKeyword(searchKeyWord) == false)
  278. continue;
  279. }
  280. result.Add(scanner);
  281. }
  282. return result;
  283. }
  284. private VisualElement MakeScannerListViewItem()
  285. {
  286. VisualElement element = new VisualElement();
  287. {
  288. var label = new Label();
  289. label.name = "Label1";
  290. label.style.unityTextAlign = TextAnchor.MiddleLeft;
  291. label.style.flexGrow = 1f;
  292. label.style.height = 20f;
  293. element.Add(label);
  294. }
  295. return element;
  296. }
  297. private void BindScannerListViewItem(VisualElement element, int index)
  298. {
  299. List<AssetArtScanner> sourceList = _scannerListView.itemsSource as List<AssetArtScanner>;
  300. var scanner = sourceList[index];
  301. var textField1 = element.Q<Label>("Label1");
  302. if (string.IsNullOrEmpty(scanner.ScannerDesc))
  303. textField1.text = scanner.ScannerName;
  304. else
  305. textField1.text = $"{scanner.ScannerName} ({scanner.ScannerDesc})";
  306. }
  307. private void ScannerListView_onSelectionChange(IEnumerable<object> objs)
  308. {
  309. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  310. if (selectScanner == null)
  311. {
  312. _scannerContentContainer.visible = false;
  313. return;
  314. }
  315. _scannerContentContainer.visible = true;
  316. _lastModifyScannerIndex = _scannerListView.selectedIndex;
  317. _scannerNameTxt.SetValueWithoutNotify(selectScanner.ScannerName);
  318. _scannerDescTxt.SetValueWithoutNotify(selectScanner.ScannerDesc);
  319. // 显示检视面板
  320. var scanSchema = selectScanner.LoadSchema();
  321. RefreshInspector(scanSchema);
  322. // 设置Schema对象
  323. if (scanSchema == null)
  324. {
  325. _scannerSchemaField.SetValueWithoutNotify(null);
  326. _schemaGuideTxt.text = string.Empty;
  327. }
  328. else
  329. {
  330. _scannerSchemaField.SetValueWithoutNotify(scanSchema);
  331. _schemaGuideTxt.text = scanSchema.GetUserGuide();
  332. }
  333. // 显示存储目录
  334. DefaultAsset saveFolder = AssetDatabase.LoadAssetAtPath<DefaultAsset>(selectScanner.SaveDirectory);
  335. if (saveFolder == null)
  336. {
  337. _outputFolderField.SetValueWithoutNotify(null);
  338. }
  339. else
  340. {
  341. _outputFolderField.SetValueWithoutNotify(saveFolder);
  342. }
  343. FillCollectorViewData();
  344. }
  345. private void AddScannerBtn_clicked()
  346. {
  347. Undo.RecordObject(AssetArtScannerSettingData.Setting, "YooAsset.AssetArtScannerWindow AddScanner");
  348. AssetArtScannerSettingData.CreateScanner("Default Scanner", string.Empty);
  349. FillScannerListViewData();
  350. }
  351. private void RemoveScannerBtn_clicked()
  352. {
  353. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  354. if (selectScanner == null)
  355. return;
  356. Undo.RecordObject(AssetArtScannerSettingData.Setting, "YooAsset.AssetArtScannerWindow RemoveScanner");
  357. AssetArtScannerSettingData.RemoveScanner(selectScanner);
  358. FillScannerListViewData();
  359. }
  360. // 收集列表相关
  361. private void FillCollectorViewData()
  362. {
  363. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  364. if (selectScanner == null)
  365. return;
  366. // 填充数据
  367. _collectorScrollView.Clear();
  368. for (int i = 0; i < selectScanner.Collectors.Count; i++)
  369. {
  370. VisualElement element = MakeCollectorListViewItem();
  371. BindCollectorListViewItem(element, i);
  372. _collectorScrollView.Add(element);
  373. }
  374. }
  375. private VisualElement MakeCollectorListViewItem()
  376. {
  377. VisualElement element = new VisualElement();
  378. VisualElement elementTop = new VisualElement();
  379. elementTop.style.flexDirection = FlexDirection.Row;
  380. element.Add(elementTop);
  381. VisualElement elementSpace = new VisualElement();
  382. elementSpace.style.flexDirection = FlexDirection.Column;
  383. element.Add(elementSpace);
  384. // Top VisualElement
  385. {
  386. var button = new Button();
  387. button.name = "Button1";
  388. button.text = "-";
  389. button.style.unityTextAlign = TextAnchor.MiddleCenter;
  390. button.style.flexGrow = 0f;
  391. elementTop.Add(button);
  392. }
  393. {
  394. var objectField = new ObjectField();
  395. objectField.name = "ObjectField1";
  396. objectField.label = "Collector";
  397. objectField.objectType = typeof(UnityEngine.Object);
  398. objectField.style.unityTextAlign = TextAnchor.MiddleLeft;
  399. objectField.style.flexGrow = 1f;
  400. elementTop.Add(objectField);
  401. var label = objectField.Q<Label>();
  402. label.style.minWidth = 63;
  403. }
  404. // Space VisualElement
  405. {
  406. var label = new Label();
  407. label.style.height = 10;
  408. elementSpace.Add(label);
  409. }
  410. return element;
  411. }
  412. private void BindCollectorListViewItem(VisualElement element, int index)
  413. {
  414. var selectScanner = _scannerListView.selectedItem as AssetArtScanner;
  415. if (selectScanner == null)
  416. return;
  417. var collector = selectScanner.Collectors[index];
  418. var collectObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(collector.CollectPath);
  419. if (collectObject != null)
  420. collectObject.name = collector.CollectPath;
  421. // Remove Button
  422. var removeBtn = element.Q<Button>("Button1");
  423. removeBtn.clicked += () =>
  424. {
  425. RemoveCollectorBtn_clicked(collector);
  426. };
  427. // Collector Path
  428. var objectField1 = element.Q<ObjectField>("ObjectField1");
  429. objectField1.SetValueWithoutNotify(collectObject);
  430. objectField1.RegisterValueChangedCallback(evt =>
  431. {
  432. collector.CollectPath = AssetDatabase.GetAssetPath(evt.newValue);
  433. objectField1.value.name = collector.CollectPath;
  434. AssetArtScannerSettingData.ModifyCollector(selectScanner, collector);
  435. });
  436. }
  437. private void AddCollectorBtn_clicked()
  438. {
  439. var selectSacnner = _scannerListView.selectedItem as AssetArtScanner;
  440. if (selectSacnner == null)
  441. return;
  442. Undo.RecordObject(AssetArtScannerSettingData.Setting, "YooAsset.AssetArtScannerWindow AddCollector");
  443. AssetArtCollector collector = new AssetArtCollector();
  444. AssetArtScannerSettingData.CreateCollector(selectSacnner, collector);
  445. FillCollectorViewData();
  446. }
  447. private void RemoveCollectorBtn_clicked(AssetArtCollector selectCollector)
  448. {
  449. var selectSacnner = _scannerListView.selectedItem as AssetArtScanner;
  450. if (selectSacnner == null)
  451. return;
  452. if (selectCollector == null)
  453. return;
  454. Undo.RecordObject(AssetArtScannerSettingData.Setting, "YooAsset.AssetArtScannerWindow RemoveCollector");
  455. AssetArtScannerSettingData.RemoveCollector(selectSacnner, selectCollector);
  456. FillCollectorViewData();
  457. }
  458. // 属性面板相关
  459. private void RefreshInspector(ScannerSchema scanSchema)
  460. {
  461. if (scanSchema == null)
  462. {
  463. UIElementsTools.SetElementVisible(_inspectorContainer, false);
  464. return;
  465. }
  466. var inspector = scanSchema.CreateInspector();
  467. if (inspector == null)
  468. {
  469. UIElementsTools.SetElementVisible(_inspectorContainer, false);
  470. return;
  471. }
  472. if (inspector.Containner is VisualElement container)
  473. {
  474. UIElementsTools.SetElementVisible(_inspectorContainer, true);
  475. _inspectorContainer.Clear();
  476. _inspectorContainer.Add(container);
  477. _inspectorContainer.style.width = inspector.Width;
  478. _inspectorContainer.style.minWidth = inspector.MinWidth;
  479. _inspectorContainer.style.maxWidth = inspector.MaxWidth;
  480. }
  481. else
  482. {
  483. Debug.LogWarning($"{nameof(ScannerSchema)} inspector container is invalid !");
  484. UIElementsTools.SetElementVisible(_inspectorContainer, false);
  485. }
  486. }
  487. }
  488. }
  489. #endif