- Home /
Custom Editor GUI Elements
The closest thing I can find is AngryAnts untested post for a `MyFocusControl`. Another resource is using Go to Declaration in monodevelop on some of the built in gui elements which just uses reflection. If anyone has some examples on GitHub, Pastebin, etc; please drop a link.
I know there is Extending UnityGUI page in the Unity manual but it only goes over combining two elements.
I am trying to create a selectable foldout for use in a EditorWindow. One issue I am having is that the gui is not updating properly with the state of the toggle. I tried setting GUI.changed because the reflection foldout source did it. I know there are ways to [set data as dirty](EditorUtility.SetDirty) in order for Unity to save the new data but I can't find anything for the GUI.
Mockup:

You might suggest that I just combine a foldout with empty label and a selectable label but then I couldn't have the label make the foldout toggle. Also I am not entirely sure on what how to get the proper control id. I have a feeling it is just a unique int so almost any of the commented out lines(below) should probably work.
Here is the decompiled reflection source for the `EditorGUI.SelectableLabel()` and `EditorGUI.Foldout()` for reference.
I realize the code below doesn't have the "selectable" part with the MouseMove event in yet.
 public static bool SelectableFoldout(Rect position, bool isExpanded, GUIContent content,  [DefaultValue ("EditorStyles.foldout")] GUIStyle style)
 {
     int controlID = 0;//GUIUtility.GetControlID ();//GUIUtility.GetControlID(content, FocusType.Keyboard, position);
     controlID = GUIUtility.GetControlID(content, FocusType.Keyboard, position);
     //controlID = GUIUtility.GetControlID(content, FocusType.Passive, position);
     //controlID = GUIUtility.GetControlID(content, FocusType.Native, position);
     //controlID = EditorGUIUtility.GetControlID(FocusType.Native);
     //controlID = "SelectableFoldout".GetHashCode();
 
     //Debug.Log("Control id: " + controlID);
 
     EventType eventType = Event.current.type;
     switch (eventType)
     {
         case EventType.MouseDown:
         {
             GUIUtility.hotControl = controlID;
             break;
         }
 
         case EventType.MouseUp:
         {
             Debug.Log("mouse up: " + Event.current.mousePosition);
             Debug.Log(position + " " + position.Contains(Event.current.mousePosition));
 
             GUIUtility.hotControl = 0;
             // Toggle the foldout
             if(position.Contains(Event.current.mousePosition))
             {
                 isExpanded = !isExpanded;
             }
 
             Debug.Log(isExpanded);
             //GUI.changed = true;
             
             break;
 
         }
         case EventType.Repaint:
         {
             style.Draw(EditorGUI.IndentedRect(position), content, controlID, isExpanded);
 
             break;
         }
     }
 
     return isExpanded;
 }
 
 // Alternative construcotr if you don't want to deal with GUIContent
 public static bool SelectableFoldout(Rect position, bool isExpanded, string text, [DefaultValue ("EditorStyles.foldout")] GUIStyle style)
 {
     return SelectableFoldout(position, isExpanded, new GUIContent(text), style);
 }
Answer by MLM · Jun 15, 2014 at 01:27 AM
In the beginning, I was trying reimplement all of the `SelectableLabel` functionality but I ended up using Reflection to get access to EditorGUI.DoTextField(..). EditorGUI.DoTextField(..) handles all of the nice text field features for many gui elements like TextField, TextArea, PasswordField, and SelectableLabel.
Usage:
 bool foldoutOpen = false;
 foldoutOpen = MLMEditorGUI.SelectableFoldout(EditorGUILayout.GetControlRect(), foldoutOpen, "Selectable Foldout", EditorStyles.label, EditorStyles.foldout);
