Unity 2018.4.8f1, EditorGUILayout methods + GUILayout.Button, GUILayout.Box, GUILayout.RepeatButton - EditorGUILayoutWrapper.cs.
I make extensive use of the MRTK2 extension service feature and its dependency injection capabilities. Unfortunately it’s not easy to see the internal state of such a service in the Unity editor while it’s running, unless you are willing to write a custom inspector for it using the Unity Editor API. This API is, to put it mildly, not very intuitive. Writing editor UIs is a cumbersome and error-prone process. If your extension service grows and holds more complex data, maintaining it becomes a chore as for every single field you have to add UI code.
- Class in UnityEngine / Implemented in:UnityEngine.IMGUIModule. Suggest a change. Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.
- Public static readonly GUILayoutOption EmptyGUIOptions. ExpandHeight(Boolean) public static GUILayoutOptions.GUILayoutOptionsInstance ExpandHeight(bool expand = true) Parameters. System.Boolean: expand: Returns. GUILayoutOptions.GUILayoutOptionsInstance: ExpandWidth(Boolean).
The lazy developer’s approach
So I started thinking: wouldn’t it be possible to write something that just iterates over the properties of a service using reflection and draw a UI on the fly? And then using the property type info to determine a field type - and fall back to a
ToString()
value in a text field. It turned out I could… although I had to solve the slight matter of recursively diving into child objects. I use a rather primitive approach to determine whether a property is a complex type or a simple type: if its ToString()
value equals its type’s FullName
, it’s a complex type and I have to dive into render it’s properties in a nested way. If not, it’s just a field. That works for almost everything. QED.Using BaseGenericServiceInspector
If you create a new service and opt for creating a Service Inspector, you get handed a class that looks like this:
In stead of adding inspector fields yourself, literally all you have to do is:
- Add
using MRTKExtensions.ServiceExtensions.Editor
on top - Delete the
DrawInspectorGUI
override - Replace the
BaseMixedRealityServiceInspector
base class byBaseGenericServiceInspector
The heart of the matter
Basically, in
BaseGenericServiceInspector
, this routine does most of the work:It simply iterates over every property, and draws a field for it. Pycharm community edition for mac. Unless the property is an object with more properties - or when the object is decorated with a
[InspectorExpand]
attribute.Actually drawing fields
Now this is pretty easy in itself. There’s actually two methods drawing fields. One draws most of the common fields, falling back to a
ToString()
value in a text field for none-specific fields or text:The
DrawCustom
method allow you do make draw more specific fields in addition to the five types I implemented. This is particularly useful for custom frameworks.1password app store download. 1Password Watchtower Receive alerts for compromised websites and vulnerable passwords so you can take action to stay secure. Digital Wallet Securely store credit and debit cards, online banking information, and PayPal logins so you can fill them from any device. Unrivaled support Whenever you need it, our global team is here to help. Get 1Password in your browser. 1Password works everywhere you do, including on Linux or Chrome OS. Easily sign in to sites, use suggested passwords, and find what you need. Everything is available at your fingertips. Get to know 1Password in your browser. Sign in to your account on 1Password.com. Select the Login item for the website and click Edit. Click “label” in a new section, and enter “One-time password”. Click to the right of the field and choose One-Time Password. On the website, choose to enter the code manually. Copy the code, then paste it in the One-Time Password field. If the website only supports QR codes, you’ll need to scan it using a. 1Password is the easiest way to store and use strong passwords. Log in to sites and fill forms securely with a single click.
Showing nested objects and properties
The weirdness of the Unity editor UI API really shows in
RenderFoldout
. I stole - and adapted - this from BaseMixedRealityProfileInspector
and I engaged in quite a bit of cargo cult programming before I had an idea what was actually going on. As you can see in RenderObjectFields
(up above), when it encounters a complex object, it calls RenderFoldout
with call to itself as an Action
but now with the property value (holding an object) as parameter.To understand how RenderFoldout works, it’s vital to understand the UI is not drawn once, and only then when it’s updated - this code is continually called. Time and time again the UI is rendered in the editor. And you really have to store state - any state - elsewhere. the good writers of
BaseMixedRealityProfileInspector
used the ‘SessionState
’ object object for that, and I dutifully copied that approach.So what happens is:
- A key is created to look into the
SessionState
, which apparently is a key-value bag that keeps it state over UI render calls - The value of the key, determining if the foldout is expanded or not, is retrieved - with false as default value
- The FoldOut is rendered. Mind you, only the header with the arrow. Not the contents. The state parameter apparently only determines whether the arrow next to the label is pointing down (open) or right (closed).
- By some magic the fact whether or not the user has clicked to open or close the FoldOut magically appears in
currentState
. By what event - beats me. - The updated state is stored back into the
SessionState
so it can be used in the next render call - And if
currentState
is true, the actual contents are rendered. This is an action, and if you look back it’s simplyRenderObjectFields
’s call to itself.
[InspectorExpand]’s use and usage
I made a passing remark already: I use a rather crude approach as to determining whether or not a property is a complex object that merits nested rendering of it’s properties. In the demo project, whose inspector display is featured in the image on top, I created a convoluted structure of objects to show off it works.
In the
ToStringOverriddenObject
I had to add the [InspectorExpand]
attribute because I intentionally have overridden the ToString()
. If it weren’t for the [InspectorExpand]
it would look like this:Commenting out the attribute will demonstrate this issue - and why this attribute is useful.
Conclusion
Guilayoutoption Height
Although this code is editor code and by it’s very nature will never run on and actual HoloLens, I think it might be a useful addition if you are using MRKT2’s Extension Services. I might even make it into a pull request one day ;). I hope people who actually understand writing Editor code - like my esteemed colleague - won’t have to laugh to hard about this.
Gui Layout Unity
Demo project can be found here.