A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3
4using UnityEditor;
5using UnityEngine;
6
7using PlasticGui;
8
9namespace Unity.PlasticSCM.Editor.UI
10{
11 internal abstract class PlasticDialog : EditorWindow, IPlasticDialogCloser
12 {
13 internal enum SizeToContent
14 {
15 Manual = 0,
16 Automatic = 1
17 }
18
19 protected virtual Rect DefaultRect
20 {
21 get
22 {
23 int pixelWidth = Screen.currentResolution.width;
24 float x = (pixelWidth - DEFAULT_WIDTH) / 2;
25 return new Rect(x, 200, DEFAULT_WIDTH, DEFAULT_HEIGHT);
26 }
27 }
28
29 protected virtual bool IsResizable { get; set; }
30
31 internal void SetSizeToContent(SizeToContent sizeToContent)
32 {
33 mSizeToContent = sizeToContent;
34 }
35
36 internal void OkButtonAction()
37 {
38 CompleteModal(ResponseType.Ok);
39 }
40
41 internal void CancelButtonAction()
42 {
43 CompleteModal(ResponseType.Cancel);
44 }
45
46 internal void CloseButtonAction()
47 {
48 CompleteModal(ResponseType.None);
49 }
50
51 internal void ApplyButtonAction()
52 {
53 CompleteModal(ResponseType.Apply);
54 }
55
56 internal void AddControlConsumingEnterKey(string controlName)
57 {
58 mControlsConsumingEnterKey.Add(controlName);
59 }
60
61 internal void RunUtility(EditorWindow parentWindow)
62 {
63 InitializeVars(parentWindow);
64
65 if (!IsResizable)
66 MakeNonResizable();
67
68 ShowUtility();
69 }
70
71 internal ResponseType RunModal(EditorWindow parentWindow)
72 {
73 InitializeVars(parentWindow);
74
75 if (!IsResizable)
76 MakeNonResizable();
77
78 if (UI.RunModal.IsAvailable())
79 {
80 UI.RunModal.Dialog(this);
81 return mAnswer;
82 }
83
84 EditorUtility.DisplayDialog(
85 PlasticLocalization.GetString(PlasticLocalization.Name.UnityVersionControl),
86 PlasticLocalization.GetString(PlasticLocalization.Name.PluginModalInformation),
87 PlasticLocalization.GetString(PlasticLocalization.Name.CloseButton));
88 return ResponseType.None;
89 }
90
91 protected void OnGUI()
92 {
93 try
94 {
95 // If the Dialog has been saved into the Unity editor layout and persisted between restarts, the methods
96 // to configure the dialogs will be skipped. Simple fix here is to close it when this state is detected.
97 // Fixes a NPE loop when the state mentioned above is occurring.
98 if (!mIsConfigured)
99 {
100 mIsClosed = true;
101 Close();
102 return;
103 }
104
105 if (!mFocusedOnce)
106 {
107 // Somehow the prevents the dialog from jumping when dragged
108 // NOTE(rafa): We cannot do every frame because the modal kidnaps focus for all processes (in mac at least)
109 Focus();
110 mFocusedOnce = true;
111 }
112
113 ProcessKeyActions();
114
115 if (mIsClosed)
116 return;
117
118 GUI.Box(new Rect(0, 0, position.width, position.height), GUIContent.none, EditorStyles.label);
119
120 float margin = 25;
121 float marginTop = 25;
122 using (new EditorGUILayout.HorizontalScope(GUILayout.Height(position.height)))
123 {
124 GUILayout.Space(margin);
125 using (new EditorGUILayout.VerticalScope(GUILayout.Height(position.height)))
126 {
127 GUILayout.Space(marginTop);
128 OnModalGUI();
129 GUILayout.Space(margin);
130 }
131 GUILayout.Space(margin);
132 }
133
134 if (!IsResizable && mSizeToContent == SizeToContent.Automatic)
135 {
136 var lastRect = GUILayoutUtility.GetLastRect();
137 float desiredHeight = lastRect.yMax;
138
139 if (position.height < desiredHeight)
140 {
141 Rect newPos = position;
142 newPos.height = desiredHeight;
143
144 position = newPos;
145 }
146
147 maxSize = position.size;
148 minSize = maxSize;
149 }
150
151 if (Event.current.type == EventType.Repaint)
152 {
153 if (mIsCompleted)
154 {
155 mIsClosed = true;
156 Close();
157 }
158 }
159 }
160 finally
161 {
162 if (mIsClosed)
163 EditorGUIUtility.ExitGUI();
164 }
165 }
166
167 void OnDestroy()
168 {
169 if (!mIsConfigured)
170 return;
171
172 SaveSettings();
173
174 if (mParentWindow == null)
175 return;
176
177 mParentWindow.Focus();
178 }
179
180 protected virtual void SaveSettings() { }
181 protected abstract void OnModalGUI();
182 protected abstract string GetTitle();
183
184 protected void Paragraph(string text)
185 {
186 GUILayout.Label(text, UnityStyles.Paragraph);
187 GUILayout.Space(DEFAULT_PARAGRAPH_SPACING);
188 }
189
190 protected void TextBlockWithEndLink(
191 string url, string formattedExplanation, GUIStyle textblockStyle)
192 {
193 ExternalLink externalLink = new ExternalLink
194 {
195 Label = url,
196 Url = url
197 };
198
199 DrawTextBlockWithLink.ForExternalLink(
200 externalLink, formattedExplanation, textblockStyle);
201 }
202
203 protected static void Title(string text)
204 {
205 GUILayout.Label(text, UnityStyles.Dialog.Toggle);
206 }
207
208 protected static bool TitleToggle(string text, bool isOn)
209 {
210 return EditorGUILayout.ToggleLeft(text, isOn, UnityStyles.Dialog.Toggle);
211 }
212
213 protected static bool TitleToggle(string text, bool isOn, GUIStyle style)
214 {
215 return EditorGUILayout.ToggleLeft(text, isOn, style);
216 }
217
218 protected static string TextEntry(
219 string label,
220 string value,
221 float width,
222 float x)
223 {
224 return TextEntry(
225 label,
226 value,
227 null,
228 width,
229 x);
230 }
231
232 protected static string TextEntry(
233 string label, string value, string controlName, float width, float x)
234 {
235 using (new EditorGUILayout.HorizontalScope())
236 {
237 EntryLabel(label);
238
239 GUILayout.FlexibleSpace();
240
241 var rt = GUILayoutUtility.GetRect(
242 new GUIContent(value), UnityStyles.Dialog.EntryLabel);
243 rt.width = width;
244 rt.x = x;
245
246 if (!string.IsNullOrEmpty(controlName))
247 GUI.SetNextControlName(controlName);
248
249 return GUI.TextField(rt, value);
250 }
251 }
252
253 protected static string ComboBox(
254 string label,
255 string value,
256 List<string> dropDownOptions,
257 GenericMenu.MenuFunction2 optionSelected,
258 float width,
259 float x)
260 {
261 using (new EditorGUILayout.HorizontalScope())
262 {
263 EntryLabel(label);
264
265 GUILayout.FlexibleSpace();
266
267 var rt = GUILayoutUtility.GetRect(
268 new GUIContent(value), UnityStyles.Dialog.EntryLabel);
269 rt.width = width;
270 rt.x = x;
271
272 return DropDownTextField.DoDropDownTextField(
273 value,
274 label,
275 dropDownOptions,
276 optionSelected,
277 rt);
278 }
279 }
280
281 protected static string PasswordEntry(
282 string label, string value, float width, float x)
283 {
284 using (new EditorGUILayout.HorizontalScope())
285 {
286 EntryLabel(label);
287
288 GUILayout.FlexibleSpace();
289
290 var rt = GUILayoutUtility.GetRect(
291 new GUIContent(value), UnityStyles.Dialog.EntryLabel);
292 rt.width = width;
293 rt.x = x;
294
295 return GUI.PasswordField(rt, value, '*');
296 }
297 }
298
299 static void EntryLabel(string labelText)
300 {
301 GUIContent labelContent = new GUIContent(labelText);
302 GUIStyle labelStyle = UnityStyles.Dialog.EntryLabel;
303
304 Rect rt = GUILayoutUtility.GetRect(labelContent, labelStyle);
305
306 GUI.Label(rt, labelText, EditorStyles.label);
307 }
308
309 protected static bool ToggleEntry(
310 string label, bool value, float width, float x)
311 {
312 var rt = GUILayoutUtility.GetRect(
313 new GUIContent(label), UnityStyles.Dialog.EntryLabel);
314 rt.width = width;
315 rt.x = x;
316
317 return GUI.Toggle(rt, value, label);
318 }
319
320 protected static bool ToggleEntry(string label, bool value)
321 {
322 var rt = GUILayoutUtility.GetRect(
323 new GUIContent(label), UnityStyles.Dialog.EntryLabel);
324
325 return GUI.Toggle(rt, value, label);
326 }
327
328 protected static bool NormalButton(string text)
329 {
330 return GUILayout.Button(
331 text, UnityStyles.Dialog.NormalButton,
332 GUILayout.MinWidth(80),
333 GUILayout.Height(25));
334 }
335
336 protected static bool AcceptButton(string text, int extraWidth = 10)
337 {
338 GUI.color = new Color(0.098f, 0.502f, 0.965f, .8f);
339
340 int textWidth = (int)((GUIStyle)UnityStyles.Dialog.AcceptButtonText)
341 .CalcSize(new GUIContent(text)).x;
342
343 bool pressed = GUILayout.Button(
344 string.Empty, GetEditorSkin().button,
345 GUILayout.MinWidth(Math.Max(80, textWidth + extraWidth)),
346 GUILayout.Height(25));
347
348 GUI.color = Color.white;
349
350 Rect buttonRect = GUILayoutUtility.GetLastRect();
351 GUI.Label(buttonRect, text, UnityStyles.Dialog.AcceptButtonText);
352
353 return pressed;
354 }
355
356 void IPlasticDialogCloser.CloseDialog()
357 {
358 OkButtonAction();
359 }
360
361 void ProcessKeyActions()
362 {
363 Event e = Event.current;
364
365 string focusedControlName = GUI.GetNameOfFocusedControl();
366
367 if (mEnterKeyAction != null &&
368 Keyboard.IsReturnOrEnterKeyPressed(e) &&
369 !ControlConsumesKey(mControlsConsumingEnterKey, focusedControlName))
370 {
371 mEnterKeyAction.Invoke();
372 e.Use();
373 return;
374 }
375
376 if (mEscapeKeyAction != null &&
377 Keyboard.IsKeyPressed(e, KeyCode.Escape))
378 {
379 mEscapeKeyAction.Invoke();
380 e.Use();
381 return;
382 }
383 }
384
385 void CompleteModal(ResponseType answer)
386 {
387 mIsCompleted = true;
388 mAnswer = answer;
389
390 Repaint();
391 }
392
393 void InitializeVars(EditorWindow parentWindow)
394 {
395 mIsConfigured = true;
396 mIsCompleted = false;
397 mIsClosed = false;
398 mAnswer = ResponseType.Cancel;
399
400 titleContent = new GUIContent(GetTitle());
401
402 mFocusedOnce = false;
403
404 position = DefaultRect;
405 mParentWindow = parentWindow;
406 }
407
408 void MakeNonResizable()
409 {
410 maxSize = DefaultRect.size;
411 minSize = maxSize;
412 }
413
414 static bool ControlConsumesKey(
415 List<string> controlsConsumingKey,
416 string focusedControlName)
417 {
418 if (string.IsNullOrEmpty(focusedControlName))
419 return false;
420
421 foreach (string controlName in controlsConsumingKey)
422 {
423 if (focusedControlName.Equals(controlName))
424 return true;
425 }
426
427 return false;
428 }
429
430 static GUISkin GetEditorSkin()
431 {
432 return EditorGUIUtility.isProSkin ?
433 EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) :
434 EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
435 }
436
437 bool mFocusedOnce;
438 bool mIsConfigured;
439 bool mIsCompleted;
440 bool mIsClosed;
441 ResponseType mAnswer;
442
443 protected Action mEnterKeyAction = null;
444 protected Action mEscapeKeyAction = null;
445
446 EditorWindow mParentWindow;
447 SizeToContent mSizeToContent = SizeToContent.Automatic;
448
449 List<string> mControlsConsumingEnterKey = new List<string>();
450
451 const float DEFAULT_WIDTH = 500f;
452 const float DEFAULT_HEIGHT = 180f;
453 const float DEFAULT_PARAGRAPH_SPACING = 10f;
454 }
455}