VisualElement borderColor repaint fail on disabled custom inspector

I have a custom inspector built using UI Toolkit. It has many visual elements. Sometimes I need the left edge those elements to be marked green or red.

I have a static utility class that handles such functions. Here is the function from that utility class that changes the color of the left edge of each element:

public static void ToggleColor(ref VisualElement element, bool condition)
{
    if (condition)
        element.style.borderLeftColor = Color.green;
    else element.style.borderLeftColor = Color.red;
}

This code mostly works fine. However, if I disable the gameObject that contains the custom inspector, I want the colors to refresh. I’ve set up a callback using EditorApplication.hierarchyChanged, which should allow me to update the colors using this static function. The calback does execute reliably, yet the colors do not change.

I also call the same color change function when the custom inspector is first drawn. It works fine whenever the custom inspector initializes. Once the disabled gameObject causes the color change to fail, I deselect the gameObject in hierarchy and select it again. This causes the custom inspector to reinitialize, and the colors become correct.

I have tried to force each element to repaint during the enable/disable callback with MarkDirtyRepaint(). I have also tried this:

element.schedule.Execute(()=>element.MarkDirtyRepaint()).ExecutedLater(10);

No luck. The disabled object has elements with colors that won’t refresh until I deselect/select it manually.

The most puzzling part:

I’ve stepped through the color change code many times. It DOES execute in all cases, and its values and references are all correct, yet it still doesn’t cause the color to change only in this situation.
Anyone have a clue what’s happening here?

Side note: I was curious as to whether UI toolkit custom inspectors always fail to redraw if the gameObject they’re on is disabled, although that would be strange. I confirmed that is not the case, because I also have some mouse-click functionality on each element that allows me to toggle the colors using the same static utility function, and that works flawlessly whether the gameObject is active or inactive.

Nevermind, I figured it out.

I was attempting to call CreateInspectorGUI() in the EditorApplication.hierarchyChanged callback in order to refresh the inspector. You can’t do that, apparently. It WILL execute your code, but the inspector will never refresh this way. I guess there must be other processes of the Editor class that prevent the inspector from redrawing despite every effort to call Repaint().

I fixed this by caching the root element that gets returned in CreateInspectorGUI, and then in that element (derived from VisualElement), I wrapped all its constructor code in an Init() function, which I can then call from the cached reference in order to “reconstruct” the previously-constructed element.

It seems to me that the element you are pointing to is not in the hierarchy anymore…
If you use the UI debugger, you can probably change the border color by hand and see it changes immediately, but also that the border color of the element actually displayed is not affected by your code change.

The ToggleColor should probably do a query to get the visual element currently in the debugger.

The element is in the hierarchy. It successfully reinstantiates when I call CreateInspectorGUI. It just doesn’t draw into the inspector. I know this because I stepped through the code and observed the allocations, plus I was able to change the color and redraw with a click event using the same static method. The code works. It just doesn’t redraw in this one use case.

The use case: execute CreateInspectorGUI() manually rather than letting the engine execute it reflexively. If you do that, the instance of each element instantiates as expected, but doesn’t render.

It can be reproduced by drawing a random color or printing a random label in CreateInspectorGUI(). That’s how I discovered the root cause. I thought the inspector WAS redrawing, but drawing the wrong color somehow, since the code was executing and its values were correctly allocated. But then I added a label with a randomly-generated string to the inspector and discovered that it was not redrawing at all, despite CreateInspectorGUI() definitely executing.

This may be intended functionality for CreateInspectorGUI(). I just wasn’t aware of that, and I think others may find it useful to know that you can’t call CreateInspectorGUI() manually and expect it to draw anything.

CreateInspectorGUI() create the element and pass it as the return value but you need to put it somewhere for it to be drawn: That is what the inspector does internally. If you call it manually there will be a second one that simply won’t be displayed unless you add it to a hierarchy somewhere.

It seems like I am missing a part of the puzzle but your static instance will have to be updated during domain reload and risk being null if the inspector is timed-sliced if there is to much CPU used for example.

Another problem would be if multiples inspector are open at the same time, what would happen of the single static field?

I would go the other route and maybe add a scheduler to your visual element that update the visual every x time.

The part of the puzzle you’re missing is me forgetting to include the ref keyword in the simplified example of my scenario. No static memory management necessary. :slight_smile: