PlasticDialog.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using PlasticGui;
  6. namespace Unity.PlasticSCM.Editor.UI
  7. {
  8. [InitializeOnLoad]
  9. internal abstract class PlasticDialog : EditorWindow, IPlasticDialogCloser
  10. {
  11. protected virtual Rect DefaultRect
  12. {
  13. get
  14. {
  15. int pixelWidth = Screen.currentResolution.width;
  16. float x = (pixelWidth - DEFAULT_WIDTH) / 2;
  17. return new Rect(x, 200, DEFAULT_WIDTH, DEFAULT_HEIGHT);
  18. }
  19. }
  20. protected virtual bool IsResizable { get; set; }
  21. internal void OkButtonAction()
  22. {
  23. CompleteModal(ResponseType.Ok);
  24. }
  25. internal void CancelButtonAction()
  26. {
  27. CompleteModal(ResponseType.Cancel);
  28. }
  29. internal void CloseButtonAction()
  30. {
  31. CompleteModal(ResponseType.None);
  32. }
  33. internal void ApplyButtonAction()
  34. {
  35. CompleteModal(ResponseType.Apply);
  36. }
  37. internal ResponseType RunModal(EditorWindow parentWindow)
  38. {
  39. InitializeVars(parentWindow);
  40. if (!IsResizable)
  41. MakeNonResizable();
  42. if (UI.RunModal.IsAvailable())
  43. {
  44. UI.RunModal.Dialog(this);
  45. return mAnswer;
  46. }
  47. EditorUtility.DisplayDialog(
  48. PlasticLocalization.GetString(PlasticLocalization.Name.PlasticSCM),
  49. PlasticLocalization.GetString(PlasticLocalization.Name.PluginModalInformation),
  50. PlasticLocalization.GetString(PlasticLocalization.Name.CloseButton));
  51. return ResponseType.None;
  52. }
  53. protected void OnGUI()
  54. {
  55. try
  56. {
  57. // If the Dialog has been saved into the Unity editor layout and persisted between restarts, the methods
  58. // to configure the dialogs will be skipped. Simple fix here is to close it when this state is detected.
  59. // Fixes a NPE loop when the state mentioned above is occuring.
  60. if (!mIsConfigured)
  61. {
  62. mIsClosed = true;
  63. Close();
  64. return;
  65. }
  66. if (Event.current.type == EventType.Layout)
  67. {
  68. EditorDispatcher.Update();
  69. }
  70. if (!mFocusedOnce)
  71. {
  72. // Somehow the prevents the dialog from jumping when dragged
  73. // NOTE(rafa): We cannot do every frame because the modal kidnaps focus for all processes (in mac at least)
  74. Focus();
  75. mFocusedOnce = true;
  76. }
  77. ProcessKeyActions();
  78. if (mIsClosed)
  79. return;
  80. GUI.Box(new Rect(0, 0, position.width, position.height), GUIContent.none, EditorStyles.label);
  81. float margin = 25;
  82. float marginTop = 25;
  83. using (new EditorGUILayout.HorizontalScope(GUILayout.Height(position.height)))
  84. {
  85. GUILayout.Space(margin);
  86. using (new EditorGUILayout.VerticalScope(GUILayout.Height(position.height)))
  87. {
  88. GUILayout.Space(marginTop);
  89. OnModalGUI();
  90. GUILayout.Space(margin);
  91. }
  92. GUILayout.Space(margin);
  93. }
  94. var lastRect = GUILayoutUtility.GetLastRect();
  95. float desiredHeight = lastRect.yMax;
  96. Rect newPos = position;
  97. newPos.height = desiredHeight;
  98. if (position.height < desiredHeight)
  99. position = newPos;
  100. if (Event.current.type == EventType.Repaint)
  101. {
  102. if (mIsCompleted)
  103. {
  104. mIsClosed = true;
  105. Close();
  106. }
  107. }
  108. }
  109. finally
  110. {
  111. if (mIsClosed)
  112. EditorGUIUtility.ExitGUI();
  113. }
  114. }
  115. void OnDestroy()
  116. {
  117. if (!mIsConfigured)
  118. return;
  119. SaveSettings();
  120. mParentWindow.Focus();
  121. }
  122. protected virtual void SaveSettings() { }
  123. protected abstract void OnModalGUI();
  124. protected abstract string GetTitle();
  125. protected void Paragraph(string text)
  126. {
  127. GUILayout.Label(text, UnityStyles.Paragraph);
  128. GUILayout.Space(DEFAULT_PARAGRAPH_SPACING);
  129. }
  130. protected void TextBlockWithEndLink(
  131. string url, string formattedExplanation,
  132. GUIStyle textblockStyle)
  133. {
  134. string explanation = string.Format(
  135. formattedExplanation, "");
  136. GUILayout.Label(explanation, textblockStyle);
  137. if (explanation == formattedExplanation)
  138. return;
  139. string coloredUrl = string.Format(
  140. "<color=\"{0}\">{1}</color>",
  141. UnityStyles.HexColors.LINK_COLOR,
  142. url);
  143. float linkWidth =
  144. textblockStyle.CalcSize(new GUIContent(url)).x;
  145. if (GUILayout.Button(coloredUrl, textblockStyle, GUILayout.Width(linkWidth)))
  146. Application.OpenURL(url);
  147. EditorGUIUtility.AddCursorRect(
  148. GUILayoutUtility.GetLastRect(), MouseCursor.Link);
  149. }
  150. protected static void Title(string text)
  151. {
  152. GUILayout.Label(text, UnityStyles.Dialog.Toggle);
  153. }
  154. protected static bool TitleToggle(string text, bool isOn)
  155. {
  156. return EditorGUILayout.ToggleLeft(text, isOn, UnityStyles.Dialog.Toggle);
  157. }
  158. protected static bool TitleToggle(string text, bool isOn, GUIStyle style)
  159. {
  160. return EditorGUILayout.ToggleLeft(text, isOn, style);
  161. }
  162. protected static string TextEntry(
  163. string label,
  164. string value,
  165. float width,
  166. float x)
  167. {
  168. return TextEntry(
  169. label,
  170. value,
  171. null,
  172. width,
  173. x);
  174. }
  175. protected static string TextEntry(
  176. string label, string value, string controlName, float width, float x)
  177. {
  178. using (new EditorGUILayout.HorizontalScope())
  179. {
  180. EntryLabel(label);
  181. GUILayout.FlexibleSpace();
  182. var rt = GUILayoutUtility.GetRect(
  183. new GUIContent(value), UnityStyles.Dialog.EntryLabel);
  184. rt.width = width;
  185. rt.x = x;
  186. if (!string.IsNullOrEmpty(controlName))
  187. GUI.SetNextControlName(controlName);
  188. return GUI.TextField(rt, value);
  189. }
  190. }
  191. protected static string ComboBox(
  192. string label,
  193. string value,
  194. string controlName,
  195. List<string> dropDownOptions,
  196. GenericMenu.MenuFunction2 optionSelected,
  197. float width,
  198. float x)
  199. {
  200. using (new EditorGUILayout.HorizontalScope())
  201. {
  202. EntryLabel(label);
  203. GUILayout.FlexibleSpace();
  204. var rt = GUILayoutUtility.GetRect(
  205. new GUIContent(value), UnityStyles.Dialog.EntryLabel);
  206. rt.width = width;
  207. rt.x = x;
  208. return DropDownTextField.DoDropDownTextField(
  209. value,
  210. label,
  211. dropDownOptions,
  212. optionSelected,
  213. rt);
  214. }
  215. }
  216. protected static string PasswordEntry(
  217. string label, string value, float width, float x)
  218. {
  219. using (new EditorGUILayout.HorizontalScope())
  220. {
  221. EntryLabel(label);
  222. GUILayout.FlexibleSpace();
  223. var rt = GUILayoutUtility.GetRect(
  224. new GUIContent(value), UnityStyles.Dialog.EntryLabel);
  225. rt.width = width;
  226. rt.x = x;
  227. return GUI.PasswordField(rt, value, '*');
  228. }
  229. }
  230. protected static bool ToggleEntry(
  231. string label, bool value, float width, float x)
  232. {
  233. var rt = GUILayoutUtility.GetRect(
  234. new GUIContent(label), UnityStyles.Dialog.EntryLabel);
  235. rt.width = width;
  236. rt.x = x;
  237. return GUI.Toggle(rt, value, label);
  238. }
  239. protected static bool NormalButton(string text)
  240. {
  241. return GUILayout.Button(
  242. text, UnityStyles.Dialog.NormalButton,
  243. GUILayout.MinWidth(80),
  244. GUILayout.Height(25));
  245. }
  246. void IPlasticDialogCloser.CloseDialog()
  247. {
  248. OkButtonAction();
  249. }
  250. void ProcessKeyActions()
  251. {
  252. Event e = Event.current;
  253. if (mEnterKeyAction != null &&
  254. Keyboard.IsReturnOrEnterKeyPressed(e))
  255. {
  256. mEnterKeyAction.Invoke();
  257. e.Use();
  258. return;
  259. }
  260. if (mEscapeKeyAction != null &&
  261. Keyboard.IsKeyPressed(e, KeyCode.Escape))
  262. {
  263. mEscapeKeyAction.Invoke();
  264. e.Use();
  265. return;
  266. }
  267. }
  268. protected static bool AcceptButton(string text)
  269. {
  270. GUI.color = new Color(0.098f, 0.502f, 0.965f, .8f);
  271. int textWidth = (int)((GUIStyle)UnityStyles.Dialog.AcceptButtonText)
  272. .CalcSize(new GUIContent(text)).x;
  273. bool pressed = GUILayout.Button(
  274. string.Empty, GetEditorSkin().button,
  275. GUILayout.MinWidth(Math.Max(80, textWidth + 10)),
  276. GUILayout.Height(25));
  277. GUI.color = Color.white;
  278. Rect buttonRect = GUILayoutUtility.GetLastRect();
  279. GUI.Label(buttonRect, text, UnityStyles.Dialog.AcceptButtonText);
  280. return pressed;
  281. }
  282. void CompleteModal(ResponseType answer)
  283. {
  284. mIsCompleted = true;
  285. mAnswer = answer;
  286. }
  287. void InitializeVars(EditorWindow parentWindow)
  288. {
  289. mIsConfigured = true;
  290. mIsCompleted = false;
  291. mIsClosed = false;
  292. mAnswer = ResponseType.Cancel;
  293. titleContent = new GUIContent(GetTitle());
  294. mFocusedOnce = false;
  295. position = DefaultRect;
  296. mParentWindow = parentWindow;
  297. }
  298. void MakeNonResizable()
  299. {
  300. maxSize = DefaultRect.size;
  301. minSize = maxSize;
  302. }
  303. static void EntryLabel(string labelText)
  304. {
  305. GUIContent labelContent = new GUIContent(labelText);
  306. GUIStyle labelStyle = UnityStyles.Dialog.EntryLabel;
  307. Rect rt = GUILayoutUtility.GetRect(labelContent, labelStyle);
  308. GUI.Label(rt, labelText, EditorStyles.label);
  309. }
  310. static GUISkin GetEditorSkin()
  311. {
  312. return EditorGUIUtility.isProSkin ?
  313. EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) :
  314. EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
  315. }
  316. bool mIsConfigured;
  317. bool mIsCompleted;
  318. bool mIsClosed;
  319. ResponseType mAnswer;
  320. protected Action mEnterKeyAction = null;
  321. protected Action mEscapeKeyAction = null;
  322. bool mFocusedOnce;
  323. Dictionary<string, string[]> mWrappedTextLines =
  324. new Dictionary<string, string[]>();
  325. EditorWindow mParentWindow;
  326. protected const float DEFAULT_LINE_SPACING = -5;
  327. const float DEFAULT_WIDTH = 500;
  328. const float DEFAULT_HEIGHT = 180;
  329. const float DEFAULT_PARAGRAPH_SPACING = 10f;
  330. static class BuildLine
  331. {
  332. internal static string ForIndex(string text, int index)
  333. {
  334. if (index < 0 || index > text.Length)
  335. return string.Empty;
  336. return text.Substring(index).Trim();
  337. }
  338. internal static string ForIndexAndLenght(string text, int index, int lenght)
  339. {
  340. if (index < 0 || index > text.Length)
  341. return string.Empty;
  342. return text.Substring(index, lenght);
  343. }
  344. }
  345. }
  346. }