- Home /
TextArea does not work text dissapears. Solution is provided.
NOTICE: This post was being written as I was still trying to get the example code to behave properly. I'm posting it anyway for future Unity newbs like my self so maybe it will save them some brain cells. The initial post is inside the -BEGIN- -END- marks. After the -END- mark I have written down my thoughts about unity in general and the solution to my own problem for posterity's sake.
-BEGIN-
I'm getting aggravated at unity's wacky way of doing things related to gui coding.
I'm using unity (free) 3.5.1f2, and I am still a newb to unity as I have just begun to start working with it. I'm coming into unity as a programmer. I've listed my test code below.
Here is the problem. Add the code to your unity project. You should have a new Menu item "CBX" select "testwin" from the drop down and you get the test window. No problem so far. Now type some text into the TextArea control and click in a scene view or anywhere in unity so that the testwin window losses focus. POOF! The text in the TextArea control disappears. WTH? Now click back on the testwin window and presto the text you typed into the TextArea control is now displayed again. ?!?!
To make matters worse type text into the TextArea and drag the testwin tab and doc it to another unity window. Brzzzt! All text in the TextArea control is gone!
This is driving me insane trying to track down just what the heck is going on here. I can't even get simple things like make a single window with a TextArea in it to work.
To make matters worse and even more confrusing! Change the line of code from ...
private string scriptText = string.Empty;
... to ...
private string scriptText;
Now repeat the steps of typing text into the TextArea control and clicking the window and clicking out of the window. But be sure to have unity's console window visible while doing this. You will see the TextArea control visible and being created when the window does not have focus (No console errors when unfocused). And when the testwin window is focused you get errors in the console ala ...
NullReferenceException: Object reference not set to an instance of an object
UnityEngine.TextEditor.ClampPos () (at C:/BuildAgent/work/b0bcff80449a48aa/Runtime/Export/TextEditor.cs:1144)
UnityEditor.EditorGUI.DoTextField (UnityEditor.RecycledTextEditor editor, Int32 id, Rect position, System.String text, UnityEngine.GUIStyle style, System.String allowedletters, System.Boolean& changed, Boolean reset, Boolean multiline, Boolean passwordField) (at C:/BuildAgent/work/b0bcff80449a48aa/Editor/MonoGenerated/Editor/EditorGUI.cs:353)
UnityEditor.EditorGUI.TextArea (Rect position, System.String text, UnityEngine.GUIStyle style) (at C:/BuildAgent/work/b0bcff80449a48aa/Editor/MonoGenerated/Editor/EditorGUI.cs:927)
UnityEditor.EditorGUILayout.TextArea (System.String text, UnityEngine.GUIStyle style, UnityEngine.GUILayoutOption[] options) (at C:/BuildAgent/work/b0bcff80449a48aa/Editor/MonoGenerated/Editor/EditorGUI.cs:3302)
UnityEditor.EditorGUILayout.TextArea (System.String text, UnityEngine.GUILayoutOption[] options) (at C:/BuildAgent/work/b0bcff80449a48aa/Editor/MonoGenerated/Editor/EditorGUI.cs:3299)
TestWindow.OnGUI () (at Assets/Editor/CBX.Unity.Editors/TestWindow.cs:22)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)
Again flabbergasting. Why throw out console errors when the window is focused but not when un-focused. It clearly appears to be working when un-focused as the TextArea is being created and displayed! /frown
using UnityEngine;
using UnityEditor;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
public class TestWindow : EditorWindow
{
// script text
private string scriptText = string.Empty;
// position of scroll view
private Vector2 scrollPos;
void OnGUI()
{
// start the scroll view
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
// show the script field
EditorGUILayout.TextArea(this.scriptText);
// close the scroll view
EditorGUILayout.EndScrollView();
}
[MenuItem("CBX/testwin")]
static void Init()
{
// get the window, show it, and hand it focus
try
{
var window = EditorWindow.GetWindow<TestWindow>();
window.title = "testwin";
window.Show();
window.Focus();
window.Repaint();
}
catch (System.Exception ex)
{
Debug.LogError(ex.Message);
}
}
}
-END-
So. What seems to be the magical solution to this problem? Change line ...
EditorGUILayout.TextArea(this.scriptText);
... to read ...
this.scriptText = EditorGUILayout.TextArea(this.scriptText);
... somehow this seems to work.
At the time of this writing I still don't quite grasp what difference it makes performing that change.
During this 2+ hour long battle royal with unity's gui coding style and searching around the net as usual I ended up solving my own problem. During this time a few four letter words were uttered.
Off the top of my head I can't recall using a gui system like unity's going back to Borlands Turbo Pascal Turbo Vision for DOS! Every gui system I've used over my programming history has been object based. There is a structure too them. There typically object based with numerous properties/methods/events on them. This is true of XAML, .NET winform controls, even asp.net etc, MVVM patterns etc.
Why unity decided to go this route, ... sigh ... I kind of understand the abstraction aspect that unity seems to try and achieve, but clearly it has thrown me for a loop. I've been conditioned to expect a certain structure and pattern when working with gui systems. And I find unity's departure from this historical pattern questionable.
First of all don't post the answer into the question! There's no problem to answer your own question, but use the answer function. You can edit your question at any time.
Second there's a big difference between Unity GUI and let's call them state-based-GUIs. In most applications the GUI needs to be repainted when a control has changed its state. They perform some calculations what areas needs to be updated to increase performance and $$anonymous$$imize overdraw / flicker.
Unity is a game engine. As such it has to redraw the entire screen every frame. Since the GUI is on top of everything it also needs to be redrawn every frame. There are no visual parts of a GUI that remain unchanged. Of course an object oriented approach would also work, but the immediate mode has also some advantages. For example you can remove any parts of the GUI just by not drawing it.
I'm a bit confused. Didn't you check the documentation? GUI.TextArea and GUILayout.TextArea show exactly how you are supposed to use it. $$anonymous$$eep in $$anonymous$$d that OnGUI is called every frame. Any changes on the text in the TextArea are returned by the function. The function can't change the variable you passed as parameter (unless it's a reference parameter). Ins$$anonymous$$d the function returns the changed string every frame. If nothing has been changed the return value would be the same as the text you passed into the function.
Btw. I love the turbo vision framework (pascal was my first language ;)) but it's a pity that noone wants to use textmode editors nowadays ;) The 80x25 mode was a dream.
Finally if you're interested in how the GUI stuff works behind the scenes (just for clarification and better understanding) i would recommend to use a .NET reflector like ILSpy to view the UnityEngine.dll and the UnityEditor.dll. It may enlighten you ;)
Answer by Kryptos · Apr 24, 2012 at 11:41 AM
If you just had read this page, you wouldn't have lost your time.
Back to the design choice. This way provides more flexibility and is less prone to unfortunate side-effects.
Firstly, in C# to pass a reference to a value-type, you need the ref keyword. But this keyword cannot be used with properties. Therefore a strict object version would not allow the use of properties.
Secondly, this allows you to check the value (and even process it) before actually changing the target variable. Think of this:
float newFloat = EditorGUILayout.FloatField( myClass.floatField );
if( CheckValue( newFloat) )
{
myClass.floatField = newFloat;
}
Thus more flexibility.