FloatFieldの作成 その2(Editor拡張)

FloatFieldの作成 その2(Editor拡張)

 以前にFloatFieldを作成した際(FloatFieldの作成)、横幅の計算にEditorWindow.positionを用いていました。しかし、スクロールバーが表示された場合、EditorWindow.positionはウィンドウの幅をであるためFloatFieldがスクロールバーで隠れてしまいます。そこで、横幅の計算をGUILayoutUtility.GetRectへ変更し、さらにPrefixLabelを使用する等の変更を行いました。

Script

 作成したScriptは以下の通りです。新たにクラスを作成し、様々な場合に対応できるよう、複数のFloatFieldを作成しました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEditor;
using UnityEngine.UI;

public enum ControlPosition
{
    Left,
    Center,
    Right
}

public class CustomEditorGUI
{
    public static float FloatField(string label, float val, GUIStyle style_label, GUIStyle style_float, Event ev)
    {
        int default_width = 65;
        Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float);
        val = CustomEditorGUI.FloatField(rect, label, val, default_width, style_label, style_float, ev);

        return val;
    }

    public static float FloatField(string label, float val, GUIStyle style_label, GUIStyle style_float, Event ev, params GUILayoutOption[] options)
    {
        int default_width = 65;
        Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
        val = CustomEditorGUI.FloatField(rect, label, val, default_width, style_label, style_float, ev);

        return val;
    }

    public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev)
    {
        Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float);
        val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);

        return val;
    }

    public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev, params GUILayoutOption[] options)
    {
        Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
        val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);

        return val;
    }

    public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev, ControlPosition controlPosition, params GUILayoutOption[] options)
    {
        EditorGUILayout.BeginHorizontal();
        {
            Rect rect;
            switch (controlPosition)
            {
                case ControlPosition.Right:
                    GUILayout.FlexibleSpace();
                    rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                    break;

                case ControlPosition.Center:
                    GUILayout.FlexibleSpace();
                    rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                    GUILayout.FlexibleSpace();
                    break;

                default:
                    rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                    break;
            }
            val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);
        }
        EditorGUILayout.EndHorizontal();

        return val;
    }

    public static float FloatField(Rect rect, string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev)
    {
        float val_speed = 0.3f;
        int id = GUIUtility.GetControlID(FocusType.Passive);

        Rect rect_control = rect;

        int margin = 3;
        int label_width = (int)(rect_control.width - text_area_width - margin);
        Rect rect_label = new Rect(rect_control) { width = label_width };
        EditorGUI.PrefixLabel(rect_label, new GUIContent(label), style_label);

        Rect rect_float = new Rect(rect_control) { x = rect_control.x + rect_control.width - text_area_width, width = text_area_width };
        val = EditorGUI.FloatField(rect_float, val, style_float);

        Vector2 mouse_pos = ev.mousePosition;
        if (ev.button == 0)
        {
            switch (ev.type)
            {
                case EventType.MouseDown:
                    if (rect_label.Contains(mouse_pos))
                    {
                        GUIUtility.hotControl = id;
                        ev.Use();
                    }
                    break;

                case EventType.MouseDrag:
                    if (GUIUtility.hotControl == id)
                    {
                        float dis = ev.delta.x;
                        val = val * 100.0f + dis * 10.0f * val_speed;
                        val = Mathf.Floor(Mathf.Abs(val)) / 100f * Mathf.Sign(val);
                        HandleUtility.Repaint();
                    }
                    break;

                case EventType.MouseUp:
                    if (GUIUtility.hotControl == id)
                    {
                        GUIUtility.hotControl = 0;
                    }
                    break;
            }
        }

        EditorGUIUtility.AddCursorRect(rect_label, MouseCursor.SlideArrow);

        return val;
    }
}

 テキストエリア部分の大きさを65と固定し、コントロール全体の幅をウィンドウ幅としています。コントロールの描画領域をGUILayoutUtility.GetRectを用いて取得しているため、スクロールバーが表示された場合にはそれに応じて横幅が調整された描画領域を取得できます。

public static float FloatField(string label, float val, GUIStyle style_label, GUIStyle style_float, Event ev)
{
    int default_width = 65;
    Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float);
    val = CustomEditorGUI.FloatField(rect, label, val, default_width, style_label, style_float, ev);

    return val;
}

 先程と同様にテキストエリアの幅は65に固定されていますが、GUILayoutOptionによってコントロール全体の幅等を変更すことができます。

public static float FloatField(string label, float val, GUIStyle style_label, GUIStyle style_float, Event ev, params GUILayoutOption[] options)
{
    int default_width = 65;
    Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
    val = CustomEditorGUI.FloatField(rect, label, val, default_width, style_label, style_float, ev);

    return val;
}

 今まで固定されていたテキストエリアの幅を変更できるようにしています。コントロール全体の幅はウィンドウの幅となります。

public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev)
{
    Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float);
    val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);

    return val;
}

 先程と同様にテキストエリアの幅を変更ができます。また、GUILayoutOptionによってコントロール全体の幅等を変更できるようにしています。

