Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SystemConvertersEditor.cs 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. #if UNITY_2022_2_OR_NEWER
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEditor.Search;
  9. using UnityEditor.UIElements;
  10. using UnityEngine.UIElements;
  11. using UnityEngine.Assertions;
  12. namespace Unity.AI.Navigation.Editor.Converter
  13. {
  14. // Status for each row item to say in which state they are in.
  15. // This will make sure they are showing the correct icon
  16. [Serializable]
  17. enum Status
  18. {
  19. Pending,
  20. Warning,
  21. Error,
  22. Success
  23. }
  24. // This is the serialized class that stores the state of each item in the list of items to convert
  25. [Serializable]
  26. class ConverterItemState
  27. {
  28. public bool isActive;
  29. // Message that will be displayed on the icon if warning or failed.
  30. public string message;
  31. // Status of the converted item, Pending, Warning, Error or Success
  32. public Status status;
  33. internal bool hasConverted = false;
  34. }
  35. // Each converter uses the active bool
  36. // Each converter has a list of active items/assets
  37. // We do this so that we can use the binding system of the UI Elements
  38. [Serializable]
  39. class ConverterState
  40. {
  41. // This is the enabled state of the whole converter
  42. public bool isEnabled;
  43. public bool isActive;
  44. public bool isLoading; // to name
  45. public bool isInitialized;
  46. public List<ConverterItemState> items = new List<ConverterItemState>();
  47. public int pending;
  48. public int warnings;
  49. public int errors;
  50. public int success;
  51. internal int index;
  52. public bool isActiveAndEnabled => isEnabled && isActive;
  53. public bool requiresInitialization => !isInitialized && isActiveAndEnabled;
  54. }
  55. [Serializable]
  56. internal struct ConverterItems
  57. {
  58. public List<ConverterItemDescriptor> itemDescriptors;
  59. }
  60. [Serializable]
  61. internal class SystemConvertersEditor : EditorWindow
  62. {
  63. public VisualTreeAsset converterEditorAsset;
  64. public VisualTreeAsset converterListAsset;
  65. public VisualTreeAsset converterItem;
  66. ScrollView m_ScrollView;
  67. List<SystemConverter> m_CoreConvertersList = new List<SystemConverter>();
  68. private bool convertButtonActive = false;
  69. // This list needs to be as long as the amount of converters
  70. List<ConverterItems> m_ItemsToConvert = new List<ConverterItems>();
  71. //List<List<ConverterItemDescriptor>> m_ItemsToConvert = new List<List<ConverterItemDescriptor>>();
  72. SerializedObject m_SerializedObject;
  73. List<string> m_ContainerChoices = new List<string>();
  74. List<SystemConverterContainer> m_Containers = new List<SystemConverterContainer>();
  75. int m_ContainerChoiceIndex = 0;
  76. // This is a list of Converter States which holds a list of which converter items/assets are active
  77. // There is one for each Converter.
  78. [SerializeField] List<ConverterState> m_ConverterStates = new List<ConverterState>();
  79. TypeCache.TypeCollection m_ConverterContainers;
  80. // Name of the index file
  81. string m_ConverterIndex = "SystemConverterIndex";
  82. public void DontSaveToLayout(EditorWindow wnd)
  83. {
  84. // Making sure that the window is not saved in layouts.
  85. Assembly assembly = typeof(EditorWindow).Assembly;
  86. var editorWindowType = typeof(EditorWindow);
  87. var hostViewType = assembly.GetType("UnityEditor.HostView");
  88. var containerWindowType = assembly.GetType("UnityEditor.ContainerWindow");
  89. var parentViewField = editorWindowType.GetField("m_Parent", BindingFlags.Instance | BindingFlags.NonPublic);
  90. var parentViewValue = parentViewField.GetValue(wnd);
  91. // window should not be saved to layout
  92. var containerWindowProperty =
  93. hostViewType.GetProperty("window", BindingFlags.Instance | BindingFlags.Public);
  94. var parentContainerWindowValue = containerWindowProperty.GetValue(parentViewValue);
  95. var dontSaveToLayoutField =
  96. containerWindowType.GetField("m_DontSaveToLayout", BindingFlags.Instance | BindingFlags.NonPublic);
  97. dontSaveToLayoutField.SetValue(parentContainerWindowValue, true);
  98. }
  99. void OnEnable()
  100. {
  101. InitIfNeeded();
  102. }
  103. void InitIfNeeded()
  104. {
  105. if (m_CoreConvertersList.Any())
  106. return;
  107. m_CoreConvertersList = new List<SystemConverter>();
  108. // This is the drop down choices.
  109. m_ConverterContainers = TypeCache.GetTypesDerivedFrom<SystemConverterContainer>();
  110. foreach (var continerType in m_ConverterContainers)
  111. {
  112. var container = (SystemConverterContainer)Activator.CreateInstance(continerType);
  113. m_Containers.Add(container);
  114. m_ContainerChoices.Add(container.name);
  115. }
  116. if (m_ConverterContainers.Any())
  117. {
  118. GetConverters();
  119. }
  120. else
  121. {
  122. ClearConverterStates();
  123. }
  124. }
  125. void ClearConverterStates()
  126. {
  127. m_CoreConvertersList.Clear();
  128. m_ConverterStates.Clear();
  129. m_ItemsToConvert.Clear();
  130. }
  131. void GetConverters()
  132. {
  133. ClearConverterStates();
  134. var converterList = TypeCache.GetTypesDerivedFrom<SystemConverter>();
  135. for (int i = 0; i < converterList.Count; ++i)
  136. {
  137. // Iterate over the converters that are used by the current container
  138. SystemConverter conv = (SystemConverter)Activator.CreateInstance(converterList[i]);
  139. if (conv.container == m_ConverterContainers[m_ContainerChoiceIndex])
  140. {
  141. m_CoreConvertersList.Add(conv);
  142. }
  143. }
  144. // this need to be sorted by Priority property
  145. m_CoreConvertersList = m_CoreConvertersList
  146. .OrderBy(o => o.priority).ToList();
  147. for (int i = 0; i < m_CoreConvertersList.Count; i++)
  148. {
  149. // Create a new ConvertState which holds the active state of the converter
  150. var converterState = new ConverterState
  151. {
  152. isEnabled = m_CoreConvertersList[i].isEnabled,
  153. isActive = false,
  154. isInitialized = false,
  155. items = new List<ConverterItemState>(),
  156. index = i,
  157. };
  158. m_ConverterStates.Add(converterState);
  159. // This just creates empty entries in the m_ItemsToConvert.
  160. // This list need to have the same amount of entries as the converters
  161. List<ConverterItemDescriptor> converterItemInfos = new List<ConverterItemDescriptor>();
  162. //m_ItemsToConvert.Add(converterItemInfos);
  163. m_ItemsToConvert.Add(new ConverterItems { itemDescriptors = converterItemInfos });
  164. }
  165. }
  166. public void CreateGUI()
  167. {
  168. InitIfNeeded();
  169. if (m_ConverterContainers.Any())
  170. {
  171. m_SerializedObject = new SerializedObject(this);
  172. converterEditorAsset.CloneTree(rootVisualElement);
  173. rootVisualElement.Q<DropdownField>("conversionsDropDown").choices = m_ContainerChoices;
  174. rootVisualElement.Q<DropdownField>("conversionsDropDown").index = m_ContainerChoiceIndex;
  175. RecreateUI();
  176. var button = rootVisualElement.Q<Button>("convertButton");
  177. button.RegisterCallback<ClickEvent>(Convert);
  178. button.SetEnabled(false);
  179. var initButton = rootVisualElement.Q<Button>("initializeButton");
  180. initButton.RegisterCallback<ClickEvent>(InitializeAllActiveConverters);
  181. }
  182. }
  183. void RecreateUI()
  184. {
  185. m_SerializedObject.Update();
  186. // This is temp now to get the information filled in
  187. rootVisualElement.Q<DropdownField>("conversionsDropDown").RegisterCallback<ChangeEvent<string>>((evt) =>
  188. {
  189. m_ContainerChoiceIndex = rootVisualElement.Q<DropdownField>("conversionsDropDown").index;
  190. GetConverters();
  191. RecreateUI();
  192. });
  193. var currentContainer = m_Containers[m_ContainerChoiceIndex];
  194. rootVisualElement.Q<Label>("conversionName").text = currentContainer.name;
  195. rootVisualElement.Q<TextElement>("conversionInfo").text = currentContainer.info;
  196. rootVisualElement.Q<Image>("converterContainerHelpIcon").image = EditorStyles.iconHelp;
  197. // Getting the scrollview where the converters should be added
  198. m_ScrollView = rootVisualElement.Q<ScrollView>("convertersScrollView");
  199. m_ScrollView.Clear();
  200. for (int i = 0; i < m_CoreConvertersList.Count; ++i)
  201. {
  202. // Making an item using the converterListAsset as a template.
  203. // Then adding the information needed for each converter
  204. VisualElement item = new VisualElement();
  205. converterListAsset.CloneTree(item);
  206. var conv = m_CoreConvertersList[i];
  207. item.SetEnabled(conv.isEnabled);
  208. item.Q<Label>("converterName").text = conv.name;
  209. item.Q<Label>("converterInfo").text = conv.info;
  210. item.Q<VisualElement>("converterTopVisualElement").tooltip = conv.info;
  211. // setup the images
  212. item.Q<Image>("pendingImage").image = EditorStyles.iconPending;
  213. item.Q<Image>("pendingImage").tooltip = "Pending";
  214. var pendingLabel = item.Q<Label>("pendingLabel");
  215. item.Q<Image>("warningImage").image = EditorStyles.iconWarn;
  216. item.Q<Image>("warningImage").tooltip = "Warnings";
  217. var warningLabel = item.Q<Label>("warningLabel");
  218. item.Q<Image>("errorImage").image = EditorStyles.iconFail;
  219. item.Q<Image>("errorImage").tooltip = "Failed";
  220. var errorLabel = item.Q<Label>("errorLabel");
  221. item.Q<Image>("successImage").image = EditorStyles.iconSuccess;
  222. item.Q<Image>("successImage").tooltip = "Success";
  223. var successLabel = item.Q<Label>("successLabel");
  224. var converterEnabledToggle = item.Q<Toggle>("converterEnabled");
  225. converterEnabledToggle.bindingPath =
  226. $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.isActive)}";
  227. pendingLabel.bindingPath =
  228. $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.pending)}";
  229. warningLabel.bindingPath =
  230. $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.warnings)}";
  231. errorLabel.bindingPath =
  232. $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.errors)}";
  233. successLabel.bindingPath =
  234. $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.success)}";
  235. VisualElement child = item;
  236. ListView listView = child.Q<ListView>("converterItems");
  237. listView.showBoundCollectionSize = false;
  238. listView.bindingPath = $"{nameof(m_ConverterStates)}.Array.data[{i}].{nameof(ConverterState.items)}";
  239. int id = i;
  240. listView.makeItem = () =>
  241. {
  242. var convertItem = converterItem.CloneTree();
  243. // Adding the contextual menu for each item
  244. convertItem.AddManipulator(new ContextualMenuManipulator(evt => AddToContextMenu(evt, id)));
  245. return convertItem;
  246. };
  247. listView.bindItem = (element, index) =>
  248. {
  249. m_SerializedObject.Update();
  250. var property = m_SerializedObject.FindProperty($"{listView.bindingPath}.Array.data[{index}]");
  251. // ListView doesn't bind the child elements for us properly, so we do that for it
  252. // In the UXML our root is a BindableElement, as we can't bind otherwise.
  253. var bindable = (BindableElement)element;
  254. bindable.BindProperty(property);
  255. // Adding index here to userData so it can be retrieved later
  256. element.userData = index;
  257. Status status = (Status)property.FindPropertyRelative("status").enumValueIndex;
  258. string info = property.FindPropertyRelative("message").stringValue;
  259. // Update the amount of things to convert
  260. child.Q<Label>("converterStats").text = $"{m_ItemsToConvert[id].itemDescriptors.Count} items";
  261. ConverterItemDescriptor convItemDesc = m_ItemsToConvert[id].itemDescriptors[index];
  262. element.Q<Label>("converterItemName").text = convItemDesc.name;
  263. element.Q<Label>("converterItemPath").text = convItemDesc.info;
  264. element.Q<Image>("converterItemHelpIcon").image = EditorStyles.iconHelp;
  265. element.Q<Image>("converterItemHelpIcon").tooltip = convItemDesc.helpLink;
  266. // Changing the icon here depending on the status.
  267. Texture2D icon = null;
  268. switch (status)
  269. {
  270. case Status.Pending:
  271. icon = EditorStyles.iconPending;
  272. break;
  273. case Status.Error:
  274. icon = EditorStyles.iconFail;
  275. break;
  276. case Status.Warning:
  277. icon = EditorStyles.iconWarn;
  278. break;
  279. case Status.Success:
  280. icon = EditorStyles.iconSuccess;
  281. break;
  282. }
  283. element.Q<Image>("converterItemStatusIcon").image = icon;
  284. element.Q<Image>("converterItemStatusIcon").tooltip = info;
  285. };
  286. listView.selectionChanged += obj => { m_CoreConvertersList[id].OnClicked(listView.selectedIndex); };
  287. listView.unbindItem = (element, index) =>
  288. {
  289. var bindable = (BindableElement)element;
  290. bindable.Unbind();
  291. };
  292. m_ScrollView.Add(item);
  293. }
  294. rootVisualElement.Bind(m_SerializedObject);
  295. var button = rootVisualElement.Q<Button>("convertButton");
  296. button.RegisterCallback<ClickEvent>(Convert);
  297. button.SetEnabled(convertButtonActive);
  298. var initButton = rootVisualElement.Q<Button>("initializeButton");
  299. initButton.RegisterCallback<ClickEvent>(InitializeAllActiveConverters);
  300. }
  301. void GetAndSetData(int i, Action onAllConvertersCompleted = null)
  302. {
  303. // This need to be in Init method
  304. // Need to get the assets that this converter is converting.
  305. // Need to return Name, Path, Initial info, Help link.
  306. // New empty list of ConverterItemInfos
  307. List<ConverterItemDescriptor> converterItemInfos = new List<ConverterItemDescriptor>();
  308. var initCtx = new InitializeConverterContext { items = converterItemInfos };
  309. var conv = m_CoreConvertersList[i];
  310. m_ConverterStates[i].isLoading = true;
  311. // This should also go to the init method
  312. // This will fill out the converter item infos list
  313. int id = i;
  314. conv.OnInitialize(initCtx, OnConverterCompleteDataCollection);
  315. void OnConverterCompleteDataCollection()
  316. {
  317. // Set the item infos list to to the right index
  318. m_ItemsToConvert[id] = new ConverterItems { itemDescriptors = converterItemInfos };
  319. m_ConverterStates[id].items = new List<ConverterItemState>(converterItemInfos.Count);
  320. // Default all the entries to true
  321. for (var j = 0; j < converterItemInfos.Count; j++)
  322. {
  323. string message = string.Empty;
  324. Status status;
  325. bool active = true;
  326. // If this data hasn't been filled in from the init phase then we can assume that there are no issues / warnings
  327. if (string.IsNullOrEmpty(converterItemInfos[j].warningMessage))
  328. {
  329. status = Status.Pending;
  330. }
  331. else
  332. {
  333. status = Status.Warning;
  334. message = converterItemInfos[j].warningMessage;
  335. active = false;
  336. m_ConverterStates[id].warnings++;
  337. }
  338. m_ConverterStates[id].items.Add(new ConverterItemState
  339. {
  340. isActive = active,
  341. message = message,
  342. status = status,
  343. hasConverted = false,
  344. });
  345. }
  346. m_ConverterStates[id].isLoading = false;
  347. m_ConverterStates[id].isInitialized = true;
  348. // Making sure that the pending amount is set to the amount of items needs converting
  349. m_ConverterStates[id].pending = m_ConverterStates[id].items.Count;
  350. EditorUtility.SetDirty(this);
  351. m_SerializedObject.ApplyModifiedProperties();
  352. CheckAllConvertersCompleted();
  353. convertButtonActive = true;
  354. // Make sure that the Convert Button is turned back on
  355. var button = rootVisualElement.Q<Button>("convertButton");
  356. button.SetEnabled(convertButtonActive);
  357. }
  358. void CheckAllConvertersCompleted()
  359. {
  360. int convertersToInitialize = 0;
  361. int convertersInitialized = 0;
  362. for (var j = 0; j < m_ConverterStates.Count; j++)
  363. {
  364. var converter = m_ConverterStates[j];
  365. // Skip inactive converters
  366. if (!converter.isActiveAndEnabled)
  367. continue;
  368. if (converter.isInitialized)
  369. convertersInitialized++;
  370. else
  371. convertersToInitialize++;
  372. }
  373. var sum = convertersToInitialize + convertersInitialized;
  374. Assert.IsFalse(sum == 0);
  375. // Show our progress so far
  376. EditorUtility.ClearProgressBar();
  377. EditorUtility.DisplayProgressBar($"Initializing converters", $"Initializing converters ({convertersInitialized}/{sum})...", (float)convertersInitialized / sum);
  378. // If all converters are initialized call the complete callback
  379. if (convertersToInitialize == 0)
  380. {
  381. onAllConvertersCompleted?.Invoke();
  382. }
  383. }
  384. }
  385. void InitializeAllActiveConverters(ClickEvent evt)
  386. {
  387. // If we use search index, go async
  388. if (ShouldCreateSearchIndex())
  389. {
  390. CreateSearchIndex(m_ConverterIndex);
  391. }
  392. // Otherwise do everything directly
  393. else
  394. {
  395. ConverterCollectData(() => { EditorUtility.ClearProgressBar(); });
  396. }
  397. void CreateSearchIndex(string name)
  398. {
  399. // Create <guid>.index in the project
  400. var title = $"Building {name} search index";
  401. EditorUtility.DisplayProgressBar(title, "Creating search index...", -1f);
  402. // Private implementation of a file naming function which puts the file at the selected path.
  403. Type assetdatabase = typeof(AssetDatabase);
  404. var indexPath = (string)assetdatabase.GetMethod("GetUniquePathNameAtSelectedPath", BindingFlags.NonPublic | BindingFlags.Static).Invoke(assetdatabase, new object[] { $"Assets/{name}.index" });
  405. // Write search index manifest
  406. System.IO.File.WriteAllText(indexPath,
  407. @"{
  408. ""roots"": [""Assets""],
  409. ""includes"": [],
  410. ""excludes"": [],
  411. ""options"": {
  412. ""types"": true,
  413. ""properties"": true,
  414. ""extended"": true,
  415. ""dependencies"": true
  416. },
  417. ""baseScore"": 9999
  418. }");
  419. // Import the search index
  420. AssetDatabase.ImportAsset(indexPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.DontDownloadFromCacheServer);
  421. EditorApplication.delayCall += () =>
  422. {
  423. // Create dummy request to ensure indexing has finished
  424. var context = SearchService.CreateContext("asset", $"p: a=\"{name}\"");
  425. SearchService.Request(context, (_, items) =>
  426. {
  427. OnSearchIndexCreated(name, indexPath, () =>
  428. {
  429. DeleteSearchIndex(context, indexPath);
  430. });
  431. });
  432. };
  433. }
  434. void OnSearchIndexCreated(string name, string path, Action onComplete)
  435. {
  436. EditorUtility.ClearProgressBar();
  437. ConverterCollectData(onComplete);
  438. }
  439. void ConverterCollectData(Action onConverterDataCollectionComplete)
  440. {
  441. EditorUtility.DisplayProgressBar($"Initializing converters", $"Initializing converters...", -1f);
  442. int convertersToConvert = 0;
  443. for (int i = 0; i < m_ConverterStates.Count; ++i)
  444. {
  445. if (m_ConverterStates[i].requiresInitialization)
  446. {
  447. convertersToConvert++;
  448. GetAndSetData(i, onConverterDataCollectionComplete);
  449. }
  450. }
  451. // If we did not kick off any converter intialization
  452. // We can complete everything immediately
  453. if (convertersToConvert == 0)
  454. {
  455. onConverterDataCollectionComplete?.Invoke();
  456. }
  457. }
  458. void DeleteSearchIndex(SearchContext context, string indexPath)
  459. {
  460. context?.Dispose();
  461. // Client code has finished with the created index. We can delete it.
  462. AssetDatabase.DeleteAsset(indexPath);
  463. EditorUtility.ClearProgressBar();
  464. }
  465. }
  466. bool ShouldCreateSearchIndex()
  467. {
  468. for (int i = 0; i < m_ConverterStates.Count; ++i)
  469. {
  470. if (m_ConverterStates[i].requiresInitialization)
  471. {
  472. var converter = m_CoreConvertersList[i];
  473. if (converter.needsIndexing)
  474. {
  475. return true;
  476. }
  477. }
  478. }
  479. return false;
  480. }
  481. void AddToContextMenu(ContextualMenuPopulateEvent evt, int coreConverterIndex)
  482. {
  483. var ve = (VisualElement)evt.target;
  484. // Checking if this context menu should be enabled or not
  485. var isActive = m_ConverterStates[coreConverterIndex].items[(int)ve.userData].isActive &&
  486. !m_ConverterStates[coreConverterIndex].items[(int)ve.userData].hasConverted;
  487. evt.menu.AppendAction("Run converter for this asset",
  488. e => { ConvertIndex(coreConverterIndex, (int)ve.userData); },
  489. isActive ? DropdownMenuAction.AlwaysEnabled : DropdownMenuAction.AlwaysDisabled);
  490. }
  491. void UpdateInfo(int stateIndex, RunItemContext ctx)
  492. {
  493. if (ctx.didFail)
  494. {
  495. m_ConverterStates[stateIndex].items[ctx.item.index].message = ctx.info;
  496. m_ConverterStates[stateIndex].items[ctx.item.index].status = Status.Error;
  497. m_ConverterStates[stateIndex].errors++;
  498. }
  499. else
  500. {
  501. m_ConverterStates[stateIndex].items[ctx.item.index].status = Status.Success;
  502. m_ConverterStates[stateIndex].success++;
  503. }
  504. m_ConverterStates[stateIndex].pending--;
  505. // Making sure that this is set here so that if user is clicking Convert again it will not run again.
  506. ctx.hasConverted = true;
  507. VisualElement child = m_ScrollView[stateIndex];
  508. child.Q<ListView>("converterItems").Rebuild();
  509. }
  510. void Convert(ClickEvent evt)
  511. {
  512. List<ConverterState> activeConverterStates = new List<ConverterState>();
  513. // Get the names of the converters
  514. // Get the amount of them
  515. // Make the string "name x/y"
  516. // Getting all the active converters to use in the cancelable progressbar
  517. foreach (ConverterState state in m_ConverterStates)
  518. {
  519. if (state.isActive && state.isInitialized)
  520. {
  521. activeConverterStates.Add(state);
  522. }
  523. }
  524. int currentCount = 0;
  525. int activeConvertersCount = activeConverterStates.Count;
  526. foreach (ConverterState activeConverterState in activeConverterStates)
  527. {
  528. currentCount++;
  529. var index = activeConverterState.index;
  530. m_CoreConvertersList[index].OnPreRun();
  531. var converterName = m_CoreConvertersList[index].name;
  532. var itemCount = m_ItemsToConvert[index].itemDescriptors.Count;
  533. string progressTitle = $"{converterName} Converter : {currentCount}/{activeConvertersCount}";
  534. for (var j = 0; j < itemCount; j++)
  535. {
  536. if (activeConverterState.items[j].isActive)
  537. {
  538. if (EditorUtility.DisplayCancelableProgressBar(progressTitle,
  539. string.Format("({0} of {1}) {2}", j, itemCount, m_ItemsToConvert[index].itemDescriptors[j].info),
  540. (float)j / (float)itemCount))
  541. break;
  542. ConvertIndex(index, j);
  543. }
  544. }
  545. m_CoreConvertersList[index].OnPostRun();
  546. AssetDatabase.SaveAssets();
  547. EditorUtility.ClearProgressBar();
  548. }
  549. }
  550. void ConvertIndex(int coreConverterIndex, int index)
  551. {
  552. if (!m_ConverterStates[coreConverterIndex].items[index].hasConverted)
  553. {
  554. m_ConverterStates[coreConverterIndex].items[index].hasConverted = true;
  555. var item = new ConverterItemInfo()
  556. {
  557. index = index,
  558. descriptor = m_ItemsToConvert[coreConverterIndex].itemDescriptors[index],
  559. };
  560. var ctx = new RunItemContext(item);
  561. m_CoreConvertersList[coreConverterIndex].OnRun(ref ctx);
  562. UpdateInfo(coreConverterIndex, ctx);
  563. }
  564. }
  565. }
  566. }
  567. #endif