You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
4.7 KiB

  1. #if UNITY_EDITOR
  2. using System;
  3. using System.Reflection;
  4. using UnityEditor;
  5. using UnityEngine;
  6. public static class WindowHelper
  7. {
  8. private class R_EditorWindow
  9. {
  10. private EditorWindow m_instance;
  11. private System.Type m_type;
  12. public R_EditorWindow( EditorWindow instance )
  13. {
  14. m_instance = instance;
  15. m_type = instance.GetType();
  16. }
  17. public object Parent
  18. {
  19. get
  20. {
  21. var field = m_type.GetField( "m_Parent", BindingFlags.Instance | BindingFlags.NonPublic );
  22. return field.GetValue( m_instance );
  23. }
  24. }
  25. }
  26. private class R_DockArea
  27. {
  28. private object m_instance;
  29. private System.Type m_type;
  30. public R_DockArea( object instance )
  31. {
  32. m_instance = instance;
  33. m_type = instance.GetType();
  34. }
  35. public object Window
  36. {
  37. get
  38. {
  39. var property = m_type.GetProperty( "window", BindingFlags.Instance | BindingFlags.Public );
  40. return property.GetValue( m_instance, null );
  41. }
  42. }
  43. public object OriginalDragSource
  44. {
  45. set
  46. {
  47. var field = m_type.GetField( "s_OriginalDragSource", BindingFlags.Static | BindingFlags.NonPublic );
  48. field.SetValue( null, value );
  49. }
  50. }
  51. public void AddTab( EditorWindow pane )
  52. {
  53. #if UNITY_2018_3_OR_NEWER
  54. var method = m_type.GetMethod( "AddTab", BindingFlags.Instance | BindingFlags.Public, null, new System.Type[] { typeof( EditorWindow ), typeof( bool ) }, null );
  55. method.Invoke( m_instance, new object[] { pane, true } );
  56. #else
  57. var method = m_type.GetMethod( "AddTab", BindingFlags.Instance | BindingFlags.Public, null, new System.Type[] { typeof( EditorWindow ) }, null );
  58. method.Invoke( m_instance, new object[] { pane } );
  59. #endif
  60. }
  61. }
  62. private class R_ContainerWindow
  63. {
  64. private object m_instance;
  65. private System.Type m_type;
  66. public R_ContainerWindow( object instance )
  67. {
  68. m_instance = instance;
  69. m_type = instance.GetType();
  70. }
  71. public object RootSplitView
  72. {
  73. get
  74. {
  75. var property = m_type.GetProperty( "rootSplitView", BindingFlags.Instance | BindingFlags.Public );
  76. return property.GetValue( m_instance, null );
  77. }
  78. }
  79. }
  80. private class R_SplitView
  81. {
  82. private object m_instance;
  83. private System.Type m_type;
  84. public R_SplitView( object instance )
  85. {
  86. m_instance = instance;
  87. m_type = instance.GetType();
  88. }
  89. public object DragOver( EditorWindow child, Vector2 screenPoint )
  90. {
  91. var method = m_type.GetMethod( "DragOver", BindingFlags.Instance | BindingFlags.Public );
  92. return method.Invoke( m_instance, new object[] { child, screenPoint } );
  93. }
  94. public void PerformDrop( EditorWindow child, object dropInfo, Vector2 screenPoint )
  95. {
  96. var method = m_type.GetMethod( "PerformDrop", BindingFlags.Instance | BindingFlags.Public );
  97. method.Invoke( m_instance, new object[] { child, dropInfo, screenPoint } );
  98. }
  99. }
  100. public enum DockPosition
  101. {
  102. Left,
  103. Top,
  104. Right,
  105. Bottom
  106. }
  107. /// <summary>
  108. /// Docks the second window to the first window at the given position
  109. /// </summary>
  110. public static void Dock( this EditorWindow wnd, EditorWindow other, DockPosition position )
  111. {
  112. var mousePosition = GetFakeMousePosition( wnd, position );
  113. var parent = new R_EditorWindow( wnd );
  114. var child = new R_EditorWindow( other );
  115. var dockArea = new R_DockArea( parent.Parent );
  116. var containerWindow = new R_ContainerWindow( dockArea.Window );
  117. var splitView = new R_SplitView( containerWindow.RootSplitView );
  118. var dropInfo = splitView.DragOver( other, mousePosition );
  119. dockArea.OriginalDragSource = child.Parent;
  120. splitView.PerformDrop( other, dropInfo, mousePosition );
  121. }
  122. /// <summary>
  123. /// Adds the the second window as a tab at the end of the first window tab list
  124. /// </summary>
  125. /// <param name="existingWindow"></param>
  126. /// <param name="newWindow"></param>
  127. public static void AddTab( this EditorWindow existingWindow, EditorWindow newWindow )
  128. {
  129. var parent = new R_EditorWindow( existingWindow );
  130. var child = new R_EditorWindow( newWindow );
  131. var dockArea = new R_DockArea( parent.Parent );
  132. dockArea.OriginalDragSource = child.Parent;
  133. dockArea.AddTab( newWindow );
  134. }
  135. private static Vector2 GetFakeMousePosition( EditorWindow wnd, DockPosition position )
  136. {
  137. Vector2 mousePosition = Vector2.zero;
  138. // The 20 is required to make the docking work.
  139. // Smaller values might not work when faking the mouse position.
  140. switch ( position )
  141. {
  142. case DockPosition.Left:
  143. mousePosition = new Vector2( 20, wnd.position.size.y / 2 );
  144. break;
  145. case DockPosition.Top:
  146. mousePosition = new Vector2( wnd.position.size.x / 2, 20 );
  147. break;
  148. case DockPosition.Right:
  149. mousePosition = new Vector2( wnd.position.size.x - 20, wnd.position.size.y / 2 );
  150. break;
  151. case DockPosition.Bottom:
  152. mousePosition = new Vector2( wnd.position.size.x / 2, wnd.position.size.y - 20 );
  153. break;
  154. }
  155. return GUIUtility.GUIToScreenPoint( mousePosition );
  156. }
  157. }
  158. #endif