public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev, params GUILayoutOption[] options)
{
    Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
    val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);

    return val;
}

 下記Scriptでは、コントロールの位置を左右中央へ調整できるようにしています。位置調整にはGUILayout.FlexibleSpaceを用いており、これはGUILayoutUtility.GetRectで取得できるRectに作用し、スペースを挿入することができます。今回は、水平方向へスペースを挿入したいので、EditorGUILayout.BeginHorizontal()~EditorGUILayout.EndHorizontal()で囲っています。

public static float FloatField(string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev, ControlPosition controlPosition, params GUILayoutOption[] options)
{
    EditorGUILayout.BeginHorizontal();
    {
        Rect rect;
        switch (controlPosition)
        {
            case ControlPosition.Right:
                GUILayout.FlexibleSpace();
                rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                break;

            case ControlPosition.Center:
                GUILayout.FlexibleSpace();
                rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                GUILayout.FlexibleSpace();
                break;

            default:
                rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, options);
                break;
        }
        val = CustomEditorGUI.FloatField(rect, label, val, text_area_width, style_label, style_float, ev);
    }
    EditorGUILayout.EndHorizontal();

    return val;
}

また、位置の指定には新たに作成した以下の列挙型を用いています。 

public enum ControlPosition
{
    Left,
    Center,
    Right
}

以下のScriptでFloatFieldの作成を行っています。EditorGUI.PrefixLabelを用いることで、LabelのGUIStyleを変更する処理を記述する必要がなくなっています。

public static float FloatField(Rect rect, string label, float val, int text_area_width, GUIStyle style_label, GUIStyle style_float, Event ev)
{
    float val_speed = 0.3f;
    int id = GUIUtility.GetControlID(FocusType.Passive);

    Rect rect_control = rect;

    int margin = 3;
    int label_width = (int)(rect_control.width - text_area_width - margin);
    Rect rect_label = new Rect(rect_control) { width = label_width };
    EditorGUI.PrefixLabel(rect_label, new GUIContent(label), style_label);

    Rect rect_float = new Rect(rect_control) { x = rect_control.x + rect_control.width - text_area_width, width = text_area_width };
    val = EditorGUI.FloatField(rect_float, val, style_float);

    Vector2 mouse_pos = ev.mousePosition;
    if (ev.button == 0)
    {
        switch (ev.type)
        {
            case EventType.MouseDown:
                if (rect_label.Contains(mouse_pos))
                {
                    GUIUtility.hotControl = id;
                    ev.Use();
                }
                break;

            case EventType.MouseDrag:
                if (GUIUtility.hotControl == id)
                {
                    float dis = ev.delta.x;
                    val = val * 100.0f + dis * 10.0f * val_speed;
                    val = Mathf.Floor(Mathf.Abs(val)) / 100f * Mathf.Sign(val);
                    HandleUtility.Repaint();
                }
                break;

            case EventType.MouseUp:
                if (GUIUtility.hotControl == id)
                {
                    GUIUtility.hotControl = 0;
                }
                break;
        }
    }

    EditorGUIUtility.AddCursorRect(rect_label, MouseCursor.SlideArrow);

    return val;
}

実行結果

 作成したFloatFieldを実行するScriptは以下の通りです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class FloatFields : EditorWindow
{
    float[] vals = { 0f, 0f, 0f, 0f, 0f, 0f};

    [MenuItem("Tools/Float Fields")]
    public static void OpenWindow()
    {
        EditorWindow ed = EditorWindow.GetWindow(typeof(FloatFields), false, "Float Fields");
        ed.minSize = new Vector2(285, 10);
    }

    private void OnGUI()
    {
        GUIStyle style_label = new GUIStyle(GUI.skin.label);
        style_label.focused.textColor = Color.blue;

        GUIStyle style_float = new GUIStyle(GUI.skin.textField);

        Event event_current = Event.current;

        vals[0] = CustomEditorGUI.FloatField("float field 1", vals[0], style_label, style_float, event_current);
        vals[1] = CustomEditorGUI.FloatField("float field 2", vals[1], style_label, style_float, event_current, GUILayout.Width(150));
        vals[2] = CustomEditorGUI.FloatField("float field 3", vals[2], 150, style_label, style_float, event_current);
        vals[3] = CustomEditorGUI.FloatField("float field 4", vals[3], 30, style_label, style_float, event_current, GUILayout.Width(230), GUILayout.Height(30));
        vals[4] = CustomEditorGUI.FloatField("float field 5", vals[4], 80, style_label, style_float, event_current, ControlPosition.Center, GUILayout.Width(200));

        Rect rect = GUILayoutUtility.GetRect(GUIContent.none, style_float, GUILayout.Width(120));
        vals[5] = CustomEditorGUI.FloatField(rect, "float field 6", vals[5], 50, style_label, style_float, event_current);
    }
}

このScriptを実行すると以下の結果が得られます。様々なレイアウトのFloatFieldが作成できるようになりました。