105 changed files with 2002886 additions and 610 deletions
			
			
		
								
									
										File diff suppressed because it is too large
									
								
							
						
					| @ -0,0 +1,9 @@ | |||
| fileFormatVersion: 2 | |||
| guid: e58df2ef30bd5d94c9987dfbf4bd8838 | |||
| folderAsset: yes | |||
| timeCreated: 1497142680 | |||
| licenseType: Store | |||
| DefaultImporter: | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,9 @@ | |||
| fileFormatVersion: 2 | |||
| guid: e88d8747d7da76847b620be9aa548d88 | |||
| folderAsset: yes | |||
| timeCreated: 1471207248 | |||
| licenseType: Store | |||
| DefaultImporter: | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,52 @@ | |||
| using System; | |||
| using System.Diagnostics; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Utility class for running async tasks within the main thread.</summary>
 | |||
|     public static class After { | |||
| 
 | |||
|         /// <summary>Wait for a condition to become true, then executes the callback.</summary>
 | |||
|         /// <param name="condition">Function that will be called every frame that returns whether to invoke the callback or not.</param>
 | |||
|         /// <param name="callback">The callback to be called when the condition becomes true.</param>
 | |||
|         /// <param name="timeoutMs">Maximum time to wait in milliseconds before cancelling the callback.</param>
 | |||
|         public static void Condition(Func<bool> condition, Action callback, double timeoutMs = 0d) { | |||
|             var update = new EditorApplication.CallbackFunction(() => { }); | |||
|             var timeoutsAt = EditorApplication.timeSinceStartup + (timeoutMs / 1000d); | |||
|             var stack = new StackFrame(1, true); | |||
| 
 | |||
|             update = () => { | |||
|                 if (timeoutMs > 0d && EditorApplication.timeSinceStartup >= timeoutsAt) { | |||
|                     EditorApplication.update -= update; | |||
|                     Logger.Error("Condition timedout at {0}:{1}", stack.GetFileName(), stack.GetFileLineNumber()); | |||
|                     return; | |||
|                 } | |||
| 
 | |||
|                 if (condition()) { | |||
|                     EditorApplication.update -= update; | |||
|                     callback(); | |||
|                 } | |||
|             }; | |||
| 
 | |||
|             EditorApplication.update += update; | |||
|         } | |||
| 
 | |||
|         /// <summary>Wait for the given amount of editor frames, then executes the callback.</summary>
 | |||
|         /// <param name="frames">The number of frames to wait for.</param>
 | |||
|         /// <param name="callback">The callback to be called after the specified frames.</param>
 | |||
|         public static void Frames(int frames, Action callback) { | |||
|             var f = 0; | |||
|             Condition(() => f++ >= frames, callback); | |||
|         } | |||
| 
 | |||
|         /// <summary>Wait for the given time, then executes the callback.</summary>
 | |||
|         /// <param name="milliseconds">How long to wait until calling the callback, in milliseconds.</param>
 | |||
|         /// <param name="callback">The callback to be called after the specified time.</param>
 | |||
|         public static void Milliseconds(double milliseconds, Action callback) { | |||
|             var end = EditorApplication.timeSinceStartup + (milliseconds / 1000f); | |||
|             Condition(() => EditorApplication.timeSinceStartup >= end, callback); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 0e5019b6f2b222d42aa01853d0e67227 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,90 @@ | |||
| using System; | |||
| using System.Diagnostics; | |||
| using System.Text; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     public static class Cmd { | |||
| 
 | |||
|         public static string Run(string command, params object[] formatArgs) { | |||
|             return Run(command, false, formatArgs); | |||
|         } | |||
| 
 | |||
|         public static string Run(string command, bool asAdmin, params object[] formatArgs) { | |||
|             command = string.Format(command, formatArgs); | |||
| 
 | |||
|             var stdout = string.Empty; | |||
|             var stderr = string.Empty; | |||
|             var exitCode = Run(command, asAdmin, out stdout, out stderr); | |||
| 
 | |||
|             if (exitCode == 0) | |||
|                 return stdout.Trim(); | |||
| 
 | |||
|             throw new Exception(string.Format("Command {0} exited with code {1}", command, exitCode)); | |||
|         } | |||
| 
 | |||
|         public static int Run(string command, bool asAdmin, out string stdout, out string stderr) { | |||
|             var proc = new Process(); | |||
|             var stdoutBuilder = new StringBuilder(); | |||
|             var stderrBuilder = new StringBuilder(); | |||
| 
 | |||
|             proc.EnableRaisingEvents = true; | |||
| 
 | |||
|             if (Application.platform == RuntimePlatform.WindowsEditor) | |||
|                 proc.StartInfo = new ProcessStartInfo() { | |||
|                     FileName = "cmd.exe", | |||
|                     Arguments = "/C \"" + command + "\"", | |||
|                     UseShellExecute = asAdmin, | |||
|                     RedirectStandardError = !asAdmin, | |||
|                     RedirectStandardOutput = !asAdmin, | |||
|                     Verb = asAdmin? "runas": "", | |||
|                     CreateNoWindow = !asAdmin, | |||
|                     WorkingDirectory = Environment.CurrentDirectory | |||
|                 }; | |||
|             else | |||
|                 proc.StartInfo = new ProcessStartInfo() { | |||
|                     FileName = "/bin/bash", | |||
|                     Arguments = "-c \"" + command + "\"", | |||
|                     UseShellExecute = asAdmin, | |||
|                     RedirectStandardError = !asAdmin, | |||
|                     RedirectStandardOutput = !asAdmin, | |||
|                     CreateNoWindow = !asAdmin, | |||
|                     WorkingDirectory = Environment.CurrentDirectory | |||
|                 }; | |||
| 
 | |||
|             if (!asAdmin) { | |||
|                 proc.OutputDataReceived += (sender, args) => { | |||
|                     if (!string.IsNullOrEmpty(args.Data)) | |||
|                         stdoutBuilder.AppendLine(args.Data); | |||
|                 }; | |||
| 
 | |||
|                 proc.ErrorDataReceived += (sender, args) => { | |||
|                     if (!string.IsNullOrEmpty(args.Data)) | |||
|                         stderrBuilder.AppendLine(args.Data); | |||
|                 }; | |||
|             } | |||
| 
 | |||
|             //proc.Exited += (sender, args) => {
 | |||
|             //    Debug.LogWarningFormat("Command {0} exited with code {1}", command, proc.ExitCode);
 | |||
|             //};
 | |||
| 
 | |||
|             if (proc.Start()) { | |||
|                 if (!asAdmin) { | |||
|                     proc.BeginOutputReadLine(); | |||
|                     proc.BeginErrorReadLine(); | |||
|                 } | |||
|                 proc.WaitForExit(); | |||
| 
 | |||
|                 stdout = stdoutBuilder.ToString(); | |||
|                 stderr = stderrBuilder.ToString(); | |||
| 
 | |||
|                 Logger.Debug("{3}: {0}\nstdout: {1}\nstderr: {2}", command, stdout, stderr, proc.ExitCode); | |||
| 
 | |||
|                 return proc.ExitCode; | |||
|             } | |||
| 
 | |||
|             throw new Exception(string.Format("Failed to start process for {0}", command)); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 6754a626da46d9a788cf7f6da2f00106 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,151 @@ | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     public class DisableSceneView { | |||
| 
 | |||
|         // TODO: Patcher resets the method to original after a while
 | |||
|         // TODO: or the shouldSkipRender is not being properly calculated
 | |||
|         private static Patcher patcher = null; | |||
| 
 | |||
|         public static bool RenderingDisabled { | |||
|             get { | |||
|                 return patcher == null ? false : patcher.IsPatched(); | |||
|             } | |||
|             set { | |||
|                 if(patcher == null) return; | |||
| 
 | |||
|                 if(value == patcher.IsPatched()) | |||
|                     return; | |||
|                 else if(!value) | |||
|                     patcher.Revert(); | |||
|                 else if(FullscreenPreferences.DisableSceneViewRendering) | |||
|                     patcher.SwapMethods(); | |||
| 
 | |||
|                 if(!value || FullscreenPreferences.DisableSceneViewRendering) | |||
|                     foreach(var c in SceneView.GetAllSceneCameras()) | |||
|                         c.gameObject.SetActive(!value); | |||
| 
 | |||
|                 Logger.Debug("{0} Scene View Rendering", value ? "Disabled" : "Enabled"); | |||
|                 SceneView.RepaintAll(); | |||
|             } | |||
|         } | |||
| 
 | |||
|         [InitializeOnLoadMethod] | |||
|         private static void Init() { | |||
|             if(!Patcher.IsSupported()) return; | |||
| 
 | |||
|             var sceneGUIName = ReflectionUtility.HasMethod(typeof(SceneView), "OnSceneGUI") ? "OnSceneGUI" : "OnGUI"; | |||
| 
 | |||
|             patcher = new Patcher( | |||
|                     typeof(SceneView).FindMethod(sceneGUIName), // Original method
 | |||
|                     typeof(DisableSceneView).FindMethod("OnGUI") // Replacement
 | |||
|                 ); | |||
| 
 | |||
|             SceneView.beforeSceneGui += OnBeforeSceneGUI; | |||
| 
 | |||
|             // Initial
 | |||
|             RenderingDisabled = Fullscreen.GetAllFullscreen().Length > 0; | |||
| 
 | |||
|             // On preferences change
 | |||
|             FullscreenPreferences.DisableSceneViewRendering.OnValueSaved += (v) => | |||
|                 RenderingDisabled = v && Fullscreen.GetAllFullscreen().Length > 0; | |||
| 
 | |||
|             // On fullscreen open
 | |||
|             FullscreenCallbacks.afterFullscreenOpen += (f) => | |||
|                 RenderingDisabled = true; | |||
| 
 | |||
|             // Disable the patching if we're the last fullscreen open
 | |||
|             FullscreenCallbacks.afterFullscreenClose += (f) => { | |||
|                 if(Fullscreen.GetAllFullscreen().Length <= 1) | |||
|                     RenderingDisabled = false; | |||
|             }; | |||
|         } | |||
| 
 | |||
|         private static void OnBeforeSceneGUI(SceneView sceneView) { | |||
|             var shouldRender = !RenderingDisabled || Fullscreen.GetFullscreenFromView(new ViewPyramid(sceneView).Container, false); | |||
|             sceneView.autoRepaintOnSceneChange = shouldRender; | |||
|         } | |||
| 
 | |||
|         // This should not be a static method, as static has no this
 | |||
|         // However, the 'this' in the method is unreliable and should be casted before using
 | |||
|         private void OnGUI() { | |||
|             var _this = (object)this as SceneView; | |||
|             var vp = new ViewPyramid(_this); | |||
|             var shouldRender = Fullscreen.GetFullscreenFromView(vp.Container, false); // Render if this window is in fullscreen
 | |||
| 
 | |||
|             if(shouldRender) { | |||
|                 // Do this outside of OnGUI to prevent controls count errors
 | |||
|                 After.Frames(1, () => EnableRenderingTemporarily()); | |||
|             } else { | |||
|                 _this.autoRepaintOnSceneChange = false; | |||
|                 CustomOnGUI(); | |||
|             } | |||
|         } | |||
| 
 | |||
|         private static class Styles { | |||
| 
 | |||
|             public static readonly GUIStyle textStyle = new GUIStyle("BoldLabel"); | |||
|             public static readonly GUIStyle backgroundShadow = new GUIStyle("InnerShadowBg"); | |||
|             public static readonly GUIStyle buttonStyle = new GUIStyle("LargeButton"); | |||
|             public static readonly GUIStyle secondaryTextStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel); | |||
| 
 | |||
|             static Styles() { | |||
|                 textStyle.wordWrap = true; | |||
|                 textStyle.alignment = TextAnchor.MiddleCenter; | |||
|             } | |||
| 
 | |||
|         } | |||
| 
 | |||
|         private void CustomOnGUI() { | |||
|             using(var mainScope = new EditorGUILayout.VerticalScope(Styles.backgroundShadow)) { | |||
|                 using(new GUIColor(Styles.textStyle.normal.textColor * 0.05f)) | |||
|                     GUI.DrawTexture(mainScope.rect, FullscreenUtility.FullscreenIcon, ScaleMode.ScaleAndCrop); | |||
| 
 | |||
|                 GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                 using(new EditorGUILayout.HorizontalScope()) { | |||
|                     GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                     using(new GUIContentColor(Styles.textStyle.normal.textColor)) | |||
|                         GUILayout.Label(FullscreenUtility.FullscreenIcon, Styles.textStyle); | |||
| 
 | |||
|                     using(new EditorGUILayout.VerticalScope()) { | |||
|                         GUILayout.Label("Scene View rendering has been disabled\nto improve fullscreen performance", Styles.textStyle); | |||
|                     } | |||
| 
 | |||
|                     GUILayout.FlexibleSpace(); | |||
|                 } | |||
| 
 | |||
|                 using(new EditorGUILayout.HorizontalScope()) { | |||
|                     GUILayout.FlexibleSpace(); | |||
|                     if(GUILayout.Button("Enable Temporarily", Styles.buttonStyle)) { | |||
|                         // Do this outside of OnGUI to prevent controls count errors
 | |||
|                         After.Frames(1, () => EnableRenderingTemporarily()); | |||
|                         var _this = (object)this as SceneView; | |||
|                         _this.Focus(); | |||
|                     } | |||
|                     if(GUILayout.Button("Enable Permanently", Styles.buttonStyle)) { | |||
|                         // Do this outside of OnGUI to prevent controls count errors
 | |||
|                         After.Frames(1, () => EnableRenderingPermanently()); | |||
|                         var _this = (object)this as SceneView; | |||
|                         _this.Focus(); | |||
|                     } | |||
|                     GUILayout.FlexibleSpace(); | |||
|                 } | |||
| 
 | |||
|                 GUILayout.Label("* This can be changed later in the preferences", Styles.secondaryTextStyle); | |||
|                 GUILayout.FlexibleSpace(); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public static void EnableRenderingTemporarily() { | |||
|             RenderingDisabled = false; | |||
|         } | |||
| 
 | |||
|         public static void EnableRenderingPermanently() { | |||
|             FullscreenPreferences.DisableSceneViewRendering.Value = false; | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 5148452bd855eb74cb938ba58e6ce9b3 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 185b2b5b57344834298a7bbe0defe069 | |||
| folderAsset: yes | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,184 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Runtime.InteropServices; | |||
| using FullscreenEditor.Windows; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
| 
 | |||
|     [System.Serializable] | |||
|     public class DisplayInfo { | |||
|         public bool PrimaryDisplay; | |||
| 
 | |||
|         public float ScreenHeight { | |||
|             get { return MonitorArea.yMax - MonitorArea.yMin; } | |||
|         } | |||
|         public float ScreenWidth { | |||
|             get { return MonitorArea.xMax - MonitorArea.xMin; } | |||
|         } | |||
| 
 | |||
|         public float scaleFactor2; | |||
| 
 | |||
|         public int LogicalScreenHeight; | |||
|         public int PhysicalScreenHeight; | |||
| 
 | |||
|         public string DeviceName { | |||
|             get { return displayDevice.DeviceName; } | |||
|         } | |||
| 
 | |||
|         public string FriendlyName { | |||
|             get { return displayDevice.DeviceString; } | |||
|         } | |||
| 
 | |||
|         public Rect MonitorArea; | |||
|         public Rect WorkArea; | |||
| 
 | |||
|         internal DevMode devMode; | |||
|         internal DisplayDevice displayDevice; | |||
| 
 | |||
|         public Rect DpiCorrectedArea { | |||
|             get { | |||
|                 var firstDisplayInfo = DisplayInfo.GetDisplay(0); | |||
|                 var monitorArea = MonitorArea; | |||
| 
 | |||
|                 var origin = monitorArea.min; | |||
|                 var size = monitorArea.size; | |||
| 
 | |||
|                 return new Rect( | |||
|                     Mathf.Round(origin.x / firstDisplayInfo.scaleFactor2), | |||
|                     Mathf.Round(origin.y / firstDisplayInfo.scaleFactor2), | |||
|                     Mathf.Round(size.x / scaleFactor2), | |||
|                     Mathf.Round(size.y / scaleFactor2) | |||
|                 ); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public Rect UnityCorrectedArea { | |||
|             get { | |||
|                 var rect = DpiCorrectedArea; | |||
|                 return InternalEditorUtility.GetBoundsOfDesktopAtPoint(rect.center); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public Rect PhysicalArea { | |||
|             get { | |||
|                 return new Rect( | |||
|                     devMode.dmPositionX, | |||
|                     devMode.dmPositionY, | |||
|                     devMode.dmPelsWidth, | |||
|                     devMode.dmPelsHeight | |||
|                 ); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public float scaleFactor { | |||
|             get { return devMode.dmPelsWidth / (MonitorArea.xMax - MonitorArea.xMin); } | |||
|         } | |||
| 
 | |||
|         public static List<DisplayInfo> GetDisplays() { | |||
|             var list = new List<DisplayInfo>(); | |||
| 
 | |||
|             try { | |||
|                 User32.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, | |||
|                     (IntPtr hMonitor, IntPtr hdcMonitor, ref NativeRect lprcMonitor, IntPtr dwData) => { | |||
|                         var mi = new MonitorInfoEx(); | |||
|                         mi.Init(); | |||
|                         mi.size = Marshal.SizeOf(mi); | |||
|                         mi.size = 72; | |||
|                         var success = User32.GetMonitorInfo(hMonitor, ref mi); | |||
|                         if (success) { | |||
|                             var di = new DisplayInfo(); | |||
|                             di.MonitorArea = mi.monitor; | |||
|                             di.WorkArea = mi.work; | |||
|                             di.PrimaryDisplay = (mi.flags & 1) != 0; | |||
| 
 | |||
|                             di.LogicalScreenHeight = GDI32.GetDeviceCaps(hMonitor, (int)GDI32.DeviceCap.VERTRES); | |||
|                             di.PhysicalScreenHeight = GDI32.GetDeviceCaps(hMonitor, (int)GDI32.DeviceCap.DESKTOPVERTRES); | |||
| 
 | |||
|                             // TransformToPixels(0, 0, out var x, out var y);
 | |||
| 
 | |||
|                             uint dpiX; | |||
|                             uint dpiY; | |||
| 
 | |||
|                             try { | |||
|                                 ShCore.GetDpiForMonitor( | |||
|                                     hMonitor, | |||
|                                     MonitorDpiType.MDT_EFFECTIVE_DPI, | |||
|                                     out dpiX, | |||
|                                     out dpiY | |||
|                                 ); | |||
|                             } catch { | |||
|                                 dpiX = 96; | |||
|                                 dpiY = 96; | |||
|                             } | |||
| 
 | |||
|                             di.scaleFactor2 = dpiX / 96f; | |||
|                             list.Add(di); | |||
|                         } else { | |||
|                             Logger.Debug("Getting monitor info failed"); | |||
|                         } | |||
| 
 | |||
|                         return true; | |||
|                     }, IntPtr.Zero); | |||
| 
 | |||
|                 AddAdditionalInfos(list); | |||
|             } catch (Exception e) { | |||
|                 Logger.Exception(e); | |||
|             } | |||
| 
 | |||
|             return list; | |||
|         } | |||
| 
 | |||
|         public static DisplayInfo GetDisplay(int index) { | |||
|             var displays = GetDisplays(); | |||
| 
 | |||
|             if (displays != null && index >= 0 && index < displays.Count) { | |||
|                 return displays[index]; | |||
|             } | |||
| 
 | |||
|             return null; | |||
|         } | |||
| 
 | |||
|         private static void AddAdditionalInfos(List<DisplayInfo> displayInfo) { | |||
|             for (int id = 0; id < displayInfo.Count; id++) { | |||
|                 var vDevMode = new DevMode(); | |||
|                 var d = new DisplayDevice(); | |||
| 
 | |||
|                 d.cb = Marshal.SizeOf(d); | |||
| 
 | |||
|                 try { | |||
|                     User32.EnumDisplayDevices(displayInfo[id].DeviceName, 0, ref d, 0); | |||
| 
 | |||
|                     d.cb = Marshal.SizeOf(d); | |||
| 
 | |||
|                     if ((d.StateFlags & DisplayDeviceStateFlags.AttachedToDesktop) == DisplayDeviceStateFlags.AttachedToDesktop) { | |||
|                         User32.EnumDisplaySettings(displayInfo[id].DeviceName, -1, ref vDevMode); | |||
|                         displayInfo[id].devMode = vDevMode; | |||
|                     } | |||
| 
 | |||
|                     displayInfo[id].displayDevice = d; | |||
|                 } catch (Exception e) { | |||
|                     Logger.Exception(e); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         private static void TransformToPixels(double unitX, double unitY, out int pixelX, out int pixelY) { | |||
|             var hDc = User32.GetDC(IntPtr.Zero); | |||
| 
 | |||
|             if (hDc != IntPtr.Zero) { | |||
|                 var dpiX = GDI32.GetDeviceCaps(hDc, (int)GDI32.DeviceCap.VERTRES); | |||
|                 var dpiY = GDI32.GetDeviceCaps(hDc, (int)GDI32.DeviceCap.DESKTOPVERTRES); | |||
| 
 | |||
|                 User32.ReleaseDC(IntPtr.Zero, hDc); | |||
| 
 | |||
|                 pixelX = (int)(((double)dpiX / 96) * unitX); | |||
|                 pixelY = (int)(((double)dpiY / 96) * unitY); | |||
|             } else { | |||
|                 throw new ArgumentNullException("Failed to get DC."); | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 9e5f6f85a367fed4bbeabff3e3248d17 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,109 @@ | |||
| using System; | |||
| using UnityEditor; | |||
| using UnityEditor.AnimatedValues; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
| 
 | |||
|     public struct GUIBackgroundColor : IDisposable { | |||
|         private readonly Color before; | |||
| 
 | |||
|         public GUIBackgroundColor(Color color) { | |||
|             before = GUI.backgroundColor; | |||
|             GUI.backgroundColor = color; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             GUI.backgroundColor = before; | |||
|         } | |||
|     } | |||
| 
 | |||
|     public struct GUIContentColor : IDisposable { | |||
|         private readonly Color before; | |||
| 
 | |||
|         public GUIContentColor(Color color) { | |||
|             before = GUI.contentColor; | |||
|             GUI.contentColor = color; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             GUI.contentColor = before; | |||
|         } | |||
|     } | |||
| 
 | |||
|     public struct GUIColor : IDisposable { | |||
|         private readonly Color before; | |||
| 
 | |||
|         public GUIColor(Color color) { | |||
|             before = GUI.color; | |||
|             GUI.color = color; | |||
|         } | |||
| 
 | |||
|         public GUIColor(Color color, float alpha) { | |||
|             before = GUI.color; | |||
|             color.a = alpha; | |||
|             GUI.color = color; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             GUI.color = before; | |||
|         } | |||
|     } | |||
| 
 | |||
|     public sealed class GUIIndent : IDisposable { | |||
|         public GUIIndent() { | |||
|             EditorGUI.indentLevel++; | |||
|         } | |||
| 
 | |||
|         public GUIIndent(string label) { | |||
|             EditorGUILayout.LabelField(label); | |||
|             EditorGUI.indentLevel++; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             EditorGUI.indentLevel--; | |||
|             EditorGUILayout.Separator(); | |||
|         } | |||
|     } | |||
| 
 | |||
|     public struct GUIEnabled : IDisposable { | |||
|         private readonly bool before; | |||
| 
 | |||
|         public GUIEnabled(bool enabled) { | |||
|             before = GUI.enabled; | |||
|             GUI.enabled = before && enabled; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             GUI.enabled = before; | |||
|         } | |||
|     } | |||
| 
 | |||
|     public sealed class GUIFade : IDisposable { | |||
|         private AnimBool anim; | |||
| 
 | |||
|         public bool Visible { get; private set; } | |||
| 
 | |||
|         public GUIFade() { | |||
|             Visible = true; | |||
|         } | |||
| 
 | |||
|         public void SetTarget(bool target) { | |||
|             if (anim == null) { | |||
|                 anim = new AnimBool(target); | |||
|                 anim.valueChanged.AddListener(() => { | |||
|                     if (EditorWindow.focusedWindow) | |||
|                         EditorWindow.focusedWindow.Repaint(); | |||
|                 }); | |||
|             } | |||
| 
 | |||
|             anim.target = target; | |||
|             Visible = EditorGUILayout.BeginFadeGroup(anim.faded); | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             EditorGUILayout.EndFadeGroup(); | |||
|         } | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 214e5c575686ed341a4cee0e6716c8a3 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,26 @@ | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     [InitializeOnLoad] | |||
|     // Issue #93
 | |||
|     public class FixGameViewMouseInput { | |||
| 
 | |||
|         static FixGameViewMouseInput() { | |||
|             FullscreenCallbacks.afterFullscreenOpen += fs => UpdateGameViewArea(fs); | |||
|         } | |||
| 
 | |||
|         public static void UpdateGameViewArea(FullscreenContainer fs) { | |||
|             After.Frames(50, () => { | |||
|                 var window = fs.ActualViewPyramid.Window; | |||
|                 if (window && window.IsOfType(Types.PlayModeView)) { | |||
|                     Logger.Debug("Fixing game view area"); | |||
|                     FullscreenUtility.FocusView(FullscreenUtility.GetMainView()); | |||
| 
 | |||
|                     // Issue #95, fix Input.mouseScrollDelta
 | |||
|                     window.Focus(); | |||
|                 } | |||
|             }); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 713b38e38f114da43b6c55d8c9bf5c5d | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,166 @@ | |||
| using System; | |||
| using System.Linq; | |||
| using System.Runtime.CompilerServices; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| [assembly: InternalsVisibleToAttribute("FullscreenTests")] | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Main entry point for finding, creating and closing <see cref="FullscreenContainer"/>.</summary>
 | |||
|     public static class Fullscreen { | |||
| 
 | |||
|         private static FullscreenContainer[] cachedFullscreen; | |||
|         private static FullscreenContainer[] cachedFullscreenAll; | |||
| 
 | |||
|         [InitializeOnLoadMethod] | |||
|         private static void InitCache() { | |||
|             GetAllFullscreen(false, true); | |||
| 
 | |||
|             FullscreenCallbacks.beforeFullscreenOpen += (f) => GetAllFullscreen(false, true); | |||
|             FullscreenCallbacks.afterFullscreenOpen += (f) => GetAllFullscreen(false, true); | |||
|             FullscreenCallbacks.beforeFullscreenClose += (f) => GetAllFullscreen(false, true); | |||
|             FullscreenCallbacks.afterFullscreenClose += (f) => GetAllFullscreen(false, true); | |||
|         } | |||
| 
 | |||
|         /// <summary>Return all <see cref="FullscreenContainer"/> instances.</summary>
 | |||
|         /// <param name="cached">Allow returning cached content.</param>
 | |||
|         /// <param name="ignoreUnknownState">Do not return fullscreen containers that don't have a valid ContainerWindow.</param>
 | |||
|         public static FullscreenContainer[] GetAllFullscreen(bool cached = true, bool ignoreUnknownState = true) { | |||
| 
 | |||
|             if(cached && cachedFullscreen != null && cachedFullscreenAll != null) | |||
|                 return ignoreUnknownState ? | |||
|                     cachedFullscreen : | |||
|                     cachedFullscreenAll; | |||
| 
 | |||
|             cachedFullscreenAll = Resources.FindObjectsOfTypeAll<FullscreenContainer>(); | |||
| 
 | |||
|             if(!ignoreUnknownState) | |||
|                 return cachedFullscreenAll; | |||
| 
 | |||
|             cachedFullscreen = cachedFullscreenAll | |||
|                 .Where(fs => fs.m_dst.Container != null) | |||
|                 .ToArray(); | |||
| 
 | |||
|             return cachedFullscreen; | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the <see cref="FullscreenContainer"/> on the given point, or null if there is none.</summary>
 | |||
|         public static FullscreenContainer GetFullscreenOnPoint(Vector2 point) { | |||
|             return GetAllFullscreen() | |||
|                 .FirstOrDefault(fullscreen => fullscreen.Rect.Contains(point)); | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the <see cref="FullscreenContainer"/> that overlaps the given rect, or null if there is none.</summary>
 | |||
|         public static FullscreenContainer GetFullscreenOnRect(Rect rect) { | |||
|             return GetAllFullscreen() | |||
|                 .FirstOrDefault(fullscreen => fullscreen.Rect.Overlaps(rect)); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns the parent <see cref="FullscreenContainer"/> for a given view or window, or null if it's not in fullscreen.</summary>
 | |||
|         /// <param name="rootView">Compare by the root view, otherwise compare by the container.</param>
 | |||
|         public static FullscreenContainer GetFullscreenFromView(ScriptableObject viewOrWindow, bool rootView = true) { | |||
|             if(!viewOrWindow) | |||
|                 return null; | |||
| 
 | |||
|             var pyramid = new ViewPyramid(viewOrWindow); | |||
| 
 | |||
|             return Fullscreen | |||
|                 .GetAllFullscreen() | |||
|                 .FirstOrDefault(fullscreen => rootView ? | |||
|                     fullscreen.ActualViewPyramid.View == pyramid.View : | |||
|                     fullscreen.ActualViewPyramid.Container == pyramid.Container | |||
|                 ); | |||
|         } | |||
| 
 | |||
|         /// <summary>Create a <see cref="FullscreenWindow"/> for a given window.</summary>
 | |||
|         /// <param name="window">The window that will go fullscreen. If null a new one will be instantiated based on the given type.</param>
 | |||
|         /// <typeparam name="T">The type of the window to instantiate if the given window is null.</typeparam>
 | |||
|         /// <returns>Returns the newly created <see cref="FullscreenWindow"/>.</returns>
 | |||
|         public static FullscreenWindow MakeFullscreen<T>(T window = null) where T : EditorWindow { | |||
|             return MakeFullscreen(typeof(T), window); | |||
|         } | |||
| 
 | |||
|         /// <summary>Create a <see cref="FullscreenWindow"/> for a given window.</summary>
 | |||
|         /// <param name="type">The type of the window to instantiate if the given window is null.</param>
 | |||
|         /// <param name="window">The window that will go fullscreen. If null a new one will be instantiated based on the given type.</param>
 | |||
|         /// <param name="disposableWindow">Set this to true when the target window was created solely for fullscreen,
 | |||
|         /// this will cause it to be destroyed once the fullscreen closes, it has no effects if the target window is null.</param>
 | |||
|         /// <returns>Returns the newly created <see cref="FullscreenWindow"/>.</returns>
 | |||
|         public static FullscreenWindow MakeFullscreen(Type type, EditorWindow window = null, bool disposableWindow = false) { | |||
|             var rect = FullscreenRects.GetFullscreenRect(FullscreenPreferences.RectSource, window); | |||
|             var fullscreen = ScriptableObject.CreateInstance<FullscreenWindow>(); | |||
| 
 | |||
|             fullscreen.OpenWindow(rect, type, window, disposableWindow); | |||
|             return fullscreen; | |||
|         } | |||
| 
 | |||
|         /// <summary>Create a <see cref="FullscreenView"/> for a given view.</summary>
 | |||
|         /// <param name="view">The view that will go fullscreen, cannot be null.</param>
 | |||
|         /// <returns>Returns the newly created <see cref="FullscreenView"/>.</returns>
 | |||
|         public static FullscreenView MakeFullscreen(ScriptableObject view) { | |||
|             if(!view) | |||
|                 throw new ArgumentNullException("view"); | |||
| 
 | |||
|             view.EnsureOfType(Types.View); | |||
| 
 | |||
|             var rect = FullscreenRects.GetFullscreenRect(FullscreenPreferences.RectSource, view); | |||
|             var fullscreen = ScriptableObject.CreateInstance<FullscreenView>(); | |||
| 
 | |||
|             fullscreen.OpenView(rect, view); | |||
| 
 | |||
|             return fullscreen; | |||
|         } | |||
| 
 | |||
|         /// <summary>Open a new fullscreen if there's none open, otherwise, close the one already open.</summary>
 | |||
|         /// <param name="window">The window that will go fullscreen. If null a new one will be instantiated based on the given type.</param>
 | |||
|         /// <typeparam name="T">The type of the window to instantiate if the given window is null.</typeparam>
 | |||
|         public static void ToggleFullscreen<T>(T window = null) where T : EditorWindow { | |||
|             ToggleFullscreen(typeof(T), window); | |||
|         } | |||
| 
 | |||
|         /// <summary>Open a new fullscreen if there's none open, otherwise, close the one already open.</summary>
 | |||
|         /// <param name="window">The window that will go fullscreen. If null a new one will be instantiated based on the given type.</param>
 | |||
|         /// <param name="type">The type of the window to instantiate if the given window is null.</param>
 | |||
|         public static void ToggleFullscreen(Type type, EditorWindow window = null) { | |||
|             var rect = FullscreenRects.GetFullscreenRect(FullscreenPreferences.RectSource, window); | |||
|             var oldFullscreen = GetFullscreenFromView(window); | |||
| 
 | |||
|             if(oldFullscreen) { | |||
|                 oldFullscreen.Close(); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             oldFullscreen = GetFullscreenOnRect(rect); | |||
| 
 | |||
|             var newFullscreen = MakeFullscreen(type, window); | |||
| 
 | |||
|             newFullscreen.didPresent += () => { | |||
|                 if(oldFullscreen) | |||
|                     oldFullscreen.Close(); | |||
|             }; | |||
|         } | |||
| 
 | |||
|         /// <summary>Open a new fullscreen if there's none open, otherwise, close the one already open.</summary>
 | |||
|         /// <param name="view">The view that will go fullscreen, cannot be null.</param>
 | |||
|         public static void ToggleFullscreen(ScriptableObject view) { | |||
|             var rect = FullscreenRects.GetFullscreenRect(FullscreenPreferences.RectSource, view); | |||
|             var oldFullscreen = GetFullscreenFromView(view); | |||
| 
 | |||
|             if(oldFullscreen) { | |||
|                 oldFullscreen.Close(); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             oldFullscreen = GetFullscreenOnRect(rect); | |||
| 
 | |||
|             var newFullscreen = MakeFullscreen(view); | |||
| 
 | |||
|             newFullscreen.didPresent += () => { | |||
|                 if(oldFullscreen) | |||
|                     oldFullscreen.Close(); | |||
|             }; | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 02b2fea24f2e74d40b0ea966449271f6 | |||
| timeCreated: 1508987359 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,32 @@ | |||
| using System; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>
 | |||
|     /// Utility callbacks for fullscreen state changes.
 | |||
|     /// </summary>
 | |||
|     public static class FullscreenCallbacks { | |||
| 
 | |||
|         /// <summary>
 | |||
|         /// Callback called before the views are restored to their original position.
 | |||
|         /// </summary>
 | |||
|         public static Action<FullscreenContainer> beforeFullscreenClose = (f) => { }; | |||
| 
 | |||
|         /// <summary>
 | |||
|         /// Callback called before the container for the fullscreen view is created and
 | |||
|         /// the views are moved between ContainerWindows.
 | |||
|         /// </summary>
 | |||
|         public static Action<FullscreenContainer> beforeFullscreenOpen = (f) => { }; | |||
| 
 | |||
|         /// <summary>
 | |||
|         /// Callback called in the OnDestroy method of the FullscreenContainer, after the
 | |||
|         /// views have been reverted to their orignal positions.
 | |||
|         /// </summary>
 | |||
|         public static Action<FullscreenContainer> afterFullscreenClose = (f) => { }; | |||
| 
 | |||
|         /// <summary>
 | |||
|         /// Callback called after the fullscreen is opened and everything is already set up.
 | |||
|         /// </summary>
 | |||
|         public static Action<FullscreenContainer> afterFullscreenOpen = (f) => { }; | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: cce9fa341a0d9e743960c6283b72e4d8 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,135 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| using UnityObject = UnityEngine.Object; | |||
| using HostView = UnityEngine.ScriptableObject; | |||
| using View = UnityEngine.ScriptableObject; | |||
| using ContainerWindow = UnityEngine.ScriptableObject; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Manages the WindowContainers, Views and Windows that will be fullscreened.</summary>
 | |||
|     public abstract partial class FullscreenContainer : ScriptableObject { | |||
| 
 | |||
|         [SerializeField] private int m_ourIndex = -1; | |||
|         [SerializeField] private bool m_old = false; | |||
| 
 | |||
|         public Action didPresent = () => Logger.Debug("'Did Present' called"); | |||
| 
 | |||
|         private static int CurrentIndex { | |||
|             get { return EditorPrefs.GetInt("FullscreenIdx", 0); } | |||
|             set { EditorPrefs.SetInt("FullscreenIdx", value); } | |||
|         } | |||
| 
 | |||
|         /// <summary>The true view pyramid of this fullscreen container.</summary>
 | |||
|         public ViewPyramid ActualViewPyramid { | |||
|             get { return new ViewPyramid(m_dst.Container); } | |||
|         } | |||
| 
 | |||
|         /// <summary>The view that is currently fullscreened.</summary>
 | |||
|         public View FullscreenedView { | |||
|             get { return ActualViewPyramid.View; } | |||
|         } | |||
| 
 | |||
|         /// <summary>Position and size of the WindowContainer created for this fullscreen.</summary>
 | |||
|         public Rect Rect { | |||
|             get { | |||
|                 return m_dst.Container ? | |||
|                     m_dst.Container.GetPropertyValue<Rect>("position") : | |||
|                     new Rect(); | |||
|             } | |||
|             set { | |||
|                 if (m_dst.Container) { | |||
|                     m_dst.Container.InvokeMethod("SetMinMaxSizes", value.size, value.size); | |||
|                     m_dst.Container.SetPropertyValue("position", value); | |||
|                     Logger.Debug("Set {0} rect to {1}", this.name, value); | |||
|                 } else | |||
|                     Logger.Debug("No container on {0}, rect will not be set", this.name); | |||
|             } | |||
|         } | |||
| 
 | |||
|         private void Update() { | |||
|             if (!m_dst.Container) | |||
|                 Close(); // Forcefully closed
 | |||
|         } | |||
| 
 | |||
|         protected virtual void OnEnable() { | |||
| 
 | |||
|             if (m_ourIndex == -1) { | |||
|                 m_ourIndex = CurrentIndex++; | |||
|                 name = string.Format("Fullscreen #{0}", m_ourIndex); | |||
|                 hideFlags = HideFlags.HideAndDontSave; | |||
|             } | |||
| 
 | |||
|             #if UNITY_2018_1_OR_NEWER
 | |||
|             EditorApplication.wantsToQuit += WantsToQuit; | |||
|             #endif
 | |||
| 
 | |||
|             if (m_old && !m_dst.Container) { | |||
|                 Logger.Warning("{0} wasn't properly closed", name); | |||
|                 // After 1 frame to prevent OnDisable and OnDestroy from being called before this methods returns
 | |||
|                 After.Frames(1, () => DestroyImmediate(this, true)); | |||
|             } | |||
| 
 | |||
|             m_old = true; | |||
|             EditorApplication.update += Update; | |||
|         } | |||
| 
 | |||
|         protected virtual void OnDisable() { | |||
|             EditorApplication.update -= Update; | |||
|             #if UNITY_2018_1_OR_NEWER
 | |||
|             EditorApplication.wantsToQuit += WantsToQuit; | |||
|             #endif
 | |||
|         } | |||
| 
 | |||
|         protected virtual void OnDestroy() { | |||
|             Logger.Debug(name + " destroyed"); | |||
| 
 | |||
|             if (m_dst.Container) { | |||
|                 m_dst.Container.InvokeMethod("Close"); | |||
|                 Logger.Warning("Destroying {0} which has open containers, always close the fullscreen before destroying it", name); | |||
|             } | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenClose(this); | |||
|         } | |||
| 
 | |||
|         /// <summary>Destroy this container and exit fullscreen.</summary>
 | |||
|         public virtual void Close() { | |||
| 
 | |||
|             FullscreenCallbacks.beforeFullscreenClose(this); | |||
| 
 | |||
|             if (!m_dst.Window && m_dst.Container) | |||
|                 Logger.Error("Placeholder window has been closed, Fullscreen Editor won't be able to restore window position"); | |||
| 
 | |||
|             if (m_dst.Container) // Container may have been destroyed by Alt+F4
 | |||
|                 m_dst.Container.InvokeMethod("Close"); // Closes the container, all its views and the windows
 | |||
| 
 | |||
|             DestroyImmediate(this, true); | |||
| 
 | |||
|         } | |||
| 
 | |||
|         /// <summary>Focus the view of this fullscreen.</summary>
 | |||
|         public virtual void Focus() { | |||
|             if (FullscreenedView && FullscreenedView.IsOfType(Types.GUIView)) | |||
|                 FullscreenUtility.FocusView(FullscreenedView); | |||
|         } | |||
| 
 | |||
|         /// <summary>Gets wheter the view of this fullscreen is focused or not.</summary>
 | |||
|         public virtual bool IsFocused() { | |||
|             return FullscreenUtility.IsViewFocused(FullscreenedView); | |||
|         } | |||
| 
 | |||
|         #if UNITY_2018_1_OR_NEWER
 | |||
|         private bool WantsToQuit() { | |||
|             // Close the fullscreen before closing the editor, this way we have a better
 | |||
|             // ensurance that the fullscreen container will not be saved to the layout.
 | |||
|             // ContainerWindow.m_DontSaveToLayout is set to true, so in Unity < 2018.1 the
 | |||
|             // fullscreen will behave the same as if it was closed by Alt+F4.
 | |||
|             Close(); | |||
|             return true; | |||
|         } | |||
|         #endif
 | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: d4d34183a65705a41a116cf6e52733a6 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,114 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| using UnityObject = UnityEngine.Object; | |||
| using HostView = UnityEngine.ScriptableObject; | |||
| using View = UnityEngine.ScriptableObject; | |||
| using ContainerWindow = UnityEngine.ScriptableObject; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Manages the WindowContainers, Views and Windows that will be fullscreened.</summary>
 | |||
|     public abstract partial class FullscreenContainer : ScriptableObject { | |||
| 
 | |||
|         /// <summary>The m_src will have all it's fields null if we've instantiated the window by ourselves.
 | |||
|         /// <para>Window - The window to fullscreen, null if we're opening a view.</para> 
 | |||
|         /// <para>View - The view to fullscreen, or window.m_Parent if we're opening a window.</para> 
 | |||
|         /// <para>Container - The source container, from the source view.</para> 
 | |||
|         /// </summary>
 | |||
|         [SerializeField] public ViewPyramid m_src; | |||
| 
 | |||
|         /// <summary>
 | |||
|         /// <para>Window - The placeholder window or a window created specially for this fullscreen.</para> 
 | |||
|         /// <para>View - HostView created for this fullscreen.</para> 
 | |||
|         /// <para>Container - The container that will be used for fullscreen, with its mode set to "Popup".</para> 
 | |||
|         /// </summary>
 | |||
|         [SerializeField] public ViewPyramid m_dst; | |||
| 
 | |||
|         /// <summary>Create a ContainerWindow and HostView, then asigns a window to them and shows it as a popup.</summary>
 | |||
|         /// <param name="rect">The initial position of the ContainerWindow, can be changed later using the <see cref="Rect"/> property.</param>
 | |||
|         /// <param name="childWindow">The initial window for the newly created ContainerWindow and HostView.</param>
 | |||
|         /// <returns>Returns the pyramid of views we've created.</returns>
 | |||
|         protected ViewPyramid CreateFullscreenViewPyramid(Rect rect, EditorWindow childWindow) { | |||
|             var hv = CreateInstance(Types.HostView); | |||
|             var cw = CreateInstance(Types.ContainerWindow); | |||
| 
 | |||
|             hv.name = name; | |||
|             cw.name = name; | |||
|             childWindow.name = name; | |||
| 
 | |||
|             hv.SetPropertyValue("actualView", childWindow); | |||
| 
 | |||
|             // Order is important here: first set rect of container, then assign main view, then apply various settings, then show.
 | |||
|             // Otherwise the rect won't be set until first resize happens.
 | |||
|             cw.SetPropertyValue("position", rect); | |||
|             cw.SetPropertyValue("rootView", hv); | |||
| 
 | |||
|             childWindow.InvokeMethod("MakeParentsSettingsMatchMe"); | |||
| 
 | |||
|             var loadPosition = false; | |||
|             var displayImmediately = true; | |||
|             var setFocus = true; | |||
| 
 | |||
|             if (cw.HasMethod("Show", new[] { typeof(int), typeof(bool), typeof(bool), typeof(bool), typeof(int) })) | |||
|                 cw.InvokeMethod("Show", (int)ShowMode.NoShadow, loadPosition, displayImmediately, setFocus, 0); | |||
|             else if (cw.HasMethod("Show", new[] { typeof(int), typeof(bool), typeof(bool) })) | |||
|                 cw.InvokeMethod("Show", (int)ShowMode.NoShadow, loadPosition, displayImmediately); | |||
|             else | |||
|                 cw.InvokeMethod("Show", (int)ShowMode.NoShadow, loadPosition, displayImmediately, setFocus); | |||
| 
 | |||
|             // set min/max size now that native window is not null so that it will e.g., use proper styleMask on macOS
 | |||
|             cw.InvokeMethod("SetMinMaxSizes", rect.size, rect.size); // min, max
 | |||
| 
 | |||
|             cw.SetFieldValue("m_ShowMode", (int)ShowMode.PopupMenu); // Prevents window decoration from being draw
 | |||
|             cw.SetFieldValue("m_DontSaveToLayout", true); | |||
| 
 | |||
|             Logger.Debug(this, "Created {0}, resolution {1:0}x{2:0}, pos {3:0}", name, rect.width, rect.height, rect.min); | |||
| 
 | |||
|             return new ViewPyramid() { Window = childWindow, View = hv, Container = cw }; | |||
| 
 | |||
|         } | |||
| 
 | |||
|         /// <summary>Prevents any repaint on the container window. This fixes some glitches on macOS.</summary>
 | |||
|         /// <param name="containerWindow">The ContainerWindow to freeze the repaints.</param>
 | |||
|         /// <param name="freeze">Wheter to freeze or unfreeze the container.</param>
 | |||
|         protected void SetFreezeContainer(ContainerWindow containerWindow, bool freeze) { | |||
|             containerWindow.InvokeMethod("SetFreezeDisplay", freeze); | |||
|         } | |||
| 
 | |||
|         /// <summary>Method that will be called just before creating the ContainerWindow for this fullscreen.</summary>
 | |||
|         protected virtual void BeforeOpening() { | |||
| 
 | |||
|             FullscreenCallbacks.beforeFullscreenOpen(this); | |||
| 
 | |||
|             if (m_dst.Container) | |||
|                 new Exception("Container already has a fullscreened view"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Method that will be called after the creation of the ContainerWindow for this fullscreen.</summary>
 | |||
|         protected virtual void AfterOpening() { | |||
| 
 | |||
|             After.Frames(2, () => { | |||
|                 UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); | |||
| 
 | |||
|                 didPresent.Invoke(); | |||
|                 didPresent = null; | |||
|             }); | |||
| 
 | |||
|             Logger.Debug(this, "{6}\n\nSRC\nWindow: {0}\nView: {1}\nContainer: {2}\n\nDST\nWindow: {3}\nView: {4}\nContainer: {5}\n", | |||
|                 m_src.Window, | |||
|                 m_src.View, | |||
|                 m_src.Container, | |||
|                 m_dst.Window, | |||
|                 m_dst.View, | |||
|                 m_dst.Container, | |||
|                 name | |||
|             ); | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenOpen(this); | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: bba1c99b7a0742446947224d1c0e961e | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,121 @@ | |||
| using System; | |||
| using System.Collections; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Toggle fullscreen upon playmode change if <see cref="FullscreenPreferences.FullscreenOnPlayEnabled"/> is set to true.</summary>
 | |||
|     [InitializeOnLoad] | |||
|     internal static class FullscreenOnPlay { | |||
| 
 | |||
|         static FullscreenOnPlay() { | |||
| 
 | |||
| #if UNITY_2017_2_OR_NEWER
 | |||
|             EditorApplication.playModeStateChanged += state => { | |||
|                 switch(state) { | |||
|                     case PlayModeStateChange.ExitingEditMode: | |||
|                         SetIsPlaying(true); | |||
|                         break; | |||
| 
 | |||
|                     case PlayModeStateChange.ExitingPlayMode: | |||
|                         SetIsPlaying(false); | |||
|                         break; | |||
| 
 | |||
|                     case PlayModeStateChange.EnteredPlayMode: | |||
|                         foreach(var fs in Fullscreen.GetAllFullscreen()) | |||
|                             if(fs && fs is FullscreenWindow && (fs as FullscreenWindow).CreatedByFullscreenOnPlay) { | |||
|                                 FixGameViewMouseInput.UpdateGameViewArea(fs); | |||
|                             } | |||
|                         break; | |||
|                 } | |||
|             }; | |||
| 
 | |||
|             EditorApplication.pauseStateChanged += state => SetIsPlaying(EditorApplication.isPlayingOrWillChangePlaymode && state == PauseState.Unpaused); | |||
| #else
 | |||
|             EditorApplication.playmodeStateChanged += () => SetIsPlaying(EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPaused); | |||
| #endif
 | |||
| 
 | |||
|         } | |||
| 
 | |||
|         private static void SetIsPlaying(bool playing) { | |||
| 
 | |||
|             var fullscreens = Fullscreen.GetAllFullscreen() | |||
|                 .Select(fullscreen => fullscreen as FullscreenWindow) | |||
|                 .Where(fullscreen => fullscreen); | |||
| 
 | |||
|             // We close all the game views created on play, even if the option was disabled in the middle of the play mode
 | |||
|             // This is done to best reproduce the default behaviour of the maximize on play
 | |||
|             if(!playing) { | |||
|                 foreach(var fs in fullscreens) | |||
|                     if(fs && fs.CreatedByFullscreenOnPlay) // fs might have been destroyed
 | |||
|                         fs.Close(); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             if(!FullscreenPreferences.FullscreenOnPlayEnabled) | |||
|                 return; // Nothing to do here
 | |||
| 
 | |||
|             if(FullscreenUtility | |||
|                 .GetGameViews() | |||
|                 .Any(gv => { | |||
|                     if(!gv) return false; | |||
| 
 | |||
|                     return PlaymodeBehaviourImplemented(gv) && GetEnterPlayModeBehavior(gv) == CustomEnterPlayModeBehavior.PlayFullscreen; | |||
|                 })) { | |||
|                 EditorUtility.DisplayDialog("Fullscreen on play conflict", "Seems like you have both Unity's built-in fullscreen on play and Fullscreen Editor's plugin enabled. Please, make sure you only have one of them enabled to prevent conflicts.", "Got it!"); | |||
|                 FullscreenPreferences.FullscreenOnPlayEnabled.Value = false; | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             var gameView = FullscreenOnPlayGameView(); | |||
| 
 | |||
|             if(!gameView) // no gameview has the fullscreen on play option enabled
 | |||
|                 return; | |||
| 
 | |||
|             foreach(var fs in fullscreens) | |||
|                 if(fs && fs.Rect.Overlaps(gameView.position)) // fs might have been destroyed
 | |||
|                     return; // We have an open fullscreen where the new one would be, so let it there
 | |||
| 
 | |||
|             if(gameView && Fullscreen.GetFullscreenFromView(gameView)) | |||
|                 return; // The gameview is already in fullscreen
 | |||
| 
 | |||
|             var gvfs = Fullscreen.MakeFullscreen(Types.GameView, gameView); | |||
|             gvfs.CreatedByFullscreenOnPlay = true; | |||
|         } | |||
| 
 | |||
|         internal static EditorWindow FullscreenOnPlayGameView() { | |||
|             var mainGv = FullscreenUtility.GetMainGameView(); | |||
| 
 | |||
|             if(mainGv) return mainGv; | |||
| 
 | |||
|             return FullscreenUtility | |||
|                 .GetGameViews() | |||
|                 .FirstOrDefault(gv => gv); | |||
|         } | |||
| 
 | |||
|         internal enum CustomEnterPlayModeBehavior { | |||
|             PlayFocused, | |||
|             PlayMaximized, | |||
|             PlayUnfocused, | |||
|             PlayFullscreen | |||
|         } | |||
| 
 | |||
|         internal static bool PlaymodeBehaviourImplemented(EditorWindow playmodeView) { | |||
|             return playmodeView.HasProperty("enterPlayModeBehavior"); | |||
|         } | |||
| 
 | |||
|         internal static CustomEnterPlayModeBehavior GetEnterPlayModeBehavior(EditorWindow playmodeView) { | |||
|             return (CustomEnterPlayModeBehavior)playmodeView.GetPropertyValue<int>("enterPlayModeBehavior"); | |||
|         } | |||
| 
 | |||
|         internal static void SetEnterPlayModeBehavior(EditorWindow playmodeView, CustomEnterPlayModeBehavior behaviour) { | |||
|             if(playmodeView.HasProperty("playModeBehaviorIdx")) | |||
|                 playmodeView.SetPropertyValue("playModeBehaviorIdx", (int)behaviour); // unity forgot to update this prop when updating the play mode behaviour
 | |||
|             playmodeView.SetPropertyValue("enterPlayModeBehavior", (int)behaviour); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: e246db13c913afe4aa7ca3064f3ee755 | |||
| timeCreated: 1509071892 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,409 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| using System.IO; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
| 
 | |||
|     /// <summary>Define a source mode to get a fullscreen rect.</summary>
 | |||
|     public enum RectSourceMode { | |||
|         /// <summary>The bounds of the main display.</summary>
 | |||
|         MainDisplay, | |||
|         /// <summary>Open on the display that the target window is located.</summary>
 | |||
|         WindowDisplay, | |||
|         /// <summary>The bounds of the display where the mouse pointer is.</summary>
 | |||
|         AtMousePosition, | |||
|         /// <summary>A rect that spans across all the displays. (Windows only)</summary>
 | |||
|         Span, | |||
|         /// <summary>A custom rect defined by <see cref="FullscreenPreferences.CustomRect"/>.</summary>
 | |||
|         Custom, | |||
|         Display1 = 0x100, | |||
|         Display2 = 0x101, | |||
|         Display3 = 0x102, | |||
|         Display4 = 0x103, | |||
|         Display5 = 0x104, | |||
|         Display6 = 0x105, | |||
|         Display7 = 0x106, | |||
|         Display8 = 0x107 | |||
|     } | |||
| 
 | |||
|     /// <summary>Contains preferences for the Fullscreen Editor plugin.</summary>
 | |||
|     [InitializeOnLoad] | |||
|     public static class FullscreenPreferences { | |||
| 
 | |||
|         private const float LABEL_WIDTH = 300f; | |||
|         private const string DEVELOPER_EMAIL = "[email protected]"; | |||
|         private const string ASSET_STORE_PAGE = "https://assetstore.unity.com/packages/tools/utilities/fullscreen-editor-69534"; | |||
|         private const string CHANGE_LOG_LINK = ASSET_STORE_PAGE + "#releases"; | |||
|         private const string REVIEWS_LINK = ASSET_STORE_PAGE + "#reviews"; | |||
|         private const string FORUM_THREAD = "https://forum.unity.com/threads/released-fullscreen-editor.661519/"; | |||
| 
 | |||
|         /// <summary>Current version of the Fullscreen Editor plugin.</summary>
 | |||
|         public static readonly Version pluginVersion = new Version(2, 2, 8); | |||
|         /// <summary>Release date of this version.</summary>
 | |||
|         public static readonly DateTime pluginDate = new DateTime(2023, 09, 07); | |||
| 
 | |||
|         private static readonly GUIContent resetSettingsContent = new GUIContent("Use Defaults", "Reset all settings to default ones"); | |||
|         private static readonly GUIContent versionContent = new GUIContent(string.Format("Version: {0} ({1:d})", pluginVersion, pluginDate)); | |||
| 
 | |||
|         private static readonly GUIContent[] links = new GUIContent[] { | |||
|             // new GUIContent("Store Page", ASSET_STORE_PAGE),
 | |||
|             new GUIContent("Forum Thread", FORUM_THREAD), | |||
|             new GUIContent("Email Contact", GetEmailURL()), | |||
|             new GUIContent("Changelog", CHANGE_LOG_LINK), | |||
|             new GUIContent("Readme", GetFilePath("Readme.pdf")), | |||
|         }; | |||
| 
 | |||
|         private static readonly string[] mosaicDropDownOptions = new[] { | |||
|             "nothing", | |||
|             "virtual display 1", | |||
|             "virtual display 2", | |||
|             "virtual display 3", | |||
|             "virtual display 4", | |||
|             "virtual display 5", | |||
|             "virtual display 6", | |||
|             "virtual display 7", | |||
|             "virtual display 8", | |||
|         }; | |||
| 
 | |||
|         internal static Action onLoadDefaults = () => { }; | |||
|         internal static readonly List<GUIContent> contents = new List<GUIContent>(); | |||
| 
 | |||
|         private static readonly PrefItem<Vector2> scroll = new PrefItem<Vector2>("Scroll", Vector2.zero, string.Empty, string.Empty); | |||
| 
 | |||
|         /// <summary>Is the window toolbar currently visible?</summary>
 | |||
|         public static readonly PrefItem<bool> ToolbarVisible; | |||
| 
 | |||
|         /// <summary>Is Fullscreen on Play currently enabled?</summary>
 | |||
|         public static readonly PrefItem<bool> FullscreenOnPlayEnabled; | |||
| 
 | |||
|         /// <summary>Defines a source to get a fullscreen rect.</summary>
 | |||
|         public static readonly PrefItem<RectSourceMode> RectSource; | |||
| 
 | |||
|         /// <summary>Custom rect to be used when <see cref="RectSource"/> is set to <see cref="RectSourceMode.Custom"/>.</summary>
 | |||
|         public static readonly PrefItem<Rect> CustomRect; | |||
| 
 | |||
|         /// <summary>Disable notifications when opening fullscreen windows.</summary>
 | |||
|         public static readonly PrefItem<bool> DisableNotifications; | |||
| 
 | |||
|         /// <summary>Keep fullscreen views below other modal and utility windows.</summary>
 | |||
|         public static readonly PrefItem<bool> KeepFullscreenBelow; | |||
| 
 | |||
|         /// <summary>Disable background SceneView rendering when there are open fullscreen views to increase performance.</summary>
 | |||
|         public static readonly PrefItem<bool> DisableSceneViewRendering; | |||
| 
 | |||
|         /// <summary>Hide toolbars of all windows, not only the fullscreened (issue #80).</summary>
 | |||
|         public static readonly PrefItem<bool> UseGlobalToolbarHiding; | |||
| 
 | |||
|         /// <summary>Defines which display renders on each screen when using Mosaic.</summary>
 | |||
|         public static readonly PrefItem<int[]> MosaicMapping; | |||
| 
 | |||
|         /// <summary>Do not attempt to use wmctrl's fullscreen on Linux environments.</summary>
 | |||
|         public static readonly PrefItem<bool> DoNotUseWmctrl; | |||
| 
 | |||
|         /// <summary>Restore cursor lock and hide state after going in and out of fullscreen.</summary>
 | |||
|         public static readonly PrefItem<bool> RestoreCursorLockAndHideState; | |||
| 
 | |||
|         [RuntimeInitializeOnLoadMethod] | |||
|         private static void SyncVersion() { // Sync for automation
 | |||
|             PlayerPrefs.SetString("PLUGIN_VERSION", pluginVersion.ToString()); | |||
|         } | |||
| 
 | |||
|         static FullscreenPreferences() { | |||
|             var rectSourceTooltip = string.Empty; | |||
| 
 | |||
|             rectSourceTooltip += "Controls where Fullscreen views opens.\n\n"; | |||
|             rectSourceTooltip += "Main Screen: Fullscreen opens on the primary screen;\n\n"; | |||
|             rectSourceTooltip += "Window Display: Open on the display that the target window is located (Windows only);\n\n"; | |||
|             rectSourceTooltip += "At Mouse Position: Fullscreen opens on the screen where the mouse pointer is;\n\n"; | |||
|             rectSourceTooltip += "Span: Fullscreen spans across all screens (Windows only);\n\n"; | |||
|             rectSourceTooltip += "Custom Rect: Fullscreen opens on the given custom Rect."; | |||
| 
 | |||
|             ToolbarVisible = new PrefItem<bool>("Toolbar", false, "Toolbar Visible", "Show and hide the toolbar on the top of some windows, like the Game View and Scene View."); | |||
|             FullscreenOnPlayEnabled = new PrefItem<bool>("FullscreenOnPlay", false, "Fullscreen On Play", "Override the \"Maximize on Play\" option of the game view to \"Fullscreen on Play\""); | |||
|             RectSource = new PrefItem<RectSourceMode>("RectSource", RectSourceMode.MainDisplay, "Placement source", rectSourceTooltip); | |||
|             CustomRect = new PrefItem<Rect>("CustomRect", FullscreenRects.GetMainDisplayRect(), "Custom Rect", string.Empty); | |||
|             DisableNotifications = new PrefItem<bool>("DisableNotifications", false, "Disable Notifications", "Disable the notifications that shows up when opening a new fullscreen view."); | |||
|             KeepFullscreenBelow = new PrefItem<bool>("KeepFullscreenBelow", true, "Keep Utility Views Above", "Keep utility views on top of fullscreen views.\nThis is useful to integrate with assets that need to keep windows open, such as Peek by Ludiq."); | |||
|             DisableSceneViewRendering = new PrefItem<bool>("DisableSceneViewRendering", true, "Disable Scene View Rendering", "Increase Fullscreen Editor performance by not rendering SceneViews while there are open fullscreen views."); | |||
|             UseGlobalToolbarHiding = new PrefItem<bool>("UseGlobalToolbarHiding", FullscreenUtility.IsMacOS, "Use global toolbar hiding", "Changes toolbars of all windows at once. This option fixes the gray bar bug on MacOS."); | |||
|             MosaicMapping = new PrefItem<int[]>("MosaicMapping", new[] { 0, 1, 2, 3, 4, 5, 6, 7 }, "Mosaic Screen Mapping", "Defines which display renders on each screen when using Mosaic."); | |||
|             DoNotUseWmctrl = new PrefItem<bool>("DoNotUseWmctrl", false, "Do not use wmctrl", "Avoid using 'wmctrl' helper when opening fullscreen windows"); | |||
|             RestoreCursorLockAndHideState = new PrefItem<bool>("RestoreCursorLockAndHideState", true, "Restore Cursor Lock and Hide State", "Restore cursor lock and hide state after going in and out of fullscreen."); | |||
| 
 | |||
|             onLoadDefaults += () => // Array won't revert automaticaly because it is changed as reference
 | |||
|                 MosaicMapping.Value = new[] { 0, 1, 2, 3, 4, 5, 6, 7 }; | |||
| 
 | |||
|             if (FullscreenUtility.MenuItemHasShortcut(Shortcut.TOOLBAR_PATH)) | |||
|                 ToolbarVisible.Content.text += string.Format(" ({0})", FullscreenUtility.TextifyMenuItemShortcut(Shortcut.TOOLBAR_PATH)); | |||
|             if (FullscreenUtility.MenuItemHasShortcut(Shortcut.FULLSCREEN_ON_PLAY_PATH)) | |||
|                 FullscreenOnPlayEnabled.Content.text += string.Format(" ({0})", FullscreenUtility.TextifyMenuItemShortcut(Shortcut.FULLSCREEN_ON_PLAY_PATH)); | |||
|         } | |||
| 
 | |||
| #if UNITY_2018_3_OR_NEWER
 | |||
|         [SettingsProvider] | |||
|         private static SettingsProvider RetrieveSettingsProvider() { | |||
|             var sp = new SettingsProvider("Preferences/Fullscreen Editor", SettingsScope.User, contents.Select(c => c.text)); | |||
|             sp.footerBarGuiHandler = OnFooterGUI; | |||
|             sp.guiHandler = (search) => { | |||
|                 EditorGUIUtility.labelWidth = LABEL_WIDTH; | |||
|                 OnPreferencesGUI(search); | |||
|             }; | |||
|             return sp; | |||
|         } | |||
| 
 | |||
|         [SettingsProvider] | |||
|         private static SettingsProvider RetrieveSettingsProviderShortcuts() { | |||
|             var sp = new SettingsProvider("Preferences/Fullscreen Editor/Shortcuts", SettingsScope.User, contents.Select(c => c.text)); | |||
|             sp.footerBarGuiHandler = OnFooterGUI; | |||
|             sp.guiHandler = (search) => { | |||
|                 EditorGUIUtility.labelWidth = LABEL_WIDTH; | |||
|                 Shortcut.DoShortcutsGUI(); | |||
|             }; | |||
|             return sp; | |||
|         } | |||
| 
 | |||
| #else
 | |||
|         [PreferenceItem("Fullscreen")] | |||
|         private static void OnPreferencesGUI() { | |||
|             scroll.Value = EditorGUILayout.BeginScrollView(scroll); | |||
|             OnPreferencesGUI(string.Empty); | |||
|             EditorGUILayout.Separator(); | |||
| 
 | |||
|             EditorGUILayout.LabelField("Shortcuts", EditorStyles.boldLabel); | |||
|             EditorGUI.indentLevel++; | |||
|             Shortcut.DoShortcutsGUI(); | |||
|             EditorGUI.indentLevel--; | |||
| 
 | |||
|             EditorGUILayout.EndScrollView(); | |||
|             OnFooterGUI(); | |||
|         } | |||
| #endif
 | |||
| 
 | |||
|         private static void OnPreferencesGUI(string search) { | |||
| 
 | |||
|             ToolbarVisible.DoGUI(); | |||
|             FullscreenOnPlayEnabled.DoGUI(); | |||
| 
 | |||
|             EditorGUILayout.Separator(); | |||
|             RectSource.DoGUI(); | |||
| 
 | |||
|             if (RectSource.Value == RectSourceMode.AtMousePosition) | |||
|                 EditorGUILayout.HelpBox("\'At mouse position\' can cause slowdowns on large projects", MessageType.Warning); | |||
| 
 | |||
|             if (!IsRectModeSupported(RectSource)) | |||
|                 EditorGUILayout.HelpBox("The selected placement source mode is not supported on this platform", MessageType.Warning); | |||
| 
 | |||
|             // Custom Rect
 | |||
|             switch (RectSource.Value) { | |||
|                 case RectSourceMode.Custom: | |||
|                     EditorGUI.indentLevel++; | |||
|                     CustomRect.DoGUI(); | |||
| 
 | |||
|                     var customRect = CustomRect.Value; | |||
| 
 | |||
|                     if (customRect.width < 300f) | |||
|                         customRect.width = 300f; | |||
|                     if (customRect.height < 300f) | |||
|                         customRect.height = 300f; | |||
| 
 | |||
|                     CustomRect.Value = customRect; | |||
| 
 | |||
|                     EditorGUI.indentLevel--; | |||
|                     break; | |||
|             } | |||
| 
 | |||
|             DisableNotifications.DoGUI(); | |||
|             KeepFullscreenBelow.DoGUI(); | |||
| 
 | |||
|             if (Patcher.IsSupported()) | |||
|                 DisableSceneViewRendering.DoGUI(); | |||
| 
 | |||
|             UseGlobalToolbarHiding.DoGUI(); | |||
|             RestoreCursorLockAndHideState.DoGUI(); | |||
| 
 | |||
|             if (FullscreenUtility.IsLinux) { | |||
|                 using (new EditorGUI.DisabledGroupScope(!FullscreenEditor.Linux.wmctrl.IsInstalled)) { | |||
|                     DoNotUseWmctrl.DoGUI(); | |||
|                 } | |||
|                 if (!FullscreenEditor.Linux.wmctrl.IsInstalled) { | |||
|                     EditorGUILayout.HelpBox("'wmctrl' not found. Try installing it with 'sudo apt-get install wmctrl'.", MessageType.Warning); | |||
|                 } else { | |||
|                     EditorGUILayout.HelpBox("Try enabling the option above if you're experiencing any kind of toolbars or offsets " + | |||
|                         "while in fullscreen mode.\nDisabling 'wmctrl' can fix issues on some Linux environments when the window manager " + | |||
|                         "does not handle fullscreen windows properly (I'm looking at you Ubuntu).", MessageType.Info); | |||
|                 } | |||
|             } | |||
| 
 | |||
|             // Mosaic
 | |||
|             if (FullscreenRects.ScreenCount > 1) { | |||
|                 EditorGUILayout.Separator(); | |||
|                 EditorGUILayout.LabelField(MosaicMapping.Content, EditorStyles.boldLabel); | |||
|                 EditorGUI.indentLevel++; | |||
|                 GUI.changed = false; | |||
|                 var mosaicMapping = MosaicMapping.Value; | |||
| 
 | |||
|                 for (var i = 0; i < mosaicMapping.Length && i < FullscreenRects.ScreenCount; i++) { | |||
|                     var val = EditorGUILayout.IntPopup(string.Format("Physical display {0} renders", i + 1), mosaicMapping[i], mosaicDropDownOptions, new[] { -1, 0, 1, 2, 3, 4, 5, 6, 7 }); | |||
|                     mosaicMapping[i] = val; | |||
|                 } | |||
| 
 | |||
|                 if (GUI.changed) | |||
|                     MosaicMapping.SaveValue(); | |||
|                 EditorGUI.indentLevel--; | |||
|             } | |||
|         } | |||
| 
 | |||
|         private static void OnFooterGUI() { | |||
| 
 | |||
|             Func<GUIContent, bool> linkLabel = (label) => | |||
|                 typeof(EditorGUILayout).HasMethod("LinkLabel", new[] { typeof(string), typeof(GUILayoutOption[]) }) ? // Issue #100
 | |||
|                 typeof(EditorGUILayout).InvokeMethod<bool>("LinkLabel", label, new GUILayoutOption[0]) : // < 2020.1
 | |||
|                 typeof(EditorGUILayout).InvokeMethod<bool>("LinkButton", label, new GUILayoutOption[0]); // >= 2020.1
 | |||
|             ; | |||
| 
 | |||
|             using (new EditorGUILayout.HorizontalScope()) { | |||
|                 GUILayout.FlexibleSpace(); | |||
|                 if (linkLabel(new GUIContent("Consider leaving a review if you're enjoying Fullscreen Editor!", REVIEWS_LINK))) | |||
|                     Application.OpenURL(REVIEWS_LINK); | |||
|                 GUILayout.FlexibleSpace(); | |||
|             } | |||
| 
 | |||
|             using (new EditorGUILayout.HorizontalScope()) { | |||
|                 GUILayout.FlexibleSpace(); | |||
|                 for (var i = 0; i < links.Length; i++) { | |||
|                     if (linkLabel(links[i])) | |||
|                         Application.OpenURL(links[i].tooltip); | |||
|                     GUILayout.Space(5f); | |||
|                 } | |||
|                 GUILayout.FlexibleSpace(); | |||
|             } | |||
| 
 | |||
|             EditorGUILayout.Separator(); | |||
| 
 | |||
|             using (new EditorGUILayout.HorizontalScope()) { | |||
|                 if (GUILayout.Button(resetSettingsContent, GUILayout.Width(120f))) | |||
|                     onLoadDefaults(); | |||
| 
 | |||
|                 using (new EditorGUI.DisabledGroupScope(EditorApplication.isCompiling)) { | |||
|                     GUI.changed = false; | |||
|                     var enable = GUILayout.Toggle(Integration.IsDirectiveDefined("FULLSCREEN_DEBUG"), "Debug", "Button"); | |||
|                     if (GUI.changed) { | |||
|                         Integration.SetDirectiveDefined("FULLSCREEN_DEBUG", enable); | |||
|                     } | |||
|                 } | |||
| 
 | |||
|                 if (GUILayout.Button("Copy debug data", GUILayout.Width(120f))) | |||
|                     CopyDisplayDebugInfo(); | |||
| 
 | |||
|                 GUILayout.FlexibleSpace(); | |||
|                 EditorGUILayout.LabelField(versionContent, GUILayout.Width(170f)); | |||
|             } | |||
| 
 | |||
|             EditorGUILayout.Separator(); | |||
|         } | |||
| 
 | |||
|         private static void CopyDisplayDebugInfo() { | |||
|             var str = new StringBuilder(); | |||
| 
 | |||
|             str.Append("Fullscreen Editor"); | |||
|             str.AppendFormat("\nVersion: {0}", pluginVersion.ToString(3)); | |||
|             str.AppendFormat("\nUnity {0}", InternalEditorUtility.GetFullUnityVersion()); | |||
|             str.AppendFormat("\n{0}", SystemInfo.operatingSystem); | |||
|             str.AppendFormat("\nScaling {0:p}", FullscreenUtility.GetDisplayScaling()); | |||
| 
 | |||
|             foreach (var display in DisplayInfo.GetDisplays()) { | |||
|                 str.AppendFormat("\n----------- DISPLAY -----------"); | |||
|                 str.AppendFormat("\nDeviceName: {0} ({1})", display.FriendlyName, display.DeviceName); | |||
|                 str.AppendFormat("\nDpiCorrectedArea: {0}", display.DpiCorrectedArea); | |||
|                 str.AppendFormat("\nUnityCorrectedArea: {0}", display.UnityCorrectedArea); | |||
|                 str.AppendFormat("\nMonitorArea: {0}", display.MonitorArea); | |||
|                 str.AppendFormat("\nPhysicalArea: {0}", display.PhysicalArea); | |||
|                 str.AppendFormat("\nWorkArea: {0}", display.WorkArea); | |||
|                 str.AppendFormat("\nLogicalScreenHeight: {0}", display.LogicalScreenHeight); | |||
|                 str.AppendFormat("\nPhysicalScreenHeight: {0}", display.PhysicalScreenHeight); | |||
|                 str.AppendFormat("\nScreenWidth: {0}", display.ScreenWidth); | |||
|                 str.AppendFormat("\nScreenHeight: {0}", display.ScreenHeight); | |||
|                 str.AppendFormat("\nPrimaryDisplay: {0}", display.PrimaryDisplay); | |||
|                 str.AppendFormat("\nscaleFactor: {0}", display.scaleFactor); | |||
|                 str.AppendFormat("\nscaleFactor2: {0}", display.scaleFactor2); | |||
|                 str.AppendFormat("\ndevMode: {0}\n", JsonUtility.ToJson(display.devMode, true)); | |||
|             } | |||
| 
 | |||
|             EditorGUIUtility.systemCopyBuffer = str.ToString(); | |||
|             EditorUtility.DisplayDialog("Debug", "Display debug data was copied to the clipboard", "OK"); | |||
|         } | |||
| 
 | |||
|         private static string GetFilePath(string file) { | |||
|             var stack = new StackFrame(0, true); | |||
|             var currentFile = stack.GetFileName(); | |||
|             var currentPath = Path.GetDirectoryName(currentFile); | |||
| 
 | |||
|             return Path.Combine(currentPath, "../" + file); | |||
|         } | |||
| 
 | |||
|         private static string GetEmailURL(Exception e = null) { | |||
|             var full = new StringBuilder(); | |||
|             var body = new StringBuilder(); | |||
| 
 | |||
| #if UNITY_2018_1_OR_NEWER
 | |||
|             Func<string, string> EscapeURL = url => UnityEngine.Networking.UnityWebRequest.EscapeURL(url).Replace("+", "%20"); | |||
| #else
 | |||
|             Func<string, string> EscapeURL = url => WWW.EscapeURL(url).Replace("+", "%20"); | |||
| #endif
 | |||
| 
 | |||
|             body.Append("\nDescribe your issue or make your request here"); | |||
|             body.Append("\n\nAdditional Information:"); | |||
|             body.AppendFormat("\nVersion: {0}", pluginVersion.ToString(3)); | |||
|             body.AppendFormat("\nUnity {0}", InternalEditorUtility.GetFullUnityVersion()); | |||
|             body.AppendFormat("\n{0}", SystemInfo.operatingSystem); | |||
| 
 | |||
|             if (e != null) | |||
|                 body.AppendFormat("\n\nEXCEPTION\n", e); | |||
| 
 | |||
|             full.Append("mailto:"); | |||
|             full.Append(DEVELOPER_EMAIL); | |||
|             full.Append("?subject="); | |||
|             full.Append(EscapeURL("Fullscreen Editor - Support")); | |||
|             full.Append("&body="); | |||
|             full.Append(EscapeURL(body.ToString())); | |||
| 
 | |||
|             return full.ToString(); | |||
|         } | |||
| 
 | |||
|         internal static bool IsRectModeSupported(RectSourceMode mode) { | |||
|             switch (mode) { | |||
|                 case RectSourceMode.Display1: | |||
|                 case RectSourceMode.Display2: | |||
|                 case RectSourceMode.Display3: | |||
|                 case RectSourceMode.Display4: | |||
|                 case RectSourceMode.Display5: | |||
|                 case RectSourceMode.Display6: | |||
|                 case RectSourceMode.Display7: | |||
|                 case RectSourceMode.Display8: | |||
|                 case RectSourceMode.Span: | |||
|                 case RectSourceMode.WindowDisplay: | |||
|                     return FullscreenUtility.IsWindows; | |||
| 
 | |||
|                 case RectSourceMode.MainDisplay: | |||
|                 case RectSourceMode.AtMousePosition: | |||
|                     return true; | |||
| 
 | |||
|                 case RectSourceMode.Custom: | |||
|                     // Custom rect is not supported on Linux
 | |||
|                     // since we're using native fullscreen
 | |||
|                     return !FullscreenUtility.IsLinux; | |||
| 
 | |||
|                 default: | |||
|                     return false; | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 75ef3c7b609ce8c409782a523b7ea64a | |||
| timeCreated: 1508376099 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,193 @@ | |||
| using System; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| using Object = UnityEngine.Object; | |||
| 
 | |||
| using FullscreenEditor.Windows; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Helper for getting fullscreen rectangles.</summary>
 | |||
|     public static class FullscreenRects { | |||
| 
 | |||
|         /// <summary>Represents a callback for user defined fullscreen rect calculation.</summary>
 | |||
|         /// <param name="mode">The mode set in <see cref="FullscreenPreferences.RectSource"/></param>
 | |||
|         /// <param name="rect">A rect calculated based on custom logic.</param>
 | |||
|         /// <returns>Whether the rect calculated should be used or not.</returns>
 | |||
|         public delegate bool FullscreenRectCallback(RectSourceMode mode, out Rect rect); | |||
| 
 | |||
|         /// <summary>The number of monitors attached to this machine, returns -1 if the platform is not supported.</summary>
 | |||
|         public static int ScreenCount { | |||
|             get { | |||
|                 if (!FullscreenUtility.IsWindows) | |||
|                     return -1; | |||
|                 const int SM_CMONITORS = 80; | |||
|                 return User32.GetSystemMetrics(SM_CMONITORS); | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Custom callback to allow the user to specify their own logic to how fullscreens will be arranged.
 | |||
|         /// Check the documentation for usage examples.</summary>
 | |||
|         public static FullscreenRectCallback CustomRectCallback { get; set; } | |||
| 
 | |||
|         /// <summary>Returns a fullscreen rect</summary>
 | |||
|         /// <param name="mode">The mode that will be used to retrieve the rect.</param>
 | |||
|         /// <param name="targetWindow">The window that will be set fullscreen.</param>
 | |||
|         public static Rect GetFullscreenRect(RectSourceMode mode, ScriptableObject targetWindow = null) { | |||
| 
 | |||
|             if (targetWindow != null && !targetWindow.IsOfType(typeof(EditorWindow)) && !targetWindow.IsOfType(Types.View)) { | |||
|                 throw new ArgumentException("Target window must be of type EditorWindow or View or null", "targetWindow"); | |||
|             } | |||
| 
 | |||
|             if (CustomRectCallback != null) { | |||
|                 var rect = new Rect(); | |||
|                 var shouldUse = CustomRectCallback(mode, out rect); | |||
| 
 | |||
|                 if (shouldUse) | |||
|                     return rect; | |||
|             } | |||
| 
 | |||
|             switch (mode) { | |||
|                 case RectSourceMode.MainDisplay: | |||
|                     return GetMainDisplayRect(); | |||
| 
 | |||
|                 case RectSourceMode.WindowDisplay: | |||
|                     if (targetWindow == null || !FullscreenUtility.IsWindows) | |||
|                         return GetMainDisplayRect(); | |||
| 
 | |||
|                     var views = new ViewPyramid(targetWindow); | |||
|                     var rect = views.Container.GetPropertyValue<Rect>("position"); | |||
| 
 | |||
|                     return GetDisplayBoundsAtPoint(rect.center); | |||
| 
 | |||
|                 case RectSourceMode.AtMousePosition: | |||
|                     return FullscreenUtility.IsWindows ? | |||
|                         GetDisplayBoundsAtPoint(FullscreenUtility.MousePosition) : | |||
|                         GetWorkAreaRect(true); | |||
| 
 | |||
|                 case RectSourceMode.Span: | |||
|                     return FullscreenUtility.IsWindows ? | |||
|                         GetVirtualScreenBounds() : | |||
|                         GetWorkAreaRect(true); | |||
| 
 | |||
|                 case RectSourceMode.Custom: | |||
|                     return GetCustomUserRect(); | |||
| 
 | |||
|                 case RectSourceMode.Display1: | |||
|                     return GetMonitorRect(0); | |||
|                 case RectSourceMode.Display2: | |||
|                     return GetMonitorRect(1); | |||
|                 case RectSourceMode.Display3: | |||
|                     return GetMonitorRect(2); | |||
|                 case RectSourceMode.Display4: | |||
|                     return GetMonitorRect(3); | |||
|                 case RectSourceMode.Display5: | |||
|                     return GetMonitorRect(4); | |||
|                 case RectSourceMode.Display6: | |||
|                     return GetMonitorRect(5); | |||
|                 case RectSourceMode.Display7: | |||
|                     return GetMonitorRect(6); | |||
|                 case RectSourceMode.Display8: | |||
|                     return GetMonitorRect(7); | |||
| 
 | |||
|                 default: | |||
|                     Logger.Warning("Invalid fullscreen mode, please fix this by changing the placement source mode in preferences."); | |||
|                     return new Rect(Vector2.zero, Vector2.one * 300f); | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns a rect with the dimensions of the main screen.
 | |||
|         /// (Note that the position may not be right for multiple screen setups)</summary>
 | |||
|         public static Rect GetMainDisplayRect() { | |||
| 
 | |||
|             if (FullscreenUtility.IsWindows) { | |||
|                 var mainDisplay = DisplayInfo | |||
|                     .GetDisplays() | |||
|                     .FirstOrDefault(d => d.PrimaryDisplay); | |||
| 
 | |||
|                 if (mainDisplay != null) | |||
|                     return mainDisplay.UnityCorrectedArea; | |||
| 
 | |||
|                 Logger.Error("No main display??? This should not happen, falling back to Screen.currentResolution"); | |||
|             } | |||
| 
 | |||
|             // Screen.currentResolution returns the resolution of the screen where
 | |||
|             // the currently focused window is located, not the main display resolution. 
 | |||
|             // This caused the bug #53 on windows.
 | |||
|             // The same behaviour was not tested on Linux as macOS
 | |||
|             return new Rect(0f, 0f, Screen.currentResolution.width, Screen.currentResolution.height); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns the rect of a given display index.</summary>
 | |||
|         public static Rect GetMonitorRect(int index) { | |||
| 
 | |||
|             if (!FullscreenUtility.IsWindows) | |||
|                 return GetMainDisplayRect(); | |||
| 
 | |||
|             var d = DisplayInfo.GetDisplay(index); | |||
| 
 | |||
|             if (d == null) { | |||
|                 Logger.Error("Display {0} not connected", index + 1); | |||
|                 return GetMainDisplayRect(); | |||
|             } | |||
| 
 | |||
|             return d.UnityCorrectedArea; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns a rect defined by the user in the preferences.</summary>
 | |||
|         public static Rect GetCustomUserRect() { | |||
|             return FullscreenPreferences.CustomRect; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns a rect covering all the screen, except for the taskbar/dock.
 | |||
|         /// On Windows it adds a 4px border and does not account for scaling (can cause bugs when using scales different than 100%).
 | |||
|         /// On macOS this returns a fullscreen rect when the main window is maximized and mouseScreen is set to true.</summary>
 | |||
|         /// <param name="mouseScreen">Should we get the rect on the screen where the mouse pointer is?</param>
 | |||
|         public static Rect GetWorkAreaRect(bool mouseScreen) { | |||
|             return Types.ContainerWindow.InvokeMethod<Rect>("FitRectToScreen", new Rect(Vector2.zero, Vector2.one * 10000f), true, mouseScreen); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns a rect covering all the screen, except for the taskbar/dock.
 | |||
|         /// On Windows it adds a 4px border and does not account for scaling (can cause bugs when using scales different than 100%).
 | |||
|         /// On macOS this returns a fullscreen rect when the main window is maximized and mouseScreen is set to true.</summary>
 | |||
|         /// <param name="container">The ContainerWindow that will be used as reference for calulating border error.</param>
 | |||
|         /// <param name="mouseScreen">Should we get the rect on the screen where the mouse pointer is?</param>
 | |||
|         public static Rect GetWorkAreaRect(Object container, bool mouseScreen) { | |||
|             return container.InvokeMethod<Rect>("FitWindowRectToScreen", new Rect(Vector2.zero, Vector2.one * 10000f), true, mouseScreen); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns the bounds rect of the screen that contains the given point. (Windows only)</summary>
 | |||
|         /// <param name="point">The point relative to <see cref="RectSourceMode.Span"/></param>
 | |||
|         public static Rect GetDisplayBoundsAtPoint(Vector2 point) { | |||
|             return InternalEditorUtility.GetBoundsOfDesktopAtPoint(point); | |||
|         } | |||
| 
 | |||
|         /// <summary>Full virtual screen bounds, spanning across all monitors. (Windows only)</summary>
 | |||
|         public static Rect GetVirtualScreenBounds() { | |||
| 
 | |||
|             if (!FullscreenUtility.IsWindows) | |||
|                 throw new NotImplementedException(); | |||
| 
 | |||
|             const int SM_XVIRTUALSCREEN = 76; | |||
|             const int SM_YVIRTUALSCREEN = 77; | |||
|             const int SM_CXVIRTUALSCREEN = 78; | |||
|             const int SM_CYVIRTUALSCREEN = 79; | |||
| 
 | |||
|             var x = User32.GetSystemMetrics(SM_XVIRTUALSCREEN); | |||
|             var y = User32.GetSystemMetrics(SM_YVIRTUALSCREEN); | |||
|             var width = User32.GetSystemMetrics(SM_CXVIRTUALSCREEN); | |||
|             var height = User32.GetSystemMetrics(SM_CYVIRTUALSCREEN); | |||
| 
 | |||
|             var rect = new Rect { | |||
|                 yMin = y, | |||
|                 xMin = x, | |||
|                 width = width, | |||
|                 height = height, | |||
|             }; | |||
| 
 | |||
|             return FullscreenUtility.DpiCorrectedArea(rect); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 6b84448503834b34ab16a1e3be4d97fd | |||
| timeCreated: 1509127111 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,432 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| using UnityObject = UnityEngine.Object; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
| 
 | |||
|     /// <summary>Clone of the internal UnityEditor.ShowMode.</summary>
 | |||
|     public enum ShowMode { | |||
|         /// <summary>Show as a normal window with max, min & close buttons.</summary>
 | |||
|         NormalWindow = 0, | |||
|         /// <summary>Used for a popup menu. On mac this means light shadow and no titlebar.</summary>
 | |||
|         PopupMenu = 1, | |||
|         /// <summary>Utility window - floats above the app. Disappears when app loses focus.</summary>
 | |||
|         Utility = 2, | |||
|         /// <summary>Window has no shadow or decorations. Used internally for dragging stuff around.</summary>
 | |||
|         NoShadow = 3, | |||
|         /// <summary>The Unity main window. On mac, this is the same as NormalWindow, except window doesn't have a close button.</summary>
 | |||
|         MainWindow = 4, | |||
|         /// <summary>Aux windows. The ones that close the moment you move the mouse out of them.</summary>
 | |||
|         AuxWindow = 5, | |||
|         /// <summary>Like PopupMenu, but without keyboard focus.</summary>
 | |||
|         Tooltip = 6, | |||
|         // Show as fullscreen window
 | |||
|         Fullscreen = 8 | |||
|     } | |||
| 
 | |||
|     /// <summary>Helper class for suppressing unity logs when calling a method that may show unwanted logs.</summary>
 | |||
|     internal class SuppressLog : IDisposable { | |||
| 
 | |||
|         private readonly bool lastState; | |||
| 
 | |||
|         internal static ILogger Logger { | |||
|             get { | |||
| #if UNITY_2017_1_OR_NEWER
 | |||
|                 return Debug.unityLogger; | |||
| #else
 | |||
|                 return Debug.logger; | |||
| #endif
 | |||
|             } | |||
|         } | |||
| 
 | |||
|         public SuppressLog() { | |||
|             lastState = Logger.logEnabled; | |||
|             Logger.logEnabled = false; | |||
|         } | |||
| 
 | |||
|         public void Dispose() { | |||
|             Logger.logEnabled = lastState; | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
|     /// <summary>Miscellaneous utilities for Fullscreen Editor.</summary>
 | |||
|     [InitializeOnLoad] | |||
|     public static class FullscreenUtility { | |||
| 
 | |||
|         /// <summary>Contains a Texture2D icon loaded from a base64.</summary>
 | |||
|         public class Icon { | |||
| 
 | |||
|             private string m_base64; | |||
|             private Texture2D m_texture; | |||
| 
 | |||
|             public Texture2D Texture { | |||
|                 get { return m_texture ? m_texture : (m_texture = FindOrLoadTexture(m_base64)); } | |||
|             } | |||
| 
 | |||
|             public Icon(string base64) { | |||
|                 m_base64 = base64; | |||
|             } | |||
| 
 | |||
|             public Icon(string base64, string proVariantBase64) { | |||
|                 m_base64 = EditorGUIUtility.isProSkin ? proVariantBase64 : base64; | |||
|             } | |||
| 
 | |||
|             public static implicit operator Texture2D(Icon icon) { | |||
|                 return icon.Texture; | |||
|             } | |||
| 
 | |||
|         } | |||
| 
 | |||
|         private static Vector2 mousePosition; | |||
| 
 | |||
|         public static bool IsWindows { get { return Application.platform == RuntimePlatform.WindowsEditor; } } | |||
|         public static bool IsMacOS { get { return Application.platform == RuntimePlatform.OSXEditor; } } | |||
|         public static bool IsLinux { get { return Application.platform == RuntimePlatform.LinuxEditor; } } | |||
| 
 | |||
|         static FullscreenUtility() { | |||
| 
 | |||
|             var lastUpdate = EditorApplication.timeSinceStartup; | |||
| 
 | |||
|             EditorApplication.update += () => { | |||
|                 if(EditorApplication.timeSinceStartup - lastUpdate > 0.5f && FullscreenPreferences.RectSource.Value == RectSourceMode.AtMousePosition) { | |||
|                     EditorApplication.RepaintHierarchyWindow(); | |||
|                     EditorApplication.RepaintProjectWindow(); | |||
|                     lastUpdate = EditorApplication.timeSinceStartup; | |||
|                 } | |||
|             }; | |||
| 
 | |||
|             EditorApplication.hierarchyWindowItemOnGUI += (rect, id) => RecalculateMousePosition(); | |||
|             EditorApplication.projectWindowItemOnGUI += (rect, id) => RecalculateMousePosition(); | |||
| 
 | |||
| #if UNITY_2019_1_OR_NEWER
 | |||
|             SceneView.duringSceneGui += sceneView => RecalculateMousePosition(); | |||
| #else
 | |||
|             SceneView.onSceneGUIDelegate += sceneView => RecalculateMousePosition(); | |||
| #endif
 | |||
| 
 | |||
|         } | |||
| 
 | |||
|         private static void RecalculateMousePosition() { | |||
|             mousePosition = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns wheter the extension is running with debugging enabled.</summary>
 | |||
|         public static bool DebugModeEnabled { | |||
|             get { | |||
| #if FULLSCREEN_DEBUG
 | |||
|                 return true; | |||
| #else
 | |||
|                 return false; | |||
| #endif
 | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>The mouse position, can be called outside an OnGUI method.</summary>
 | |||
|         public static Vector2 MousePosition { get { return mousePosition; } } | |||
| 
 | |||
|         /// <summary>The icon of this plugin.</summary>
 | |||
|         public static readonly Icon FullscreenIcon = new Icon( | |||
|             "iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAMAAABgZ9sFAAAApVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+4/eNVAAAANnRSTlMA/AfHA2sLtBTy8MxYO/nk9u3Qj06wNCceGeCppF9LPhDot5iBewHYn1JDQS8sIyCJdkdluZF8FV+iAAAB6klEQVRIx+2SWbKbMBBFJcAYC8xswAMznme/pPe/tNASLgImVH5TeefLvhxaouuS/xTpHrwi0nJ/BZE04O2IeomrcqHnTL+RN4HLLH16qOJr/53YYiYI9pcmS/MmMdk+6OoTaLHOPErsNpqlPZ2KnIJpMzerk0x3bAq00efDujw5BuFNws+OwsCY7Md169SJfTaug/nVhsoWYFRHtkqTqZ4I6LA+e1j8qScJe8NHuB4M6+brtgJkreJmZPHty/hjkWLJpk9CmU/UJaLxk8y1RgwAcELSxbfA9gnR+A3WClGnAMAqlaC+OpE+FxcSvPGWwkbBHwXkxx1BXQ7JJ8upz1s5qS+ARAdRBvw/iDSYquSfRflDbCz4k6wyhGEUGl+Y7g9t5QlT1O4LyHlBjgAF7ltzaSx9LH1DAaenOpY+qGfPsF5pretgP3rL3G0AUDcsQOrjRdVZjDpA2bFFQ35ITxMdmmD0Jbo+yVDXezdHXcZmt31KHEA8Fx996G/283c4F+2nY7q8bNOry6MRfd3ZmVaM67OHMQ+jDHe1vF1T4ycd1SlQ07F8jLycmZQOTsdFthw0jKIFIMPTD8yGhlJpRpTvxHaKfsHux225kFcOq9rMc5yVvPGeSUR2Q6VcXk7z3888n68a+eav+AWMDWJNSUXp9QAAAABJRU5ErkJggg==" | |||
|         ); | |||
| 
 | |||
|         /// <summary>A smaller icon of this plugin.</summary>
 | |||
|         public static readonly Icon FullscreenIconSmall = new Icon( | |||
|             "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAclBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmbn+Ue6AAAAJXRSTlMABfscDPdX3LmNgF9IKxPtoXdvQxiumWlTJPLjkYcx6M3KxrM+6Owz4AAAAKJJREFUCNdlzlkOwyAMBNDBkECALDRkb9LV979infazliz5eX4GS/Cd6wFE1/mQcWHm4qmhH4Vc5WmaGwXVBPq5pTYCmFdqxRNfPFOJwJx2sX55pK3CaCdUQwZMhtJKFtAG/5PVNzeS1wo47rWqbG+cLUHJoOLD8SY/pnijN67csl0A5bkoqBfzuki/xnTM4vHs3xlEe/aPqKd9GGoAwY1pbj58lQvfBIytyAAAAABJRU5ErkJggg==", | |||
|             "iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAAAclBMVEUAAAC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSyGfY5AAAAJXRSTlMABfsM91ccGNy5jYBfSCsT7aF3b0OumWlTJPLjkYcx6M3KxrM+ZTSY8wAAAKJJREFUCNdljtsSgyAMRJegAgIqeNdqr/n/X2ycPjYzO5Oz+3KwBp/cACC65INBz8zFy6B6FvKVF9PSKqg20I876iKAZaNOeObeM5UIzPkQrt4eea8x2Rn1aABtoColASqN/zOySbTsjQLOR6NqO2hnS1DWqPl0vEvHFO/0wY07tiugPBcFDcK8reLX6sQsPF3+SSPayz+imY9xbAAEN+Wl/QKAZAvuNVnEigAAAABJRU5ErkJggg==" | |||
|         ); | |||
| 
 | |||
|         /// <summary>The icon to show on the game view toolbar when fullscreen on play is enabled.</summary>
 | |||
|         public static readonly Icon FullscreenOnPlayIcon = new Icon( | |||
|             "iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAZlBMVEUAAAAZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRnaqT0eAAAAIXRSTlMA9rSVTunBWTwgFNzOrvvYo4x1ZGA4MALTx6eae3dTCgkQ40nLAAAAcklEQVQY053OORaFIBBE0WpsBQSchz+qvf9NSmCHJlZ4g3cKj8ddbD3q6pWmSwqRnjH/SIxKQ1WN4yOkMpSuDKuhLX41ZOEt3IJxxe0mLA7Ww0LLb87NkGvDJYYk7fkPNYWK0H8G9yIqY2rzHx9ix3i6E/A3BM5M0CXHAAAAAElFTkSuQmCC", | |||
|             "iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAY1BMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+aRQ2gAAAAIHRSTlMA9rSV6cFZPCAU3M6uT/vYo4x1ZGBMODAJ08enmnt3U4mFYqQAAABvSURBVBjTnc43AoAwDATBkwHbOJBz1P9fiQvUUqByitXh98XG1xZlsYbhlYy5HTGdxEqkoqLEsjOJdLnJHRTd/pCQhtUwM/qPZwNmA22hIeUtpqZLte4VRRyWtIeqTITpmjC2zCJ9qNMe63wT8fce2Z8EsL04YKYAAAAASUVORK5CYII=" | |||
|         ); | |||
| 
 | |||
|         /// <summary>Show the notification of a newly opened <see cref="FullscreenView"/> about using the shortcut to close.</summary>
 | |||
|         /// <param name="window">The fullscreen window.</param>
 | |||
|         /// <param name="menuItemPath">The <see cref="MenuItem"/> path containing a shortcut.</param>
 | |||
|         internal static void ShowFullscreenExitNotification(EditorWindow window, string menuItemPath) { | |||
|             if(FullscreenPreferences.DisableNotifications) | |||
|                 return; | |||
| 
 | |||
|             var notification = string.Format("Press {0} to exit fullscreen", TextifyMenuItemShortcut(menuItemPath)); | |||
|             ShowFullscreenNotification(window, notification); | |||
|         } | |||
| 
 | |||
|         /// <summary>Show a fullscreen notification in an <see cref="EditorWindow"/>.</summary>
 | |||
|         /// <param name="window">The host of the notification.</param>
 | |||
|         /// <param name="message">The message to show.</param>
 | |||
|         public static void ShowFullscreenNotification(EditorWindow window, string message) { | |||
|             if(!window) | |||
|                 return; | |||
| 
 | |||
|             window.ShowNotification(new GUIContent(message, FullscreenIcon)); | |||
|             window.Repaint(); | |||
| 
 | |||
|             if(EditorWindow.mouseOverWindow) // This definitely made sense when I made it, so I won't remove
 | |||
|                 EditorWindow.mouseOverWindow.Repaint(); | |||
|         } | |||
| 
 | |||
|         /// <summary>Does the given <see cref="MenuItem"/> path contains a key binding?</summary>
 | |||
|         public static bool MenuItemHasShortcut(string menuItemPath) { | |||
|             var index = menuItemPath.LastIndexOf(" "); | |||
| 
 | |||
|             if(index++ == -1) | |||
|                 return false; | |||
| 
 | |||
|             var shortcut = menuItemPath.Substring(index).Replace("_", ""); | |||
|             var evt = Event.KeyboardEvent(shortcut); | |||
| 
 | |||
|             shortcut = InternalEditorUtility.TextifyEvent(evt); | |||
| 
 | |||
|             return !shortcut.Equals("None", StringComparison.InvariantCultureIgnoreCase); | |||
|         } | |||
| 
 | |||
|         /// <summary>Gets a human-readable shortcut.</summary>
 | |||
|         /// <param name="menuItemPath">The <see cref="MenuItem"/> path containing a shortcut.</param>
 | |||
|         /// <returns></returns>
 | |||
|         public static string TextifyMenuItemShortcut(string menuItemPath) { | |||
|             var index = menuItemPath.LastIndexOf(" "); | |||
| 
 | |||
|             if(index++ == -1) | |||
|                 return "None"; | |||
| 
 | |||
|             var shortcut = menuItemPath.Substring(index).Replace("_", ""); | |||
|             var evt = Event.KeyboardEvent(shortcut); | |||
| 
 | |||
|             shortcut = InternalEditorUtility.TextifyEvent(evt); | |||
| 
 | |||
|             return shortcut; | |||
|         } | |||
| 
 | |||
|         private static Texture2D FindOrLoadTexture(string base64) { | |||
|             var found = GetRef<Texture2D>(base64); | |||
| 
 | |||
|             return found ? | |||
|                 found : | |||
|                 LoadTexture(base64); | |||
|         } | |||
| 
 | |||
|         private static Texture2D LoadTexture(string base64) { | |||
|             try { | |||
|                 var texture = new Texture2D(1, 1, TextureFormat.ARGB32, false, true); | |||
|                 var bytes = Convert.FromBase64String(base64); | |||
| 
 | |||
|                 texture.name = base64; | |||
|                 texture.hideFlags = HideFlags.HideAndDontSave; | |||
|                 texture.LoadImage(bytes); | |||
| 
 | |||
|                 return texture; | |||
|             } catch(Exception e) { | |||
|                 Logger.Error("Failed to load texture: {0}", e); | |||
|                 return null; | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Find an object by it's name and type.</summary>
 | |||
|         /// <param name="name">The name of the object to search for.</param>
 | |||
|         /// <typeparam name="T">The type of the object to search for.</typeparam>
 | |||
|         public static T GetRef<T>(string name) where T : UnityObject { | |||
|             return Resources.FindObjectsOfTypeAll<T>().FirstOrDefault(obj => obj.name == name); | |||
|         } | |||
| 
 | |||
|         /// <summary>Find an object by it's name and type.</summary>
 | |||
|         /// <param name="name">The name of the object to search for.</param>
 | |||
|         /// <param name="type">The type of the object to search for.</param> 
 | |||
|         public static UnityObject GetRef(Type type, string name) { | |||
|             return Resources.FindObjectsOfTypeAll(type).FirstOrDefault(obj => obj.name == name); | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the main view.</summary>
 | |||
|         public static ScriptableObject GetMainView() { | |||
|             var containers = Resources.FindObjectsOfTypeAll(Types.MainView); | |||
| 
 | |||
|             if(containers.Length > 0) | |||
|                 return containers[0] as ScriptableObject; | |||
| 
 | |||
|             throw new Exception("Couldn't find main view"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the main game view.</summary> 
 | |||
|         public static EditorWindow GetMainGameView() { | |||
|             if(Types.GameView.HasMethod("GetMainGameView")) { // Removed in 2019.3 alpha
 | |||
|                 return Types.GameView.InvokeMethod<EditorWindow>("GetMainGameView"); | |||
|             } else if(Types.PreviewEditorWindow.HasMethod("GetMainPreviewWindow")) { // Removed in 2019.3 beta
 | |||
|                 return Types.PreviewEditorWindow.InvokeMethod<EditorWindow>("GetMainPreviewWindow"); | |||
|             } else { // if (Types.PlayModeView.HasMethod("GetMainPlayModeView"))
 | |||
|                 return Types.PlayModeView.InvokeMethod<EditorWindow>("GetMainPlayModeView"); | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Get all the game views. This returns even the docked game views which are not visible.</summary> 
 | |||
|         public static EditorWindow[] GetGameViews() { | |||
|             return Resources | |||
|                 .FindObjectsOfTypeAll(Types.GameView) | |||
|                 .Cast<EditorWindow>() | |||
|                 .ToArray(); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns the focused view if it is a dock area with more than one tab, otherwise, returns the focused window.</summary> 
 | |||
|         public static ScriptableObject GetFocusedViewOrWindow() { | |||
|             var mostSpecificView = Types.GUIView.GetPropertyValue<ScriptableObject>("focusedView"); | |||
| 
 | |||
|             if(!mostSpecificView) | |||
|                 return null; | |||
| 
 | |||
|             // The most specific obj is a window, open it instead of the view
 | |||
|             if(mostSpecificView.IsOfType(Types.HostView, false)) | |||
|                 return EditorWindow.focusedWindow; | |||
| 
 | |||
|             var viewHierarchy = GetViewHierarchy(mostSpecificView); | |||
|             var leastSpecificView = viewHierarchy.LastOrDefault(); | |||
| 
 | |||
|             // The view hierarchy has the same length of all the views in this ContainerWindow
 | |||
|             // This means there are no cousins views handled by a split group on the surroundings of this one
 | |||
|             // So, we're alone on the container
 | |||
|             var alone = leastSpecificView.GetPropertyValue<Array>("allChildren").Length == viewHierarchy.Length; | |||
| 
 | |||
|             if(alone && EditorWindow.focusedWindow.InvokeMethod<int>("GetNumTabs") > 1) | |||
|                 alone = false; // But, we may not be the only tab on the host view
 | |||
| 
 | |||
|             // If the focused view is in the main view, or we are the only child on this view, then we open the window
 | |||
|             return alone || leastSpecificView.IsOfType(Types.MainView) ? | |||
|                 EditorWindow.focusedWindow : | |||
|                 leastSpecificView; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns all the parents of a given view.</summary>
 | |||
|         public static ScriptableObject[] GetViewHierarchy(ScriptableObject view) { | |||
|             if(!view) | |||
|                 return new ScriptableObject[0]; | |||
| 
 | |||
|             view.EnsureOfType(Types.View); | |||
| 
 | |||
|             var list = new List<ScriptableObject>() { view }; | |||
|             var parent = view.GetPropertyValue<ScriptableObject>("parent"); | |||
| 
 | |||
|             while(parent) { // Get the least specific view
 | |||
|                 view = parent; | |||
|                 list.Add(view); | |||
|                 parent = view.GetPropertyValue<ScriptableObject>("parent"); | |||
|             } | |||
| 
 | |||
|             return list.ToArray(); | |||
|         } | |||
| 
 | |||
|         /// <summary>Get all the children view of a given view.</summary> 
 | |||
|         public static ScriptableObject[] GetAllViewChildren(ScriptableObject view) { | |||
|             if(!view) | |||
|                 return new ScriptableObject[0]; | |||
| 
 | |||
|             view.EnsureOfType(Types.View); | |||
| 
 | |||
|             return view.GetPropertyValue<Array>("allChildren") | |||
|                 .Cast<ScriptableObject>() | |||
|                 .ToArray(); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns wheter a given view is focused or not.</summary> 
 | |||
|         public static bool IsViewFocused(ScriptableObject view) { | |||
|             if(!view) | |||
|                 return false; | |||
| 
 | |||
|             view.EnsureOfType(Types.View); | |||
| 
 | |||
|             var focused = Types.GUIView.GetPropertyValue<ScriptableObject>("focusedView"); | |||
|             var children = GetAllViewChildren(view); | |||
| 
 | |||
|             return children.Contains(focused); | |||
|         } | |||
| 
 | |||
|         /// <summary>Focus a view.</summary> 
 | |||
|         public static void FocusView(ScriptableObject guiView) { | |||
|             if(!guiView) | |||
|                 return; | |||
| 
 | |||
|             // guiView.EnsureOfType(Types.GUIView);
 | |||
|             if(guiView.IsOfType(Types.GUIView)) | |||
|                 guiView.InvokeMethod("Focus"); | |||
|             else { | |||
|                 var vp = new ViewPyramid(guiView); | |||
|                 var vc = vp.Container; | |||
|                 var methodName = "Internal_BringLiveAfterCreation"; | |||
| 
 | |||
|                 if(vc) { | |||
|                     if(vc.HasMethod(methodName, new Type[] { typeof(bool), typeof(bool), typeof(bool) })) | |||
|                         // displayImmediately, setFocus, showMaximized
 | |||
|                         vc.InvokeMethod(methodName, false, true, false); | |||
|                     else | |||
|                         // displayImmediately, setFocus
 | |||
|                         vc.InvokeMethod(methodName, false, true); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary> Returns the display scaling of the editor, e.g. 1.5 if 125%</summary>
 | |||
|         public static float GetDisplayScaling() { | |||
|             return EditorGUIUtility.pixelsPerPoint; | |||
|         } | |||
| 
 | |||
|         /// <summary> Returns a screen rect corrected to fit the editor scaling</summary>
 | |||
|         public static Rect DpiCorrectedArea(Rect area) { | |||
|             var scaling = GetDisplayScaling(); | |||
|             area.width /= scaling; | |||
|             area.height /= scaling; | |||
|             return area; | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the default height of the editor toolbars.</summary>
 | |||
|         public static float GetToolbarHeight() { | |||
|             try { | |||
|                 if(typeof(EditorGUI).HasField("kWindowToolbarHeight")) { | |||
|                     var result = typeof(EditorGUI).GetFieldValue<object>("kWindowToolbarHeight"); | |||
|                     if(result is int) | |||
|                         return (int)result; | |||
|                     else | |||
|                         return result.GetPropertyValue<float>("value"); | |||
|                 } else | |||
|                     return 17f; // Default on < 2019.3 versions
 | |||
|             } catch(Exception e) { | |||
|                 if(FullscreenUtility.DebugModeEnabled) | |||
|                     Debug.LogException(e); | |||
|                 return 17f; | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Set the default height of the editor toolbars.</summary>
 | |||
|         public static bool SetToolbarHeight(float value) { | |||
|             try { | |||
|                 // On Unity blow 2019.3 this is a const field and cannot be changed
 | |||
|                 if(typeof(EditorGUI).HasField("kWindowToolbarHeight")) { | |||
|                     var result = typeof(EditorGUI).GetFieldValue<object>("kWindowToolbarHeight"); | |||
|                     result.SetFieldValue("m_Value", value); | |||
|                     return true; | |||
|                 } else { | |||
|                     return false; | |||
|                 } | |||
|             } catch(Exception e) { | |||
|                 if(FullscreenUtility.DebugModeEnabled) | |||
|                     Debug.LogException(e); | |||
|                 return false; | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>Set Game View target display.</summary>
 | |||
|         public static void SetGameViewDisplayTarget(EditorWindow gameView, int display) { | |||
|             gameView.EnsureOfType(Types.GameView); | |||
| 
 | |||
|             if(gameView.HasProperty("targetDisplay")) { | |||
|                 gameView.SetPropertyValue("targetDisplay", display); | |||
|             } else if(gameView.HasField("m_TargetDisplay")) { | |||
|                 gameView.SetFieldValue("m_TargetDisplay", display); | |||
|             } else { | |||
|                 Logger.Error("Could not set Game View target display"); | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 7b3d579b1fea3fe48a089150e74369a0 | |||
| timeCreated: 1508966650 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,63 @@ | |||
| using System; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| using ContainerWindow = UnityEngine.ScriptableObject; | |||
| using HostView = UnityEngine.ScriptableObject; | |||
| using View = UnityEngine.ScriptableObject; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     public class FullscreenView : FullscreenContainer { | |||
| 
 | |||
|         protected void SwapViews(View a, View b) { | |||
|             var containerA = a.GetPropertyValue<ContainerWindow>("window"); | |||
|             var containerB = b.GetPropertyValue<ContainerWindow>("window"); | |||
| 
 | |||
|             SetFreezeContainer(containerA, true); | |||
|             SetFreezeContainer(containerB, true); | |||
| 
 | |||
|             Logger.Debug("Swapping views {0} and {1} @ {2} and {3}", a, b, containerA, containerB); | |||
| 
 | |||
|             containerA.SetPropertyValue("rootView", b); | |||
|             containerB.SetPropertyValue("rootView", a); | |||
| 
 | |||
|             SetFreezeContainer(containerA, true); | |||
|             SetFreezeContainer(containerB, true); | |||
|         } | |||
| 
 | |||
|         internal void OpenView(Rect rect, ScriptableObject view) { | |||
|             if(!view) | |||
|                 throw new ArgumentNullException("view"); | |||
| 
 | |||
|             view.EnsureOfType(Types.View); | |||
| 
 | |||
|             if(FullscreenUtility.IsLinux) | |||
|                 throw new PlatformNotSupportedException("Linux does not support fullscreen from View class"); | |||
| 
 | |||
|             if(Fullscreen.GetFullscreenFromView(view)) { | |||
|                 Logger.Debug("Tried to fullscreen a view already in fullscreen"); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             BeforeOpening(); | |||
| 
 | |||
|             var placeholder = CreateInstance<PlaceholderWindow>(); | |||
| 
 | |||
|             m_src = new ViewPyramid(view); | |||
|             m_dst = CreateFullscreenViewPyramid(rect, placeholder); | |||
| 
 | |||
|             SwapViews(m_src.View, m_dst.View); | |||
|             Rect = rect; | |||
| 
 | |||
|             AfterOpening(); | |||
|         } | |||
| 
 | |||
|         public override void Close() { | |||
| 
 | |||
|             if(m_src.View && m_dst.View) | |||
|                 SwapViews(m_src.View, m_dst.View); // Swap back the source view
 | |||
| 
 | |||
|             base.Close(); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 050fcc381b31f914db19a2e191357a66 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,234 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| using ContainerWindow = UnityEngine.ScriptableObject; | |||
| using HostView = UnityEngine.ScriptableObject; | |||
| using View = UnityEngine.ScriptableObject; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     public class FullscreenWindow : FullscreenContainer { | |||
| 
 | |||
|         [SerializeField] private RectOffset m_rectOffset; | |||
|         [SerializeField] private RectOffset m_toolbarOffset; | |||
|         [SerializeField] private bool m_createdByFullscreenOnPlay; | |||
| 
 | |||
|         public RectOffset ClipOffset { | |||
|             get { return m_rectOffset; } | |||
|             set { | |||
|                 if(m_dst.View) { | |||
|                     m_rectOffset = value; | |||
|                     m_dst.View.InvokeMethod("SetPosition", value.Add(new Rect(Vector2.zero, Rect.size))); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         internal bool CreatedByFullscreenOnPlay { | |||
|             get { return m_createdByFullscreenOnPlay; } | |||
|             set { m_createdByFullscreenOnPlay = value; } | |||
|         } | |||
| 
 | |||
|         public bool HasToolbarOffset { get { return ToolbarOffset != null; } } | |||
| 
 | |||
|         public virtual RectOffset ToolbarOffset { | |||
|             get { | |||
|                 if(m_toolbarOffset == null) | |||
|                     m_toolbarOffset = new RectOffset(0, 0, (int)FullscreenUtility.GetToolbarHeight(), 0); | |||
|                 return m_toolbarOffset; | |||
|             } | |||
|         } | |||
| 
 | |||
|         private void SwapWindows(EditorWindow a, EditorWindow b) { | |||
|             var parentA = a.GetFieldValue<View>("m_Parent"); | |||
|             var parentB = b.GetFieldValue<View>("m_Parent"); | |||
| 
 | |||
|             var containerA = parentA.GetPropertyValue<ContainerWindow>("window"); | |||
|             var containerB = parentB.GetPropertyValue<ContainerWindow>("window"); | |||
| 
 | |||
|             var selectedPaneA = parentA.GetPropertyValue<EditorWindow>("actualView"); | |||
|             var selectedPaneB = parentB.GetPropertyValue<EditorWindow>("actualView"); | |||
| 
 | |||
|             SetFreezeContainer(containerA, true); | |||
|             SetFreezeContainer(containerB, true); | |||
| 
 | |||
|             Logger.Debug("Swapping windows {0} and {1} @ {2} and {3}", a, b, parentA, parentB); | |||
| 
 | |||
|             parentA.SetPropertyValue("actualView", b); | |||
|             parentB.SetPropertyValue("actualView", a); | |||
| 
 | |||
|             ReplaceDockAreaPane(parentA, a, b); | |||
|             ReplaceDockAreaPane(parentB, b, a); | |||
| 
 | |||
|             a.InvokeMethod("MakeParentsSettingsMatchMe"); | |||
|             b.InvokeMethod("MakeParentsSettingsMatchMe"); | |||
| 
 | |||
|             if(selectedPaneA != a) | |||
|                 parentA.SetPropertyValue("actualView", selectedPaneA); | |||
|             if(selectedPaneB != b) | |||
|                 parentB.SetPropertyValue("actualView", selectedPaneB); | |||
| 
 | |||
|             SetFreezeContainer(containerA, false); | |||
|             SetFreezeContainer(containerB, false); | |||
|         } | |||
| 
 | |||
|         protected void ReplaceDockAreaPane(View dockArea, EditorWindow originalPane, EditorWindow newPane) { | |||
|             if(dockArea.HasField("m_Panes")) { | |||
|                 var dockedPanes = dockArea.GetFieldValue<List<EditorWindow>>("m_Panes"); | |||
|                 var dockIndex = dockedPanes.IndexOf(originalPane); | |||
|                 dockedPanes[dockIndex] = newPane; | |||
|             } | |||
|         } | |||
| 
 | |||
|         public void SetToolbarStatus(bool toolbarVisible) { | |||
|             if(!HasToolbarOffset) | |||
|                 return; | |||
| 
 | |||
|             if(FullscreenPreferences.UseGlobalToolbarHiding) | |||
|                 return; | |||
| 
 | |||
|             ClipOffset = toolbarVisible ? new RectOffset() : ToolbarOffset; | |||
|         } | |||
| 
 | |||
|         public override void Focus() { | |||
|             var window = ActualViewPyramid.Window; | |||
| 
 | |||
|             if(window) | |||
|                 window.Focus(); | |||
|             else | |||
|                 base.Focus(); | |||
|         } | |||
| 
 | |||
|         public override bool IsFocused() { | |||
|             return EditorWindow.focusedWindow && EditorWindow.focusedWindow == ActualViewPyramid.Window; | |||
|         } | |||
| 
 | |||
|         protected override void AfterOpening() { | |||
|             base.AfterOpening(); | |||
| 
 | |||
|             Focus(); | |||
| 
 | |||
|             if(m_src.Window) | |||
|                 m_dst.Window.titleContent = m_src.Window.titleContent; // Copy the title of the window to the placeholder
 | |||
| 
 | |||
|             SetToolbarStatus(FullscreenPreferences.ToolbarVisible); // Hide/show the toolbar
 | |||
|             // macOS doesn't like fast things, so we'll wait a bit and do it again
 | |||
|             // Looks like Linux does not like it too
 | |||
|             After.Milliseconds(100d, () => SetToolbarStatus(FullscreenPreferences.ToolbarVisible)); | |||
| 
 | |||
|             var notificationWindow = ActualViewPyramid.Window; | |||
| 
 | |||
|             After.Milliseconds(50d, () => { | |||
|                 if(!notificationWindow) // Might have been closed
 | |||
|                     return; | |||
| 
 | |||
|                 var menuItemPath = string.Empty; | |||
|                 if(notificationWindow.IsOfType(Types.GameView)) { | |||
|                     menuItemPath = Fullscreen | |||
|                         .GetAllFullscreen() | |||
|                         .Where(fs => fs.ActualViewPyramid.Window && fs.ActualViewPyramid.Window.IsOfType(Types.GameView)) | |||
|                         .Count() > 1 ? | |||
|                         Shortcut.MOSAIC_PATH : | |||
|                         Shortcut.GAME_VIEW_PATH; | |||
|                 } else if(notificationWindow is SceneView) | |||
|                     menuItemPath = Shortcut.SCENE_VIEW_PATH; | |||
|                 else | |||
|                     menuItemPath = Shortcut.CURRENT_VIEW_PATH; | |||
| 
 | |||
|                 FullscreenUtility.ShowFullscreenExitNotification(notificationWindow, menuItemPath); | |||
|             }); | |||
| 
 | |||
|         } | |||
| 
 | |||
|         protected override void OnEnable() { | |||
|             base.OnEnable(); | |||
|             FullscreenPreferences.ToolbarVisible.OnValueSaved += SetToolbarStatus; | |||
|         } | |||
| 
 | |||
|         protected override void OnDisable() { | |||
|             base.OnDisable(); | |||
|             FullscreenPreferences.ToolbarVisible.OnValueSaved -= SetToolbarStatus; | |||
|         } | |||
| 
 | |||
|         internal void OpenWindow<T>(Rect rect, T window = null) where T : EditorWindow { | |||
|             OpenWindow(rect, typeof(T), window); | |||
|         } | |||
| 
 | |||
|         internal void OpenWindow(Rect rect, Type type, EditorWindow window = null, bool disposableWindow = false) { | |||
|             if(type == null) | |||
|                 throw new ArgumentNullException("type"); | |||
| 
 | |||
|             if(!type.IsOfType(typeof(EditorWindow))) | |||
|                 throw new ArgumentException("Type must be inherited from UnityEditor.EditorWindow", "type"); | |||
| 
 | |||
|             if(window is PlaceholderWindow) { | |||
|                 FullscreenUtility.ShowFullscreenNotification(window, "Wanna fullscreen the placeholder?\nSorry, not possible"); | |||
|                 Logger.Debug("Tried to fullscreen a placeholder window"); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             if(Fullscreen.GetFullscreenFromView(window)) { | |||
|                 FullscreenUtility.ShowFullscreenNotification(window, "You can't fullscreen a window already in fullscreen"); | |||
|                 Logger.Debug("Tried to fullscreen a view already in fullscreen"); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             if(window && window.HasProperty("isFullscreen") && window.GetPropertyValue<bool>("isFullscreen")) { | |||
|                 Logger.Debug("Tried to fullscreen a view already using Unity's built-in fullscreen"); | |||
|                 window.ShowNotification(new GUIContent("This is a built-in fullscreen and not a Fullscreen Editor instance. Use Ctrl+Shift+F7 or Alt-F4 to close it.")); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             BeforeOpening(); | |||
| 
 | |||
|             if(window) | |||
|                 m_src = new ViewPyramid(window); | |||
| 
 | |||
|             var childWindow = window ? | |||
|                 (EditorWindow)CreateInstance<PlaceholderWindow>() : | |||
|                 (EditorWindow)CreateInstance(type); // Instantiate a new window for this fullscreen
 | |||
| 
 | |||
|             m_dst = CreateFullscreenViewPyramid(rect, childWindow); | |||
| 
 | |||
|             if(window) // We can't swap the src window if we didn't create a placeholder window
 | |||
|                 SwapWindows(m_src.Window, m_dst.Window); | |||
| 
 | |||
|             Rect = rect; | |||
| 
 | |||
|             if(disposableWindow && childWindow is PlaceholderWindow) { | |||
|                 childWindow.Close(); // Close the pyramid we created because disposable views are not restore later
 | |||
|                 m_dst.Window = m_src.Window; | |||
|             } | |||
| 
 | |||
|             AfterOpening(); | |||
|         } | |||
| 
 | |||
|         internal bool IsPlaceholderVisible() { | |||
|             if(!(m_dst.Window is PlaceholderWindow)) | |||
|                 return false; | |||
| 
 | |||
|             var pyramid = new ViewPyramid(m_dst.Window); | |||
| 
 | |||
|             if(!pyramid.View || !pyramid.View.IsOfType(Types.HostView)) | |||
|                 return false; | |||
| 
 | |||
|             var actualView = pyramid.View.GetPropertyValue<View>("actualView"); | |||
| 
 | |||
|             return actualView == m_dst.Window; | |||
|         } | |||
| 
 | |||
|         public override void Close() { | |||
| 
 | |||
|             var shouldRefocus = IsFocused() && IsPlaceholderVisible(); | |||
| 
 | |||
|             if(m_src.Window && m_dst.Window) | |||
|                 SwapWindows(m_src.Window, m_dst.Window); // Swap back the source window
 | |||
| 
 | |||
|             base.Close(); | |||
| 
 | |||
|             if(shouldRefocus && m_src.Window) | |||
|                 m_src.Window.Focus(); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 3b311009b9693cf4f89b79d7ce034d70 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,18 @@ | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     [InitializeOnLoad] | |||
|     // Issues #98 #96 #97 and #99
 | |||
|     public class GameViewLowResolutionAspectRatios { | |||
| 
 | |||
|         static GameViewLowResolutionAspectRatios() { | |||
|             FullscreenCallbacks.afterFullscreenOpen += fs => { | |||
|                 var window = fs.ActualViewPyramid.Window; | |||
| 
 | |||
|                 if (window && window.HasProperty("lowResolutionForAspectRatios")) | |||
|                     window.SetPropertyValue("lowResolutionForAspectRatios", false); | |||
|             }; | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,7 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 5a92ff5ca1709dd43bd7675f90970e44 | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,35 @@ | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     [InitializeOnLoad] | |||
|     public class GameViewVSync { | |||
| 
 | |||
|         static GameViewVSync() { | |||
|             FullscreenCallbacks.afterFullscreenOpen += (fs) => { | |||
|                 RefreshViewVSync(fs.ActualViewPyramid.Window); | |||
|             }; | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenClose += (fs) => { | |||
|                 RefreshViewVSync(fs.m_src.Window); | |||
|             }; | |||
|         } | |||
| 
 | |||
|         private static void RefreshViewVSync(EditorWindow window) { | |||
|             if (window && window.HasProperty("vSyncEnabled")) { | |||
|                 var vsyncEnabled = window.GetPropertyValue<bool>("vSyncEnabled"); | |||
| 
 | |||
|                 // reset vsync
 | |||
|                 window.SetPropertyValue("vSyncEnabled", vsyncEnabled); | |||
| 
 | |||
|                 var view = new ViewPyramid(window).View; | |||
| 
 | |||
|                 // fallback when above doesn't work
 | |||
|                 if (view.HasMethod("EnableVSync")) | |||
|                     view.InvokeMethod("EnableVSync", vsyncEnabled); | |||
|                 else | |||
|                     Logger.Debug(string.Format("View {0} does not support vsync", view.GetType())); | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: dee0189166f305e43b19aa0dea5568c2 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,41 @@ | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     [InitializeOnLoad] | |||
|     public class GlobalToolbarHiding { | |||
| 
 | |||
|         private static readonly float defaultToolbarHeight; | |||
| 
 | |||
|         private static bool GlobalToolbarShouldBeHidden { | |||
|             get { | |||
|                 return !FullscreenPreferences.ToolbarVisible && | |||
|                     Fullscreen.GetAllFullscreen(false).Length > 0; | |||
|             } | |||
|         } | |||
| 
 | |||
|         static GlobalToolbarHiding() { | |||
|             defaultToolbarHeight = FullscreenUtility.GetToolbarHeight(); | |||
| 
 | |||
|             FullscreenPreferences.UseGlobalToolbarHiding.OnValueSaved += v => { | |||
|                 if (!v) | |||
|                     FullscreenUtility.SetToolbarHeight(defaultToolbarHeight); | |||
|             }; | |||
| 
 | |||
|             FullscreenPreferences.ToolbarVisible.OnValueSaved += v => UpdateGlobalToolbarStatus(); | |||
|             UpdateGlobalToolbarStatus(); | |||
| 
 | |||
|             After.Frames(2, () => // Why? IDK
 | |||
|                 UpdateGlobalToolbarStatus() | |||
|             ); | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenClose += fs => UpdateGlobalToolbarStatus(); | |||
|             FullscreenCallbacks.afterFullscreenOpen += fs => UpdateGlobalToolbarStatus(); | |||
|         } | |||
| 
 | |||
|         public static void UpdateGlobalToolbarStatus() { | |||
|             if (FullscreenPreferences.UseGlobalToolbarHiding) | |||
|                 FullscreenUtility.SetToolbarHeight(GlobalToolbarShouldBeHidden ? 0f : defaultToolbarHeight); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 2d6d3d596f5ac6a45a6a4b9fc0f431c2 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,81 @@ | |||
| using System; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Helper class for enabling/disabling compilation symbols.</summary>
 | |||
|     public static class Integration { | |||
| 
 | |||
|         private static string[] GetAllDefines() { | |||
|             var currentBuildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; | |||
|             var scriptDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentBuildTarget); | |||
|             var split = scriptDefines.Split(new [] { ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); | |||
| 
 | |||
|             return split; | |||
|         } | |||
| 
 | |||
|         private static void SetAllDefines(string[] value) { | |||
|             var currentBuildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; | |||
|             var currentScriptDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentBuildTarget); | |||
|             var scriptDefines = value.Length > 0 ? | |||
|                 value.Aggregate((a, b) => a + ";" + b) : | |||
|                 string.Empty; | |||
| 
 | |||
|             if (currentScriptDefines == scriptDefines) | |||
|                 return; // Nothing has changed
 | |||
| 
 | |||
|             PlayerSettings.SetScriptingDefineSymbolsForGroup(currentBuildTarget, scriptDefines); | |||
| 
 | |||
|             RequestScriptReload(); | |||
|         } | |||
| 
 | |||
|         public static void RequestScriptReload() { | |||
|             if (typeof(EditorUtility).HasMethod("RequestScriptReload")) { | |||
|                 typeof(EditorUtility).InvokeMethod("RequestScriptReload"); | |||
|             } | |||
|             if (typeof(InternalEditorUtility).HasMethod("RequestScriptReload")) { | |||
|                 typeof(InternalEditorUtility).InvokeMethod("RequestScriptReload"); | |||
|             } else { | |||
|                 Logger.Error("Could not reload scripts"); | |||
|             } | |||
| 
 | |||
|         } | |||
| 
 | |||
|         /// <summary>Toggle a given define symbol.</summary>
 | |||
|         /// <param name="directive">The define symbol to toggle.</param>
 | |||
|         public static void ToggleDirectiveDefined(string directive) { | |||
|             var defined = IsDirectiveDefined(directive); | |||
|             SetDirectiveDefined(directive, !defined); | |||
|         } | |||
| 
 | |||
|         /// <summary>Enable or disable a given define symbol.</summary>
 | |||
|         /// <param name="directive">The define symbol to set.</param>
 | |||
|         /// <param name="enabled">Wheter to enable or disable this directive.</param>
 | |||
|         public static void SetDirectiveDefined(string directive, bool enabled) { | |||
|             if (IsDirectiveDefined(directive) == enabled) | |||
|                 return; // Flag already enabled/disabled
 | |||
| 
 | |||
|             if (enabled) | |||
|                 SetAllDefines(GetAllDefines() | |||
|                     .Concat(new [] { directive }) | |||
|                     .ToArray() | |||
|                 ); | |||
|             else | |||
|                 SetAllDefines(GetAllDefines() | |||
|                     .Where(d => d != directive) | |||
|                     .ToArray() | |||
|                 ); | |||
| 
 | |||
|             Logger.Debug("Compiler directive {0} {1} defined", directive, enabled? "": "not"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Get wheter the given directive is enabled or not.</summary>
 | |||
|         /// <param name="directive">The name of the define symbol to check.</param> 
 | |||
|         public static bool IsDirectiveDefined(string directive) { | |||
|             return GetAllDefines().Any(d => d == directive); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: c3d15b472fc220e4aa5fa2cb88e3b16c | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,42 @@ | |||
| using System; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Class containing types of UnityEditor internal classes.</summary>
 | |||
|     public static class Types { | |||
| 
 | |||
|         /// <summary>UnityEditor.HostView</summary>
 | |||
|         public static readonly Type HostView = ReflectionUtility.FindClass("UnityEditor.HostView"); | |||
| 
 | |||
|         /// <summary>UnityEditor.ContainerWindow</summary>
 | |||
|         public static readonly Type ContainerWindow = ReflectionUtility.FindClass("UnityEditor.ContainerWindow"); | |||
| 
 | |||
|         /// <summary>UnityEditor.View</summary>
 | |||
|         public static readonly Type View = ReflectionUtility.FindClass("UnityEditor.View"); | |||
| 
 | |||
|         /// <summary>UnityEditor.GUIView</summary>
 | |||
|         public static readonly Type GUIView = ReflectionUtility.FindClass("UnityEditor.GUIView"); | |||
| 
 | |||
|         /// <summary>UnityEditor.GameView</summary>
 | |||
|         public static readonly Type GameView = ReflectionUtility.FindClass("UnityEditor.GameView"); | |||
| 
 | |||
|         /// <summary>UnityEditor.PreviewEditorWindow</summary>
 | |||
|         public static readonly Type PreviewEditorWindow = ReflectionUtility.FindClass("UnityEditor.PreviewEditorWindow"); | |||
| 
 | |||
|         /// <summary>UnityEditor.PlayModeView</summary>
 | |||
|         public static readonly Type PlayModeView = ReflectionUtility.FindClass("UnityEditor.PlayModeView"); | |||
| 
 | |||
|         /// <summary>UnityEditor.MainView</summary>
 | |||
|         public static readonly Type MainView = ReflectionUtility.FindClass("UnityEditor.MainView"); | |||
| 
 | |||
|         /// <summary>UnityEditor.WindowLayout</summary>
 | |||
|         public static readonly Type WindowLayout = ReflectionUtility.FindClass("UnityEditor.WindowLayout"); | |||
| 
 | |||
|         /// <summary>UnityEngine.EnumDataUtility</summary>
 | |||
|         public static readonly Type EnumDataUtility = ReflectionUtility.FindClass("UnityEngine.EnumDataUtility"); | |||
| 
 | |||
|         /// <summary>UnityEditor.PlayModeView.EnterPlayModeBehavior</summary>
 | |||
|         // Enum type
 | |||
|         public static readonly Type EnterPlayModeBehavior = PlayModeView?.GetNestedType("EnterPlayModeBehavior"); | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: af47dde264c784a49afe8110a07f82c8 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,65 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     internal static class KeepFullscreenBelow { | |||
|         [InitializeOnLoadMethod] | |||
|         private static void InitPatch() { | |||
|             var eApp = typeof(EditorApplication); | |||
|             var callback = eApp.GetFieldValue<EditorApplication.CallbackFunction>("windowsReordered"); | |||
|             callback += () => BringWindowsAbove(); | |||
|             eApp.SetFieldValue("windowsReordered", callback); | |||
|             FullscreenCallbacks.afterFullscreenOpen += (f) => BringWindowsAbove(); | |||
|         } | |||
| 
 | |||
|         // https://github.com/mukaschultze/fullscreen-editor/issues/54
 | |||
|         // This is needed because ContainerWindows created by ShowAsDropDown are not
 | |||
|         // returned by 'windows' property
 | |||
|         public static IEnumerable<ScriptableObject> GetAllContainerWindowsOrdered() { | |||
|             var ordered = Types.ContainerWindow | |||
|                 .GetPropertyValue<ScriptableObject[]>("windows") | |||
|                 .Reverse(); | |||
| 
 | |||
|             var missing = Resources | |||
|                 .FindObjectsOfTypeAll(Types.ContainerWindow) | |||
|                 .Select(cw => cw as ScriptableObject); | |||
| 
 | |||
|             return ordered | |||
|                 .Concat(missing) | |||
|                 .Distinct(); | |||
|         } | |||
| 
 | |||
|         public static void BringWindowsAbove() { | |||
| 
 | |||
|             if (!FullscreenPreferences.KeepFullscreenBelow) | |||
|                 return; | |||
| 
 | |||
|             var fullscreens = Fullscreen.GetAllFullscreen(); | |||
|             if (fullscreens.Length == 0) | |||
|                 return; | |||
| 
 | |||
|             var methodName = "Internal_BringLiveAfterCreation"; | |||
|             var windows = GetAllContainerWindowsOrdered() | |||
|                 .Where(w => !Fullscreen.GetFullscreenFromView(w)) | |||
|                 .Where(w => { | |||
|                     if (w.GetPropertyValue<int>("showMode") == (int)ShowMode.MainWindow) | |||
|                         return false; // Main Window should be kept below everything
 | |||
| 
 | |||
|                     if (fullscreens.FirstOrDefault((f) => f.m_src.Container == w)) | |||
|                         return false; // Keep other fullscreen containers below
 | |||
| 
 | |||
|                     return true; | |||
|                 }); | |||
| 
 | |||
|             foreach (var w in windows) { | |||
|                 if (w.HasMethod(methodName, new Type[] { typeof(bool), typeof(bool), typeof(bool) })) | |||
|                     w.InvokeMethod(methodName, true, false, false); | |||
|                 else | |||
|                     w.InvokeMethod(methodName, true, false); | |||
|             } | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: d0e2f75c74537fd47ba55a34fa3fd7ef | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: f159b5c7b27f37c1cac894c9b84d8069 | |||
| folderAsset: yes | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,22 @@ | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor.Linux { | |||
|     internal static class NativeFullscreenHooks { | |||
| 
 | |||
|         [InitializeOnLoadMethod] | |||
|         private static void Init() { | |||
|             if (!FullscreenUtility.IsLinux) | |||
|                 return; | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenOpen += (fs) => { | |||
|                 if (wmctrl.IsInstalled && !FullscreenPreferences.DoNotUseWmctrl.Value) | |||
|                     wmctrl.SetNativeFullscreen(true, fs.m_dst.Container); | |||
|             }; | |||
|             FullscreenCallbacks.beforeFullscreenClose += (fs) => { | |||
|                 if (wmctrl.IsInstalled && !FullscreenPreferences.DoNotUseWmctrl.Value) | |||
|                     wmctrl.SetNativeFullscreen(false, fs.m_dst.Container); | |||
|             }; | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 848050aee5eb0cd9f904d899474c60d1 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,75 @@ | |||
| using System; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor.Linux { | |||
|     /// <summary>wmctrl is a tool to interact with an X Window manager available on Linux platforms.</summary>
 | |||
|     public static class wmctrl { | |||
| 
 | |||
|         static wmctrl() { | |||
|             try { | |||
|                 var stdout = string.Empty; | |||
|                 var stderr = string.Empty; | |||
|                 var exitCode = Cmd.Run("which wmctrl", false, out stdout, out stderr); | |||
| 
 | |||
|                 IsInstalled = exitCode == 0; | |||
|             } catch (Exception e) { | |||
|                 Logger.Debug("Could not run command 'which wmctrl': {0}", e); | |||
|                 IsInstalled = false; | |||
|             } | |||
|         } | |||
| 
 | |||
|         public static readonly bool IsInstalled; | |||
| 
 | |||
|         private static string Run(string format, params object[] args) { | |||
|             if (!FullscreenUtility.IsLinux) | |||
|                 throw new PlatformNotSupportedException("wmctrl is only available on Linux based platforms"); | |||
| 
 | |||
|             if (FullscreenPreferences.DoNotUseWmctrl.Value) { | |||
|                 Logger.Debug("wmctrl being invoked while DoNotUseWmctrl is enabled"); | |||
|             } | |||
| 
 | |||
|             var result = Cmd.Run("wmctrl " + format, args); | |||
|             Logger.Debug("wmctrl exited with stdio: {0}", result); | |||
|             return result; | |||
|         } | |||
| 
 | |||
|         /// <summary>Enable or disable native fullscreen for a given window.</summary>
 | |||
|         /// <param name="fullscreen">Should the window be fullscreen or not.</param>
 | |||
|         /// <param name="window">The window to changed. If null the active window will be fullscreened.</param>
 | |||
|         public static void SetNativeFullscreen(bool fullscreen, EditorWindow window) { | |||
|             if (window) | |||
|                 window.Focus(); | |||
| 
 | |||
|             Run("-r ':ACTIVE:' -b {0},fullscreen", fullscreen ? "add" : "remove"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Enable or disable native fullscreen for a given view.</summary>
 | |||
|         /// <param name="fullscreen">Should the view be fullscreen or not.</param>
 | |||
|         /// <param name="view">The view to changed. If null the active view will be fullscreened.</param>
 | |||
|         public static void SetNativeFullscreen(bool fullscreen, ScriptableObject view) { | |||
|             if (view) | |||
|                 FullscreenUtility.FocusView(view); | |||
| 
 | |||
|             Run("-r ':ACTIVE:' -b {0},fullscreen", fullscreen ? "add" : "remove"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Toggles native fullscreen for a given window.</summary>
 | |||
|         /// <param name="window">The window to be toggled fullscreen.</param>
 | |||
|         public static void ToggleNativeFullscreen(EditorWindow window) { | |||
|             if (window) | |||
|                 window.Focus(); | |||
| 
 | |||
|             Run("-r ':ACTIVE:' -b toggle,fullscreen"); | |||
|         } | |||
| 
 | |||
|         /// <summary>Toggles native fullscreen for a given view.</summary>
 | |||
|         /// <param name="view">The view to be toggled fullscreen.</param>
 | |||
|         public static void ToggleNativeFullscreen(ScriptableObject view) { | |||
|             if (view) | |||
|                 FullscreenUtility.FocusView(view); | |||
| 
 | |||
|             Run("-r ':ACTIVE:' -b toggle,fullscreen"); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 8385b56377b3215b59deee56f21c295b | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,56 @@ | |||
| using System; | |||
| using System.Diagnostics; | |||
| using UnityEngine; | |||
| using Object = UnityEngine.Object; | |||
| using UnityDebug = UnityEngine.Debug; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Helper class for logging and debugging.</summary>
 | |||
|     public static class Logger { | |||
| 
 | |||
|         private const string LOG_PREFIX = "Fullscreen Editor: "; | |||
| 
 | |||
|         [Conditional("FULLSCREEN_DEBUG")] | |||
|         public static void Debug(string message, params object[] args) { | |||
|             UnityDebug.LogFormat(LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         [Conditional("FULLSCREEN_DEBUG")] | |||
|         public static void Debug(Object context, string message, params object[] args) { | |||
|             UnityDebug.LogFormat(context, LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Log(string message, params object[] args) { | |||
|             UnityDebug.LogFormat(LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Log(Object context, string message, params object[] args) { | |||
|             UnityDebug.LogFormat(context, LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Warning(string message, params object[] args) { | |||
|             UnityDebug.LogWarningFormat(LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Warning(Object context, string message, params object[] args) { | |||
|             UnityDebug.LogWarningFormat(context, LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Error(string message, params object[] args) { | |||
|             UnityDebug.LogErrorFormat(LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Error(Object context, string message, params object[] args) { | |||
|             UnityDebug.LogErrorFormat(context, LOG_PREFIX + message, args); | |||
|         } | |||
| 
 | |||
|         public static void Exception(Exception exception) { | |||
|             UnityDebug.LogException(exception); | |||
|         } | |||
| 
 | |||
|         public static void Exception(Object context, Exception exception) { | |||
|             UnityDebug.LogException(exception, context); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 10b1d1ff89b6766448444cad56571715 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,176 @@ | |||
| using System; | |||
| using System.Collections; | |||
| using System.Linq; | |||
| using FullscreenEditor.Linux; | |||
| using FullscreenEditor.Windows; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     internal static class MenuItems { | |||
| 
 | |||
|         [MenuItem(Shortcut.TOOLBAR_PATH, true)] | |||
|         [MenuItem(Shortcut.FULLSCREEN_ON_PLAY_PATH, true)] | |||
|         private static bool SetCheckMarks() { | |||
|             Menu.SetChecked(Shortcut.TOOLBAR_PATH, FullscreenPreferences.ToolbarVisible); | |||
|             Menu.SetChecked(Shortcut.FULLSCREEN_ON_PLAY_PATH, FullscreenPreferences.FullscreenOnPlayEnabled); | |||
|             return true; | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.TOOLBAR_PATH, false, 0)] | |||
|         private static void Toolbar() { | |||
|             FullscreenPreferences.ToolbarVisible.Value = !FullscreenPreferences.ToolbarVisible; | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.FULLSCREEN_ON_PLAY_PATH, false, 0)] | |||
|         private static void FullscreenOnPlay() { | |||
|             FullscreenPreferences.FullscreenOnPlayEnabled.Value = !FullscreenPreferences.FullscreenOnPlayEnabled; | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.CURRENT_VIEW_PATH, false, 100)] | |||
|         private static void CVMenuItem() { | |||
|             var focusedView = FullscreenUtility.IsLinux ? | |||
|                 EditorWindow.focusedWindow : // Linux does not support View fullscreen, only EditorWindow
 | |||
|                 FullscreenUtility.GetFocusedViewOrWindow(); | |||
| 
 | |||
|             if (!focusedView || focusedView is PlaceholderWindow) | |||
|                 return; | |||
| 
 | |||
|             if (focusedView is EditorWindow) | |||
|                 Fullscreen.ToggleFullscreen(focusedView as EditorWindow); | |||
|             else | |||
|                 Fullscreen.ToggleFullscreen(focusedView); | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.GAME_VIEW_PATH, false, 100)] | |||
|         private static void GVMenuItem() { | |||
|             var gameView = FindCandidateForFullscreen(Types.PlayModeView ?? Types.GameView, FullscreenUtility.GetMainGameView()); | |||
|             Fullscreen.ToggleFullscreen(Types.GameView, gameView); | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.SCENE_VIEW_PATH, false, 100)] | |||
|         private static void SVMenuItem() { | |||
|             var sceneView = FindCandidateForFullscreen<SceneView>(SceneView.lastActiveSceneView); | |||
|             Fullscreen.ToggleFullscreen(sceneView); | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.MAIN_VIEW_PATH, false, 100)] | |||
|         private static void MVMenuItem() { | |||
|             var mainView = FullscreenUtility.GetMainView(); | |||
| 
 | |||
|             if (FullscreenUtility.IsLinux) { | |||
|                 if (wmctrl.IsInstalled) | |||
|                     wmctrl.ToggleNativeFullscreen(mainView); | |||
|                 else | |||
|                     Logger.Warning("wmctrl not installed, cannot fullscreen main view. Install it using 'sudo apt-get install wmctrl'"); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             if (!mainView) { | |||
|                 Logger.Error("No Main View found, this should not happen"); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             Fullscreen.ToggleFullscreen(mainView); | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.MOSAIC_PATH, true, 100)] | |||
|         private static bool MosaicValidate() { | |||
|             return FullscreenRects.ScreenCount >= 2; | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.MOSAIC_PATH, false, 100)] | |||
|         private static void MosaicMenuItem() { | |||
| 
 | |||
|             var openFullscreens = Fullscreen.GetAllFullscreen(); | |||
| 
 | |||
|             if (openFullscreens.Length > 0) { | |||
|                 foreach (var fs in openFullscreens) | |||
|                     fs.Close(); | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             var displays = DisplayInfo | |||
|                 .GetDisplays() | |||
|                 .Where(d => (d.displayDevice.StateFlags & DisplayDeviceStateFlags.AttachedToDesktop) != 0) | |||
|                 .ToList(); | |||
| 
 | |||
|             for (var i = 0; i < displays.Count && i < 8; i++) { | |||
|                 var targetDisplay = FullscreenPreferences.MosaicMapping.Value[i]; | |||
| 
 | |||
|                 if (targetDisplay < 0) { | |||
|                     continue; // -1 means none
 | |||
|                 } | |||
| 
 | |||
|                 var candidate = FindCandidateForFullscreen(Types.GameView, FullscreenUtility.GetMainGameView()); | |||
| 
 | |||
|                 if (candidate) { | |||
|                     candidate = EditorWindow.Instantiate(candidate); | |||
|                     candidate.Show(); | |||
|                 } | |||
| 
 | |||
|                 var fs = ScriptableObject.CreateInstance<FullscreenWindow>(); | |||
|                 var rect = displays[i].UnityCorrectedArea; | |||
|                 fs.OpenWindow(rect, Types.GameView, candidate, true); | |||
| 
 | |||
|                 var gameView = fs.ActualViewPyramid.Window; | |||
| 
 | |||
|                 FullscreenUtility.SetGameViewDisplayTarget(gameView, targetDisplay); | |||
| 
 | |||
|             } | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.CLOSE_ALL_FULLSCREEN, false, 250)] | |||
|         private static void CloseAll() { | |||
|             foreach (var fs in Fullscreen.GetAllFullscreen()) | |||
|                 fs.Close(); | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.CLOSE_ALL_FULLSCREEN, true, 250)] | |||
|         private static bool CloseAllValidate() { | |||
|             return Fullscreen.GetAllFullscreen().Length > 0; | |||
|         } | |||
| 
 | |||
|         [MenuItem(Shortcut.PREFERENCES_PATH, false, 1000)] | |||
|         private static void OpenPreferences() { | |||
| #if UNITY_2018_3_OR_NEWER
 | |||
|             var windowType = ReflectionUtility.FindClass("UnityEditor.SettingsWindow"); | |||
|             windowType.InvokeMethod("Show", SettingsScope.User, "Preferences/Fullscreen Editor"); | |||
| #else
 | |||
|             var windowType = ReflectionUtility.FindClass("UnityEditor.PreferencesWindow"); | |||
|             windowType.InvokeMethod("ShowPreferencesWindow"); | |||
|             After.Frames(3, () => { | |||
|                 var window = EditorWindow.GetWindow(windowType); | |||
|                 var sections = window.GetFieldValue<IList>("m_Sections").Cast<object>().ToList(); | |||
|                 var index = sections.FindIndex(section => section.GetFieldValue<GUIContent>("content").text == "Fullscreen"); | |||
|                 window.SetPropertyValue("selectedSectionIndex", index); | |||
|             }); | |||
| #endif
 | |||
|         } | |||
| 
 | |||
|         private static T FindCandidateForFullscreen<T>(T mainCandidate = null) where T : EditorWindow { | |||
|             return FindCandidateForFullscreen(typeof(T), mainCandidate) as T; | |||
|         } | |||
| 
 | |||
|         private static EditorWindow FindCandidateForFullscreen(Type type, EditorWindow mainCandidate = null) { | |||
|             if (type == null) | |||
|                 throw new ArgumentNullException("type"); | |||
| 
 | |||
|             if (!type.IsOfType(typeof(EditorWindow))) | |||
|                 throw new ArgumentException("Invalid type, type must inherit from UnityEditor.EditorWindow", "type"); | |||
| 
 | |||
|             if (mainCandidate && !mainCandidate.IsOfType(type)) | |||
|                 throw new ArgumentException("Main candidate type must match the type argument or be null", "mainCandidate"); | |||
| 
 | |||
|             // if (mainCandidate && !Fullscreen.GetFullscreenFromView(mainCandidate))
 | |||
|             if (mainCandidate) | |||
|                 return mainCandidate; // Our candidate is not null and is not fullscreened either
 | |||
| 
 | |||
|             return Resources // Returns the first window of our type that is not in fullscreen
 | |||
|                 .FindObjectsOfTypeAll(type) | |||
|                 .Cast<EditorWindow>() | |||
|                 .FirstOrDefault(window => !Fullscreen.GetFullscreenFromView(window)); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 52ca7a5600c87a844b5f81e0ec14bd7e | |||
| timeCreated: 1509037117 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,137 @@ | |||
| using System; | |||
| using System.Globalization; | |||
| using System.Reflection; | |||
| using System.Runtime.CompilerServices; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     // error CS0702: A constraint cannot be special class `System.Delegate'
 | |||
|     // Unity 2018.3.11f1 - .NET 3.5 equivalent
 | |||
|     // public class Patcher<T> : Patcher where T : Delegate {
 | |||
|     //     public Patcher(T method, T replacement) : base(method.GetMethodInfo(), replacement.GetMethodInfo()) { }
 | |||
| 
 | |||
|     //     public Patcher(MethodBase method, T replacement) : base(method, replacement.GetMethodInfo()) { }
 | |||
|     // }
 | |||
| 
 | |||
|     public unsafe class Patcher { | |||
| 
 | |||
|         private bool swapped; | |||
|         private MethodBase method; | |||
|         private MethodInfo replacement; | |||
|         private byte[] backup = new byte[25]; | |||
| 
 | |||
|         private IntPtr pBody; | |||
|         private IntPtr pBorrowed; | |||
| 
 | |||
|         public Patcher(MethodBase method, MethodInfo replacement) { | |||
|             if(!IsSupported()) | |||
|                 throw new PlatformNotSupportedException("Not supported on non x86_x64 processors"); | |||
| 
 | |||
|             this.method = method; | |||
|             this.replacement = replacement; | |||
|         } | |||
| 
 | |||
|         public static bool IsSupported() { | |||
|             if(FullscreenUtility.IsMacOS) return false; | |||
|             // Does not work on ARM/M1 macs
 | |||
|             return CultureInfo.InvariantCulture.CompareInfo.IndexOf(SystemInfo.processorType, "ARM", CompareOptions.IgnoreCase) == -1 && Environment.Is64BitProcess; | |||
|         } | |||
| 
 | |||
|         public bool IsPatched() { | |||
|             // var cursor = (byte * )pBody.ToPointer();
 | |||
|             // var isOriginal = backup.All(b => * (cursor++) == b); 
 | |||
| 
 | |||
|             return swapped; | |||
|         } | |||
| 
 | |||
|         public void Revert() { | |||
|             if(!swapped) { | |||
|                 throw new Exception("Methods is not patched"); | |||
|             } | |||
|             swapped = false; | |||
| 
 | |||
|             unsafe { | |||
|                 var cursor = (byte*)pBody.ToPointer(); | |||
|                 for(var i = 0; i < backup.Length; i++) { | |||
|                     *(cursor++) = backup[i]; | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         public void InvokeOriginal(object obj, params object[] parameters) { | |||
|             try { | |||
|                 if(IsPatched()) | |||
|                     Revert(); | |||
|                 method.Invoke(obj, parameters); | |||
|             } finally { | |||
|                 if(!IsPatched()) | |||
|                     SwapMethods(); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public void SwapMethods() { | |||
|             if(swapped) { | |||
|                 throw new Exception("Methods already patched"); | |||
|             } | |||
|             swapped = true; | |||
| 
 | |||
|             RuntimeHelpers.PrepareMethod(method.MethodHandle); | |||
|             RuntimeHelpers.PrepareMethod(replacement.MethodHandle); | |||
| 
 | |||
|             pBody = method.MethodHandle.GetFunctionPointer(); | |||
|             pBorrowed = replacement.MethodHandle.GetFunctionPointer(); | |||
| 
 | |||
|             unsafe { | |||
| 
 | |||
|                 var ptr = (byte*)pBody.ToPointer(); | |||
|                 var ptr2 = (byte*)pBorrowed.ToPointer(); | |||
|                 var ptrDiff = ptr2 - ptr - 5; | |||
|                 var relativeJumpAvailable = ptrDiff < (long)0xFFFFFFFF && ptrDiff > (long)-0xFFFFFFFF; | |||
|                 var doNotUseRelativeJump = true; // See issues #69 and #89
 | |||
| 
 | |||
|                 Logger.Debug("Relative jump is {0}available \\ {1}bit platform", relativeJumpAvailable ? "" : "not ", sizeof(IntPtr) * 8); | |||
| 
 | |||
|                 // Backup orignal opcodes so we can revert it later
 | |||
|                 for(var i = 0; i < backup.Length; i++) { | |||
|                     backup[i] = *(ptr + i); | |||
|                 } | |||
| 
 | |||
|                 if(!doNotUseRelativeJump && relativeJumpAvailable) { | |||
|                     // 32-bit relative jump, available on both 32 and 64 bit arch.
 | |||
|                     // Debug.Trace($"diff is {ptrDiff} doing relative jmp");
 | |||
|                     // Debug.Trace("patching on {0:X}, target: {1:X}", (ulong)ptr, (ulong)ptr2);
 | |||
|                     *ptr = 0xE9; // JMP
 | |||
|                     *((uint*)(ptr + 1)) = (uint)ptrDiff; | |||
|                 } else { | |||
|                     // Debug.Trace($"diff is {ptrDiff} doing push+ret trampoline");
 | |||
|                     // Debug.Trace("patching on {0:X}, target: {1:X}", (ulong)ptr, (ulong)ptr2);
 | |||
|                     if(sizeof(IntPtr) == 8) { | |||
|                         // For 64bit arch and likely 64bit pointers, do:
 | |||
|                         // PUSH bits 0 - 32 of addr
 | |||
|                         // MOV [RSP+4] bits 32 - 64 of addr
 | |||
|                         // RET
 | |||
|                         var cursor = ptr; | |||
|                         *(cursor++) = 0x68; // PUSH
 | |||
|                         *((uint*)cursor) = (uint)ptr2; | |||
|                         cursor += 4; | |||
|                         *(cursor++) = 0xC7; // MOV [RSP+4]
 | |||
|                         *(cursor++) = 0x44; | |||
|                         *(cursor++) = 0x24; | |||
|                         *(cursor++) = 0x04; | |||
|                         *((uint*)cursor) = (uint)((ulong)ptr2 >> 32); | |||
|                         cursor += 4; | |||
|                         *(cursor++) = 0xC3; // RET
 | |||
|                     } else { | |||
|                         // For 32bit arch and 32bit pointers, do: PUSH addr, RET.
 | |||
|                         *ptr = 0x68; | |||
|                         *((uint*)(ptr + 1)) = (uint)ptr2; | |||
|                         *(ptr + 5) = 0xC3; | |||
|                     } | |||
|                 } | |||
| 
 | |||
|                 // Logger.Debug("Patched 0x{0:X} to 0x{1:X}.", (ulong)ptr, (ulong)ptr2);
 | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: fa394636ccd0ab449adc0275212a1707 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,174 @@ | |||
| using System; | |||
| using System.Collections; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>The window that will be shown in the place of the original view when creating a fullscreen container.</summary>
 | |||
|     public class PlaceholderWindow : EditorWindow { | |||
| 
 | |||
|         private const float PREVIEW_FRAMERATE = 24f; | |||
| 
 | |||
|         private static class Styles { | |||
| 
 | |||
|             public static readonly GUIStyle textStyle = new GUIStyle("BoldLabel"); | |||
|             public static readonly GUIStyle backgroundShadow = new GUIStyle("InnerShadowBg"); | |||
|             public static readonly GUIStyle buttonStyle = new GUIStyle("LargeButton"); | |||
| 
 | |||
|             static Styles() { | |||
|                 textStyle.wordWrap = true; | |||
|                 textStyle.alignment = TextAnchor.MiddleCenter; | |||
|             } | |||
| 
 | |||
|         } | |||
| 
 | |||
|         [SerializeField] private Vector2 m_scroll; | |||
|         [SerializeField] private FullscreenContainer m_fullscreenContainer; | |||
|         [SerializeField] private bool m_containerForcefullyClosed; | |||
| 
 | |||
|         private double m_nextUpdate; | |||
|         private RenderTexture m_previewRT; | |||
| 
 | |||
|         private FullscreenContainer FullscreenContainer { | |||
|             get { | |||
|                 if (m_containerForcefullyClosed) | |||
|                     return null; | |||
|                 if (!m_fullscreenContainer) | |||
|                     m_fullscreenContainer = FullscreenUtility.GetRef<FullscreenContainer>(name); | |||
|                 if (!m_fullscreenContainer) | |||
|                     m_containerForcefullyClosed = true; | |||
| 
 | |||
|                 return m_fullscreenContainer; | |||
|             } | |||
|             set { m_fullscreenContainer = value; } | |||
|         } | |||
| 
 | |||
|         private bool PreviewSupported { | |||
|             get { | |||
|                 return FullscreenContainer && | |||
|                     !FullscreenContainer.Rect.Overlaps(position) && | |||
|                     FullscreenContainer.m_dst.View && | |||
|                     FullscreenContainer.m_src.Window && | |||
|                     FullscreenContainer.m_dst.View.HasMethod("GrabPixels"); | |||
|             } | |||
|         } | |||
| 
 | |||
|         private void Update() { | |||
|             if (EditorApplication.timeSinceStartup < m_nextUpdate) | |||
|                 return; | |||
| 
 | |||
|             m_nextUpdate = EditorApplication.timeSinceStartup + (1f / PREVIEW_FRAMERATE); | |||
|             RenderTexture.ReleaseTemporary(m_previewRT); | |||
|             m_previewRT = null; | |||
| 
 | |||
|             if (!PreviewSupported || m_containerForcefullyClosed) | |||
|                 return; | |||
| 
 | |||
|             var view = FullscreenContainer ? FullscreenContainer.m_dst.View : null; | |||
|             var width = (int)FullscreenContainer.Rect.width; | |||
|             var height = (int)FullscreenContainer.Rect.height; | |||
| 
 | |||
|             if (!view || width < 10 || height < 10) | |||
|                 return; | |||
| 
 | |||
|             m_previewRT = RenderTexture.GetTemporary(width, height, 0); | |||
|             view.InvokeMethod("GrabPixels", m_previewRT, new Rect(0f, 0f, width, height)); | |||
|             Repaint(); | |||
|         } | |||
| 
 | |||
|         private void OnDisable() { | |||
|             RenderTexture.ReleaseTemporary(m_previewRT); | |||
|         } | |||
| 
 | |||
|         private void OnGUI() { | |||
| 
 | |||
|             using(var scrollScope = new EditorGUILayout.ScrollViewScope(m_scroll)) | |||
|             using(var mainScope = new EditorGUILayout.VerticalScope(Styles.backgroundShadow)) { | |||
| 
 | |||
|                 m_scroll = scrollScope.scrollPosition; | |||
| 
 | |||
|                 using(new GUIColor(Color.white, 0.1f)) | |||
|                 if (m_previewRT) { | |||
|                     var rtRect = SystemInfo.graphicsUVStartsAtTop ? | |||
|                         Rect.MinMaxRect(0f, 1f, 1f, 0f) : // Direct3D like
 | |||
|                         Rect.MinMaxRect(0f, 0f, 1f, 1f); // OpenGL like
 | |||
| 
 | |||
|                     GUI.DrawTextureWithTexCoords(mainScope.rect, m_previewRT, rtRect); | |||
|                 } | |||
| 
 | |||
|                 using(new GUIColor(Styles.textStyle.normal.textColor * 0.05f)) | |||
|                 GUI.DrawTexture(mainScope.rect, FullscreenUtility.FullscreenIcon, ScaleMode.ScaleAndCrop); | |||
| 
 | |||
|                 GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                 using(new EditorGUILayout.HorizontalScope()) { | |||
|                     GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                     using(new GUIContentColor(Styles.textStyle.normal.textColor)) | |||
|                     GUILayout.Label(FullscreenUtility.FullscreenIcon, Styles.textStyle); | |||
| 
 | |||
|                     using(new EditorGUILayout.VerticalScope()) { | |||
|                         if (FullscreenContainer && FullscreenContainer.ActualViewPyramid.Container) { | |||
|                             GUILayout.Label("The view that lives here is in fullscreen mode", Styles.textStyle); | |||
|                             GUILayout.Label("Don't close this placeholder", Styles.textStyle); | |||
|                         } else { | |||
|                             GUILayout.Label("The view that lived here was forcefully closed while in fullscreen, restore is not available", Styles.textStyle); | |||
|                             GUILayout.Label("Consider using the shortcuts to exit fullscreen", Styles.textStyle); | |||
|                             GUILayout.Label("You may close this placeholder", Styles.textStyle); | |||
|                         } | |||
| 
 | |||
|                     } | |||
|                     GUILayout.FlexibleSpace(); | |||
|                 } | |||
| 
 | |||
|                 using(new EditorGUILayout.HorizontalScope()) { | |||
|                     GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                     if (FullscreenContainer && FullscreenContainer.ActualViewPyramid.Container) { | |||
|                         if (GUILayout.Button("Restore View", Styles.buttonStyle)) | |||
|                             FullscreenContainer.Close(); | |||
|                     } else if (GUILayout.Button("Close Placeholder", Styles.buttonStyle)) | |||
|                         Close(); | |||
|                     GUILayout.FlexibleSpace(); | |||
|                 } | |||
| 
 | |||
|                 GUILayout.FlexibleSpace(); | |||
| 
 | |||
|                 // This GUI may be called after "this" has been destroyed, that causes a NullReference for the "name" getter
 | |||
|                 // Not sure why this happens tho
 | |||
|                 EditorGUILayout.LabelField(SafeObjToString(() => name)); | |||
|                 EditorGUILayout.Space(); | |||
| 
 | |||
|                 if (FullscreenUtility.DebugModeEnabled) { | |||
|                     EditorGUILayout.LabelField("Forcefully Closed", SafeObjToString(() => m_containerForcefullyClosed)); | |||
| 
 | |||
|                     EditorGUILayout.LabelField("Container", SafeObjToString(() => FullscreenContainer)); | |||
|                     EditorGUILayout.LabelField("Rect", SafeObjToString(() => FullscreenContainer.Rect)); | |||
| 
 | |||
|                     EditorGUILayout.LabelField("SRC Window", SafeObjToString(() => FullscreenContainer.m_src.Window)); | |||
|                     EditorGUILayout.LabelField("SRC View", SafeObjToString(() => FullscreenContainer.m_src.View)); | |||
|                     EditorGUILayout.LabelField("SRC Container", SafeObjToString(() => FullscreenContainer.m_src.Container)); | |||
| 
 | |||
|                     EditorGUILayout.LabelField("DST Window", SafeObjToString(() => FullscreenContainer.m_dst.Window)); | |||
|                     EditorGUILayout.LabelField("DST View", SafeObjToString(() => FullscreenContainer.m_dst.View)); | |||
|                     EditorGUILayout.LabelField("DST Container", SafeObjToString(() => FullscreenContainer.m_dst.Container)); | |||
| 
 | |||
|                     EditorGUILayout.Space(); | |||
|                 } | |||
| 
 | |||
|             } | |||
|         } | |||
| 
 | |||
|         private string SafeObjToString<T>(Func<T> obj) { | |||
|             try { | |||
|                 var i = obj(); | |||
|                 return i == null? "null": i.ToString(); | |||
|             } catch { | |||
|                 return "invalid"; | |||
|             } | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 88d5675826e7edf4e8642372ea3fe9da | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,128 @@ | |||
| using System; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Helper class for saving preferences.</summary>
 | |||
|     /// <typeparam name="T">The type you want to save, must be marked as <see cref="SerializableAttribute"/></typeparam>
 | |||
|     [Serializable] | |||
|     public sealed class PrefItem<T> { | |||
| 
 | |||
|         [SerializeField] | |||
|         private T savedValue; | |||
| 
 | |||
|         /// <summary>The key for saving the value.</summary>
 | |||
|         public string Key { get; private set; } | |||
| 
 | |||
|         /// <summary>The default value to use when there's none saved.</summary>
 | |||
|         public T DefaultValue { get; private set; } | |||
| 
 | |||
|         /// <summary>A label and an explanation of what this item is for.</summary>
 | |||
|         public GUIContent Content { get; private set; } | |||
| 
 | |||
|         /// <summary>Callback called whenever the saved value changes.</summary>
 | |||
|         public Action<T> OnValueSaved { get; set; } | |||
| 
 | |||
|         /// <summary>The value saved by this instance.</summary>
 | |||
|         public T Value { | |||
|             get { return savedValue; } | |||
|             set { | |||
|                 if (!savedValue.Equals(value)) { | |||
|                     savedValue = value; | |||
|                     SaveValue(); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         public PrefItem(string key, T defaultValue, string text, string tooltip) { | |||
|             Key = "Fullscreen." + key; | |||
| 
 | |||
|             FullscreenPreferences.onLoadDefaults += DeleteValue; | |||
| 
 | |||
|             Content = new GUIContent(text, tooltip); | |||
|             FullscreenPreferences.contents.Add(Content); | |||
|             DefaultValue = savedValue = defaultValue; | |||
|             LoadValue(); | |||
|         } | |||
| 
 | |||
|         private void LoadValue() { | |||
|             try { | |||
|                 if (EditorPrefs.HasKey(Key)) | |||
|                     JsonUtility.FromJsonOverwrite(EditorPrefs.GetString(Key), this); | |||
|             } catch (Exception e) { | |||
|                 Logger.Warning("Failed to load {0}, using default value: {1}", Key, e); | |||
|                 savedValue = DefaultValue; | |||
|                 SaveValue(); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public void SaveValue() { | |||
|             try { | |||
|                 EditorPrefs.SetString(Key, JsonUtility.ToJson(this)); | |||
|                 Logger.Debug("Saved value to key {0}:\n{1}", Key, EditorPrefs.GetString(Key)); | |||
| 
 | |||
|                 if (OnValueSaved != null) | |||
|                     OnValueSaved.Invoke(Value); | |||
| 
 | |||
|                 InternalEditorUtility.RepaintAllViews(); | |||
|             } catch (Exception e) { | |||
|                 Logger.Warning("Failed to save {0}: {1}", Key, e); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public void DeleteValue() { | |||
|             EditorPrefs.DeleteKey(Key); | |||
|             savedValue = DefaultValue; | |||
| 
 | |||
|             if (OnValueSaved != null) | |||
|                 OnValueSaved.Invoke(savedValue); | |||
|         } | |||
| 
 | |||
|         public static implicit operator T(PrefItem<T> pb) { return pb.Value; } | |||
| 
 | |||
|         public static implicit operator GUIContent(PrefItem<T> pb) { return pb.Content; } | |||
| 
 | |||
|     } | |||
| 
 | |||
|     /// <summary>Helper class for drawing the <see cref="PrefItem{T}"/> in the <see cref="PreferencesWindow"/>.</summary>
 | |||
|     public static class PrefItemGUI { | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<int> pref) { | |||
|             pref.Value = EditorGUILayout.IntField(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<float> pref) { | |||
|             pref.Value = EditorGUILayout.FloatField(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<int> pref, int min, int max) { | |||
|             pref.Value = EditorGUILayout.IntSlider(pref.Content, pref.Value, min, max); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<float> pref, float min, float max) { | |||
|             pref.Value = EditorGUILayout.Slider(pref.Content, pref.Value, min, max); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<bool> pref) { | |||
|             pref.Value = EditorGUILayout.Toggle(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<string> pref) { | |||
|             pref.Value = EditorGUILayout.TextField(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<Color> pref) { | |||
|             pref.Value = EditorGUILayout.ColorField(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<Rect> pref) { | |||
|             pref.Value = EditorGUILayout.RectField(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|         public static void DoGUI(this PrefItem<RectSourceMode> pref) { | |||
|             pref.Value = (RectSourceMode)EditorGUILayout.EnumPopup(pref.Content, pref.Value); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 4122d1a6d249e2845aa5f93eb83c2b50 | |||
| timeCreated: 1508428743 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,197 @@ | |||
| using System; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| using UnityEditor; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Class containing method extensions for getting private and internal members.</summary>
 | |||
|     public static class ReflectionUtility { | |||
| 
 | |||
|         private static Assembly[] cachedAssemblies; | |||
| 
 | |||
|         public const BindingFlags FULL_BINDING = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; | |||
| 
 | |||
|         /// <summary>Find a type by its name.</summary>
 | |||
|         public static Type FindClass(string name) { | |||
|             // return typeof(Editor).Assembly.GetType(name, false, true);
 | |||
|             var result = FindTypeInAssembly(name, typeof(Editor).Assembly); | |||
| 
 | |||
|             if (result != null) | |||
|                 return result; | |||
| 
 | |||
|             if (cachedAssemblies == null) | |||
|                 cachedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); | |||
| 
 | |||
|             for (var i = 0; i < cachedAssemblies.Length; i++) { | |||
|                 result = FindTypeInAssembly(name, cachedAssemblies[i]); | |||
| 
 | |||
|                 if (result != null) | |||
|                     return result; | |||
|             } | |||
| 
 | |||
|             return result; | |||
|         } | |||
| 
 | |||
|         private static Type FindTypeInAssembly(string name, Assembly assembly) { | |||
|             return assembly == null ? | |||
|                 null : | |||
|                 assembly.GetType(name, false, true); | |||
|         } | |||
| 
 | |||
|         /// <summary>Find a field of a type by its name.</summary>
 | |||
|         public static FieldInfo FindField(this Type type, string fieldName, bool throwNotFound = true) { | |||
|             if (type == null) | |||
|                 throw new ArgumentNullException("type"); | |||
| 
 | |||
|             var field = type.GetField(fieldName, FULL_BINDING); | |||
| 
 | |||
|             if (field == null && throwNotFound) | |||
|                 throw new MissingFieldException(type.FullName, fieldName); | |||
| 
 | |||
|             return field; | |||
|         } | |||
| 
 | |||
|         /// <summary>Find a property of a type by its name.</summary>
 | |||
|         public static PropertyInfo FindProperty(this Type type, string propertyName, bool throwNotFound = true) { | |||
|             if (type == null) | |||
|                 throw new ArgumentNullException("type"); | |||
| 
 | |||
|             var prop = type.GetProperty(propertyName, FULL_BINDING); | |||
| 
 | |||
|             if (prop == null && throwNotFound) | |||
|                 throw new MissingMemberException(type.FullName, propertyName); | |||
| 
 | |||
|             return prop; | |||
|         } | |||
| 
 | |||
|         /// <summary>Find a method of a type by its name.</summary>
 | |||
|         public static MethodInfo FindMethod(this Type type, string methodName, Type[] args = null, bool throwNotFound = true) { | |||
|             if (type == null) | |||
|                 throw new ArgumentNullException("type"); | |||
| 
 | |||
|             MethodInfo method; | |||
| 
 | |||
|             if (args == null) { | |||
|                 method = type.GetMethod(methodName, FULL_BINDING); | |||
|                 // method = type.GetMethods(FULL_BINDING)
 | |||
|                 //     .Where(m => m.Name == methodName)
 | |||
|                 //     .FirstOrDefault();
 | |||
|             } else { | |||
|                 method = type.GetMethod(methodName, FULL_BINDING, null, args, null); | |||
| 
 | |||
|                 // There are very specific cases where the above method may not bind properly
 | |||
|                 // e.g. when the method declares an enum and the arg type is an int, so we ignore the args 
 | |||
|                 // and hope that there are no ambiguity of methods
 | |||
|                 if (method == null) { | |||
|                     method = FindMethod(type, methodName, null, throwNotFound); | |||
| 
 | |||
|                     if (method != null && method.GetParameters().Length != args.Length) | |||
|                         method = null; | |||
|                 } | |||
|             } | |||
| 
 | |||
|             if (method == null && throwNotFound) | |||
|                 throw new MissingMethodException(type.FullName, methodName); | |||
| 
 | |||
|             return method; | |||
|         } | |||
| 
 | |||
|         /// <summary>Get the value of the static field.</summary>
 | |||
|         public static T GetFieldValue<T>(this Type type, string fieldName) { return (T)type.FindField(fieldName).GetValue(null); } | |||
| 
 | |||
|         /// <summary>Get the value of the instance field.</summary>
 | |||
|         public static T GetFieldValue<T>(this object obj, string fieldName) { return (T)obj.GetType().FindField(fieldName).GetValue(obj); } | |||
| 
 | |||
|         /// <summary>Set the value of the static field.</summary>
 | |||
|         public static void SetFieldValue(this Type type, string fieldName, object value) { type.FindField(fieldName).SetValue(null, value); } | |||
| 
 | |||
|         /// <summary>Set the value of the instance field.</summary>
 | |||
|         public static void SetFieldValue(this object obj, string fieldName, object value) { obj.GetType().FindField(fieldName).SetValue(obj, value); } | |||
| 
 | |||
|         /// <summary>Get the value of the static property.</summary>
 | |||
|         public static T GetPropertyValue<T>(this Type type, string propertyName) { return (T)type.FindProperty(propertyName).GetValue(null, null); } | |||
| 
 | |||
|         /// <summary>Get the value of the instance property.</summary>
 | |||
|         public static T GetPropertyValue<T>(this object obj, string propertyName) { return (T)obj.GetType().FindProperty(propertyName).GetValue(obj, null); } | |||
| 
 | |||
|         /// <summary>Set the value of the static property.</summary>
 | |||
|         public static void SetPropertyValue(this Type type, string propertyName, object value) { type.FindProperty(propertyName).SetValue(null, value, null); } | |||
| 
 | |||
|         /// <summary>Set the value of the instance property.</summary>
 | |||
|         public static void SetPropertyValue(this object obj, string propertyName, object value) { obj.GetType().FindProperty(propertyName).SetValue(obj, value, null); } | |||
| 
 | |||
|         /// <summary>Invoke a static method on the type and return the result.</summary>
 | |||
|         public static T InvokeMethod<T>(this Type type, string methodName, params object[] args) { return (T)type.FindMethod(methodName, args.Select(a => a.GetType()).ToArray()).Invoke(null, args); } | |||
| 
 | |||
|         /// <summary>Invoke a method on the object instance and return the result.</summary>
 | |||
|         public static T InvokeMethod<T>(this object obj, string methodName, params object[] args) { return (T)obj.GetType().FindMethod(methodName, args.Select(a => a.GetType()).ToArray()).Invoke(obj, args); } | |||
| 
 | |||
|         /// <summary>Invoke a static method on the type.</summary>
 | |||
|         public static void InvokeMethod(this Type type, string methodName, params object[] args) { type.FindMethod(methodName, args.Select(a => a.GetType()).ToArray()).Invoke(null, args); } | |||
| 
 | |||
|         /// <summary>Invoke a method on the object instance.</summary>
 | |||
|         public static void InvokeMethod(this object obj, string methodName, params object[] args) { obj.GetType().FindMethod(methodName, args.Select(a => a.GetType()).ToArray()).Invoke(obj, args); } | |||
| 
 | |||
|         /// <summary>Returns wheter the given type is the same as another one.</summary>
 | |||
|         /// <param name="toCheck">Type that will be checked.</param>
 | |||
|         /// <param name="type">Type to check against.</param>
 | |||
|         /// <param name="orInherited">Returns true if the checked type is inherited from the type argument.</param>
 | |||
|         public static bool IsOfType(this Type toCheck, Type type, bool orInherited = true) { | |||
|             return type == toCheck || (orInherited && type.IsAssignableFrom(toCheck)); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns wheter the given instance is of a given type.</summary>
 | |||
|         /// <param name="obj">The instance to check.</param>
 | |||
|         /// <param name="type">Type to check against.</param>
 | |||
|         /// <param name="orInherited">Returns true if the instance is inherited from the type argument.</param>
 | |||
|         public static bool IsOfType<T>(this T obj, Type type, bool orInherited = true) { | |||
|             return obj.GetType().IsOfType(type, orInherited); | |||
|         } | |||
| 
 | |||
|         /// <summary>Throws an exception if the instance is not of the given type.</summary>
 | |||
|         /// <param name="obj">The instance to check.</param>
 | |||
|         /// <param name="type">Type to check against.</param>
 | |||
|         /// <param name="orInherited">Do not throw if the instance is inherited from the type argument.</param>
 | |||
|         public static void EnsureOfType<T>(this T obj, Type type, bool orInherited = true) { | |||
|             if (!obj.IsOfType(type, orInherited)) | |||
|                 throw new InvalidCastException( | |||
|                     string.Format("Object {0} must be of type {1}{2}", | |||
|                         obj.GetType().FullName, | |||
|                         type.FullName, | |||
|                         orInherited? " or inherited from it": "" | |||
|                     ) | |||
|                 ); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the type defines the static field.</summary>
 | |||
|         public static bool HasField(this Type type, string fieldName) { | |||
|             return type.FindField(fieldName, false) != null; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the type defines the static property.</summary>
 | |||
|         public static bool HasProperty(this Type type, string propertyName) { | |||
|             return type.FindProperty(propertyName, false) != null; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the type defines the static method.</summary>
 | |||
|         public static bool HasMethod(this Type type, string methodName, Type[] args = null) { | |||
|             return type.FindMethod(methodName, args, false) != null; | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the object type defines the instance field.</summary>
 | |||
|         public static bool HasField(this object obj, string fieldName) { | |||
|             return obj.GetType().HasField(fieldName); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the object type defines the instance property.</summary>
 | |||
|         public static bool HasProperty(this object obj, string propertyName) { | |||
|             return obj.GetType().HasProperty(propertyName); | |||
|         } | |||
| 
 | |||
|         /// <summary>Returns whether the object type defines the instance method.</summary>
 | |||
|         public static bool HasMethod(this object obj, string methodName, Type[] args = null) { | |||
|             return obj.GetType().HasMethod(methodName, args); | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: bf03c2f7a14d3bf4da513b03829e3157 | |||
| timeCreated: 1508968711 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,43 @@ | |||
| using System; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     [InitializeOnLoad] | |||
|     public class RestoreCursorState { | |||
| 
 | |||
|         static RestoreCursorState() { | |||
|             var cursorVisible = Cursor.visible; | |||
| 
 | |||
|             // this is where the magic happens
 | |||
|             Action<FullscreenContainer> magic = (fs) => { | |||
|                 if (!FullscreenPreferences.RestoreCursorLockAndHideState) return; | |||
| 
 | |||
|                 // frame count doesn't seem to make much of a difference,
 | |||
|                 // but I think it's best to do this after the view is
 | |||
|                 // focused by the "FixGameViewMouseInput" class
 | |||
|                 After.Frames(55, () => { | |||
|                     var gameView = fs.m_dst.Window && fs.m_dst.Window.IsOfType(Types.GameView) ? fs.m_dst.Window : | |||
|                                     fs.m_src.Window && fs.m_src.Window.IsOfType(Types.GameView) ? fs.m_src.Window : null; | |||
| 
 | |||
|                     if (!EditorApplication.isPaused && gameView && gameView.IsOfType(Types.GameView) && gameView.HasMethod("AllowCursorLockAndHide")) { | |||
|                         gameView.InvokeMethod("AllowCursorLockAndHide", true); | |||
|                         Unsupported.SetAllowCursorHide(true); | |||
|                         Cursor.visible = cursorVisible; | |||
|                     } | |||
|                 }); | |||
|             }; | |||
| 
 | |||
|             Action<FullscreenContainer> storeCursorVisible = (fs) => { | |||
|                 cursorVisible = Cursor.visible; | |||
|             }; | |||
| 
 | |||
|             FullscreenCallbacks.afterFullscreenOpen += magic; | |||
|             FullscreenCallbacks.afterFullscreenClose += magic; | |||
| 
 | |||
|             FullscreenCallbacks.beforeFullscreenOpen += storeCursorVisible; | |||
|             FullscreenCallbacks.beforeFullscreenClose += storeCursorVisible; | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: dff90cc9864f9c041a937e8fd130ee9c | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,289 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Diagnostics; | |||
| using System.IO; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| using System.Text; | |||
| using UnityEditor; | |||
| using UnityEditorInternal; | |||
| using UnityEngine; | |||
| using Debug = UnityEngine.Debug; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
| 
 | |||
|     [AttributeUsage(AttributeTargets.Field)] | |||
|     internal class DynamicMenuItemAttribute : Attribute { | |||
| 
 | |||
|         public bool AllowNoneValue { get; private set; } | |||
| 
 | |||
|         public DynamicMenuItemAttribute(bool allowNoneValue) { | |||
|             AllowNoneValue = allowNoneValue; | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
|     internal class Shortcut { | |||
| 
 | |||
|         #region Fields
 | |||
|         //Always end with an space if the path has no shortcut
 | |||
|         [DynamicMenuItem(true)] public const string TOOLBAR_PATH = "Fullscreen/Show Toolbar _F8"; | |||
|         [DynamicMenuItem(true)] public const string FULLSCREEN_ON_PLAY_PATH = "Fullscreen/Fullscreen On Play "; | |||
|         [DynamicMenuItem(true)] public const string PREFERENCES_PATH = "Fullscreen/Preferences... "; | |||
|         [DynamicMenuItem(false)] public const string CURRENT_VIEW_PATH = "Fullscreen/Focused View _F9"; | |||
|         [DynamicMenuItem(false)] public const string GAME_VIEW_PATH = "Fullscreen/Game View _F10"; | |||
|         [DynamicMenuItem(false)] public const string SCENE_VIEW_PATH = "Fullscreen/Scene View _F11"; | |||
|         [DynamicMenuItem(false)] public const string MAIN_VIEW_PATH = "Fullscreen/Main View _F12"; | |||
|         [DynamicMenuItem(false)] public const string MOSAIC_PATH = "Fullscreen/Mosaic %F10"; | |||
|         [DynamicMenuItem(true)] public const string CLOSE_ALL_FULLSCREEN = "Fullscreen/Close All %F12"; | |||
| 
 | |||
|         private const char CTRL_CHAR = '%'; | |||
|         private const char SHIFT_CHAR = '#'; | |||
|         private const char ALT_CHAR = '&'; | |||
|         private const char NONE_CHAR = '_'; | |||
| 
 | |||
|         private static readonly List<Shortcut> fieldsInfo = new List<Shortcut>(); | |||
|         /* fixformat ignore:start */ | |||
|         private static readonly string[] keys = new string[] { | |||
|             "None", | |||
|             "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", | |||
|             "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", | |||
|             "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", | |||
|             "LEFT", "RIGHT", "UP", "DOWN", "HOME", "END", "PGUP", "PGDN" | |||
|         }; | |||
|         /* fixformat ignore:end */ | |||
| 
 | |||
|         private static bool changed; | |||
|         #endregion
 | |||
| 
 | |||
|         #region Properties
 | |||
|         public bool Ctrl { get; set; } | |||
|         public bool Shift { get; set; } | |||
|         public bool Alt { get; set; } | |||
|         public int KeyCode { get; set; } | |||
| 
 | |||
|         public bool AllowNoneValue { get; private set; } | |||
|         public string FieldName { get; private set; } | |||
|         public string BaseString { get; private set; } | |||
|         public string Label { get { return BaseString.Substring(BaseString.LastIndexOf('/') + 1); } } | |||
| 
 | |||
|         private static bool IsSourceFile { get { return !string.IsNullOrEmpty(ThisFilePath) && File.Exists(ThisFilePath); } } | |||
|         private static string ThisFilePath { | |||
|             get { | |||
|                 try { | |||
|                     return new StackFrame(true).GetFileName(); | |||
|                 } catch (Exception e) { | |||
|                     Logger.Exception(e); | |||
|                     return string.Empty; | |||
|                 } | |||
|             } | |||
|         } | |||
|         #endregion
 | |||
| 
 | |||
|         #region Constructors
 | |||
|         static Shortcut() { | |||
|             var type = typeof(Shortcut); | |||
|             var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); | |||
| 
 | |||
|             if (fields != null) | |||
|                 foreach (var field in fields) { | |||
|                     var att = field.GetCustomAttributes(typeof(DynamicMenuItemAttribute), false); | |||
| 
 | |||
|                     if (att != null) | |||
|                         for (var i = 0; i < att.Length; i++) | |||
|                             fieldsInfo.Add(new Shortcut((DynamicMenuItemAttribute)att[i], field)); | |||
|                 } | |||
|         } | |||
| 
 | |||
|         public Shortcut(DynamicMenuItemAttribute shortcutAttribute, FieldInfo field) { | |||
|             FieldName = field.Name; | |||
|             AllowNoneValue = shortcutAttribute.AllowNoneValue; | |||
| 
 | |||
|             var constant = (string)field.GetValue(null); | |||
|             var lastSpace = constant.LastIndexOf(' ') + 1; | |||
| 
 | |||
|             if (!constant.EndsWith(" ")) | |||
|                 BaseString = constant.Remove(lastSpace); | |||
|             else { | |||
|                 BaseString = constant; | |||
|                 return; | |||
|             } | |||
| 
 | |||
|             constant = constant.Substring(lastSpace); | |||
| 
 | |||
|             if (string.IsNullOrEmpty(constant)) | |||
|                 return; | |||
| 
 | |||
|             Ctrl = constant.Contains(CTRL_CHAR); | |||
|             Shift = constant.Contains(SHIFT_CHAR); | |||
|             Alt = constant.Contains(ALT_CHAR); | |||
| 
 | |||
|             constant = constant.Replace(CTRL_CHAR.ToString(), string.Empty); | |||
|             constant = constant.Replace(SHIFT_CHAR.ToString(), string.Empty); | |||
|             constant = constant.Replace(ALT_CHAR.ToString(), string.Empty); | |||
|             constant = constant.Replace(NONE_CHAR.ToString(), string.Empty); | |||
| 
 | |||
|             KeyCode = Array.IndexOf(keys, constant); | |||
| 
 | |||
|             if (KeyCode < 0 || KeyCode >= keys.Length) { | |||
|                 Logger.Warning("Invalid shortcut term: {0}", constant); | |||
|                 KeyCode = 0; | |||
|             } | |||
|         } | |||
|         #endregion
 | |||
| 
 | |||
|         public string GetShortcutString() { | |||
|             if (KeyCode == 0) | |||
|                 return ""; | |||
| 
 | |||
|             var result = new StringBuilder(); | |||
| 
 | |||
|             if (!Ctrl && !Shift && !Alt) | |||
|                 result.Append(NONE_CHAR); | |||
|             else { | |||
|                 if (Ctrl) | |||
|                     result.Append(CTRL_CHAR); | |||
|                 if (Shift) | |||
|                     result.Append(SHIFT_CHAR); | |||
|                 if (Alt) | |||
|                     result.Append(ALT_CHAR); | |||
|             } | |||
| 
 | |||
|             result.Append(keys[KeyCode]); | |||
| 
 | |||
|             return result.ToString(); | |||
|         } | |||
| 
 | |||
|         #region Methods
 | |||
|         public override string ToString() { | |||
|             return BaseString + GetShortcutString(); | |||
|         } | |||
| 
 | |||
|         public static void DoShortcutsGUI() { | |||
|             GUI.changed = false; | |||
| 
 | |||
|             using(new EditorGUI.DisabledGroupScope(EditorApplication.isCompiling || !IsSourceFile)) { | |||
| 
 | |||
|                 if (InternalEditorUtility.GetUnityVersion() >= new Version(2019, 1)) | |||
|                     EditorGUILayout.HelpBox(string.Format("You can set custom shortcuts on a per user basis by editing them under {0} menu", FullscreenUtility.IsMacOS ? "Unity/Shortcuts" : "Edit/Shortcuts"), MessageType.Info); | |||
| 
 | |||
|                 foreach (var field in fieldsInfo) | |||
|                     DrawShortcut(field); | |||
| 
 | |||
|                 var duplicated = AnyDuplicates(); | |||
|                 var invalid = AnyInvalid(); | |||
| 
 | |||
|                 if (duplicated) | |||
|                     EditorGUILayout.HelpBox("Some menu items have the same keystroke, this is not allowed.", MessageType.Error); | |||
| 
 | |||
|                 if (invalid) | |||
|                     EditorGUILayout.HelpBox("Some menu items don't have a valid keystroke, you won't be able to use their correspondent fullscreens.", MessageType.Warning); | |||
| 
 | |||
|                 using(new EditorGUI.DisabledGroupScope(duplicated || !changed)) | |||
|                 if (GUILayout.Button("Apply Shortcuts")) | |||
|                     ApplyChanges(); | |||
|             } | |||
| 
 | |||
|             if (GUI.changed) | |||
|                 changed = true; | |||
|         } | |||
| 
 | |||
|         private static void ApplyChanges() { | |||
|             if (EditorApplication.isCompiling) | |||
|                 return; | |||
| 
 | |||
|             AssetDatabase.StartAssetEditing(); | |||
| 
 | |||
|             foreach (var field in fieldsInfo) | |||
|                 ReplaceConstant(field.FieldName, field); | |||
| 
 | |||
|             AssetDatabase.StopAssetEditing(); | |||
|             AssetDatabase.Refresh(); | |||
|         } | |||
| 
 | |||
|         private static bool AnyInvalid() { | |||
|             foreach (var field in fieldsInfo) | |||
|                 if (field == null || !field.AllowNoneValue && field.KeyCode == 0) | |||
|                     return true; | |||
| 
 | |||
|             return false; | |||
|         } | |||
| 
 | |||
|         private static bool AnyDuplicates() { | |||
|             for (var i = 0; i < fieldsInfo.Count; i++) | |||
|                 for (var j = i + 1; j < fieldsInfo.Count; j++) { | |||
|                     var fieldI = fieldsInfo[i]; | |||
|                     var fieldJ = fieldsInfo[j]; | |||
| 
 | |||
|                     if (fieldI == null || fieldJ == null || | |||
|                         (fieldI.KeyCode != 0 && fieldI.GetShortcutString() == fieldJ.GetShortcutString())) | |||
|                         return true; | |||
|                 } | |||
| 
 | |||
|             return false; | |||
|         } | |||
| 
 | |||
|         private static Shortcut DrawShortcut(Shortcut shortcut) { | |||
|             using(new EditorGUILayout.HorizontalScope()) { | |||
|                 EditorGUILayout.LabelField(shortcut.Label, GUILayout.Width(130f)); | |||
| 
 | |||
|                 shortcut.Ctrl = GUILayout.Toggle(shortcut.Ctrl, FullscreenUtility.IsMacOS ? "Cmd" : "Ctrl", EditorStyles.miniButtonLeft, GUILayout.Width(50f)); | |||
|                 shortcut.Shift = GUILayout.Toggle(shortcut.Shift, "Shift", EditorStyles.miniButtonMid, GUILayout.Width(50f)); | |||
|                 shortcut.Alt = GUILayout.Toggle(shortcut.Alt, "Alt", EditorStyles.miniButtonRight, GUILayout.Width(50f)); | |||
|                 shortcut.KeyCode = EditorGUILayout.Popup(shortcut.KeyCode, keys); | |||
| 
 | |||
|                 if (GUILayout.Button(new GUIContent("X", "Clear Shortcut"))) { | |||
|                     shortcut.Ctrl = false; | |||
|                     shortcut.Shift = false; | |||
|                     shortcut.Alt = false; | |||
|                     shortcut.KeyCode = 0; | |||
|                 } | |||
|             } | |||
| 
 | |||
|             return shortcut; | |||
|         } | |||
| 
 | |||
|         private static void ReplaceConstant(string constantName, object newValue) { | |||
|             try { | |||
|                 if (!IsSourceFile) { | |||
|                     Logger.Error("Could not find the source code file to change value"); | |||
|                     return; | |||
|                 } | |||
| 
 | |||
|                 var fileText = new StringBuilder(); | |||
|                 var changed = false; | |||
| 
 | |||
|                 using(var file = File.OpenText(ThisFilePath)) | |||
|                 while (!file.EndOfStream) { | |||
|                     var line = file.ReadLine(); | |||
| 
 | |||
|                     if (!line.Contains(constantName)) { | |||
|                         fileText.AppendLine(line); | |||
|                         continue; | |||
|                     } | |||
| 
 | |||
|                     var indexOfValue = line.IndexOf('='); | |||
| 
 | |||
|                     fileText.Append(line.Remove(indexOfValue)); | |||
|                     fileText.AppendLine(string.Format("= \"{0}\";", newValue)); | |||
|                     fileText.Append(file.ReadToEnd()); | |||
| 
 | |||
|                     changed = true; | |||
|                 } | |||
| 
 | |||
|                 fileText = fileText.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); | |||
| 
 | |||
|                 if (changed) | |||
|                     File.WriteAllText(ThisFilePath, fileText.ToString()); | |||
|                 else | |||
|                     Logger.Warning("Failed to find field {0} on {1}", constantName, ThisFilePath); | |||
|             } catch (Exception e) { | |||
|                 Logger.Exception(e); | |||
|                 Logger.Error("Failed to save Fullscreen Editor shortcuts"); | |||
|             } | |||
|         } | |||
|         #endregion
 | |||
| 
 | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 88013eba7392a9e418e55e3229a5f144 | |||
| timeCreated: 1508377869 | |||
| licenseType: Store | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,98 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using UnityEditor; | |||
| using UnityEngine; | |||
| using UnityObject = UnityEngine.Object; | |||
| using HostView = UnityEngine.ScriptableObject; | |||
| using View = UnityEngine.ScriptableObject; | |||
| using ContainerWindow = UnityEngine.ScriptableObject; | |||
| 
 | |||
| namespace FullscreenEditor { | |||
|     /// <summary>Represents the pyramid containing all the elements that make up a window.</summary>
 | |||
|     [Serializable] | |||
|     public struct ViewPyramid { | |||
| 
 | |||
|         /// <summary>The actual window, may be null if the pyramid was created from a view or container.</summary>
 | |||
|         public EditorWindow Window { | |||
|             get { | |||
|                 if (!m_window && m_windowInstanceID != 0) | |||
|                     m_window = (EditorWindow)EditorUtility.InstanceIDToObject(m_windowInstanceID); | |||
|                 return m_window; | |||
|             } | |||
|             set { | |||
|                 m_window = value; | |||
|                 m_windowInstanceID = m_window ? m_window.GetInstanceID() : 0; | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>View that controls how the window (and child view) are drawn.</summary>
 | |||
|         public View View { | |||
|             get { | |||
|                 if (!m_view && m_viewInstanceID != 0) | |||
|                     m_view = (View)EditorUtility.InstanceIDToObject(m_viewInstanceID); | |||
|                 return m_view; | |||
|             } | |||
|             set { | |||
|                 value.EnsureOfType(Types.View); | |||
|                 m_view = value; | |||
|                 m_viewInstanceID = m_view ? m_view.GetInstanceID() : 0; | |||
|             } | |||
|         } | |||
| 
 | |||
|         /// <summary>The native window.</summary>
 | |||
|         public ContainerWindow Container { | |||
|             get { | |||
|                 if (!m_container && m_containerInstanceID != 0) | |||
|                     m_container = (ContainerWindow)EditorUtility.InstanceIDToObject(m_containerInstanceID); | |||
|                 return m_container; | |||
|             } | |||
|             set { | |||
|                 value.EnsureOfType(Types.ContainerWindow); | |||
|                 m_container = value; | |||
|                 m_containerInstanceID = m_container ? m_container.GetInstanceID() : 0; | |||
|             } | |||
|         } | |||
| 
 | |||
|         [SerializeField] private EditorWindow m_window; | |||
|         [SerializeField] private View m_view; | |||
|         [SerializeField] private ContainerWindow m_container; | |||
| 
 | |||
|         [SerializeField] private int m_windowInstanceID; | |||
|         [SerializeField] private int m_viewInstanceID; | |||
|         [SerializeField] private int m_containerInstanceID; | |||
| 
 | |||
|         /// <summary>Create a new instance and automatically assigns the window, view and container.</summary>
 | |||
|         public ViewPyramid(ScriptableObject viewOrWindow) { | |||
| 
 | |||
|             if (!viewOrWindow) { | |||
|                 m_window = null; | |||
|                 m_view = null; | |||
|                 m_container = null; | |||
|             } else if (viewOrWindow.IsOfType(typeof(EditorWindow))) { | |||
|                 m_window = viewOrWindow as EditorWindow; | |||
|                 m_view = m_window.GetFieldValue<View>("m_Parent"); | |||
|                 m_container = m_view.GetPropertyValue<ContainerWindow>("window"); | |||
|             } else if (viewOrWindow.IsOfType(Types.View)) { | |||
|                 m_window = null; | |||
|                 m_view = viewOrWindow; | |||
|                 m_container = m_view.GetPropertyValue<ContainerWindow>("window"); | |||
|             } else if (viewOrWindow.IsOfType(Types.ContainerWindow)) { | |||
|                 m_window = null; | |||
|                 m_view = viewOrWindow.GetPropertyValue<ContainerWindow>("rootView"); | |||
|                 m_container = viewOrWindow; | |||
|             } else { | |||
|                 throw new ArgumentException("Param must be of type EditorWindow, View or ContainerWindow", "viewOrWindow"); | |||
|             } | |||
| 
 | |||
|             if (!m_window && m_view && m_view.IsOfType(Types.HostView)) | |||
|                 m_window = m_view.GetPropertyValue<EditorWindow>("actualView"); | |||
| 
 | |||
|             m_windowInstanceID = m_window ? m_window.GetInstanceID() : 0; | |||
|             m_viewInstanceID = m_view ? m_view.GetInstanceID() : 0; | |||
|             m_containerInstanceID = m_container ? m_container.GetInstanceID() : 0; | |||
| 
 | |||
|         } | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 26219245258051b44a2dc559b5e5584f | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: ad11a5b026b2b8244849852d5718d291 | |||
| folderAsset: yes | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,17 @@ | |||
| using System; | |||
| using System.Runtime.InteropServices; | |||
| 
 | |||
| namespace FullscreenEditor.Windows { | |||
|     internal static class GDI32 { | |||
| 
 | |||
|         // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
 | |||
|         public enum DeviceCap { | |||
|             VERTRES = 10, | |||
|             DESKTOPVERTRES = 117, | |||
|         } | |||
| 
 | |||
|         [DllImport("gdi32.dll")] | |||
|         public static extern int GetDeviceCaps(IntPtr hDc, int nIndex); | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: d2e63aa031b326e4c932e1015ea72da1 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,18 @@ | |||
| using System; | |||
| using System.Runtime.InteropServices; | |||
| 
 | |||
| namespace FullscreenEditor.Windows { | |||
| 
 | |||
|     internal enum MonitorDpiType { | |||
|         MDT_EFFECTIVE_DPI = 0, | |||
|         MDT_ANGULAR_DPI = 1, | |||
|         MDT_RAW_DPI = 2, | |||
|     } | |||
| 
 | |||
|     internal static class ShCore { | |||
| 
 | |||
|         [DllImport("shcore.dll")] | |||
|         internal static extern uint GetDpiForMonitor(IntPtr hmonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY); | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 452ce41cefb917e42b0f681115e84f84 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,127 @@ | |||
| using System; | |||
| using System.Runtime.InteropServices; | |||
| using UnityEngine; | |||
| 
 | |||
| namespace FullscreenEditor.Windows { | |||
|     [System.Serializable] | |||
|     [StructLayout(LayoutKind.Sequential)] | |||
|     internal struct NativeRect { | |||
|         public int left; | |||
|         public int top; | |||
|         public int right; | |||
|         public int bottom; | |||
| 
 | |||
|         public static implicit operator Rect(NativeRect other) { | |||
|             return Rect.MinMaxRect( | |||
|                 other.left, | |||
|                 other.top, | |||
|                 other.right, | |||
|                 other.bottom | |||
|             ); | |||
|         } | |||
| 
 | |||
|         public static implicit operator NativeRect(Rect other) { | |||
|             return new NativeRect { | |||
|                 left = (int)other.xMin, | |||
|                 top = (int)other.yMin, | |||
|                 right = (int)other.xMax, | |||
|                 bottom = (int)other.yMax | |||
|             }; | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
|     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | |||
|     internal struct MonitorInfoEx { | |||
| 
 | |||
|         private const int CCHDEVICENAME = 0x20; | |||
| 
 | |||
|         public int size; | |||
|         public NativeRect monitor; | |||
|         public NativeRect work; | |||
|         public uint flags; | |||
| 
 | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] | |||
|         public string DeviceName; | |||
| 
 | |||
|         public void Init() { | |||
|             this.size = 40 + 1 * CCHDEVICENAME; | |||
|             this.DeviceName = string.Empty; | |||
|         } | |||
| 
 | |||
|     } | |||
| 
 | |||
|     [System.Serializable] | |||
|     [StructLayout(LayoutKind.Sequential)] | |||
|     internal struct DevMode { | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] | |||
|         public string dmDeviceName; | |||
|         public short dmSpecVersion; | |||
|         public short dmDriverVersion; | |||
|         public short dmSize; | |||
|         public short dmDriverExtra; | |||
|         public int dmFields; | |||
|         public int dmPositionX; | |||
|         public int dmPositionY; | |||
|         public ScreenOrientation dmDisplayOrientation; | |||
|         public int dmDisplayFixedOutput; | |||
|         public short dmColor; | |||
|         public short dmDuplex; | |||
|         public short dmYResolution; | |||
|         public short dmTTOption; | |||
|         public short dmCollate; | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)] | |||
|         public string dmFormName; | |||
|         public short dmLogPixels; | |||
|         public int dmBitsPerPel; | |||
|         public int dmPelsWidth; | |||
|         public int dmPelsHeight; | |||
|         public int dmDisplayFlags; | |||
|         public int dmDisplayFrequency; | |||
|         public int dmICMMethod; | |||
|         public int dmICMIntent; | |||
|         public int dmMediaType; | |||
|         public int dmDitherType; | |||
|         public int dmReserved1; | |||
|         public int dmReserved2; | |||
|         public int dmPanningWidth; | |||
|         public int dmPanningHeight; | |||
|     } | |||
| 
 | |||
|     [Flags] | |||
|     internal enum DisplayDeviceStateFlags : int { | |||
|         /// <summary>The device is part of the desktop.</summary>
 | |||
|         AttachedToDesktop = 0x1, | |||
|         MultiDriver = 0x2, | |||
|         /// <summary>The device is part of the desktop.</summary>
 | |||
|         PrimaryDevice = 0x4, | |||
|         /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
 | |||
|         MirroringDriver = 0x8, | |||
|         /// <summary>The device is VGA compatible.</summary>
 | |||
|         VGACompatible = 0x10, | |||
|         /// <summary>The device is removable; it cannot be the primary display.</summary>
 | |||
|         Removable = 0x20, | |||
|         /// <summary>The device has more display modes than its output devices support.</summary>
 | |||
|         ModesPruned = 0x8000000, | |||
|         Remote = 0x4000000, | |||
|         Disconnect = 0x2000000 | |||
|     } | |||
| 
 | |||
|     [System.Serializable] | |||
|     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | |||
|     internal struct DisplayDevice { | |||
|         [MarshalAs(UnmanagedType.U4)] | |||
|         public int cb; | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] | |||
|         public string DeviceName; | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |||
|         public string DeviceString; | |||
|         [MarshalAs(UnmanagedType.U4)] | |||
|         public DisplayDeviceStateFlags StateFlags; | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |||
|         public string DeviceID; | |||
|         [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | |||
|         public string DeviceKey; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: dd1767e9696fe0a4ca0d7d06b1384272 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,32 @@ | |||
| using System; | |||
| using System.Runtime.InteropServices; | |||
| 
 | |||
| namespace FullscreenEditor.Windows { | |||
| 
 | |||
|     internal static class User32 { | |||
| 
 | |||
|         public delegate bool EnumMonitorsDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref NativeRect lprcMonitor, IntPtr dwData); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, EnumMonitorsDelegate lpfnEnum, IntPtr dwData); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfoEx lpmi); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DevMode devMode); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DisplayDevice lpDisplayDevice, uint dwFlags); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern IntPtr GetDC(IntPtr hWnd); | |||
| 
 | |||
|         [DllImport("user32.dll")] | |||
|         public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc); | |||
| 
 | |||
|         [DllImport("user32.dll", SetLastError = true)] | |||
|         public static extern int GetSystemMetrics(int smIndex); | |||
| 
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 2d1661fb151eb4d48a4dc4e4294a3351 | |||
| MonoImporter: | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   defaultReferences: [] | |||
|   executionOrder: 0 | |||
|   icon: {instanceID: 0} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,15 @@ | |||
| { | |||
|     "name": "FullscreenEditor", | |||
|     "references": [], | |||
|     "optionalUnityReferences": [], | |||
|     "includePlatforms": [ | |||
|         "Editor" | |||
|     ], | |||
|     "excludePlatforms": [], | |||
|     "allowUnsafeCode": true, | |||
|     "overrideReferences": false, | |||
|     "precompiledReferences": [], | |||
|     "autoReferenced": true, | |||
|     "defineConstraints": [], | |||
|     "versionDefines": [] | |||
| } | |||
| @ -0,0 +1,7 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 838d3286f0973344ab6e99d3951012f7 | |||
| AssemblyDefinitionImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
								
									Binary file not shown.
								
							
						
					| @ -0,0 +1,9 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 0c3e30c36dfe6484c88009ed994d43a6 | |||
| timeCreated: 1510520595 | |||
| licenseType: Store | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,604 @@ | |||
| %YAML 1.1 | |||
| %TAG !u! tag:unity3d.com,2011: | |||
| --- !u!74 &7400000 | |||
| AnimationClip: | |||
|   m_ObjectHideFlags: 0 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Celestial_Camera | |||
|   serializedVersion: 7 | |||
|   m_Legacy: 0 | |||
|   m_Compressed: 0 | |||
|   m_UseHighQualityCurve: 1 | |||
|   m_RotationCurves: [] | |||
|   m_CompressedRotationCurves: [] | |||
|   m_EulerCurves: | |||
|   - curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: {x: -2.116, y: 19.367, z: 13.463} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 30.066668 | |||
|         value: {x: -2.116, y: 19.367, z: 13.463} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 45 | |||
|         value: {x: -75.75094, y: 58.858097, z: -73.22502} | |||
|         inSlope: {x: -0.95258343, y: 0.51087934, z: -1.1214467} | |||
|         outSlope: {x: -0.95258343, y: 0.51087934, z: -1.1214467} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 89.9 | |||
|         value: {x: -90, y: 66.5, z: -90} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: {x: 267, y: 66.5, z: 270} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     path: Center | |||
|   m_PositionCurves: | |||
|   - curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: {x: -0.43242157, y: 6.093788, z: -0.90608734} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 45.516666 | |||
|         value: {x: -0.43242157, y: 6.093788, z: -0.90608734} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 90.05 | |||
|         value: {x: 11.8, y: 92.06, z: -0.90608734} | |||
|         inSlope: {x: 0, y: 1.0901738, z: 0} | |||
|         outSlope: {x: 0, y: 1.0901738, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 160.73334 | |||
|         value: {x: 11.8, y: 131.7, z: -0.90608734} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: {x: 11.8, y: 123.5, z: -0.90608734} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     path: Center/Overhead | |||
|   m_ScaleCurves: [] | |||
|   m_FloatCurves: | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 0 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       - serializedVersion: 3 | |||
|         time: 0.06666667 | |||
|         value: 1 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       - serializedVersion: 3 | |||
|         time: 226.71666 | |||
|         value: 0 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_Enabled | |||
|     path: Center/Overhead | |||
|     classID: 20 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   m_PPtrCurves: [] | |||
|   m_SampleRate: 60 | |||
|   m_WrapMode: 0 | |||
|   m_Bounds: | |||
|     m_Center: {x: 0, y: 0, z: 0} | |||
|     m_Extent: {x: 0, y: 0, z: 0} | |||
|   m_ClipBindingConstant: | |||
|     genericBindings: | |||
|     - serializedVersion: 2 | |||
|       path: 2073857273 | |||
|       attribute: 1 | |||
|       script: {fileID: 0} | |||
|       typeID: 4 | |||
|       customType: 0 | |||
|       isPPtrCurve: 0 | |||
|       isIntCurve: 0 | |||
|       isSerializeReferenceCurve: 0 | |||
|     - serializedVersion: 2 | |||
|       path: 1197272594 | |||
|       attribute: 4 | |||
|       script: {fileID: 0} | |||
|       typeID: 4 | |||
|       customType: 4 | |||
|       isPPtrCurve: 0 | |||
|       isIntCurve: 0 | |||
|       isSerializeReferenceCurve: 0 | |||
|     - serializedVersion: 2 | |||
|       path: 2073857273 | |||
|       attribute: 3305885265 | |||
|       script: {fileID: 0} | |||
|       typeID: 20 | |||
|       customType: 0 | |||
|       isPPtrCurve: 0 | |||
|       isIntCurve: 0 | |||
|       isSerializeReferenceCurve: 0 | |||
|     pptrCurveMapping: [] | |||
|   m_AnimationClipSettings: | |||
|     serializedVersion: 2 | |||
|     m_AdditiveReferencePoseClip: {fileID: 0} | |||
|     m_AdditiveReferencePoseTime: 0 | |||
|     m_StartTime: 0 | |||
|     m_StopTime: 226.71666 | |||
|     m_OrientationOffsetY: 0 | |||
|     m_Level: 0 | |||
|     m_CycleOffset: 0 | |||
|     m_HasAdditiveReferencePose: 0 | |||
|     m_LoopTime: 0 | |||
|     m_LoopBlend: 0 | |||
|     m_LoopBlendOrientation: 0 | |||
|     m_LoopBlendPositionY: 0 | |||
|     m_LoopBlendPositionXZ: 0 | |||
|     m_KeepOriginalOrientation: 0 | |||
|     m_KeepOriginalPositionY: 1 | |||
|     m_KeepOriginalPositionXZ: 0 | |||
|     m_HeightFromFeet: 0 | |||
|     m_Mirror: 0 | |||
|   m_EditorCurves: | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 0 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       - serializedVersion: 3 | |||
|         time: 0.06666667 | |||
|         value: 1 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       - serializedVersion: 3 | |||
|         time: 226.71666 | |||
|         value: 0 | |||
|         inSlope: Infinity | |||
|         outSlope: Infinity | |||
|         tangentMode: 103 | |||
|         weightedMode: 0 | |||
|         inWeight: 0 | |||
|         outWeight: 0 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_Enabled | |||
|     path: Center/Overhead | |||
|     classID: 20 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: -2.116 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 30.066668 | |||
|         value: -2.116 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45 | |||
|         value: -75.75094 | |||
|         inSlope: -0.95258343 | |||
|         outSlope: -0.95258343 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 89.9 | |||
|         value: -90 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: 267 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: localEulerAnglesRaw.x | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 19.367 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 30.066668 | |||
|         value: 19.367 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45 | |||
|         value: 58.858097 | |||
|         inSlope: 0.51087934 | |||
|         outSlope: 0.51087934 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 89.9 | |||
|         value: 66.5 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: 66.5 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: localEulerAnglesRaw.y | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 13.463 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 30.066668 | |||
|         value: 13.463 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45 | |||
|         value: -73.22502 | |||
|         inSlope: -1.1214467 | |||
|         outSlope: -1.1214467 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 89.9 | |||
|         value: -90 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: 270 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: localEulerAnglesRaw.z | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: -0.43242157 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45.516666 | |||
|         value: -0.43242157 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 90.05 | |||
|         value: 11.8 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: 11.8 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalPosition.x | |||
|     path: Center/Overhead | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 6.093788 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45.516666 | |||
|         value: 6.093788 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 90.05 | |||
|         value: 92.06 | |||
|         inSlope: 1.0901738 | |||
|         outSlope: 1.0901738 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 160.73334 | |||
|         value: 131.7 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.7 | |||
|         value: 123.5 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalPosition.y | |||
|     path: Center/Overhead | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: -0.90608734 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 45.516666 | |||
|         value: -0.90608734 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalPosition.z | |||
|     path: Center/Overhead | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   m_EulerEditorCurves: | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: [] | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalEulerAngles.x | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: [] | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalEulerAngles.y | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: [] | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalEulerAngles.z | |||
|     path: Center | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   m_HasGenericRootTransform: 0 | |||
|   m_HasMotionFloatCurves: 0 | |||
|   m_Events: [] | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 878ac42e62151e449883be273813cf24 | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 7400000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,72 @@ | |||
| %YAML 1.1 | |||
| %TAG !u! tag:unity3d.com,2011: | |||
| --- !u!1102 &-2336639578266700500 | |||
| AnimatorState: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Celestial_Camera | |||
|   m_Speed: 1 | |||
|   m_CycleOffset: 0 | |||
|   m_Transitions: [] | |||
|   m_StateMachineBehaviours: [] | |||
|   m_Position: {x: 50, y: 50, z: 0} | |||
|   m_IKOnFeet: 0 | |||
|   m_WriteDefaultValues: 1 | |||
|   m_Mirror: 0 | |||
|   m_SpeedParameterActive: 0 | |||
|   m_MirrorParameterActive: 0 | |||
|   m_CycleOffsetParameterActive: 0 | |||
|   m_TimeParameterActive: 0 | |||
|   m_Motion: {fileID: 7400000, guid: 878ac42e62151e449883be273813cf24, type: 2} | |||
|   m_Tag:  | |||
|   m_SpeedParameter:  | |||
|   m_MirrorParameter:  | |||
|   m_CycleOffsetParameter:  | |||
|   m_TimeParameter:  | |||
| --- !u!1107 &-1897080819518823633 | |||
| AnimatorStateMachine: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Base Layer | |||
|   m_ChildStates: | |||
|   - serializedVersion: 1 | |||
|     m_State: {fileID: -2336639578266700500} | |||
|     m_Position: {x: 200, y: 0, z: 0} | |||
|   m_ChildStateMachines: [] | |||
|   m_AnyStateTransitions: [] | |||
|   m_EntryTransitions: [] | |||
|   m_StateMachineTransitions: {} | |||
|   m_StateMachineBehaviours: [] | |||
|   m_AnyStatePosition: {x: 50, y: 20, z: 0} | |||
|   m_EntryPosition: {x: 50, y: 120, z: 0} | |||
|   m_ExitPosition: {x: 800, y: 120, z: 0} | |||
|   m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} | |||
|   m_DefaultState: {fileID: -2336639578266700500} | |||
| --- !u!91 &9100000 | |||
| AnimatorController: | |||
|   m_ObjectHideFlags: 0 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Celestial_Camera | |||
|   serializedVersion: 5 | |||
|   m_AnimatorParameters: [] | |||
|   m_AnimatorLayers: | |||
|   - serializedVersion: 5 | |||
|     m_Name: Base Layer | |||
|     m_StateMachine: {fileID: -1897080819518823633} | |||
|     m_Mask: {fileID: 0} | |||
|     m_Motions: [] | |||
|     m_Behaviours: [] | |||
|     m_BlendingMode: 0 | |||
|     m_SyncedLayerIndex: -1 | |||
|     m_DefaultWeight: 0 | |||
|     m_IKPass: 0 | |||
|     m_SyncedLayerAffectsTiming: 0 | |||
|     m_Controller: {fileID: 9100000} | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: f3c3667eb3544b947980f65d4a6f636e | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 9100000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
								
									
										File diff suppressed because it is too large
									
								
							
						
					| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 79817a218c93541429796aa747c77da2 | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 7400000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,249 @@ | |||
| %YAML 1.1 | |||
| %TAG !u! tag:unity3d.com,2011: | |||
| --- !u!74 &7400000 | |||
| AnimationClip: | |||
|   m_ObjectHideFlags: 0 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Scaler_1 | |||
|   serializedVersion: 7 | |||
|   m_Legacy: 0 | |||
|   m_Compressed: 0 | |||
|   m_UseHighQualityCurve: 1 | |||
|   m_RotationCurves: [] | |||
|   m_CompressedRotationCurves: [] | |||
|   m_EulerCurves: [] | |||
|   m_PositionCurves: [] | |||
|   m_ScaleCurves: | |||
|   - curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: {x: 1, y: 1, z: 1} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 62.433334 | |||
|         value: {x: 1, y: 1, z: 1} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 114.833336 | |||
|         value: {x: 50, y: 50, z: 50} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       - serializedVersion: 3 | |||
|         time: 226.16667 | |||
|         value: {x: 1, y: 1, z: 1} | |||
|         inSlope: {x: 0, y: 0, z: 0} | |||
|         outSlope: {x: 0, y: 0, z: 0} | |||
|         tangentMode: 0 | |||
|         weightedMode: 0 | |||
|         inWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|         outWeight: {x: 0.33333334, y: 0.33333334, z: 0.33333334} | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     path:  | |||
|   m_FloatCurves: [] | |||
|   m_PPtrCurves: [] | |||
|   m_SampleRate: 60 | |||
|   m_WrapMode: 0 | |||
|   m_Bounds: | |||
|     m_Center: {x: 0, y: 0, z: 0} | |||
|     m_Extent: {x: 0, y: 0, z: 0} | |||
|   m_ClipBindingConstant: | |||
|     genericBindings: | |||
|     - serializedVersion: 2 | |||
|       path: 0 | |||
|       attribute: 3 | |||
|       script: {fileID: 0} | |||
|       typeID: 4 | |||
|       customType: 0 | |||
|       isPPtrCurve: 0 | |||
|       isIntCurve: 0 | |||
|       isSerializeReferenceCurve: 0 | |||
|     pptrCurveMapping: [] | |||
|   m_AnimationClipSettings: | |||
|     serializedVersion: 2 | |||
|     m_AdditiveReferencePoseClip: {fileID: 0} | |||
|     m_AdditiveReferencePoseTime: 0 | |||
|     m_StartTime: 0 | |||
|     m_StopTime: 226.16667 | |||
|     m_OrientationOffsetY: 0 | |||
|     m_Level: 0 | |||
|     m_CycleOffset: 0 | |||
|     m_HasAdditiveReferencePose: 0 | |||
|     m_LoopTime: 0 | |||
|     m_LoopBlend: 0 | |||
|     m_LoopBlendOrientation: 0 | |||
|     m_LoopBlendPositionY: 0 | |||
|     m_LoopBlendPositionXZ: 0 | |||
|     m_KeepOriginalOrientation: 0 | |||
|     m_KeepOriginalPositionY: 1 | |||
|     m_KeepOriginalPositionXZ: 0 | |||
|     m_HeightFromFeet: 0 | |||
|     m_Mirror: 0 | |||
|   m_EditorCurves: | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 62.433334 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 114.833336 | |||
|         value: 50 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.16667 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalScale.x | |||
|     path:  | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 62.433334 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 114.833336 | |||
|         value: 50 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.16667 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalScale.y | |||
|     path:  | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   - serializedVersion: 2 | |||
|     curve: | |||
|       serializedVersion: 2 | |||
|       m_Curve: | |||
|       - serializedVersion: 3 | |||
|         time: 0 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 62.433334 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 114.833336 | |||
|         value: 50 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       - serializedVersion: 3 | |||
|         time: 226.16667 | |||
|         value: 1 | |||
|         inSlope: 0 | |||
|         outSlope: 0 | |||
|         tangentMode: 136 | |||
|         weightedMode: 0 | |||
|         inWeight: 0.33333334 | |||
|         outWeight: 0.33333334 | |||
|       m_PreInfinity: 2 | |||
|       m_PostInfinity: 2 | |||
|       m_RotationOrder: 4 | |||
|     attribute: m_LocalScale.z | |||
|     path:  | |||
|     classID: 4 | |||
|     script: {fileID: 0} | |||
|     flags: 0 | |||
|   m_EulerEditorCurves: [] | |||
|   m_HasGenericRootTransform: 0 | |||
|   m_HasMotionFloatCurves: 0 | |||
|   m_Events: [] | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 4ce2eb7d55dd3ec48aa18d5ee704c297 | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 7400000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,72 @@ | |||
| %YAML 1.1 | |||
| %TAG !u! tag:unity3d.com,2011: | |||
| --- !u!1107 &-6794572299121012679 | |||
| AnimatorStateMachine: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Base Layer | |||
|   m_ChildStates: | |||
|   - serializedVersion: 1 | |||
|     m_State: {fileID: -3716467034663456527} | |||
|     m_Position: {x: 200, y: 0, z: 0} | |||
|   m_ChildStateMachines: [] | |||
|   m_AnyStateTransitions: [] | |||
|   m_EntryTransitions: [] | |||
|   m_StateMachineTransitions: {} | |||
|   m_StateMachineBehaviours: [] | |||
|   m_AnyStatePosition: {x: 50, y: 20, z: 0} | |||
|   m_EntryPosition: {x: 50, y: 120, z: 0} | |||
|   m_ExitPosition: {x: 800, y: 120, z: 0} | |||
|   m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} | |||
|   m_DefaultState: {fileID: -3716467034663456527} | |||
| --- !u!1102 &-3716467034663456527 | |||
| AnimatorState: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Scaler_1 | |||
|   m_Speed: 1 | |||
|   m_CycleOffset: 0 | |||
|   m_Transitions: [] | |||
|   m_StateMachineBehaviours: [] | |||
|   m_Position: {x: 50, y: 50, z: 0} | |||
|   m_IKOnFeet: 0 | |||
|   m_WriteDefaultValues: 1 | |||
|   m_Mirror: 0 | |||
|   m_SpeedParameterActive: 0 | |||
|   m_MirrorParameterActive: 0 | |||
|   m_CycleOffsetParameterActive: 0 | |||
|   m_TimeParameterActive: 0 | |||
|   m_Motion: {fileID: 7400000, guid: 4ce2eb7d55dd3ec48aa18d5ee704c297, type: 2} | |||
|   m_Tag:  | |||
|   m_SpeedParameter:  | |||
|   m_MirrorParameter:  | |||
|   m_CycleOffsetParameter:  | |||
|   m_TimeParameter:  | |||
| --- !u!91 &9100000 | |||
| AnimatorController: | |||
|   m_ObjectHideFlags: 0 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: f_avg | |||
|   serializedVersion: 5 | |||
|   m_AnimatorParameters: [] | |||
|   m_AnimatorLayers: | |||
|   - serializedVersion: 5 | |||
|     m_Name: Base Layer | |||
|     m_StateMachine: {fileID: -6794572299121012679} | |||
|     m_Mask: {fileID: 0} | |||
|     m_Motions: [] | |||
|     m_Behaviours: [] | |||
|     m_BlendingMode: 0 | |||
|     m_SyncedLayerIndex: -1 | |||
|     m_DefaultWeight: 0 | |||
|     m_IKPass: 0 | |||
|     m_SyncedLayerAffectsTiming: 0 | |||
|     m_Controller: {fileID: 9100000} | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 8b023f1ac0ba4c044a5bc2235a03635a | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 9100000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
| @ -0,0 +1,72 @@ | |||
| %YAML 1.1 | |||
| %TAG !u! tag:unity3d.com,2011: | |||
| --- !u!1107 &-3472790725957294762 | |||
| AnimatorStateMachine: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Base Layer | |||
|   m_ChildStates: | |||
|   - serializedVersion: 1 | |||
|     m_State: {fileID: 5216019962439093801} | |||
|     m_Position: {x: 200, y: 0, z: 0} | |||
|   m_ChildStateMachines: [] | |||
|   m_AnyStateTransitions: [] | |||
|   m_EntryTransitions: [] | |||
|   m_StateMachineTransitions: {} | |||
|   m_StateMachineBehaviours: [] | |||
|   m_AnyStatePosition: {x: 50, y: 20, z: 0} | |||
|   m_EntryPosition: {x: 50, y: 120, z: 0} | |||
|   m_ExitPosition: {x: 800, y: 120, z: 0} | |||
|   m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} | |||
|   m_DefaultState: {fileID: 5216019962439093801} | |||
| --- !u!91 &9100000 | |||
| AnimatorController: | |||
|   m_ObjectHideFlags: 0 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: f_avg_Pelvis | |||
|   serializedVersion: 5 | |||
|   m_AnimatorParameters: [] | |||
|   m_AnimatorLayers: | |||
|   - serializedVersion: 5 | |||
|     m_Name: Base Layer | |||
|     m_StateMachine: {fileID: -3472790725957294762} | |||
|     m_Mask: {fileID: 0} | |||
|     m_Motions: [] | |||
|     m_Behaviours: [] | |||
|     m_BlendingMode: 0 | |||
|     m_SyncedLayerIndex: -1 | |||
|     m_DefaultWeight: 0 | |||
|     m_IKPass: 0 | |||
|     m_SyncedLayerAffectsTiming: 0 | |||
|     m_Controller: {fileID: 9100000} | |||
| --- !u!1102 &5216019962439093801 | |||
| AnimatorState: | |||
|   serializedVersion: 6 | |||
|   m_ObjectHideFlags: 1 | |||
|   m_CorrespondingSourceObject: {fileID: 0} | |||
|   m_PrefabInstance: {fileID: 0} | |||
|   m_PrefabAsset: {fileID: 0} | |||
|   m_Name: Scaler_1 | |||
|   m_Speed: 1 | |||
|   m_CycleOffset: 0 | |||
|   m_Transitions: [] | |||
|   m_StateMachineBehaviours: [] | |||
|   m_Position: {x: 50, y: 50, z: 0} | |||
|   m_IKOnFeet: 0 | |||
|   m_WriteDefaultValues: 1 | |||
|   m_Mirror: 0 | |||
|   m_SpeedParameterActive: 0 | |||
|   m_MirrorParameterActive: 0 | |||
|   m_CycleOffsetParameterActive: 0 | |||
|   m_TimeParameterActive: 0 | |||
|   m_Motion: {fileID: 7400000, guid: 4ce2eb7d55dd3ec48aa18d5ee704c297, type: 2} | |||
|   m_Tag:  | |||
|   m_SpeedParameter:  | |||
|   m_MirrorParameter:  | |||
|   m_CycleOffsetParameter:  | |||
|   m_TimeParameter:  | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 4462ca0677c3ba248823102a030626a9 | |||
| NativeFormatImporter: | |||
|   externalObjects: {} | |||
|   mainObjectFileID: 9100000 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
								
									Binary file not shown.
								
							
						
					| @ -0,0 +1,11 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 26c4676e7cb80a24ebb49468ddd83ee8 | |||
| ScriptedImporter: | |||
|   internalIDToNameTable: [] | |||
|   externalObjects: {} | |||
|   serializedVersion: 2 | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
|   script: {fileID: 11500000, guid: 183c1cb276390b14e883dae29f585dfa, type: 3} | |||
|   _containerType: 0 | |||
| @ -0,0 +1,8 @@ | |||
| fileFormatVersion: 2 | |||
| guid: 1d7ddd38c1062d743980eb5c7f0ad9c8 | |||
| folderAsset: yes | |||
| DefaultImporter: | |||
|   externalObjects: {} | |||
|   userData:  | |||
|   assetBundleName:  | |||
|   assetBundleVariant:  | |||
Some files were not shown because too many files changed in this diff
					Loading…
					
					
				
		Reference in new issue