Search Unity

Float input GUI item

Discussion in 'Immediate Mode GUI (IMGUI)' started by Pixelstudio_nl, Mar 30, 2011.

  1. Pixelstudio_nl

    Pixelstudio_nl

    Joined:
    Jun 22, 2009
    Posts:
    179
    Hello people,

    The editorgui / editorguilayout has a nice FloatField GUI item (wich works perfect).
    Does any1 have such a component for the GUI/GUILayout ?

    my problem is, i use a textfield (wich is the only one i can use) for a input field where i need floats "0.1"
    but as soon as i type "1." it gets parsed as 1, so its not possible to enter a good float number...

    Why are those EditorGUI items not in the GUI available ?

    Cheers
     
  2. reissgrant

    reissgrant

    Joined:
    Aug 20, 2009
    Posts:
    726
    Have you tried using the EditorGUILayout instead of solely GUILayout ? I've used gui calls in editorgui before. Maybe it will work?

    Code (csharp):
    1.  
    2.  
    3. EditorGUILayout.FloatField (  myFloat  );
    4.  
    5.  
    *edit doesn't work...

    But this does:
    Code (csharp):
    1.  
    2. var myString : String = ".001";
    3. var myFloat : float;
    4.  
    5. function OnGUI () {
    6.  
    7.     myString = GUILayout.TextField (myString, 25);
    8.    
    9.     myFloat = parseFloat(myString);
    10.    
    11. }
    12.  
    Still would be nice to have a float field though...
     
    Last edited: Mar 30, 2011
  3. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    I know this is very old but alot of people are still searching for this.
    I got a workaround that somewhat works:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Diagnostics;
    3. using System.Runtime.CompilerServices;
    4. using System.Collections.Generic;
    5.  
    6. //.....
    7.  
    8. private static Dictionary<string, string> floatFields = new Dictionary<string, string> ();
    9.  
    10. /// <summary>
    11. /// Mimics UnityEditor.EditorGUILayout.FloatField by taking a float and returning the edited float.
    12. /// </summary>
    13. /// <param name="callingObj">The object the call comes from. Important for distinction between unique FloatFields!</param>
    14. [MethodImpl(MethodImplOptions.NoInlining)]
    15. public static float FloatField (float value, object callingObj)
    16. {
    17.     // Fetch information about the calling instance (to identify the floatField)
    18.     StackFrame frame = new StackFrame (1, true);
    19.     string floatFieldID = "Obj:" + (callingObj != null? callingObj.GetHashCode ().ToString () : "Null") +
    20.             "_File:" + frame.GetFileName () +
    21.             "_Line:" + frame.GetFileLineNumber ();
    22.     //UnityEngine.Debug.Log ("Own ID: '" + floatFieldID + "'");
    23.  
    24.     bool hasField = floatFields.ContainsKey (floatFieldID);
    25.     string textValue = GUILayout.TextField (hasField? floatFields [floatFieldID] : value.ToString ());
    26.     if (textValue.Split (new char[] {'.', ','}, System.StringSplitOptions.None).Length > 2) // if there are two dots, remove the second one and every fellow digit
    27.         textValue.Remove (textValue.LastIndexOf ('.'));
    28.  
    29.     if (float.TryParse (textValue, out value) && !textValue.EndsWith ("."))
    30.     { // if we don't have something to keep (any information that would be lost when parsing)
    31.         if (hasField) // but we have a record of this (previously not parseable), remove it now (as it has become parseable)
    32.             floatFields.Remove (floatFieldID);
    33.     }
    34.     else if (!hasField) // we have something we want to keep (any information that would be lost when parsing) and we don't already have it recorded, add it
    35.         floatFields.Add (floatFieldID, textValue);
    36.  
    37.     return value;
    38. }
    39.  
    40. /// <summary>
    41. /// Mimics UnityEditor.EditorGUILayout.FloatField by taking a label and a float and returning the edited float.
    42. /// </summary>
    43. /// <param name="callingObj">The object the call comes from. Important for distinction between unique FloatFields!</param>
    44. public static float FloatField (GUIContent label, float value, object callingObj)
    45. {
    46.     GUILayout.BeginHorizontal ();
    47.     GUILayout.Label (label, GUILayout.Width (146));
    48.     value = FloatField (value, callingObj);
    49.     GUILayout.EndHorizontal ();
    50.     return value;
    51. }
    EDIT: It's needlessly slow and crap, see below for a perfect version;)
     
    Last edited: Sep 5, 2015
  4. Seneral

    Seneral

    Joined:
    Jun 2, 2014
    Posts:
    1,206
    Even faster and easier, probably the best it can go:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Globalization;
    3. using System.Collections.Generic;
    4.  
    5. #if !UNITY_EDITOR
    6. private static int activeFloatField = -1;
    7. private static float activeFloatFieldLastValue = 0;
    8. private static string activeFloatFieldString = "";
    9. #endif
    10. /// <summary>
    11. /// Float Field for ingame purposes. Behaves exactly like UnityEditor.EditorGUILayout.FloatField
    12. /// </summary>
    13. public static float FloatField (float value)
    14. {
    15. #if UNITY_EDITOR
    16.     return UnityEditor.EditorGUILayout.FloatField (value);
    17. #else
    18.    
    19.     // Get rect and control for this float field for identification
    20.     Rect pos = GUILayoutUtility.GetRect (new GUIContent (value.ToString ()), GUI.skin.label, new GUILayoutOption[] { GUILayout.ExpandWidth (false), GUILayout.MinWidth (40) });
    21.     int floatFieldID = GUIUtility.GetControlID ("FloatField".GetHashCode (), FocusType.Keyboard, pos) + 1;
    22.     if (floatFieldID == 0)
    23.         return value;
    24.    
    25.     bool recorded = activeFloatField == floatFieldID;
    26.     bool active = floatFieldID == GUIUtility.keyboardControl;
    27.    
    28.     if (active && recorded && activeFloatFieldLastValue != value)
    29.     { // Value has been modified externally
    30.         activeFloatFieldLastValue = value;
    31.         activeFloatFieldString = value.ToString ();
    32.     }
    33.    
    34.     // Get stored string for the text field if this one is recorded
    35.     string str = recorded? activeFloatFieldString : value.ToString ();
    36.    
    37.     // pass it in the text field
    38.     string strValue = GUI.TextField (pos, str);
    39.    
    40.     // Update stored value if this one is recorded
    41.     if (recorded)
    42.         activeFloatFieldString = strValue;
    43.    
    44.     // Try Parse if value got changed. If the string could not be parsed, ignore it and keep last value
    45.     bool parsed = true;
    46.     if (strValue != value.ToString ())
    47.     {
    48.         float newValue;
    49.         parsed = float.TryParse (strValue, out newValue);
    50.         if (parsed)
    51.             value = activeFloatFieldLastValue = newValue;
    52.     }
    53.    
    54.     if (active && !recorded)
    55.     { // Gained focus this frame
    56.         activeFloatField = floatFieldID;
    57.         activeFloatFieldString = strValue;
    58.         activeFloatFieldLastValue = value;
    59.     }
    60.     else if (!active && recorded)
    61.     { // Lost focus this frame
    62.         activeFloatField = -1;
    63.         if (!parsed)
    64.             value = strValue.ForceParse ();
    65.     }
    66.    
    67.     return value;
    68. #endif
    69. }
    70.  
    71. /// <summary>
    72. /// Float Field for ingame purposes. Behaves exactly like UnityEditor.EditorGUILayout.FloatField
    73. /// </summary>
    74. public static float FloatField (GUIContent label, float value)
    75. {
    76. #if UNITY_EDITOR
    77.     return UnityEditor.EditorGUILayout.FloatField (label, value);
    78. #else
    79.     GUILayout.BeginHorizontal ();
    80.     GUILayout.Label (label, label != GUIContent.none? GUILayout.ExpandWidth (true) : GUILayout.ExpandWidth (false));
    81.     value = FloatField (value);
    82.     GUILayout.EndHorizontal ();
    83.     return value;
    84. #endif
    85. }
    86.  
    87. /// <summary>
    88. /// Forces to parse to float by cleaning string if necessary
    89. /// </summary>
    90. public static float ForceParse (this string str)
    91. {
    92.     // try parse
    93.     float value;
    94.     if (float.TryParse (str, out value))
    95.         return value;
    96.    
    97.     // Clean string if it could not be parsed
    98.     bool recordedDecimalPoint = false;
    99.     List<char> strVal = new List<char> (str);
    100.     for (int cnt = 0; cnt < strVal.Count; cnt++)
    101.     {
    102.         UnicodeCategory type = CharUnicodeInfo.GetUnicodeCategory (str[cnt]);
    103.         if (type != UnicodeCategory.DecimalDigitNumber)
    104.         {
    105.             strVal.RemoveRange (cnt, strVal.Count-cnt);
    106.             break;
    107.         }
    108.         else if (str[cnt] == '.')
    109.         {
    110.             if (recordedDecimalPoint)
    111.             {
    112.                 strVal.RemoveRange (cnt, strVal.Count-cnt);
    113.                 break;
    114.             }
    115.             recordedDecimalPoint = true;
    116.         }
    117.     }
    118.    
    119.     // Parse again
    120.     if (strVal.Count == 0)
    121.         return 0;
    122.     str = new string (strVal.ToArray ());
    123.     if (!float.TryParse (str, out value))
    124.         Debug.LogError ("Could not parse " + str);
    125.     return value;
    126. }
    The idea is: Differenciate the single float fields with a unique control id and store current string value and additional data needed for parsing. Basically you can type in anything, which, if it's able to parse, is directly returned, and if you wrote crap (letters, etc.), it will clean up the string and parse upon unfocusing.

    Hope that gives you what you expect, it's fast and reliable and is, besides the slider feature, exactly like the original.
     
    Last edited: Sep 5, 2015
    JohannesMP, MikeAtOO and glitchers like this.
  5. Pangamini

    Pangamini

    Joined:
    Aug 8, 2012
    Posts:
    54
    Hello. I am maintaining my own public git repository under MIT license, with common helper classes that I am using in various projects. Would it be OK for me to include this FloatField code?