Regression in TreeView::updateVisibleItems

Hello there!
So this has been an issue for me for quite a while and now that I have time to report it, I’m discovering that it’s been happening since 2021.
With commit 70968d4, Ed solved a performance issue in the TreeView class when calculating the positions of the items.
Unfortunately, this leads to a sort of a graphic glitch because the calculation now happens asynchronously, so the TreeView disappears for a bit before the items are correctly displayed again.
It’s a regression because it didn’t happen before this commit and I feel I’m starting to display symptoms of epilepsy every time I refresh my TreeViews.
To see what I mean, try to replace TreeViewport::Async::yes with TreeViewport::Async::no in the updateVisibleItems function.

Can you please allocate a bit of time to look into this?

Well, I tried to assemble a quick testing application and in doing so I realised that the problem is a bit hard to reproduce.
In my use case the message thread is busy doing other things whenever that TreeView function is triggered (allocations and other stuff in different areas of the application, think of the TreeView as a preset browser). This explains why it’s not easy to spot the behaviour in the JUCE demo or in a simple test app.
But the culprit is definitely there.

Any chance you could change this:

class TreeView::TreeViewport  : public Viewport,
                                private AsyncUpdater

to this?

class TreeView::TreeViewport  : public Viewport,
                                public AsyncUpdater

At least in this way I could do a dynamic cast on the TreeView’s Viewport and call AsyncUpdater::handleUpdateNowIfNeeded()…

If I understand your problem correctly, what happens is:

  • The tree view clears its view (or something like that) and schedules a redraw to a later time on the message thread
  • The message thread is busy doing stuff
  • And know too many ms later redraws the tree view

Is that correct?

I think the main problem here at hand is, that the tree view is “disappearing” while it is waiting for the redraw. I think this is the issue the JUCE team should fix on their end and if you are still experiencing it to be too slow, you should try to clear out work from the message thread on your end.
Looking at the named method updateVisibleItems I can’t spot the reason of everything disappearing in there. What exactly are you doing to the treeview, so that it disappears?

Hi there! Yes, this is exactly what’s happening.

Here’s what I’m doing:
1 - the user selects a preset from another area of the application (not from the TreeView)
2 - in response to the event, preset data is loaded and everything is updated in the UI (read: allocations)
3 - at the end of this, the TreeView is refreshed in order to display the preset that has just been loaded

As you can see, the message thread becomes busy before triggering the TreeView refresh.
So it’s a bit convoluted, plus the requirement is that the TreeView should close all its sub items apart from the ones that are parent of the preset item.

Reducing workload on the message thread is a bit complicated since it’s quite a large application but I was able to find a quick and dirty workaround, which consists in inverting points 2 and 3 of the list.

But what I still don’t quite understand is which part of the code triggers the TreeView to disappear? There has to be some kind of mechanism “clear all, cue async redraw” in place for that to happen which doesn’t seem quiet right to me.

There are multiple function calls that trigger that function.
In my case it boils down to removing all sub items from the root item and then adding new items recursively. This triggers calls to TreeViewItem::treeHasChanged, which in turn calls updateVisibleItems on the TreeView.

Isn’t it possible to create a new hidden treeview and swap it with the old once ready, simulating some kind of double buffering ?

The thought crossed my mind as well.
Are you suggesting an implementation inside the TreeView class or an external one, involving two TreeView instances? The latter would be a bit complicated since there’s no hook to determine when the tree is ready, hence my request to modify the visibility of the AsyncUpdater inheritance.

I’m suggesting two tree views. Well you should know when you are done filling a new tree isn’t it (as in manage yourself the async rebuild of the new one) ?