MLMEditorGUI.cs:
 using UnityEngine;
 using UnityEngine.Internal;
 using UnityEditor;
 using System;
 using System.Reflection;
 
 public static class MLMEditorGUI
 {
 
     public struct GUIStructure
     {
         public Rect rect;
         public GUIContent content;
     }
 
     static Type recycledTextEditorType;
     static object recycledTextEditor;
     static MethodInfo EditorGUI_DoTextField_MethodInfo;
     static MethodInfo RecycledTextEditor_MouseDragSelectsWholeWords_MethodInfo;
     static MethodInfo RecycledTextEditor_SelectAll_MethodInfo;
 
     static DateTime lastClickTime = new DateTime();
     static int lastClickCount = 0;
 
     // Static Contstructor
     static MLMEditorGUI()
     {
         lastClickTime = new DateTime();
         lastClickCount = 0;
 
         // Instantiate a EditorGUI.RecycledTextEditor which is internal...
         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static;
         Type editorGUIType = typeof(EditorGUI).Assembly.GetType("UnityEditor.EditorGUI");
 
         if(editorGUIType != null)
         {
             recycledTextEditorType = editorGUIType.GetNestedType("RecycledTextEditor", bindingFlags);
 
             if(recycledTextEditorType != null)
             {
                 recycledTextEditor = Activator.CreateInstance(recycledTextEditorType);
 
                 if(recycledTextEditor != null)
                 {
                     RecycledTextEditor_MouseDragSelectsWholeWords_MethodInfo = recycledTextEditorType.GetMethod("MouseDragSelectsWholeWords");
                     RecycledTextEditor_SelectAll_MethodInfo = recycledTextEditorType.GetMethod("SelectAll");
 
                     EditorGUI_DoTextField_MethodInfo = typeof(EditorGUI).GetMethod("DoTextField", bindingFlags);
                 }
             }
         }
     }
 
 
 
 
     public static bool SelectableFoldout(Rect position, bool isExpanded, GUIContent content, [DefaultValue ("EditorStyles.label")] GUIStyle labelStyle, [DefaultValue ("EditorStyles.foldout")] GUIStyle foldoutStyle)
     {
         
         Rect controlPosition = EditorGUI.IndentedRect(position);
 
         int controlID = 0;//GUIUtility.GetControlID ();//GUIUtility.GetControlID(content, FocusType.Keyboard, position);
         controlID = GUIUtility.GetControlID(content, FocusType.Keyboard, controlPosition);
 
         
         GUIStyle foldoutPaintStyle = new GUIStyle(foldoutStyle);
         foldoutStyle.padding.right = 0;
         foldoutStyle.margin.right = 0;
 
         GUIStyle normalLabelStyle = new GUIStyle(labelStyle);
         normalLabelStyle.margin.left = 0;
         normalLabelStyle.padding.left = 0;
         
         Rect startRect = new Rect(controlPosition);
         startRect.width = 0;
         
         GUIContent foldoutContent = new GUIContent("");
         Rect foldoutRectPos = GetHorizPosFromLastPos(new GUIStructure{rect=startRect, content=foldoutContent}, foldoutPaintStyle);
 
 
         Rect wholeRectPos = GetHorizPosFromLastPos(new GUIStructure{rect=foldoutRectPos, content=content}, normalLabelStyle);
 
         EventType eventType = Event.current.type;
         switch (eventType)
         {
         case EventType.MouseDown:
         {
             if (wholeRectPos.Contains(Event.current.mousePosition) && Event.current.button == 0)
             {
                 // If we are triple clicking
                 // We have to do this because Event.current.clickCount never returns 3
                 if(lastClickCount == 2 && (DateTime.Now - lastClickTime).TotalSeconds < .3f)
                 {
                     //Debug.Log("Selecting All");
                     if(RecycledTextEditor_SelectAll_MethodInfo != null)
                     {
                         RecycledTextEditor_SelectAll_MethodInfo.Invoke(recycledTextEditor, new object[] {});
                     }
 
                     Event.current.Use();
                 }
 
                 lastClickTime = DateTime.Now;
                 lastClickCount = Event.current.clickCount;
             }
             // If we arn't clicking the label reset the select word feature in the text editor
             else
             {
                 // Make the texteditor by default not select whole words because that can get annoying
                 if(RecycledTextEditor_MouseDragSelectsWholeWords_MethodInfo != null)
                 {
                     // editor.MouseDragSelectsWholeWords(true);
                     RecycledTextEditor_MouseDragSelectsWholeWords_MethodInfo.Invoke(recycledTextEditor, new object[] { false });
                 }
             }
 
             break;
         }
         case EventType.MouseUp:
         {
             //Debug.Log("mouse up: " + Event.current.mousePosition);
             //Debug.Log(position + " " + position.Contains(Event.current.mousePosition));
             
             GUIUtility.hotControl = 0;
             // Toggle the foldout
             if(controlPosition.Contains(Event.current.mousePosition))
             {
                 isExpanded = !isExpanded;
                 //Debug.Log(isExpanded);
                 
                 Event.current.Use();
             }
             
             break;
             
         }
         case EventType.keyDown:
         {
             // Use up the key presses so they can't mess with the label
             if (Event.current.keyCode != KeyCode.Tab)
             {
                 Event.current.Use();
             }
 
             break;
         }
         case EventType.ExecuteCommand:
         {
             // Use the paste and cut so they can't mess with the label
             if (Event.current.commandName == "Paste" || Event.current.commandName == "Cut")
             {
                 Event.current.Use();
             }
             
             break;
         }
         case EventType.Repaint:
         {
             foldoutPaintStyle.Draw(foldoutRectPos, foldoutContent, controlID, isExpanded);
 
             break;
         }
         }
 
 
         // Call the EditorGUI.DoTextField
         // Which handles all of the fancy selecting and stuff
         if(recycledTextEditor != null)
         {
             if(EditorGUI_DoTextField_MethodInfo != null)
             {
                 Rect labelRectPos = GetHorizPosFromLastPos(new GUIStructure{rect=foldoutRectPos, content=content}, normalLabelStyle);
 
                 object[] args = new object[] { recycledTextEditor, controlID, labelRectPos, content.text, normalLabelStyle, null, false, false, true, false };
                 // DoTextField(EditorGUI.RecycledTextEditor editor, int id, Rect position, string text, GUIStyle style, string allowedletters, out bool changed, bool reset, bool multiline, bool passwordField)
                 EditorGUI_DoTextField_MethodInfo.Invoke(null, args);
             }
         }
 
 
         return isExpanded;
     }
 
     // Alternative construcotr if you don't want to deal with GUIContent
     public static bool SelectableFoldout(Rect position, bool isExpanded, string text, [DefaultValue ("EditorStyles.label")] GUIStyle labelStyle, [DefaultValue ("EditorStyles.foldout")] GUIStyle foldoutStyle)
     {
         return SelectableFoldout(position, isExpanded, new GUIContent(text), labelStyle, foldoutStyle);
     }
 
     static Rect GetHorizPosFromLastPos(GUIStructure structure, GUIStyle style)
     {
         Vector2 newContentSize = style.CalcSize(structure.content);
         
         return new Rect(structure.rect.x + structure.rect.width, structure.rect.y, newContentSize.x, newContentSize.y);
     }
 
     static string SubStringStartToEnd(string msg, int startIndex, int endIndex)
     {
         return msg.Substring(startIndex, endIndex-startIndex);
     }
     static GUIContent SubStringStartToEnd(GUIContent content, int startIndex, int endIndex)
     {
         return new GUIContent(SubStringStartToEnd(content.text, startIndex, endIndex));
     }
 
 
 }
Code without reflection:
Also you can see the code I had before I switched over to using reflection to gain access to DoTextField in this GitHub gist. It is not complete but I think someone may find it very useful for their own GUI elements.
Your answer
 
 
              koobas.hobune.stream
koobas.hobune.stream 
                       
                
                       
			     
			 
                