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("m_Parent"); var parentB = b.GetFieldValue("m_Parent"); var containerA = parentA.GetPropertyValue("window"); var containerB = parentB.GetPropertyValue("window"); var selectedPaneA = parentA.GetPropertyValue("actualView"); var selectedPaneB = parentB.GetPropertyValue("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>("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(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("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() : (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("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(); } } }