Supporting Both Unity Light and Dark Skins

Maybe you have an idea for a cool unique tool, and you want to put it on the Asset Store. But, what if you only have the free, or personal, edition of Unity, like I do. That means that you only have the light skin, right? Not…exactly. It would not make sense for Unity to write their UI code twice, once for light and again for dark, so they actually include both within every Editor installation.

Unity uses GUISkins for “Immediate Mode” (IMGUI) controls. This is also true for the Unity Editor itself. They even provided a way for us to access the built-in skins that they use.

public static GUISkin EditorGUIUtility.GetBuiltinSkin(EditorSkin skin);

This method takes an enum value and returns an instance of a GUISkin. The enum values are Game, Inspector, and Scene. These names are a bit misleading. They actually have no connection with the EditorWindows of the same names. At least, none that I can find. Pretty much everything that the Unity devs use to draw every part of every built-in EditorWindow can be found within these three GUISkins.

Okay, so we can get a reference to the built-in Unity GUISkins. So what? What’s that got to do with the light and dark skins? Well it turns out that the light skin corresponds to EditorSkin.Inspector, and the dark skin is EditorSkin.Scene. The skin for Game doesn’t actually have much in it of note.

So, we can get to the built-in skins that Unity uses for the Editor. Now we need to figure out how to use them. A GUISkin is simply a collection of named GUIStyles, along with a few settings that are used by some GUI controls. Some styles are accessible through properties, such as box, label, button, etc. There is also an array called customStyles. This can hold as many GUIStyles as you need. If you have a GUISkin asset (one can be created using the Create menu), you can add styles to the array and fill them out using the Inspector. To access any style within a skin, use the following method.

public GUIStyle GUISkin.GetStyle(string styleName);

This method takes the name of a style in the skin and returns the associated style. But, what are the style names that Unity is using? Is there a list somewhere? Nope. Besides, what we really need is some way to browse through all of the styles in the Inspector. There are a couple of ways to do this.

The first is very simple. Make the skin the selected object.

public static class InspectEditorSkins
{
	[MenuItem("Tools/Inspect Dark Skin")]
	public static void MenuItem_InspectDarkSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
	}
	
	[MenuItem("Tools/Inspect Light Skin")]
	public static void MenuItem_InspectLightSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);
	}
	
	[MenuItem("Tools/Inspect Game Skin")]
	public static void MenuItem_InspectGameSkin()
	{
		Selection.activeObject = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Game);
	}
}

Now, selecting one of the menu items will let us inspect each skin. Notice, though, that they are all showing as ‘disabled’ in the Inspector. This is because these assets are, in a way, locked. Read-only, you might say. It makes sense. You wouldn’t want just anyone to screw around with these if you were Unity.

What I have found easier to work with long term is to make copies of the skins and save them as assets. Here is the code I have been using.

public static class EditorSkinsExtractor
{
    [MenuItem("Assets/Extract Editor Skins")]
    public static void MenuItem_ExtractEditorSkins()
    {
		string unityVersion = Application.unityVersion;
		string skinsFolder = "EditorSkins_" + unityVersion;
		string fullPath = Application.dataPath + "/" + skinsFolder;

		if (!System.IO.Directory.Exists(fullPath))
		{
			AssetDatabase.CreateFolder("Assets", skinsFolder);
		}

		if (System.IO.Directory.Exists(fullPath))
		{
			string relativePath = "Assets/" + skinsFolder + "/EditorSkin-";

			ExtractSkin(relativePath, EditorSkin.Game);
			ExtractSkin(relativePath, EditorSkin.Inspector);
			ExtractSkin(relativePath, EditorSkin.Scene);
		}
		else
		{
			Debug.LogError("Failed to create folder");
		}
	}

	private static void ExtractSkin(string relativePath, EditorSkin skinType)
	{
		GUISkin skin = ScriptableObject.Instantiate(EditorGUIUtility.GetBuiltinSkin(skinType)) as GUISkin;
		AssetDatabase.CreateAsset(skin, relativePath + skinType + ".guiskin");
	}
}

This will create a folder for the Unity version currently running, and then create copies of the skins and save them as assets within the folder. Now you can peruse at will!

Now remember, the Scene skin is the dark skin and the Inspector skin is the light one. Select either and expand Custom Styles. Let’s find something familiar. As you scroll through the list, you’ll notice that the names are in alphabetical order. This is nice for when you’re looking for something specific. Scroll all the way down to “IN LockButton”. Can you guess what that might be? It’s the lock button from the IN-spector. Now take a look at “PR Insertion”. That is the blue line that shows up when you are moving items in the PR-oject view! Fun! Of course, that blue line is also used in other places, like the Hierarchy window.  I’m also using it directly in JumpTo, so that my window feels native.

An excerpt straight from JumpTo…

//create some public properties to hold your styles
public GUIStyle LinkLabelStyle { get; private set; }
public GUIStyle DragDropInsertionStyle { get; private set; }

//NOTE: you must work with GUIStyles within OnGUI()

GUISkin editorSkin = null;
if (EditorGUIUtility.isProSkin)
	editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
else
	editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector);

//here we are creating copies of the built-in style, and then making
//  whatever changes may be necessary for our use-case
LinkLabelStyle = new GUIStyle(editorSkin.GetStyle("PR Label"));
LinkLabelStyle.name = "Link Label Style";
LinkLabelStyle.padding.left = 8;

DragDropInsertionStyle = new GUIStyle(editorSkin.GetStyle("PR Insertion"));
DragDropInsertionStyle.imagePosition = ImagePosition.ImageOnly;
DragDropInsertionStyle.contentOffset = new Vector2(0.0f, -16.0f);

The if statement will guarantee that we are pulling the correct style for the end-user’s version of Unity. Now your IMGUI code can be completely ignorant of the skin being used. In theory, anyway…

These styles can be passed as arguments to GUILayout methods, or used directly using the GUIStyle.Draw() method.

I always try to give credit where it is due. The inspiration came from this thread in the Unity forum, in a post from user dodo. I’d had the same question as the OP, Tonzie, and this was what was needed.