using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using PlasticGui; namespace Unity.PlasticSCM.Editor.UI { internal abstract class PlasticDialog : EditorWindow, IPlasticDialogCloser { protected virtual Rect DefaultRect { get { int pixelWidth = Screen.currentResolution.width; float x = (pixelWidth - DEFAULT_WIDTH) / 2; return new Rect(x, 200, DEFAULT_WIDTH, DEFAULT_HEIGHT); } } protected virtual bool IsResizable { get; set; } internal void OkButtonAction() { CompleteModal(ResponseType.Ok); } internal void CancelButtonAction() { CompleteModal(ResponseType.Cancel); } internal void CloseButtonAction() { CompleteModal(ResponseType.None); } internal void ApplyButtonAction() { CompleteModal(ResponseType.Apply); } internal ResponseType RunModal(EditorWindow parentWindow) { InitializeVars(parentWindow); if (!IsResizable) MakeNonResizable(); if (UI.RunModal.IsAvailable()) { UI.RunModal.Dialog(this); return mAnswer; } EditorUtility.DisplayDialog( PlasticLocalization.GetString(PlasticLocalization.Name.PlasticSCM), PlasticLocalization.GetString(PlasticLocalization.Name.PluginModalInformation), PlasticLocalization.GetString(PlasticLocalization.Name.CloseButton)); return ResponseType.None; } protected void OnGUI() { try { // If the Dialog has been saved into the Unity editor layout and persisted between restarts, the methods // to configure the dialogs will be skipped. Simple fix here is to close it when this state is detected. // Fixes a NPE loop when the state mentioned above is occurring. if (!mIsConfigured) { mIsClosed = true; Close(); return; } if (Event.current.type == EventType.Layout) { EditorDispatcher.Update(); } if (!mFocusedOnce) { // Somehow the prevents the dialog from jumping when dragged // NOTE(rafa): We cannot do every frame because the modal kidnaps focus for all processes (in mac at least) Focus(); mFocusedOnce = true; } ProcessKeyActions(); if (mIsClosed) return; GUI.Box(new Rect(0, 0, position.width, position.height), GUIContent.none, EditorStyles.label); float margin = 25; float marginTop = 25; using (new EditorGUILayout.HorizontalScope(GUILayout.Height(position.height))) { GUILayout.Space(margin); using (new EditorGUILayout.VerticalScope(GUILayout.Height(position.height))) { GUILayout.Space(marginTop); OnModalGUI(); GUILayout.Space(margin); } GUILayout.Space(margin); } var lastRect = GUILayoutUtility.GetLastRect(); float desiredHeight = lastRect.yMax; Rect newPos = position; newPos.height = desiredHeight; if (position.height < desiredHeight) position = newPos; if (Event.current.type == EventType.Repaint) { if (mIsCompleted) { mIsClosed = true; Close(); } } } finally { if (mIsClosed) EditorGUIUtility.ExitGUI(); } } void OnDestroy() { if (!mIsConfigured) return; SaveSettings(); if (mParentWindow == null) return; mParentWindow.Focus(); } protected virtual void SaveSettings() { } protected abstract void OnModalGUI(); protected abstract string GetTitle(); protected void Paragraph(string text) { GUILayout.Label(text, UnityStyles.Paragraph); GUILayout.Space(DEFAULT_PARAGRAPH_SPACING); } protected void TextBlockWithEndLink( string url, string formattedExplanation, GUIStyle textblockStyle) { DrawTextBlockWithEndLink.For(url, formattedExplanation, textblockStyle); } protected static void Title(string text) { GUILayout.Label(text, UnityStyles.Dialog.Toggle); } protected static bool TitleToggle(string text, bool isOn) { return EditorGUILayout.ToggleLeft(text, isOn, UnityStyles.Dialog.Toggle); } protected static bool TitleToggle(string text, bool isOn, GUIStyle style) { return EditorGUILayout.ToggleLeft(text, isOn, style); } protected static string TextEntry( string label, string value, float width, float x) { return TextEntry( label, value, null, width, x); } protected static string TextEntry( string label, string value, string controlName, float width, float x) { using (new EditorGUILayout.HorizontalScope()) { EntryLabel(label); GUILayout.FlexibleSpace(); var rt = GUILayoutUtility.GetRect( new GUIContent(value), UnityStyles.Dialog.EntryLabel); rt.width = width; rt.x = x; if (!string.IsNullOrEmpty(controlName)) GUI.SetNextControlName(controlName); return GUI.TextField(rt, value); } } protected static string ComboBox( string label, string value, string controlName, List dropDownOptions, GenericMenu.MenuFunction2 optionSelected, float width, float x) { using (new EditorGUILayout.HorizontalScope()) { EntryLabel(label); GUILayout.FlexibleSpace(); var rt = GUILayoutUtility.GetRect( new GUIContent(value), UnityStyles.Dialog.EntryLabel); rt.width = width; rt.x = x; return DropDownTextField.DoDropDownTextField( value, label, dropDownOptions, optionSelected, rt); } } protected static string PasswordEntry( string label, string value, float width, float x) { using (new EditorGUILayout.HorizontalScope()) { EntryLabel(label); GUILayout.FlexibleSpace(); var rt = GUILayoutUtility.GetRect( new GUIContent(value), UnityStyles.Dialog.EntryLabel); rt.width = width; rt.x = x; return GUI.PasswordField(rt, value, '*'); } } protected static bool ToggleEntry( string label, bool value, float width, float x) { var rt = GUILayoutUtility.GetRect( new GUIContent(label), UnityStyles.Dialog.EntryLabel); rt.width = width; rt.x = x; return GUI.Toggle(rt, value, label); } protected static bool NormalButton(string text) { return GUILayout.Button( text, UnityStyles.Dialog.NormalButton, GUILayout.MinWidth(80), GUILayout.Height(25)); } void IPlasticDialogCloser.CloseDialog() { OkButtonAction(); } void ProcessKeyActions() { Event e = Event.current; if (mEnterKeyAction != null && Keyboard.IsReturnOrEnterKeyPressed(e)) { mEnterKeyAction.Invoke(); e.Use(); return; } if (mEscapeKeyAction != null && Keyboard.IsKeyPressed(e, KeyCode.Escape)) { mEscapeKeyAction.Invoke(); e.Use(); return; } } protected static bool AcceptButton(string text, int extraWidth = 10) { GUI.color = new Color(0.098f, 0.502f, 0.965f, .8f); int textWidth = (int)((GUIStyle)UnityStyles.Dialog.AcceptButtonText) .CalcSize(new GUIContent(text)).x; bool pressed = GUILayout.Button( string.Empty, GetEditorSkin().button, GUILayout.MinWidth(Math.Max(80, textWidth + extraWidth)), GUILayout.Height(25)); GUI.color = Color.white; Rect buttonRect = GUILayoutUtility.GetLastRect(); GUI.Label(buttonRect, text, UnityStyles.Dialog.AcceptButtonText); return pressed; } void CompleteModal(ResponseType answer) { mIsCompleted = true; mAnswer = answer; } void InitializeVars(EditorWindow parentWindow) { mIsConfigured = true; mIsCompleted = false; mIsClosed = false; mAnswer = ResponseType.Cancel; titleContent = new GUIContent(GetTitle()); mFocusedOnce = false; position = DefaultRect; mParentWindow = parentWindow; } void MakeNonResizable() { maxSize = DefaultRect.size; minSize = maxSize; } static void EntryLabel(string labelText) { GUIContent labelContent = new GUIContent(labelText); GUIStyle labelStyle = UnityStyles.Dialog.EntryLabel; Rect rt = GUILayoutUtility.GetRect(labelContent, labelStyle); GUI.Label(rt, labelText, EditorStyles.label); } static GUISkin GetEditorSkin() { return EditorGUIUtility.isProSkin ? EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene) : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector); } bool mIsConfigured; bool mIsCompleted; bool mIsClosed; ResponseType mAnswer; protected Action mEnterKeyAction = null; protected Action mEscapeKeyAction = null; bool mFocusedOnce; Dictionary mWrappedTextLines = new Dictionary(); EditorWindow mParentWindow; protected const float DEFAULT_LINE_SPACING = -5f; const float DEFAULT_WIDTH = 500f; const float DEFAULT_HEIGHT = 180f; const float DEFAULT_PARAGRAPH_SPACING = 10f; static class BuildLine { internal static string ForIndex(string text, int index) { if (index < 0 || index > text.Length) return string.Empty; return text.Substring(index).Trim(); } internal static string ForIndexAndLenght(string text, int index, int lenght) { if (index < 0 || index > text.Length) return string.Empty; return text.Substring(index, lenght); } } } }