Custom inspector - resize array intfield problems

Hello everyone :),

Well, this is kinda weird to explain.
So this is the problem,
I have an array in my custom inspector, and I have an intField to set its size, so on every OnInspectorGUI checks if its size has changed and creates the new array, draws it, and so on.

The problem is that, as an example, I have 10 objects, and I want to expand my array to 12, so I enter my intField, and start typing my new value, but suddenly, because the value changed when I press “1”, the value changes, and the array resets to one.

The issue here is that I might not want want to lose the previous array objects, I only wanted to expand them.

My first idea was to implement some sort of ‘Set size’ button, but that seems silly :P.

Then I thought about creating a second array to keep a sort of ‘old’ list, so when it resize, only the new elements in the new array resize will be really ‘new’ (in the previous example, only two of them would be new elements). Its not pretty, but at least the information isn’t lost.

But then I watched that the built in inspector actually changes array size only when enter is pressed on the size field (or when the field loses focus).

Does anyone have an idea of how to implement that kind of functionality?

Thanks a lot,

Italo F. Capasso B. AKA “Edwood Grant”

Edit: I realized there is a forum section only for Unity GUI topics, can please somebody move it there if it does not fit here? Sorry…

Do you have this problem only for this textfield or does it happens to all ?

I have this “bug” after playing my network training project. To remove it, I have to restart Unity…

Something like this should work: (untested)

static string s_EditedValue = null;
static int s_EditedField = 0;
public static string StringField (string label, string value)
{
	GUILayout.BeginHorizontal ();
		GUILayout.Label (label);
		int controlID = GUIUtility.GetControlID (FocusType.Passive);
		if (s_EditedField != controlID)
		{
			GUI.enabled = s_EditedField == 0;
			if (GUILayout.Button (value, GUI.skin.GetStyle ("Label")))
			{
				s_EditedValue = value;
				s_EditedField = controlID;
			}
		}
		else
		{
			if (Event.current.type == EventType.KeyDown  Event.current.character == '\n')
			{
				Event.current.Use ();
				value = s_EditedValue;
				s_EditedValue = null;
				s_EditedField = 0;
				GUIUtility.hotControl = 0;
				GUIUtility.keyboardControl = 0;
				GUILayout.Label (value);
			}
			else
			{
				s_EditedValue = GUILayout.TextField (s_EditedValue);
			}
		}
	GUILayout.EndHorizontal ();
	return value;
}

Hello again :slight_smile:

AngryAnt, I tested your code, and well it works as expected when pressing enter. But for example, when leaving focus nothing happens. And I think that I have to press twice the control, first for “access” and one for selecting the field (to actually type inside).
Probably its related to having to use a GUI button to make it work properly.

But apart from that it works nicely :).

I’ll see if I can improve it to make it work perfectly :)… maybe I can detect mouse presses outside of the control? (Using that fancy event class :slight_smile: )

Italo F. Capasso B. AKA “Edwood Grant”

Indeed. The above example was an idea to get you started. Polish is indeed needed.

Well by investigating and testing here and there I managed to get an special Int Field working, Using tool tips for getting focus (its kinda ugly since it uses the control id for this so the tool tip is something like “789”… I did not use label for tool tip because there might be fields with same name, while control ids are unique).

As far as I know this works nicely :), and it works almost exactly as an array size paramater in the inspector :P… The only problem being that you can type letters inside, but oh well… ate least if it can’t parse the number it will not change it

Cannot guarantee that there are no bugs though, so be careful.

I am sure this can be extended to other types of fields, such as floats or strings.

Hope this is useful to someone…

public static string s_EditedValue = string.Empty;
public static string s_LastTooltip = string.Empty;
public static int s_EditedField = 0;

/// <summary>
/// Creates an special IntField that only chages th actual value when pressing enter or losing focus
/// </summary>
/// <param name="label">The label of the int field</param>
/// <param name="value">The value of the intfield</param>
/// <returns>The valuefo the intfield</returns>
public static int IntField(string label, int value)
{
	// Get current control id
	int controlID = GUIUtility.GetControlID(FocusType.Passive);

	// Assign real value if out of focus or enter pressed, 
	// the edited value cannot be empty and the tooltip must match to the current control
	if ((controlID.ToString() == s_LastTooltip  s_EditedValue != string.Empty) 
		((Event.current.type == EventType.KeyDown  Event.current.character == '\n') || (Event.current.type == EventType.MouseDown)))
	{
		// Draw textfield, somehow this makes it work better when pressing enter
		// No idea why...
		EditorGUILayout.BeginHorizontal();
		s_EditedValue = EditorGUILayout.TextField(new GUIContent(label, controlID.ToString()), s_EditedValue, EditorStyles.numberField);
		EditorGUILayout.EndHorizontal();

		// Parse number
		int number = 0;
		if (int.TryParse(s_EditedValue, out number))
		{
			value = number;
		}

		// Reset values, the edite value must go bak to its orginal state
		s_EditedValue = value.ToString();
		s_EditedField = 0;
		return value;
	}
	else
	{
		// Only draw this if the field is not being edited
		if (s_EditedField != controlID)
		{
			// Draw textfield with current original value
			EditorGUILayout.BeginHorizontal();
			EditorGUILayout.TextField(new GUIContent(label, controlID.ToString()), value.ToString(), EditorStyles.numberField);
			EditorGUILayout.EndHorizontal();

			// Save last tooltip if gets focus... also save control id
			if (GUI.tooltip == controlID.ToString())
			{
				s_LastTooltip = GUI.tooltip;
				s_EditedField = controlID;
			}
		}
		else
		{
			// Draw textfield, now with current edited value
			EditorGUILayout.BeginHorizontal();
			s_EditedValue = EditorGUILayout.TextField(new GUIContent(label, controlID.ToString()), s_EditedValue, EditorStyles.numberField);
			EditorGUILayout.EndHorizontal();
		}
	}

	return value;
}

I did find that this was the best working solution for me on this problem.

I did make one change.

This code:

((Event.current.type == EventType.KeyDown  Event.current.character == '\n') || (Event.current.type == EventType.MouseDown)))

could be:

((Event.current.Equals (Event.KeyboardEvent ("[enter]")) || Event.current.Equals (Event.KeyboardEvent ("return")) || (Event.current.type == EventType.MouseDown))))

@edwood_grant

Hey! Thanks a lot, have been struggling on this for hours, your intfield works like a charm straight out of the box.

I think this should be a built in EditorGUILayout feature, lets face, this is inspector default behaviour, its crazy that you have to make an workarround to reproduce it…It should be called array size field.
Also weird that theres so few topics on this.

damn…it just works well for one of those fields actually…if you have a dynamic number of those(array inside arrays) it get really messy(letting the mouse over another field set its value to the previous field setted one…)

The working solution for this will be ugly as hell…any tips are welcome.