258 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			258 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | namespace Fusion.Editor { | ||
|  | #if FUSION_WEAVER && UNITY_EDITOR | ||
|  |   using System; | ||
|  |   using System.Collections.Generic; | ||
|  |   using Photon.Realtime; | ||
|  |   using UnityEditor; | ||
|  |   using UnityEngine; | ||
|  |   using EditorUtility = UnityEditor.EditorUtility; | ||
|  | 
 | ||
|  |   public partial class FusionHubWindow : EditorWindow { | ||
|  |     private const int NavWidth = 256 + 2; | ||
|  | 
 | ||
|  |     private static bool? _ready; // true after InitContent(), reset onDestroy, onEnable, etc. | ||
|  | 
 | ||
|  |     private static Vector2 _windowSize; | ||
|  |     private static Vector2 _windowPosition = new Vector2(100, 100); | ||
|  | 
 | ||
|  |     private int _currentSection; | ||
|  | 
 | ||
|  |     [MenuItem("Tools/Fusion/Fusion Hub &f", false, 0)] | ||
|  |     public static void Open() { | ||
|  |       if (Application.isPlaying) { | ||
|  |         return; | ||
|  |       } | ||
|  | 
 | ||
|  |       var window = GetWindow<FusionHubWindow>(true, Constants.WindowTitle, true); | ||
|  |       window.position = new Rect(_windowPosition, _windowSize); | ||
|  |       window.Show(); | ||
|  |     } | ||
|  | 
 | ||
|  |     private static void ReOpen() { | ||
|  |       if (_ready.HasValue && _ready.Value == false) { | ||
|  |         Open(); | ||
|  |       } | ||
|  | 
 | ||
|  |       EditorApplication.update -= ReOpen; | ||
|  |     } | ||
|  | 
 | ||
|  |     private void OnEnable() { | ||
|  |       _ready = false; | ||
|  |       _windowSize = new Vector2(800, 540); | ||
|  | 
 | ||
|  |       this.minSize = _windowSize; | ||
|  | 
 | ||
|  |       // Pre-load Release History | ||
|  |       this.PrepareReleaseHistoryText(); | ||
|  |       wantsMouseMove = true; | ||
|  |     } | ||
|  | 
 | ||
|  |     private void OnDestroy() { | ||
|  |       _ready = false; | ||
|  |     } | ||
|  | 
 | ||
|  |     private void OnGUI() { | ||
|  |       // skip until ready | ||
|  |       if (InitContent() == false) { return; } | ||
|  | 
 | ||
|  |       GUI.skin = FusionHubSkin; | ||
|  | 
 | ||
|  |       _windowPosition = this.position.position; | ||
|  | 
 | ||
|  |       // full window wrapper | ||
|  |       using (new EditorGUILayout.HorizontalScope(GUI.skin.window)) { | ||
|  |         // Left Nav menu | ||
|  |         using (new EditorGUILayout.VerticalScope(GUILayout.MaxWidth(NavWidth), GUILayout.MinWidth(NavWidth))) { | ||
|  |           DrawHeader(); | ||
|  |           DrawLeftNavMenu(); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Right Main Content | ||
|  |         using (new EditorGUILayout.VerticalScope()) { | ||
|  |           DrawContent(); | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       DrawFooter(); | ||
|  | 
 | ||
|  |       // Force repaints while mouse is over the window, to keep Hover graphics working (Unity quirk) | ||
|  |       var timeSinceStartup = Time.realtimeSinceStartupAsDouble; | ||
|  |       if (Event.current.type == EventType.MouseMove && timeSinceStartup > _nextForceRepaint) { | ||
|  |         // Cap the repaint rate a bit since we are forcing repaint on mouse move | ||
|  |         _nextForceRepaint = timeSinceStartup + .05f; | ||
|  |         Repaint(); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     private double _nextForceRepaint; | ||
|  |     private Vector2 _scrollRect; | ||
|  | 
 | ||
|  |     private void DrawContent() { | ||
|  |       var section = _sections[_currentSection]; | ||
|  |       GUILayout.Label(section.Description, headerTextStyle); | ||
|  | 
 | ||
|  |       using (new EditorGUILayout.VerticalScope(FusionHubSkin.box)) { | ||
|  |         using (var scrollScope = new EditorGUILayout.ScrollViewScope(_scrollRect)) { | ||
|  |           _scrollRect = scrollScope.scrollPosition; | ||
|  |           section.DrawMethod.Invoke(); | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawWelcomeSection() { | ||
|  |       // Top Welcome content box | ||
|  |       GUILayout.Label(Constants.WelcomeText); | ||
|  |       GUILayout.Space(16); | ||
|  | 
 | ||
|  |       DrawSetupAppIdBox(); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawSetupSection() { | ||
|  |       DrawSetupAppIdBox(); | ||
|  |       DrawButtonAction(Icon.FusionIcon, "Fusion Network Project Settings", "Network settings specific to Fusion.", | ||
|  |         callback: () => NetworkProjectConfigUtilities.PingGlobalConfigAsset(true)); | ||
|  |       DrawButtonAction(Icon.PhotonCloud, "Photon App Settings", "Network settings specific to the Photon transport.", | ||
|  |         callback: () => { | ||
|  |           EditorGUIUtility.PingObject(Photon.Realtime.PhotonAppSettings.Global); | ||
|  |           Selection.activeObject = Photon.Realtime.PhotonAppSettings.Global; | ||
|  |         }); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawDocumentationSection() { | ||
|  |       DrawButtonAction(Icon.Documentation, "Fusion Introduction", "The Fusion Introduction web page.", callback: OpenURL(Constants.UrlFusionIntro)); | ||
|  |       DrawButtonAction(Icon.Documentation, "SDK and Release Notes", "Link to the latest Fusion version SDK.", callback: OpenURL(Constants.UrlFusionSDK)); | ||
|  |       DrawButtonAction(Icon.Documentation, "API Reference", "The API library reference documentation.", callback: OpenURL(Constants.UrlFusionDocApi)); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawSamplesSection() { | ||
|  |       GUILayout.Label("Tutorials", headerLabelStyle); | ||
|  |       DrawButtonAction(Icon.Samples, "Fusion 100 Tutorial", "Fusion Fundamentals Tutorial", callback: OpenURL(Constants.UrlFusion100)); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawFusionReleaseSection() { | ||
|  |       GUILayout.Label(fusionReleaseHistory, releaseNotesStyle); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawReleaseHistoryItem(string label, List<string> items) { | ||
|  |       if (items != null && items.Count > 0) { | ||
|  |         GUILayout.BeginVertical(); | ||
|  |         { | ||
|  |           GUILayout.Space(5); | ||
|  | 
 | ||
|  |           foreach (string text in items) { | ||
|  |             GUILayout.Label(string.Format("- {0}.", text), textLabelStyle); | ||
|  |           } | ||
|  |         } | ||
|  |         GUILayout.EndVertical(); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawSupportSection() { | ||
|  |       GUILayout.BeginVertical(); | ||
|  |       GUILayout.Space(5); | ||
|  |       GUILayout.Label(Constants.Support, textLabelStyle); | ||
|  |       GUILayout.EndVertical(); | ||
|  | 
 | ||
|  |       GUILayout.Space(15); | ||
|  | 
 | ||
|  |       DrawButtonAction(Icon.Community, Constants.DiscordHeader, Constants.DiscordText, callback: OpenURL(Constants.UrlDashboardProfile)); | ||
|  |       DrawButtonAction(Icon.Documentation, Constants.DocumentationHeader, Constants.DocumentationText, callback: OpenURL(Constants.UrlFusionDocsOnline)); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawSetupAppIdBox() { | ||
|  |       var realtimeSettings = Photon.Realtime.PhotonAppSettings.Global; | ||
|  |       var realtimeAppId = realtimeSettings.AppSettings.AppIdFusion; | ||
|  |       // Setting up AppId content box. | ||
|  |       using (new EditorGUILayout.VerticalScope(FusionHubSkin.GetStyle("SteelBox") /*contentBoxStyle*/)) { | ||
|  |         GUILayout.Label(Constants.RealtimeAppidSetupInstructions); | ||
|  | 
 | ||
|  |         DrawButtonAction(Icon.PhotonCloud, "Open the Photon Dashboard", callback: OpenURL(Constants.UrlDashboard)); | ||
|  |         EditorGUILayout.Space(4); | ||
|  | 
 | ||
|  |         using (new EditorGUILayout.HorizontalScope(FusionHubSkin.GetStyle("SteelBox"))) { | ||
|  |           EditorGUI.BeginChangeCheck(); | ||
|  |           GUILayout.Label("Fusion App Id:", GUILayout.Width(120)); | ||
|  |           var icon = IsAppIdValid() ? CorrectIcon : EditorGUIUtility.FindTexture("console.erroricon.sml"); | ||
|  |           GUILayout.Label(icon, GUILayout.Width(24), GUILayout.Height(24)); | ||
|  |           var editedAppId = EditorGUILayout.TextField("", realtimeAppId, FusionHubSkin.textField, GUILayout.Height(24)); | ||
|  |            | ||
|  |           // Check for changes and validate the AppId | ||
|  |           if (EditorGUI.EndChangeCheck() && Guid.TryParse(editedAppId, out _)) { | ||
|  |             // Update the AppId | ||
|  |             realtimeSettings.AppSettings.AppIdFusion = editedAppId; | ||
|  |             EditorUtility.SetDirty(realtimeSettings); | ||
|  |             AssetDatabase.SaveAssets(); | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawLeftNavMenu() { | ||
|  |       for (var i = 0; i < _sections.Length; ++i) { | ||
|  |         var section = _sections[i]; | ||
|  |         if (DrawNavButton(section, _currentSection == i)) { | ||
|  |           _currentSection = i; | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawHeader() { | ||
|  |       GUILayout.Label(GetIcon(Icon.ProductLogo), _navbarHeaderGraphicStyle); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawFooter() { | ||
|  |       GUILayout.BeginHorizontal(FusionHubSkin.window); | ||
|  |       { | ||
|  |         GUILayout.Label("\u00A9 2022, Exit Games GmbH. All rights reserved."); | ||
|  |       } | ||
|  |       GUILayout.EndHorizontal(); | ||
|  |     } | ||
|  | 
 | ||
|  |     private bool DrawNavButton(Section section, bool currentSection) { | ||
|  |       var content = new GUIContent() { text = "  " + section.Title, image = GetIcon(section.Icon), }; | ||
|  | 
 | ||
|  |       var renderStyle = currentSection ? buttonActiveStyle : GUI.skin.button; | ||
|  |       return GUILayout.Button(content, renderStyle); | ||
|  |     } | ||
|  | 
 | ||
|  |     private void DrawButtonAction(Icon icon, string header, string description = null, bool? active = null, Action callback = null, int? width = null) { | ||
|  |       DrawButtonAction(GetIcon(icon), header, description, active, callback, width); | ||
|  |     } | ||
|  | 
 | ||
|  |     private static void DrawButtonAction(Texture2D icon, string header, string description = null, bool? active = null, Action callback = null, int? width = null) { | ||
|  |       var padding = GUI.skin.button.padding.top + GUI.skin.button.padding.bottom; | ||
|  |       var height = icon.height + padding; | ||
|  | 
 | ||
|  |       var renderStyle = active.HasValue && active.Value == true ? buttonActiveStyle : GUI.skin.button; | ||
|  |       // Draw text separately (not part of button guiconent) to have control over the space between the icon and the text. | ||
|  |       var rect = EditorGUILayout.GetControlRect(false, height, width.HasValue ? GUILayout.Width(width.Value) : GUILayout.ExpandWidth(true)); | ||
|  |       var clicked = GUI.Button(rect, icon, renderStyle); | ||
|  |       GUI.Label(new Rect(rect) { xMin = rect.xMin + icon.width + 20 }, | ||
|  |         description == null ? "<b>" + header + "</b>" : string.Format("<b>{0}</b>\n{1}", header, "<color=#aaaaaa>" + description + "</color>")); | ||
|  |       if (clicked && callback != null) { | ||
|  |         callback.Invoke(); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     /// <summary> | ||
|  |     /// Unity handling for post asset processing callback. Checks existence of settings assets every time assets change. | ||
|  |     /// </summary> | ||
|  |     private class FusionHubPostProcessor : AssetPostprocessor { | ||
|  |       private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { | ||
|  |         EnsureAssetExists(); | ||
|  |       } | ||
|  | 
 | ||
|  |       /// <summary> | ||
|  |       /// Attempts enforce existence of singleton. If Editor is not ready, this method will be deferred one editor update and try again until it succeeds. | ||
|  |       /// </summary> | ||
|  |       [UnityEditor.Callbacks.DidReloadScripts] | ||
|  |       private static void EnsureAssetExists() { | ||
|  |         if (!PhotonAppSettings.TryGetGlobal(out var global) || global.AppSettings.AppIdFusion == null) { | ||
|  |           FusionEditorLog.Trace($"Opening HUB due to missing settings."); | ||
|  |           EditorApplication.delayCall += () => { Open(); }; | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | #endif | ||
|  | } |