What I am trying to achieve is to dock an editor window below, on top or to the left or right of the game view in Unity's editor via script (for simplicity, lets just focus on docking above the game view). Look at this video for an example of what I want to achieve.
EDIT
As the question was mark as off topic, here is a code sample. The problem that I have is that dropInfo
inside the Dock
method is null, and therefore the PerformDrop
method of the SplitView
causes a null ref error.
public static class Docker
{
private class _EditorWindow
{
private EditorWindow instance;
private Type type;
public _EditorWindow( EditorWindow instance )
{
this.instance = instance;
type = instance.GetType();
}
public object m_Parent
{
get
{
var field = type.GetField( "m_Parent", BindingFlags.Instance | BindingFlags.NonPublic );
return field.GetValue( instance );
}
}
}
private class _DockArea
{
private object instance;
private Type type;
public _DockArea( object instance )
{
this.instance = instance;
type = instance.GetType();
}
public object window
{
get
{
var property = type.GetProperty( "window", BindingFlags.Instance | BindingFlags.Public );
return property.GetValue( instance, null );
}
}
public object s_OriginalDragSource
{
set
{
var field = type.GetField( "s_OriginalDragSource", BindingFlags.Static | BindingFlags.NonPublic );
field.SetValue( null, value );
}
}
}
private class _ContainerWindow
{
private object instance;
private Type type;
public _ContainerWindow( object instance )
{
this.instance = instance;
type = instance.GetType();
}
public object rootSplitView
{
get
{
var property = type.GetProperty( "rootSplitView", BindingFlags.Instance | BindingFlags.Public );
return property.GetValue( instance, null );
}
}
}
private class _SplitView
{
private object instance;
private Type type;
public _SplitView( object instance )
{
this.instance = instance;
type = instance.GetType();
}
public object DragOver( EditorWindow child, Vector2 screenPoint )
{
var method = type.GetMethod( "DragOver", BindingFlags.Instance | BindingFlags.Public );
return method.Invoke( instance, new object[] { child, screenPoint } );
}
public void PerformDrop( EditorWindow child, object dropInfo, Vector2 screenPoint )
{
var method = type.GetMethod( "PerformDrop", BindingFlags.Instance | BindingFlags.Public );
method.Invoke( instance, new object[] { child, dropInfo, screenPoint } );
}
}
public enum DockPosition
{
Left,
Top,
Right,
Bottom
}
/// <summary>
/// Docks the second window to the first window at the given position
/// </summary>
public static void Dock( this EditorWindow wnd, EditorWindow other, DockPosition position )
{
var mousePosition = GetFakeMousePosition( wnd, position );
var parent = new _EditorWindow( wnd );
var child = new _EditorWindow( other );
var dockArea = new _DockArea( parent.m_Parent );
var containerWindow = new _ContainerWindow( dockArea.window );
var splitView = new _SplitView( containerWindow.rootSplitView );
var dropInfo = splitView.DragOver( other, mousePosition );
dockArea.s_OriginalDragSource = child.m_Parent;
splitView.PerformDrop( other, dropInfo, mousePosition );
}
private static Vector2 GetFakeMousePosition( EditorWindow wnd, DockPosition position )
{
Vector2 mousePosition = Vector2.zero;
// The 20 is required to make the docking work.
// Smaller values might not work when faking the mouse position.
switch ( position )
{
case DockPosition.Left:
mousePosition = new Vector2( 20, wnd.position.size.y / 2 );
break;
case DockPosition.Top:
mousePosition = new Vector2( wnd.position.size.x / 2, 20 );
break;
case DockPosition.Right:
mousePosition = new Vector2( wnd.position.size.x - 20, wnd.position.size.y / 2 );
break;
case DockPosition.Bottom:
mousePosition = new Vector2( wnd.position.size.x / 2, wnd.position.size.y - 20 );
break;
}
return new Vector2(wnd.position.x + mousePosition.x, wnd.position.y + mousePosition.y);
}
}
public static class SomeStaticClass
{
[MenuItem("DOCK TESTING/Dock Above")]
public static void DockAbove()
{
SysType gameViewType = Assembly.GetAssembly(typeof(Editor)).GetType("UnityEditor.GameView");
EditorWindow baseWindow = EditorWindow.GetWindow(gameViewType);
EditorWindow newWindow = (EditorWindow)ScriptableObject.CreateInstance(gameViewType);
baseWindow.Dock(newWindow, Docker.DockPosition.Top);
}
}