using SRF; namespace SRDebugger.Editor { using System; using System.IO; using UnityEditor; using UnityEngine; static class SRInternalEditorUtil { // Path to this file from the root path private const string TestPath = "SRDebugger/README.txt"; private static GUIStyle _bgStyle; private static Texture2D _logoTexture; private static Texture2D _welcomeLogoTexture; private static Texture2D _bgTexture; private static GUIStyle _middleAlign; /// /// Finds the path to the SRDebugger installation folder (e.g. Assets/StompyRobot/SRDebugger) /// /// public static string GetRootPath() { // Find assets that match this file name var potentialAssets = AssetDatabase.FindAssets("README"); foreach (var potentialAsset in potentialAssets) { var path = AssetDatabase.GUIDToAssetPath(potentialAsset); if (path.Contains(TestPath)) { var rootPath = Path.GetDirectoryName(path); return rootPath; } } throw new Exception("Unable to find SRDebugger root path. Please ensure the README file in StompyRobot/SRDebugger still exists."); } /// /// Finds the path to an SRDebugger asset relative to the installation root. /// /// /// public static string GetAssetPath(string relativeToRoot) { if (!relativeToRoot.StartsWith("/")) { relativeToRoot = "/" + relativeToRoot; } var p = GetRootPath() + relativeToRoot; return p; } public static T LoadResource(string path) where T : UnityEngine.Object { var p = GetAssetPath(path); //Debug.Log("[SRDebugger] Loading " + p); var asset = AssetDatabase.LoadAssetAtPath(p, typeof(T)); return asset as T; } #if !DISABLE_SRDEBUGGER public enum SettingsResult { Cache, Loaded, Waiting, Error } public static class EditorSettings { internal const string SettingsFilePath = "/usr/Resources/SRDebugger/Settings.asset"; internal const string DisabledSettingsFilePath = "/usr" + SRDebugEditor.DisabledDirectoryPostfix + "/Resources/SRDebugger/Settings.asset"; private static Settings _instance; public static SettingsResult TryGetOrCreate(out Settings instance, out string message) { if (_instance != null) { instance = _instance; message = string.Empty; return SettingsResult.Cache; } try { SettingsResult result = InternalTryGetOrCreateSettings(out _instance, out message); instance = _instance; return result; } catch (Exception e) { instance = null; message = e.ToString(); return SettingsResult.Error; } } public static void ClearCache() { Settings.ClearCache(); // Just in case runtime settings are loaded. if (_instance == null) { return; } var instance = _instance; _instance = null; Resources.UnloadAsset(instance); instance = null; GC.Collect(); EditorUtility.UnloadUnusedAssetsImmediate(); Resources.UnloadUnusedAssets(); } private static SettingsResult InternalTryGetOrCreateSettings(out Settings instance, out string message) { instance = null; message = null; if (EditorApplication.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode) { message = "Settings can only be modified while in edit-mode. Exit play mode to make changes to SRDebugger settings."; return SettingsResult.Waiting; } // If compiling, wait for that to finish. We might be disabling/enabling SRDebugger and don't want to recreate the settings while that is in progress. if (EditorApplication.isCompiling || EditorApplication.isUpdating) { message = "Waiting for Unity to finish compiling/updating..."; return SettingsResult.Waiting; } // Check if there is a 'disabled' settings file, we don't want to create a new settings file if a disabled one exists. string disabledSettingsFile = SRInternalEditorUtil.GetAssetPath(DisabledSettingsFilePath); if (File.Exists(disabledSettingsFile)) { message = "A settings file already exists but is disabled. Please ensure SRDebugger is correctly enabled or disabled."; return SettingsResult.Error; } // Get resources folder path var settingsAssetPath = SRInternalEditorUtil.GetAssetPath(SettingsFilePath); // Load existing asset. if (File.Exists(settingsAssetPath)) { instance = AssetDatabase.LoadAssetAtPath(settingsAssetPath); if (instance == null) { message = "Error loading settings asset."; return SettingsResult.Error; } return SettingsResult.Loaded; } Debug.Log("[SRDebugger] Creating settings asset at {0}".Fmt(settingsAssetPath)); instance = ScriptableObject.CreateInstance(); string containingDirectory = Path.GetDirectoryName(settingsAssetPath); if (containingDirectory == null) { message = "Error finding target settings directory."; return SettingsResult.Error; } // Create directory if it doesn't exist Directory.CreateDirectory(containingDirectory); // Save instance if in editor AssetDatabase.CreateAsset(instance, settingsAssetPath); return SettingsResult.Loaded; } } #endif public static Texture2D GetLogo() { if (_logoTexture != null) { return _logoTexture; } return _logoTexture = LoadResource("Editor/Logo_" + (EditorGUIUtility.isProSkin ? "DarkBG" : "LightBG") + ".png"); } public static Texture2D GetWelcomeLogo() { if (_welcomeLogoTexture != null) { return _welcomeLogoTexture; } return _welcomeLogoTexture = LoadResource("Editor/WelcomeLogo_" + (EditorGUIUtility.isProSkin ? "DarkBG" : "LightBG") + ".png"); } public static Texture2D GetBackground() { if (_bgTexture != null) { return _bgTexture; } return _bgTexture = LoadResource("Editor/BG_" + (EditorGUIUtility.isProSkin ? "Dark" : "Light") + ".png"); } public static void DrawLogo(Texture2D logo) { if (logo == null) { Debug.LogError("Error loading SRDebugger logo"); return; } #if !DISABLE_SRDEBUGGER var rect = #endif EditorGUILayout.BeginVertical(); GUILayout.Space(15); EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.DrawTexture( GUILayoutUtility.GetRect(logo.width, logo.width, logo.height, logo.height, GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(false)), logo); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); GUILayout.Space(15); EditorGUILayout.EndVertical(); #if !DISABLE_SRDEBUGGER var size = EditorStyles.miniLabel.CalcSize(new GUIContent(SRDebug.Version)); GUI.Label(new Rect(rect.xMax - size.x, rect.yMax - size.y, size.x, size.y), SRDebug.Version, EditorStyles.miniLabel); #endif } public static bool DrawInspectorFoldout(bool isVisible, string content) { isVisible = EditorGUILayout.Foldout(isVisible, content, Styles.InspectorHeaderFoldoutStyle); EditorGUILayout.Separator(); return isVisible; } public static void BeginDrawBackground() { if (_bgStyle == null) { _bgStyle = new GUIStyle(); _bgStyle.margin = _bgStyle.padding = new RectOffset(0, 0, 0, 0); } var rect = EditorGUILayout.BeginVertical(_bgStyle); DrawTextureTiled(rect, GetBackground()); } public static void EndDrawBackground() { EditorGUILayout.EndVertical(); } public static void DrawTextureTiled(Rect rect, Texture2D tex) { GUI.BeginGroup(rect); var tilesX = Mathf.Max(1, Mathf.CeilToInt(rect.width / tex.width)); var tilesY = Mathf.Max(1, Mathf.CeilToInt(rect.height / tex.height)); for (var x = 0; x < tilesX; x++) { for (var y = 0; y < tilesY; y++) { var pos = new Rect(x * tex.width, y * tex.height, tex.width, tex.height); pos.x += rect.x; pos.y += rect.y; GUI.DrawTexture(pos, tex, ScaleMode.ScaleAndCrop); } } GUI.EndGroup(); } public static bool ClickableLabel(string text, GUIStyle style) { var rect = EditorGUILayout.BeginVertical(Styles.NoPaddingNoMargin); GUILayout.Label(text, style); EditorGUILayout.EndVertical(); if (Event.current.type == EventType.MouseUp && rect.Contains(Event.current.mousePosition)) { return true; } return false; } #if !DISABLE_SRDEBUGGER public static void DrawLayoutPreview(Rect rect, Settings settings) { const int profilerWidth = 120; const int profilerHeight = 70; const int optionsWidth = 150; const int optionsHeight = 36; if (_middleAlign == null) { _middleAlign = new GUIStyle(EditorStyles.helpBox); _middleAlign.alignment = TextAnchor.MiddleCenter; } var iconPath = "Editor/Icons/" + (EditorGUIUtility.isProSkin ? "Light" : "Dark"); const float consoleHeight = 90; GUI.Box(rect, "", EditorStyles.helpBox); var consoleAlignment = settings.ConsoleAlignment; var consoleRect = new Rect(rect.x, consoleAlignment == ConsoleAlignment.Top ? rect.y : rect.yMax - consoleHeight, rect.width, consoleHeight); GUI.Box(consoleRect, new GUIContent(LoadResource(iconPath + "/console-25.png"), "Console"), _middleAlign); var workRect = rect; if (consoleAlignment == ConsoleAlignment.Top) { workRect.yMin += consoleHeight; } else { workRect.yMax -= consoleHeight; } var opAlignment = settings.OptionsAlignment; var proAlignment = settings.ProfilerAlignment; GUI.Box(GetAlignedRect(profilerWidth, profilerHeight, proAlignment, workRect), new GUIContent(LoadResource(iconPath + "/profiler-25.png"), "Profiler"), _middleAlign); var optionsRect = workRect; if ((opAlignment == PinAlignment.TopCenter && proAlignment == PinAlignment.TopLeft) || (opAlignment == PinAlignment.BottomCenter && proAlignment == PinAlignment.BottomLeft)) { optionsRect.x += profilerWidth; optionsRect.width -= profilerWidth; } else if ((opAlignment == PinAlignment.TopCenter && proAlignment == PinAlignment.TopRight) || opAlignment == PinAlignment.BottomCenter && proAlignment == PinAlignment.BottomRight) { optionsRect.width -= profilerWidth; } GUI.Box(GetAlignedRect(optionsWidth, optionsHeight, opAlignment, optionsRect), new GUIContent(LoadResource(iconPath + "/options-25.png"), "Pinned Options"), _middleAlign); if (settings.EnableTrigger != Settings.TriggerEnableModes.Off) { GUI.Box(GetAlignedRect(25, 25, settings.TriggerPosition, rect), new GUIContent("", "Entry Trigger"), _middleAlign); } } private static Rect GetAlignedRect(int width, int height, PinAlignment alignment, Rect workRect) { var rect = new Rect(0, 0, width, height); if (alignment == PinAlignment.BottomLeft || alignment == PinAlignment.BottomRight || alignment == PinAlignment.BottomCenter) { rect.position = new Vector2(0, workRect.height - rect.height); } else if (alignment == PinAlignment.CenterLeft || alignment == PinAlignment.CenterRight) { rect.position = new Vector2(0, workRect.height / 2 - rect.height / 2); } if (alignment == PinAlignment.TopRight || alignment == PinAlignment.BottomRight || alignment == PinAlignment.CenterRight) { rect.position += new Vector2(workRect.width - rect.width, 0); } else if (alignment == PinAlignment.TopCenter || alignment == PinAlignment.BottomCenter) { rect.position += new Vector2(workRect.width / 2 - rect.width / 2, 0); } rect.position += workRect.position; return rect; } #endif public static void RenderGif(Rect pos, Texture2D map, int frameNo, int frameWidth, int frameHeight, int perLine, int paddingX = 0, int paddingY = 0) { var x = frameNo % perLine; var y = Mathf.FloorToInt((float)frameNo / perLine); var xCoord = x * (frameWidth + paddingX); var yCoord = (y + 1) * (frameHeight + paddingY); var texCoords = new Rect( xCoord / (float)map.width, (map.height - yCoord) / (float)map.height, (frameWidth) / (float)map.width, (frameHeight) / (float)map.height); GUI.DrawTextureWithTexCoords(pos, map, texCoords); //Debug.Log(texCoords); //Debug.Log("x: " + x + ", y: " + y); } public static void DrawFooterLayout(float width) { EditorGUILayout.BeginHorizontal(); var margin = (EditorStyles.miniButton.padding.left) / 2f; width = width - margin * 2; if (GUILayout.Button("Web Site", GUILayout.Width(width / 2f - margin))) { Application.OpenURL(SRDebugEditorStrings.Current.SettingsWebSiteUrl); } if (GUILayout.Button("Asset Store Page", GUILayout.Width(width / 2f - margin))) { Application.OpenURL(SRDebugEditorStrings.Current.SettingsAssetStoreUrl); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("Documentation", GUILayout.Width(width / 2f - margin))) { Application.OpenURL(SRDebugEditorStrings.Current.SettingsDocumentationUrl); } if (GUILayout.Button("Support", GUILayout.Width(width / 2f - margin))) { Application.OpenURL( SRDebugEditorStrings.Current.SettingsSupportUrl); } EditorGUILayout.EndHorizontal(); } public static class Styles { private static GUIStyle _inspectorHeaderStyle; private static GUIStyle _inspectorHeaderFoldoutStyle; private static GUIStyle _settingsHeaderBoxStyle; private static GUIStyle _headerLabel; private static GUIStyle _paragraphLabel; private static GUIStyle _paragraphLabelItalic; private static GUIStyle _radioButtonDescription; private static GUIStyle _radioButton; private static GUIStyle _leftToggleButton; private static GUIStyle _noPaddingNoMargin; private static GUIStyle _richTextLabel; private static GUIStyle _listBulletPoint; public static string LinkColour { get { if (EditorGUIUtility.isProSkin) { return "#7C8CB9"; } return "#0032E6"; } } public static GUIStyle InspectorHeaderStyle { get { if (_inspectorHeaderStyle == null) { _inspectorHeaderStyle = new GUIStyle(EditorStyles.boldLabel); _inspectorHeaderStyle.fontSize = 12; } return _inspectorHeaderStyle; } } public static GUIStyle InspectorHeaderFoldoutStyle { get { if (_inspectorHeaderFoldoutStyle == null) { _inspectorHeaderFoldoutStyle = new GUIStyle(EditorStyles.foldout); _inspectorHeaderFoldoutStyle.fontSize = 12; _inspectorHeaderFoldoutStyle.fontStyle = FontStyle.Bold; } return _inspectorHeaderFoldoutStyle; } } public static GUIStyle SettingsHeaderBoxStyle { get { if (_settingsHeaderBoxStyle == null) { _settingsHeaderBoxStyle = new GUIStyle("OL Title"); _settingsHeaderBoxStyle.padding = new RectOffset(0, 0, 0, 0); _settingsHeaderBoxStyle.margin = new RectOffset(0, 0, 0, 0); _settingsHeaderBoxStyle.clipping = TextClipping.Clip; _settingsHeaderBoxStyle.overflow = new RectOffset(0, 0, 0, 0); //_settingsHeaderBoxStyle.border = new RectOffset(1, 1, 1, 1); _settingsHeaderBoxStyle.fixedHeight = 0.5f; } return _settingsHeaderBoxStyle; } } public static GUIStyle HeaderLabel { get { if (_headerLabel == null) { _headerLabel = new GUIStyle(EditorStyles.largeLabel); _headerLabel.fontSize = 18; _headerLabel.fontStyle = FontStyle.Normal; _headerLabel.margin = new RectOffset(5, 5, 5, 5); } return _headerLabel; } } public static GUIStyle ParagraphLabel { get { if (_paragraphLabel == null) { _paragraphLabel = new GUIStyle(EditorStyles.label); _paragraphLabel.margin = new RectOffset(5, 5, 5, 5); _paragraphLabel.wordWrap = true; _paragraphLabel.richText = true; } return _paragraphLabel; } } public static GUIStyle ParagraphLabelItalic { get { if (_paragraphLabelItalic == null) { _paragraphLabelItalic = new GUIStyle(EditorStyles.label); _paragraphLabelItalic.margin = new RectOffset(5, 5, 5, 5); _paragraphLabelItalic.wordWrap = true; _paragraphLabelItalic.richText = true; _paragraphLabelItalic.fontStyle = FontStyle.Italic; } return _paragraphLabelItalic; } } public static GUIStyle LeftToggleButton { get { if (_leftToggleButton == null) { _leftToggleButton = new GUIStyle(EditorStyles.label); _leftToggleButton.contentOffset = new Vector2(_leftToggleButton.contentOffset.x + 5, _leftToggleButton.contentOffset.y); } return _leftToggleButton; } } public static GUIStyle RadioButton { get { if (_radioButton == null) { _radioButton = new GUIStyle(EditorStyles.radioButton); _radioButton.contentOffset = new Vector2(_radioButton.contentOffset.x + 5, _radioButton.contentOffset.y); } return _radioButton; } } public static GUIStyle RadioButtonDescription { get { if (_radioButtonDescription == null) { _radioButtonDescription = new GUIStyle(ParagraphLabel); _radioButtonDescription.padding.left = (int)RadioButton.contentOffset.x + RadioButton.padding.left; } return _radioButtonDescription; } } public static GUIStyle NoPaddingNoMargin { get { if (_noPaddingNoMargin == null) { _noPaddingNoMargin = new GUIStyle(); _noPaddingNoMargin.margin = new RectOffset(0, 0, 0, 0); _noPaddingNoMargin.padding = new RectOffset(0, 0, 0, 0); } return _noPaddingNoMargin; } } public static GUIStyle RichTextLabel { get { if (_richTextLabel == null) { _richTextLabel = new GUIStyle(EditorStyles.label); _richTextLabel.richText = true; _richTextLabel.margin = new RectOffset(2, 2, 0, 0); } return _richTextLabel; } } public static GUIStyle ListBulletPoint { get { if (_listBulletPoint == null) { _listBulletPoint = new GUIStyle(EditorStyles.miniBoldLabel); _listBulletPoint.wordWrap = true; _listBulletPoint.margin = new RectOffset(6, 2, 0, 0); } return _listBulletPoint; } } } } }