Unity 2021+: how to set the (min/max) width of a PropertyField?

What options are there for styling the Label of a PropertyField now, that work in the 2021/2022 versions of UIToolkit?

The old workarounds are still possible, but they are now more annoying: previously you had to wait for one GeometryChangedEvent (typically: a frame), then use something like:

field.Q<Label>().style ...

to grab the label and style it. This almost works, but most of the time UIToolkit now requires you to wait multiple GCEs, with the first Nx GCEs giving you PropertyFields that are missing their child elements (even though they were fully constructed and added already!). I’ve got a class that is literally sitting there listening to GCEs and checking “Are UIToolkit’s PropertyFields actually there yet, or are they still placeholders?” until all the fields have been ‘created’. It’s a horrible hack.

Some further investigation…

UIToolkit recent versions overwrite PropertyFields after they’ve been constructed. Previous versions did not do this.

I have no idea which part of UIToolkit is doing this, or why. It’s deeply annoying.

Hi. PropertyFields are filled when their bindings are actually processed by Unity, and that changes not only with Unity versions, but also with a lot of other factors. One way to avoid waiting for the binding to be processed, if you have an idea how the hierarchy inside the property looks, is to use a stylesheet. You could add a USS class to the property field, and use a child selector like this to style its contents: .my-property-field > .unity-base-field > .unity-base-field__label.

A nice thing about using stylesheets is that you can also modify the ratio used for auto-sized labels in the inspector. Unity adds a special class to all fields that should have dynamically sized labels in the inspector in its own property drawers, and you can add it too if you want. For those fields, you can use the custom properties “–unity-property-field-label-width-ratio” and “–unity-property-field-label-base-min-width” directly in the .unity-base-field element to affect its label’s size in the inspector.

What do you mean by this?:

Are the PropertyField elements being replaced completely with other elements? Are their contents changing multiple times? Are their attributes changing?

Based on recent experiments: I believe this isn’t entirely true. I believe that the Unity 2021+ versions of UIToolkit: during Binding they ignore certain PropertyFields depending on the specific content of the field. The docs imply this (although it requires you to read them carefully) - and I tested it by hand and found it to be the case: many PropertyFields dont build during Bind().

It seems they are built by a ‘catch-all’ piece of code that runs later. I have found no documentation on this, and (so far) haven’t found any official statements from UIToolkit team to describe what is supposed to happen, we can only guess.

Also: I haven’t been able to find source code for the Bind() implementation yet, so I haven’t been able to prove/disprove it (I only briefly tried to decompile it, but it was being injected - so although I couldn’t find it on a quick attempt it might be included somewhere, I just need to find where).

Unfortunately I can’t. USS is for basic UIs that don’t require code to generate them, and mine are already more complex/richer than that.

Thanks - I haven’t tried this, if it works it could be a better solution.

Because of all the problems with PropertyField and the lack of docs, I have now written my own implementation - “BetterPropertyField” - that doesn’t have Unity’s bugs in it. I’m still using PropertyField where I can - anywhere that I don’t care about the layout, and can tolerate Unity’s weird layout choices - but using my custom one where I need finer control.

(most of Unity’s PropertyField class is dealing with Enums or USS - if you take that all away, it’s a very small class. You can then re-implement it better quite quickly).

I don’t know which of those three is what’s actually happening (I haven’t checked the memory locations to see if it’s the object being replaced, the parameters being replaced, or the parameter contents being overwritten), but yes: I can create a PropertyField, grab its child elements, style them … and then UIToolkit overwrites the style values with its own ones.

However, I’ve now written my own async library to wrap UIToolkit and remove the annoying async updates, and force them to be synchronous again. This makes it easy to avoid some UIToolkit bugs. I think it was a mistake for UIToolkit to try and do this asynchronously – until/unless the UIToolkit team adds approrpiate callbacks (Which we’ve been requesting for 5+ years now, with no success) then async API calls are absurd and a bad mistake: async APIs only work if you add all the edges for other developers to trigger their own code off.

I’m not saying that the property fields are filled when you call Bind. I’m saying that the property fields are filled when Unity processes these binding instructions. This doesn’t happen immediately when calling Bind; I believe it’s for performance reasons. In general, I think this is when they are processed:

  • Bindings for fields returned by an Editor’s CreateInspectorGUI are bound immediately after the method returns. You don’t even have to call Bind().
  • Bindings for other fields are processed when they are attached to a panel. If you call Bind() on a field that is already attached to a panel, it will be processed immediately.
  • The processing of all binding instructions can be delayed further when the Editor is being slow. This tends to happen after Domain Reloads.

Here is the main code behind it.

I use USS with complex UIs that require code to generate them all the time. The declarative capabilities of stylesheets can help a lot in cases like these, as they are applied automatically whenever an element appears. If you share a particular example where you are struggling to use USS, I could give you some ideas.

I think you need to use USS for this to work, though.

Here’s the code for PropertyField. I think you are in part right that you can implement its basic capabilities with not much code, but there’s a lot more that this class covers. I think it could be hard to take care of stuff like property drawers, property decorators, IMGUI implementations of those drawers and decorators, foldouts for composite properties, nested property drawers, managed references changing types, and anything else the future brings.

But hey, if it works for you, I won’t knock it down. I also like to avoid the overhead of PropertyFields when I can.

Hmm. I may have a couple of ideas here. What style values change?

Just so I understand this properly: What does “attached to a panel” mean, precisely? Is this “is displayed somewhere already on screen”, or something else?

Yeah, this is horrible. When I effectively removed this (by wrapping everything, and creating the callbacks that UIToolkit is missing), everything worked better, and performance increased. UIToolkit became easier to work with, I was able to delete a lot of code that only existed to workaround UIToolkit problems, etc. I think the current design of delayed Binding is bad, and I hope it gets reverted/replaced at some point with something better.

OK, but … trying to solve a code problem by adding stylesheets … might work, but it makes the overall problem space bigger (not least: I have to deal with the cross-integration between them, and I become exposed to all the bugs in USS). I had working code that was 100% correct in UIToolkit up to 2021, and much much simpler than tyring to combine stylesheets with code.

So: for me it’s worth a lot to maintain the simplicity that already existed (pre-2021) - in which case I see “using stylsheets to (badly) solve this, with extra work, and extra code/files/etc to maintain” as my worst case scenario, the one I’d only do if there’s literally no other option.

I’d love to see any worked examples of using stylesheets with code, the two integrated. All the UIT examples I’ve seen to date have been purely one, or purely the other (and in general: if its a trival UI with some images then USS is good, but for everything else - every ‘real’ UI - the USS examples are always vastly more writing / bigger files / lots more coding to achieve what the code route did super quick and easy).

Yeah, if you can choose whether to use PropertyField or not, on a case by case basis - so you can choose when to avoid those many other requirements - it’s easy to create a PropertyField replacement that works for ‘the times when PF fails’.

“min-width” and “width” are the two that caused me to notice the problems and start the thread.

I can see why “width” is overwritten - because Unity is constantly regenerting it. I don’t see why “min-width” is being overwritten: it appears UIToolkit constantly overwrites it for … no reason at all.