- Home /
Programmatically change editor layout
I'm working on a project with several custom editor windows and I have several different layouts depending on the stage I'm working on. I'd like to implement a simple button or menu item that programmatically changes to one of the others layouts and starts some operations. My problem is that I have no idea how to load a layout programmatically and I didn't find anything close in the documentation. I'm wondering if it is even possible ... any help? :)
If you create a feature request here http://feedback.unity3d.com/ I would be happy to vote for you :) Just post a link here as a comment.
Answer by numberkruncher · Jan 18, 2013 at 03:37 PM
Unity does not expose a function to adjust the layout of the Unity editor manually. However there is a hack which someone kindly told me about on the IRC channel. I would give them credit but I honestly cannot remember who told me :S
WARNING - You cannot load Unity 3.x layouts in Unity 4.x because it will crash. In fact, you cannot even place a Unity 3.x layout asset in a Unity 4.x project because it will crash.
Example of Usage:
Ensure that the folder Assets/Editor/Layouts/
exists before using the following hack:
using UnityEngine;
using UnityEditor;
public static class LayoutHackExample {
[MenuItem("Layout Hack/Save Layout")]
static void SaveLayoutHack() {
// Saving the current layout to an asset
LayoutUtility.SaveLayoutToAsset("Assets/Editor/Layouts/Your Layout.wlt");
}
[MenuItem("Layout Hack/Load Layout")]
static void LoadLayoutHack() {
// Loading layout from an asset
LayoutUtility.LoadLayoutFromAsset("Assets/Editor/Layouts/Your Layout.wlt");
}
}
Installing a Layout:
If you want to install a layout into the drop-down list then you can programatically copy the layout. Here is how to determine the path for your layout:
string targetPath = Path.Combine(LayoutUtility.LayoutsPath, "Layout Name.wlt");
Utility Class Source:
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;
using Type = System.Type;
public static class LayoutUtility {
private static MethodInfo _miLoadWindowLayout;
private static MethodInfo _miSaveWindowLayout;
private static MethodInfo _miReloadWindowLayoutMenu;
private static bool _available;
private static string _layoutsPath;
static LayoutUtility() {
Type tyWindowLayout = Type.GetType("UnityEditor.WindowLayout,UnityEditor");
Type tyEditorUtility = Type.GetType("UnityEditor.EditorUtility,UnityEditor");
Type tyInternalEditorUtility = Type.GetType("UnityEditorInternal.InternalEditorUtility,UnityEditor");
if (tyWindowLayout != null && tyEditorUtility != null && tyInternalEditorUtility != null) {
MethodInfo miGetLayoutsPath = tyWindowLayout.GetMethod("GetLayoutsPath", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
_miLoadWindowLayout = tyWindowLayout.GetMethod("LoadWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
_miSaveWindowLayout = tyWindowLayout.GetMethod("SaveWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
_miReloadWindowLayoutMenu = tyInternalEditorUtility.GetMethod("ReloadWindowLayoutMenu", BindingFlags.Public | BindingFlags.Static);
if (miGetLayoutsPath == null || _miLoadWindowLayout == null || _miSaveWindowLayout == null || _miReloadWindowLayoutMenu == null)
return;
_layoutsPath = (string)miGetLayoutsPath.Invoke(null, null);
if (string.IsNullOrEmpty(_layoutsPath))
return;
_available = true;
}
}
// Gets a value indicating whether all required Unity API
// functionality is available for usage.
public static bool IsAvailable {
get { return _available; }
}
// Gets absolute path of layouts directory.
// Returns `null` when not available.
public static string LayoutsPath {
get { return _layoutsPath; }
}
// Save current window layout to asset file.
// `assetPath` must be relative to project directory.
public static void SaveLayoutToAsset(string assetPath) {
SaveLayout(Path.Combine(Directory.GetCurrentDirectory(), assetPath));
}
// Load window layout from asset file.
// `assetPath` must be relative to project directory.
public static void LoadLayoutFromAsset(string assetPath) {
if (_miLoadWindowLayout != null) {
string path = Path.Combine(Directory.GetCurrentDirectory(), assetPath);
_miLoadWindowLayout.Invoke(null, new object[] { path });
}
}
// Save current window layout to file.
// `path` must be absolute.
public static void SaveLayout(string path) {
if (_miSaveWindowLayout != null)
_miSaveWindowLayout.Invoke(null, new object[] { path });
}
}
dude... i still have to check this, but thanks! If it works you did me an enormous favor :D
And yes, it's working perfectly! Thanks a lot and sorry for the delay! :)
This is really great, but how do i install the new layout in my dropdown list? i did not find a way to get the path of the files in this list.
Forget the last comment, the targetPath is exactly the path im searching for. But now i have another question. The new Layout has been saved to the path but is not shown in the dropdown list. Only if i delete some of the other scripts, it is shown. I guess the DropDown list needs to be updated but i dont know how to do this without deleting one of the other layouts
Nice finding indeed. But it doesn't seem to be up to date, _miLoadWindowLayout is null using unity 5.3. (I didn't test in any other)
A quick look at UnityEditor.dll using ILDAsm reveals that a new parameter has been added. So line 25 should be replaced by :
_miLoadWindowLayout = tyWindowLayout.Get$$anonymous$$ethod("LoadWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), **typeof(bool)** }, null);
I'm using this to switch layouts based on play or edit mode. I set that new boolean parameter to false but unfortunately, it works when going to play but quits unity when going back to edit mode.
Doesn't anybody know what that boolean is ?
Edit: still no idea what that boolean is but the crash was a bug in my code :)
Wow! thank you :) I also don't know what the bool parameter means. OT: I don't understand what the code do. Are you binding to a function in an unmanaged DLL?
Answer by ensomniac · Dec 26, 2018 at 05:18 PM
2019 Update - Tested with Unity 2018.2.0f2
If you're just looking to save and load layouts programmatically and you're using a new version of the Unity Editor, this code should help you out.
Usage: LayoutUtility.SaveLayout(path); LayoutUtility.LoadLayout(path);
Place in: Assets/Scripts/Editor/LayoutUtility.cs
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Reflection;
using Type = System.Type;
public static class LayoutUtility {
private enum MethodType {Save, Load};
static MethodInfo GetMethod (MethodType method_type) {
Type layout = Type.GetType("UnityEditor.WindowLayout,UnityEditor");
MethodInfo save = null;
MethodInfo load = null;
if (layout != null) {
load = layout.GetMethod("LoadWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string), typeof(bool)}, null);
save = layout.GetMethod("SaveWindowLayout", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string)}, null);
}
if (method_type == MethodType.Save) {
return save;
}
else {
return load;
}
}
public static void SaveLayout(string path) {
path = Path.Combine(Directory.GetCurrentDirectory(), path);
GetMethod(MethodType.Save).Invoke(null, new object[] {path});
}
public static void LoadLayout(string path) {
path = Path.Combine(Directory.GetCurrentDirectory(), path);
GetMethod(MethodType.Load).Invoke(null, new object[] { path, false });
}
}
Your answer
