Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by MLM · Jun 10, 2014 at 12:16 AM · guieditoreditor-scriptingeditorgui

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:

SelectableFoldout 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);
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

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.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

21 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

EditorGUI like light explorer window 1 Answer

Lining up GUI 1 Answer

Editor GUI Foldout header style customization 0 Answers

How to customize editor to show, in an array of scriptA within scriptB, the public vars of each scriptA[n] element? 1 Answer

EditorGUI, EditorGUILayout, GUI, GUILayout... pshhh... WHEN TO USE WHAT?! 3 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges