ProfileWindow.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor.AddressableAssets.Settings;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine;
  6. using UnityEngine.AddressableAssets;
  7. using System.Linq;
  8. #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
  9. using Unity.Services.CCD.Management.Models;
  10. #endif
  11. namespace UnityEditor.AddressableAssets.GUI
  12. {
  13. internal class ProfileWindow : EditorWindow
  14. {
  15. //Min and Max proportion of the window that ProfilePane can take up
  16. const float k_MinProfilePaneWidth = 0.10f;
  17. const float k_MaxProfilePaneWidth = 0.6f;
  18. private const float k_MinLabelWidth = 155f;
  19. private const float k_ApproxCharWidth = 8.5f;
  20. const float k_DefaultHorizontalSplitterRatio = 0.33f;
  21. const int k_SplitterThickness = 2;
  22. const int k_ToolbarHeight = 20;
  23. const int k_ItemRectPadding = 15;
  24. //amount of padding between variable items
  25. const float k_VariableItemPadding = 5f;
  26. //Default length of the Label within the Variables Pane
  27. private float m_LabelWidth = 155f;
  28. private float m_FieldBufferWidth = 0f;
  29. GUIStyle m_ItemRectPadding;
  30. float m_HorizontalSplitterRatio = k_DefaultHorizontalSplitterRatio;
  31. private ProfileDataSourceSettings m_ProfileDataSource;
  32. internal AddressableAssetSettings settings
  33. {
  34. get { return AddressableAssetSettingsDefaultObject.Settings; }
  35. }
  36. internal ProfileDataSourceSettings dataSourceSettings
  37. {
  38. get
  39. {
  40. if (m_ProfileDataSource == null)
  41. m_ProfileDataSource = ProfileDataSourceSettings.GetSettings();
  42. return m_ProfileDataSource;
  43. }
  44. }
  45. private ProfileTreeView m_ProfileTreeView;
  46. private bool m_IsResizingHorizontalSplitter;
  47. internal static bool m_Reload = false;
  48. private Vector2 m_ProfilesPaneScrollPosition;
  49. private Vector2 m_VariablesPaneScrollPosition;
  50. private int m_ProfileIndex = -1;
  51. public int ProfileIndex
  52. {
  53. get { return m_ProfileIndex; }
  54. set { m_ProfileIndex = value; }
  55. }
  56. private GUIStyle m_ButtonStyle;
  57. private Dictionary<string, bool?> m_foldouts = new Dictionary<string, bool?>();
  58. private Dictionary<string, bool> m_CustomGroupTypes = new Dictionary<string, bool>();
  59. [MenuItem("Window/Asset Management/Addressables/Profiles", priority = 2051)]
  60. internal static void ShowWindow()
  61. {
  62. var settings = AddressableAssetSettingsDefaultObject.Settings;
  63. if (settings == null)
  64. {
  65. EditorUtility.DisplayDialog("Error", "Attempting to open Addressables Profiles window, but no Addressables Settings file exists. \n\nOpen 'Window/Asset Management/Addressables/Groups' for more info.", "Ok");
  66. return;
  67. }
  68. GetWindow<ProfileWindow>().Show();
  69. }
  70. internal static void DrawOutline(Rect rect, float size)
  71. {
  72. Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
  73. if (EditorGUIUtility.isProSkin)
  74. {
  75. color.r = 0.12f;
  76. color.g = 0.12f;
  77. color.b = 0.12f;
  78. }
  79. if (Event.current.type != EventType.Repaint)
  80. return;
  81. Color orgColor = UnityEngine.GUI.color;
  82. UnityEngine.GUI.color = UnityEngine.GUI.color * color;
  83. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, size), EditorGUIUtility.whiteTexture);
  84. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.yMax - size, rect.width, size), EditorGUIUtility.whiteTexture);
  85. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
  86. UnityEngine.GUI.DrawTexture(new Rect(rect.xMax - size, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
  87. UnityEngine.GUI.color = orgColor;
  88. }
  89. #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
  90. private async void Awake()
  91. {
  92. if (CloudProjectSettings.projectId != String.Empty) await ProfileDataSourceSettings.UpdateCCDDataSourcesAsync(CloudProjectSettings.projectId, false);
  93. }
  94. #endif
  95. private void OnEnable()
  96. {
  97. Undo.undoRedoPerformed += MarkForReload;
  98. titleContent = new GUIContent("Addressables Profiles");
  99. m_ItemRectPadding = new GUIStyle();
  100. m_ItemRectPadding.padding = new RectOffset(k_ItemRectPadding, k_ItemRectPadding, k_ItemRectPadding, k_ItemRectPadding);
  101. }
  102. private void OnDisable()
  103. {
  104. Undo.undoRedoPerformed -= MarkForReload;
  105. }
  106. internal static void MarkForReload()
  107. {
  108. m_Reload = true;
  109. }
  110. GUIStyle GetStyle(string styleName)
  111. {
  112. GUIStyle s = UnityEngine.GUI.skin.FindStyle(styleName);
  113. if (s == null)
  114. s = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle(styleName);
  115. if (s == null)
  116. {
  117. Addressables.LogError("Missing built-in guistyle " + styleName);
  118. s = new GUIStyle();
  119. }
  120. return s;
  121. }
  122. void TopToolbar(Rect toolbarPos)
  123. {
  124. if (m_ButtonStyle == null)
  125. m_ButtonStyle = GetStyle("ToolbarButton");
  126. m_ButtonStyle.alignment = TextAnchor.MiddleLeft;
  127. GUILayout.BeginArea(new Rect(0, 0, toolbarPos.width, k_ToolbarHeight));
  128. GUILayout.BeginHorizontal(EditorStyles.toolbar);
  129. {
  130. var guiMode = new GUIContent("Create");
  131. Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown);
  132. if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown))
  133. {
  134. var menu = new GenericMenu();
  135. menu.AddItem(new GUIContent("Profile"), false, NewProfile);
  136. menu.AddItem(new GUIContent("Variable (All Profiles)"), false, () => NewVariable(rMode));
  137. menu.AddItem(new GUIContent("Build and Load Path Variables (All Profiles)"), false, () => NewPathPair(rMode));
  138. menu.DropDown(rMode);
  139. }
  140. GUILayout.FlexibleSpace();
  141. }
  142. GUILayout.EndHorizontal();
  143. GUILayout.EndArea();
  144. }
  145. void NewVariable(Rect displayRect)
  146. {
  147. try
  148. {
  149. displayRect.y += 22f;
  150. PopupWindow.Show(displayRect,
  151. new ProfileNewVariablePopup(position.width, position.height, 0, m_ProfileTreeView, settings));
  152. }
  153. catch (ExitGUIException)
  154. {
  155. // Exception not being caught through OnGUI call
  156. }
  157. }
  158. void NewPathPair(Rect displayRect)
  159. {
  160. try
  161. {
  162. displayRect.y += 22f;
  163. PopupWindow.Show(displayRect,
  164. new ProfileNewPathPairPopup(position.width, position.height, 0, m_ProfileTreeView, settings));
  165. }
  166. catch (ExitGUIException)
  167. {
  168. // Exception not being caught through OnGUI call
  169. }
  170. }
  171. //Contains all of the profile names, primarily implemented in ProfileTreeView
  172. void ProfilesPane(Rect profilesPaneRect)
  173. {
  174. DrawOutline(profilesPaneRect, 1);
  175. GUILayout.BeginArea(profilesPaneRect);
  176. {
  177. m_ProfilesPaneScrollPosition = GUILayout.BeginScrollView(m_ProfilesPaneScrollPosition);
  178. Rect r = new Rect(profilesPaneRect);
  179. r.y = 0;
  180. var profiles = settings.profileSettings.profiles;
  181. if (m_ProfileTreeView == null || m_ProfileTreeView.Names.Count != profiles.Count || m_Reload)
  182. {
  183. m_Reload = false;
  184. m_ProfileTreeView = new ProfileTreeView(new TreeViewState(), profiles, this, ProfileTreeView.CreateHeader());
  185. }
  186. m_ProfileTreeView.OnGUI(r);
  187. GUILayout.EndScrollView();
  188. }
  189. GUILayout.EndArea();
  190. }
  191. //Displays all variables for the currently selected profile and initializes each variable's context menu
  192. void VariablesPane(Rect variablesPaneRect)
  193. {
  194. DrawOutline(variablesPaneRect, 1);
  195. Event evt = Event.current;
  196. AddressableAssetProfileSettings.BuildProfile selectedProfile = GetSelectedProfile();
  197. if (selectedProfile == null) return;
  198. if (evt.isMouse || evt.isKey)
  199. {
  200. m_ProfileTreeView.lastClickedProfile = ProfileIndex;
  201. }
  202. //ensures amount of visible text is not affected by label width
  203. float fieldWidth = variablesPaneRect.width - (2 * k_ItemRectPadding) + m_FieldBufferWidth;
  204. if (!EditorGUIUtility.labelWidth.Equals(m_LabelWidth))
  205. EditorGUIUtility.labelWidth = m_LabelWidth;
  206. int maxLabelLen = 0;
  207. int maxFieldLen = 0;
  208. GUILayout.BeginArea(variablesPaneRect);
  209. EditorGUI.indentLevel++;
  210. List<ProfileGroupType> groupTypes = ProfileGroupType.CreateGroupTypes(selectedProfile);
  211. HashSet<string> drawnGroupTypes = new HashSet<string>();
  212. //Displaying Path Groups
  213. foreach (ProfileGroupType groupType in groupTypes)
  214. {
  215. bool? foldout;
  216. m_foldouts.TryGetValue(groupType.GroupTypePrefix, out foldout);
  217. GUILayout.Space(5);
  218. Rect pathPairRect = EditorGUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Width(fieldWidth + k_VariableItemPadding - k_SplitterThickness), GUILayout.MinWidth(fieldWidth + k_VariableItemPadding - k_SplitterThickness) });
  219. m_foldouts[groupType.GroupTypePrefix] = EditorGUILayout.Foldout(foldout != null ? foldout.Value : true, groupType.GroupTypePrefix, true);
  220. Rect dsDropdownRect = EditorGUILayout.BeginHorizontal(new GUILayoutOption[] { GUILayout.Width(fieldWidth - m_LabelWidth), GUILayout.MinWidth(fieldWidth - m_LabelWidth) });
  221. string dropdownText = DetermineOptionString(groupType);
  222. bool dsDropdown = EditorGUILayout.DropdownButton(new GUIContent(dropdownText), FocusType.Keyboard, new GUILayoutOption[] { GUILayout.Width(fieldWidth - m_LabelWidth) });
  223. if (evt.type == EventType.ContextClick)
  224. CreatePairPrefixContextMenu(variablesPaneRect, pathPairRect, groupType, evt);
  225. EditorGUILayout.EndHorizontal();
  226. EditorGUILayout.EndHorizontal();
  227. DrawDataSourceDropDowns(dsDropdownRect, groupType, dsDropdown);
  228. //Specific Grouped variables
  229. List<ProfileGroupType.GroupTypeVariable> pathVariables = new List<ProfileGroupType.GroupTypeVariable>();
  230. pathVariables.Add(groupType.GetVariableBySuffix(AddressableAssetSettings.kBuildPath));
  231. drawnGroupTypes.Add(groupType.GetName(groupType.GetVariableBySuffix(AddressableAssetSettings.kBuildPath)));
  232. pathVariables.Add(groupType.GetVariableBySuffix(AddressableAssetSettings.kLoadPath));
  233. drawnGroupTypes.Add(groupType.GetName(groupType.GetVariableBySuffix(AddressableAssetSettings.kLoadPath)));
  234. if (m_foldouts[groupType.GroupTypePrefix].Value)
  235. {
  236. bool custom;
  237. m_CustomGroupTypes.TryGetValue(groupType.GroupTypePrefix, out custom);
  238. EditorGUI.BeginDisabledGroup(!custom);
  239. EditorGUI.indentLevel++;
  240. //Displaying Path Groups
  241. foreach (var variable in pathVariables)
  242. {
  243. Rect newPathRect = EditorGUILayout.BeginVertical();
  244. string newPath = EditorGUILayout.TextField(groupType.GetName(variable), variable.Value, new GUILayoutOption[] { GUILayout.Width(fieldWidth) });
  245. EditorGUILayout.EndVertical();
  246. if (newPath != variable.Value && ProfileIndex == m_ProfileTreeView.lastClickedProfile)
  247. {
  248. Undo.RecordObject(settings, "Variable value changed");
  249. settings.profileSettings.SetValue(selectedProfile.id, groupType.GetName(variable), newPath);
  250. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  251. }
  252. }
  253. EditorGUI.indentLevel--;
  254. EditorGUI.EndDisabledGroup();
  255. }
  256. }
  257. //Display all other variables
  258. for (var i = 0; i < settings.profileSettings.profileEntryNames.Count; i++)
  259. {
  260. AddressableAssetProfileSettings.ProfileIdData curVariable = settings.profileSettings.profileEntryNames[i];
  261. if (!drawnGroupTypes.Contains(curVariable.ProfileName))
  262. {
  263. GUILayout.Space(5);
  264. Rect newValueRect = EditorGUILayout.BeginVertical();
  265. string newValue = EditorGUILayout.TextField(curVariable.ProfileName, selectedProfile.values[i].value, new GUILayoutOption[] { GUILayout.Width(fieldWidth) });
  266. EditorGUILayout.EndVertical();
  267. if (newValue != selectedProfile.values[i].value && ProfileIndex == m_ProfileTreeView.lastClickedProfile)
  268. {
  269. Undo.RecordObject(settings, "Variable value changed");
  270. settings.profileSettings.SetValue(selectedProfile.id, settings.profileSettings.profileEntryNames[i].ProfileName, newValue);
  271. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  272. }
  273. if (evt.type == EventType.ContextClick)
  274. {
  275. CreateVariableContextMenu(variablesPaneRect, newValueRect, curVariable, evt);
  276. }
  277. }
  278. maxLabelLen = Math.Max(maxLabelLen, curVariable.ProfileName.Length);
  279. }
  280. EditorGUI.indentLevel--;
  281. GUILayout.EndArea();
  282. //Update the label width to the maximum of the minimum acceptable label width and the amount of
  283. //space required to contain the longest variable name
  284. m_LabelWidth = Mathf.Max(maxLabelLen * k_ApproxCharWidth, k_MinLabelWidth);
  285. m_FieldBufferWidth = Mathf.Clamp((maxFieldLen * k_ApproxCharWidth) - fieldWidth, 0f, float.MaxValue);
  286. }
  287. void DrawDataSourceDropDowns(Rect dsDropdownRect, ProfileGroupType groupType, bool showDropdown)
  288. {
  289. Rect fixedDropdownRect = new Rect(
  290. //Determine correct position for dropdown window
  291. new Vector2(
  292. dsDropdownRect.x,
  293. dsDropdownRect.y
  294. ),
  295. new Vector2(dsDropdownRect.width, 120)
  296. );
  297. if (showDropdown)
  298. {
  299. ProfileDataSourceDropdownWindow dataSourceDropdownWindow = new ProfileDataSourceDropdownWindow(fixedDropdownRect, groupType);
  300. //TODO: Add Event Handler Here
  301. dataSourceDropdownWindow.ValueChanged += DataSourceDropdownValueChanged;
  302. PopupWindow.Show(dsDropdownRect, dataSourceDropdownWindow);
  303. }
  304. }
  305. internal void DataSourceDropdownValueChanged(object sender, ProfileDataSourceDropdownWindow.DropdownWindowEventArgs e)
  306. {
  307. m_CustomGroupTypes[e.GroupType.GroupTypePrefix] = e.IsCustom;
  308. if (!e.IsCustom)
  309. {
  310. var selectedProfile = GetSelectedProfile();
  311. Undo.RecordObject(settings, "Variable value changed");
  312. settings.profileSettings.SetValue(selectedProfile.id, e.GroupType.GetName(e.GroupType.GetVariableBySuffix(AddressableAssetSettings.kBuildPath)), e.Option.BuildPath);
  313. settings.profileSettings.SetValue(selectedProfile.id, e.GroupType.GetName(e.GroupType.GetVariableBySuffix(AddressableAssetSettings.kLoadPath)), e.Option.LoadPath);
  314. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  315. }
  316. }
  317. private string DetermineOptionString(ProfileGroupType groupType)
  318. {
  319. ProfileGroupType selectedGroupType = dataSourceSettings.FindGroupType(groupType);
  320. if (selectedGroupType != null)
  321. {
  322. bool custom;
  323. m_CustomGroupTypes.TryGetValue(groupType.GroupTypePrefix, out custom);
  324. if (custom && ProfileIndex == m_ProfileTreeView.lastClickedProfile)
  325. return "Custom";
  326. m_CustomGroupTypes[groupType.GroupTypePrefix] = false;
  327. #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
  328. //Could ERR if user has group type prefix that starts with CCD
  329. if (selectedGroupType.GroupTypePrefix.StartsWith("CCD"))
  330. {
  331. var parts = selectedGroupType.GroupTypePrefix.Split(ProfileGroupType.k_PrefixSeparator);
  332. var badgeName = String.Join(ProfileGroupType.k_PrefixSeparator.ToString(), parts, 3, parts.Length - 3);
  333. var bucketName = selectedGroupType.GetVariableBySuffix($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}").Value;
  334. return String.Join(ProfileGroupType.k_PrefixSeparator.ToString(), new string[]
  335. {
  336. "CCD",
  337. bucketName,
  338. badgeName
  339. });
  340. }
  341. #endif
  342. return selectedGroupType.GroupTypePrefix;
  343. }
  344. else
  345. {
  346. m_CustomGroupTypes[groupType.GroupTypePrefix] = true;
  347. return "Custom";
  348. }
  349. }
  350. void CreatePairPrefixContextMenu(Rect parentWindow, Rect menuRect, ProfileGroupType groupType, Event evt)
  351. {
  352. if (menuRect.Contains(evt.mousePosition))
  353. {
  354. GenericMenu menu = new GenericMenu();
  355. menu.AddDisabledItem(new GUIContent(groupType.GroupTypePrefix));
  356. menu.AddSeparator("");
  357. menu.AddItem(new GUIContent("Rename Path Prefix (All Profiles)"), false, () => { RenamePathPair(groupType, parentWindow, menuRect); });
  358. menu.AddItem(new GUIContent("Delete Path Pair (All Profiles)"), false, () => { DeletePathPair(groupType); });
  359. menu.ShowAsContext();
  360. evt.Use();
  361. }
  362. }
  363. //Creates the context menu for the selected variable
  364. void CreateVariableContextMenu(Rect parentWindow, Rect menuRect, AddressableAssetProfileSettings.ProfileIdData variable, Event evt)
  365. {
  366. if (menuRect.Contains(evt.mousePosition))
  367. {
  368. GenericMenu menu = new GenericMenu();
  369. //Displays name of selected variable so user can be confident they're deleting/renaming the right one
  370. menu.AddDisabledItem(new GUIContent(variable.ProfileName));
  371. menu.AddSeparator("");
  372. menu.AddItem(new GUIContent("Rename Variable (All Profiles)"), false, () => { RenameVariable(variable, parentWindow, menuRect); });
  373. menu.AddItem(new GUIContent("Delete Variable (All Profiles)"), false, () => { DeleteVariable(variable); });
  374. menu.ShowAsContext();
  375. evt.Use();
  376. }
  377. }
  378. void RenamePathPair(ProfileGroupType groupType, Rect parentWindow, Rect displayRect)
  379. {
  380. try
  381. {
  382. //Determines the current variable rect location
  383. Rect variableRect = new Rect(parentWindow.x + 2.5f, displayRect.y + 1.5f, m_LabelWidth, k_ToolbarHeight * 2);
  384. PopupWindow.Show(variableRect, new PathPairRenamePopup(m_LabelWidth, groupType, settings));
  385. }
  386. catch (ExitGUIException)
  387. {
  388. // Exception not being caught through OnGUI call
  389. }
  390. }
  391. //Opens ProfileRenameVariablePopup
  392. void RenameVariable(AddressableAssetProfileSettings.ProfileIdData profileVariable, Rect parentWindow, Rect displayRect)
  393. {
  394. try
  395. {
  396. //Determines the current variable rect location
  397. Rect variableRect = new Rect(parentWindow.x + 2.5f, displayRect.y + 1.5f, m_LabelWidth, k_ToolbarHeight * 2);
  398. PopupWindow.Show(variableRect, new ProfileRenameVariablePopup(m_LabelWidth, profileVariable, settings));
  399. }
  400. catch (ExitGUIException)
  401. {
  402. // Exception not being caught through OnGUI call
  403. }
  404. }
  405. void DeletePathPair(ProfileGroupType groupType)
  406. {
  407. var buildPathData = settings.profileSettings.GetProfileDataByName(groupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kBuildPath);
  408. var loadPathData = settings.profileSettings.GetProfileDataByName(groupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kLoadPath);
  409. if (loadPathData == default(AddressableAssetProfileSettings.ProfileIdData) || buildPathData == default(AddressableAssetProfileSettings.ProfileIdData))
  410. {
  411. Debug.LogError("An error occured while getting one of the path pair variables.");
  412. return;
  413. }
  414. Undo.RecordObject(settings, "Profile Variable Deleted");
  415. settings.profileSettings.RemoveValue(buildPathData.Id);
  416. settings.profileSettings.RemoveValue(loadPathData.Id);
  417. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  418. }
  419. void DeleteVariable(AddressableAssetProfileSettings.ProfileIdData toBeDeleted)
  420. {
  421. Undo.RecordObject(settings, "Profile Variable Deleted");
  422. settings.profileSettings.RemoveValue(toBeDeleted.Id);
  423. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  424. }
  425. //Returns the BuildProfile currently selected in the ProfilesPane
  426. AddressableAssetProfileSettings.BuildProfile GetSelectedProfile()
  427. {
  428. return m_ProfileTreeView.GetSelectedProfile();
  429. }
  430. //Creates a new BuildProfile and reloads the ProfilesPane
  431. void NewProfile()
  432. {
  433. var uniqueProfileName = settings.profileSettings.GetUniqueProfileName("New Profile");
  434. if (!string.IsNullOrEmpty(uniqueProfileName))
  435. {
  436. Undo.RecordObject(settings, "New Profile Created");
  437. //Either copy values from the selected profile, or if there is no selected profile, copy from the default
  438. string idToCopyFrom = GetSelectedProfile() != null
  439. ? GetSelectedProfile().id
  440. : settings.profileSettings.profiles[0].id;
  441. settings.profileSettings.AddProfile(uniqueProfileName, idToCopyFrom);
  442. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
  443. m_ProfileTreeView.Reload();
  444. }
  445. }
  446. private void OnGUI()
  447. {
  448. if (settings == null) return;
  449. if (m_IsResizingHorizontalSplitter)
  450. m_HorizontalSplitterRatio = Mathf.Clamp(Event.current.mousePosition.x / position.width,
  451. k_MinProfilePaneWidth, k_MaxProfilePaneWidth);
  452. var toolbarRect = new Rect(0, 0, position.width, position.height);
  453. var profilesPaneRect = new Rect(0, k_ToolbarHeight, (position.width * m_HorizontalSplitterRatio), position.height);
  454. var variablesPaneRect = new Rect(profilesPaneRect.width + k_SplitterThickness, k_ToolbarHeight,
  455. position.width - profilesPaneRect.width - k_SplitterThickness, position.height - k_ToolbarHeight);
  456. var horizontalSplitterRect = new Rect(profilesPaneRect.width, k_ToolbarHeight, k_SplitterThickness, position.height - k_ToolbarHeight);
  457. EditorGUIUtility.AddCursorRect(horizontalSplitterRect, MouseCursor.ResizeHorizontal);
  458. if (Event.current.type == EventType.MouseDown && horizontalSplitterRect.Contains(Event.current.mousePosition))
  459. m_IsResizingHorizontalSplitter = true;
  460. else if (Event.current.type == EventType.MouseUp)
  461. m_IsResizingHorizontalSplitter = false;
  462. TopToolbar(toolbarRect);
  463. ProfilesPane(profilesPaneRect);
  464. VariablesPane(variablesPaneRect);
  465. if (m_IsResizingHorizontalSplitter)
  466. Repaint();
  467. }
  468. class PathPairRenamePopup : PopupWindowContent
  469. {
  470. internal Rect m_WindowRect;
  471. internal ProfileGroupType m_ProfileGroupType;
  472. internal List<ProfileGroupType> m_AllProfileGroupTypes;
  473. internal AddressableAssetSettings m_Settings;
  474. internal string m_NewName;
  475. internal float m_LabelWidth;
  476. public PathPairRenamePopup(float labelWidth, ProfileGroupType profileGroupType, AddressableAssetSettings settings)
  477. {
  478. m_LabelWidth = labelWidth;
  479. m_ProfileGroupType = profileGroupType;
  480. m_Settings = settings;
  481. m_NewName = profileGroupType.GroupTypePrefix;
  482. UnityEngine.GUI.enabled = true;
  483. }
  484. public override Vector2 GetWindowSize()
  485. {
  486. return new Vector2(m_LabelWidth, 40);
  487. }
  488. public override void OnGUI(Rect windowRect)
  489. {
  490. GUILayout.BeginArea(windowRect);
  491. Event evt = Event.current;
  492. bool hitEnter = evt.type == EventType.KeyDown && (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter);
  493. m_NewName = GUILayout.TextField(m_NewName);
  494. if (GUILayout.Button("Save") || hitEnter)
  495. {
  496. if (string.IsNullOrEmpty(m_NewName))
  497. Debug.LogError("Path pair prefix cannot be empty.");
  498. else if (m_NewName == m_ProfileGroupType.GroupTypePrefix)
  499. editorWindow.Close();
  500. else if (VariableWithNewPrefixAlreadyExists())
  501. Debug.LogError("One or more build or load path variables with prefix '" + m_NewName + "' already exist. Please rename them or pick a different prefix.");
  502. else if (m_NewName.Trim().Length == 0) // new name cannot only contain spaces
  503. Debug.LogError("Path pair prefix cannot be only spaces");
  504. else
  505. {
  506. var loadPathVariableData = m_Settings.profileSettings.GetProfileDataByName(m_ProfileGroupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kLoadPath);
  507. var buildPathVariableData = m_Settings.profileSettings.GetProfileDataByName(m_ProfileGroupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kBuildPath);
  508. if (loadPathVariableData == default(AddressableAssetProfileSettings.ProfileIdData) || buildPathVariableData == default(AddressableAssetProfileSettings.ProfileIdData))
  509. Debug.LogError("Valid path pair to rename not found.");
  510. else
  511. {
  512. Undo.RecordObject(m_Settings, "Path pair prefix Renamed");
  513. m_ProfileGroupType.GroupTypePrefix = m_NewName;
  514. loadPathVariableData.SetName(m_ProfileGroupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kLoadPath, m_Settings.profileSettings);
  515. buildPathVariableData.SetName(m_ProfileGroupType.GroupTypePrefix + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kBuildPath, m_Settings.profileSettings);
  516. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings, true);
  517. editorWindow.Close();
  518. }
  519. }
  520. }
  521. GUILayout.EndArea();
  522. }
  523. bool VariableWithNewPrefixAlreadyExists()
  524. {
  525. bool loadPathAlreadyExists = m_Settings.profileSettings.GetProfileDataByName(m_NewName + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kLoadPath)
  526. != default(AddressableAssetProfileSettings.ProfileIdData);
  527. bool buildPathAlreadyExists = m_Settings.profileSettings.GetProfileDataByName(m_NewName + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kBuildPath)
  528. != default(AddressableAssetProfileSettings.ProfileIdData);
  529. return loadPathAlreadyExists || buildPathAlreadyExists;
  530. }
  531. }
  532. class ProfileRenameVariablePopup : PopupWindowContent
  533. {
  534. internal float m_LabelWidth;
  535. internal AddressableAssetProfileSettings.ProfileIdData m_ProfileVariable;
  536. internal AddressableAssetSettings m_Settings;
  537. internal string m_NewName;
  538. public ProfileRenameVariablePopup(float labelWidth, AddressableAssetProfileSettings.ProfileIdData profileVariable, AddressableAssetSettings settings)
  539. {
  540. m_LabelWidth = labelWidth;
  541. m_ProfileVariable = profileVariable;
  542. m_Settings = settings;
  543. m_NewName = m_ProfileVariable.ProfileName;
  544. UnityEngine.GUI.enabled = true;
  545. }
  546. public override Vector2 GetWindowSize()
  547. {
  548. return new Vector2(m_LabelWidth, 40);
  549. }
  550. public override void OnGUI(Rect windowRect)
  551. {
  552. GUILayout.BeginArea(windowRect);
  553. Event evt = Event.current;
  554. bool hitEnter = evt.type == EventType.KeyDown && (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter);
  555. m_NewName = GUILayout.TextField(m_NewName);
  556. if (GUILayout.Button("Save") || hitEnter)
  557. {
  558. if (string.IsNullOrEmpty(m_NewName))
  559. Debug.LogError("Variable name cannot be empty.");
  560. else if (m_NewName == m_ProfileVariable.ProfileName)
  561. editorWindow.Close();
  562. else if (m_NewName != m_Settings.profileSettings.GetUniqueProfileEntryName(m_NewName))
  563. Debug.LogError("Profile variable '" + m_NewName + "' already exists.");
  564. else if (m_NewName.Trim().Length == 0) // new name cannot only contain spaces
  565. Debug.LogError("Name cannot be only spaces");
  566. else
  567. {
  568. Undo.RecordObject(m_Settings, "Profile Variable Renamed");
  569. m_ProfileVariable.SetName(m_NewName, m_Settings.profileSettings);
  570. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings, true);
  571. editorWindow.Close();
  572. }
  573. }
  574. GUILayout.EndArea();
  575. }
  576. }
  577. class ProfileNewVariablePopup : PopupWindowContent
  578. {
  579. internal float m_WindowWidth;
  580. internal float m_WindowHeight;
  581. internal float m_xOffset;
  582. internal string m_Name;
  583. internal string m_Value;
  584. internal bool m_NeedsFocus = true;
  585. internal AddressableAssetSettings m_Settings;
  586. ProfileTreeView m_ProfileTreeView;
  587. public ProfileNewVariablePopup(float width, float height, float xOffset, ProfileTreeView profileTreeView, AddressableAssetSettings settings)
  588. {
  589. m_WindowWidth = width;
  590. m_WindowHeight = height;
  591. m_xOffset = xOffset;
  592. m_Settings = settings;
  593. m_Name = m_Settings.profileSettings.GetUniqueProfileEntryName("New Entry");
  594. m_Value = Application.dataPath;
  595. m_ProfileTreeView = profileTreeView;
  596. }
  597. public override Vector2 GetWindowSize()
  598. {
  599. float width = Mathf.Clamp(m_WindowWidth * 0.375f, Mathf.Min(600, m_WindowWidth - m_xOffset), m_WindowWidth);
  600. float height = Mathf.Clamp(65, Mathf.Min(65, m_WindowHeight), m_WindowHeight);
  601. return new Vector2(width, height);
  602. }
  603. public override void OnGUI(Rect windowRect)
  604. {
  605. GUILayout.Space(5);
  606. Event evt = Event.current;
  607. bool hitEnter = evt.type == EventType.KeyDown && (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter);
  608. EditorGUIUtility.labelWidth = 90;
  609. m_Name = EditorGUILayout.TextField("Variable Name", m_Name);
  610. m_Value = EditorGUILayout.TextField("Default Value", m_Value);
  611. UnityEngine.GUI.enabled = m_Name.Length != 0;
  612. if (GUILayout.Button("Save") || hitEnter)
  613. {
  614. if (string.IsNullOrEmpty(m_Name))
  615. Debug.LogError("Variable name cannot be empty.");
  616. else if (m_Name != m_Settings.profileSettings.GetUniqueProfileEntryName(m_Name))
  617. Debug.LogError("Profile variable '" + m_Name + "' already exists.");
  618. else
  619. {
  620. Undo.RecordObject(m_Settings, "Profile Variable " + m_Name + " Created");
  621. m_Settings.profileSettings.CreateValue(m_Name, m_Value);
  622. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings);
  623. m_ProfileTreeView.Reload();
  624. editorWindow.Close();
  625. }
  626. }
  627. }
  628. }
  629. class ProfileNewPathPairPopup : PopupWindowContent
  630. {
  631. internal float m_WindowWidth;
  632. internal float m_WindowHeight;
  633. internal float m_xOffset;
  634. internal string m_Name;
  635. internal string m_BuildPath;
  636. internal string m_LoadPath;
  637. internal bool m_NeedsFocus = true;
  638. internal AddressableAssetSettings m_Settings;
  639. ProfileTreeView m_ProfileTreeView;
  640. public ProfileNewPathPairPopup(float width, float height, float xOffset, ProfileTreeView profileTreeView, AddressableAssetSettings settings)
  641. {
  642. m_WindowWidth = width;
  643. m_WindowHeight = height;
  644. m_xOffset = xOffset;
  645. m_Settings = settings;
  646. m_Name = m_Settings.profileSettings.GetUniqueProfileEntryName("New Entry");
  647. m_BuildPath = Application.dataPath;
  648. m_LoadPath = Application.dataPath;
  649. m_ProfileTreeView = profileTreeView;
  650. }
  651. public override Vector2 GetWindowSize()
  652. {
  653. float width = Mathf.Clamp(m_WindowWidth * 0.375f, Mathf.Min(600, m_WindowWidth - m_xOffset), m_WindowWidth);
  654. float height = Mathf.Clamp(85, Mathf.Min(85, m_WindowHeight), m_WindowHeight);
  655. return new Vector2(width, height);
  656. }
  657. public override void OnGUI(Rect windowRect)
  658. {
  659. GUILayout.Space(5);
  660. Event evt = Event.current;
  661. bool hitEnter = evt.type == EventType.KeyDown && (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter);
  662. EditorGUIUtility.labelWidth = 120;
  663. m_Name = EditorGUILayout.TextField("Prefix Name", m_Name);
  664. m_BuildPath = EditorGUILayout.TextField("Build Path Value", m_BuildPath);
  665. m_LoadPath = EditorGUILayout.TextField("Load Path Value", m_LoadPath);
  666. UnityEngine.GUI.enabled = m_Name.Length != 0;
  667. if (GUILayout.Button("Save") || hitEnter)
  668. {
  669. string buildPathName = m_Name + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kBuildPath;
  670. string loadPathName = m_Name + ProfileGroupType.k_PrefixSeparator + AddressableAssetSettings.kLoadPath;
  671. if (string.IsNullOrEmpty(m_Name))
  672. Debug.LogError("Variable name cannot be empty.");
  673. else if (buildPathName != m_Settings.profileSettings.GetUniqueProfileEntryName(buildPathName))
  674. Debug.LogError("Profile variable '" + buildPathName + "' already exists.");
  675. else if (loadPathName != m_Settings.profileSettings.GetUniqueProfileEntryName(loadPathName))
  676. Debug.LogError("Profile variable '" + loadPathName + "' already exists.");
  677. else
  678. {
  679. Undo.RecordObject(m_Settings, "Profile Path Pair Created");
  680. m_Settings.profileSettings.CreateValue(buildPathName, m_BuildPath);
  681. m_Settings.profileSettings.CreateValue(loadPathName, m_LoadPath);
  682. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings);
  683. m_ProfileTreeView.Reload();
  684. editorWindow.Close();
  685. }
  686. }
  687. }
  688. }
  689. }
  690. }