事件
せっかくつくったエディタ拡張が、エディタのテーマを変更したら文字が背景に同化した……
個人プロジェクトならまだいいですが、人に配るものとなるとこうはいきません。
いやしかし、全部の要素のUSSを書き換えて文字色・背景色を変えるのは難しいですし、新しい要素を追加するときも面倒です。
環境によらずつねに同じ色で表示されてほしい……
そんなわけで今回は、UIElementsでテーマをダークテーマに(またはライトテーマに)固定するというお話です。
UIElementsとエディタテーマ
UIElementsはUXML/USSという言語でUIを作ります。ちょうどHTML/CSSとよく似た仕組みで、USSがスタイルを制御するファイルにあたります。
UIElementsでは、読み込んでいるテーマに応じて自動的にそのテーマ用のUSSが適用されるようになっています。
これはUIElements Debuggerをみるとよくわかります。適当なEditorWindowのタイトル部分を右クリックしてUIElements Debuggerを見てみましょう:
ありました。 DefaultCommonLight_inter.ussっていうこいつです。ダークテーマの時はDefaultCommonDark_inter.ussというUSSが読み込まれます。
「じゃあこのファイルを探してきて読み込めばいいのでは?」と行きたいところですが、実はこのファイル、Unityのエディタビルトインアセットとして保存されています。EditorGUIUtility.Load(string path)で読み込めるのですが、Unityのバージョンによってアセットのパスが同一である保証はありません。ついでに使用している言語やフォントによってUSSが異なります。
つまりどういうことかというと、ちゃんとUnity側のAPIを通してスタイルシートを取得するのがよさそうということです。
Unity側の処理を見てみる
Unity側にEditorWindowに対して自動的にテーマに応じたUSSを適用する処理が存在しているわけですが、そこの処理を拝借してくればよさそうです。
EditorWindowがテーマを取得しているのは以下の箇所です。
UnityCsReference/EditorWindow.cs at master · Unity-Technologies/UnityCsReference
ここからさらに処理を辿ると、 UIElementsEditorUtility に辿り着きました。
UnityCsReference/UIElementsEditorUtility.cs at master · Unity-Technologies/UnityCsReference
ありました。UIElementsEditorUtility.s_DefaultCommonDarkStyleSheetとUIElementsEditorUtility.s_DefaultCommonLightStyleSheetです。
しかし(予想はしていましたが)、こいつらはinternalです。つまりリフレクションの出番です。ゴリ押していきましょう。
using System.Reflection;
using UnityEditor;
using UnityEngine.UIElements;
public class SkinFixedEditorWindow : EditorWindow
{
[MenuItem("Window/SkinFixedEditorWindow")]
public static void ShowWindow()
{
GetWindow(typeof(SkinFixedEditorWindow));
}
private static StyleSheet defaultCommonDarkStyleSheet;
private static StyleSheet defaultCommonLightStyleSheet;
private static void GetSkins()
{
//UnityEditor.UIElementsのアセンブリを取得
var assembly = typeof(Toolbar).Assembly;
//UIElementsEditorUtilityのTypeを取得
var type = assembly.GetType("UnityEditor.UIElements.UIElementsEditorUtility");
if (type == null) return;
//StyleSheetを格納するフィールドを取得
var darkField = type.GetField("s_DefaultCommonDarkStyleSheet", BindingFlags.Static | BindingFlags.NonPublic);
var lightField = type.GetField("s_DefaultCommonLightStyleSheet", BindingFlags.Static | BindingFlags.NonPublic);
if (darkField == null || lightField == null) return;
//staticなフィールドとして値を取得
defaultCommonDarkStyleSheet = (StyleSheet) darkField.GetValue(null);
defaultCommonLightStyleSheet = (StyleSheet) lightField.GetValue(null);
}
private void OnEnable()
{
GetSkins();
}
}
あとは取得したUSSをEditorWindowのrootVisualElementに適用してあげます。
private void OnEnable()
{
GetSkins();
//Darkテーマに設定
rootVisualElement.styleSheets.Clear();
rootVisualElement.styleSheets.Add(defaultCommonDarkStyleSheet);
//EditorWindowのデフォルトの背景色はUIElementsの色ではなく地の色なので、背景色だけはUIElements側に手動で設定する必要がある
rootVisualElement.style.backgroundColor = new StyleColor(new Color(0.22f, 0.22f, 0.22f));
rootVisualElement.style.flexGrow = 1f;
//VisualElementを配置してみる
var button = new Button();
button.text = "ボタン";
rootVisualElement.Add(button);
var textField = new TextField("テキスト");
rootVisualElement.Add(textField);
var floatField = new FloatField("数値");
rootVisualElement.Add(floatField);
}
そんなわけで、LigthtテーマのUnityの上にDarkテーマ固定のEditorWindowを出現させることに成功しました。