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?
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 });
}
}
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 });
}
}
Hi pascal, try wi a repaint().
it’s just a guess though, I’m not sure it will work and I can’t test it
bye!