SearchService Request doesn't work if a modal window is open

Hello Everyone.

For an editor tool I am working on I need to create a modal window that let the user pick an asset and in order to find the available assets I need to use SearchService.Request().

The problem is, when a modal window is opened, some of the editor internal updates are blocked and, as a result, the search request doesn’t complete.

To be more accurate, while a modal editor window is opened EditorApplication.update and EditorWindow.Update() are no longer called but async functions and VisualElement schedulers still are. This behavior is incoherent between platforms, it happen on Windows but not on Linux (I didn’t test on Mac).

Before you ask, yes, I NEED a modal window for this. There are just too many things that could break if the user could tamper with other parts of the editor while the window is opened. And yes I have to perform the search request while the window is opened, I can’t do it before. So this is a major problem for me. Modal windows are a very common UX paradigm and there is no reason we shouldn’t use them within the Unity editor.

I opened a bug report about the modal window blocking the editor (IN-42896) and it was closed as a won’t fix.

So the unholy solution I came up with is to call Internal_InvokeTickEvents myself while the window is open.

internalTick = ReflectionUtils.GetStaticMethod(typeof(EditorApplication), "Internal_InvokeTickEvents", new Type[] {});

var searchList = SearchService.Request(searchContext, SearchFlags.WantsMore);
using (var it = searchList.GetEnumerator())
{
  internalTick?.Invoke(null, new object[] {});
  while (it.MoveNext())
  {
    internalTick?.Invoke(null, new object[] {});
    var item = it.Current;
    // do something with item
    await Task.Yield();
  }
}

You don’t like it? Me neither!

I really need a better solution for that. There is no reason a modal window should block the search function. So here are some propositions that could sole this issue:

  • Don’t block editor updates while a modal window is opened. I understand that modifying this behavior could cause regressions but the current behavior is not good and incoherent between platforms. Maybe this could be configurable by the script. Maybe when opening a modal window the user could pass an optional parameter telling Unity if the updates should be blocked or not.
  • Make SearchService not rely on editor updates. Looking at the code I don’t even understand why it needs it. Calling MoveNext() should be enough for the request to update.

Please Unity devs, I need a solution for this.
Thank you very much.

Hi @ChiwTheNeko ,

I didn’t know that Update wasn’t called when a modal window is opened. The Search team will look to see what can be done about this.

In the meantime, your solution is good enough. We use a similar solution in our internal test framework to make our test “synchronous”.

I will keep you posted.

Sebastien

Hi @ChiwTheNeko ,

After a few tests there is no easy solution to your problem.

1- All editor ticking is disabled when a ModalWindow is opened. The Modal Window is “eating all inputs” and doesn’t tick the editor anymore.

2- Due to the async nature of search, we rely on editor ticking. That said, this is something we want to fix. What you are doing in your example: actively pulling on the search iterator should be enough. I don’t want to commit to a timeline where we will fix this but it is on our todo.

This means you basically have 3 choices:
1- You make your window NOT modal. From your explanation this is something you don’t want since your workflow need to be modal.

2- You perform the Search BEFORE popping the window.

3- Or you keep ticking the EditorApplication like you are currently doing. When we fix the ticking of the seach session, you wil lbe able to remove this manual ticking.

Hope this helps (just a little bit).

Sebastien

Hello @sebastienp_unity. Thank you for taking the time to look into it, it helps a lot. I will keep my workaround for now and wait for your fix.

A few more questions and ideas:

  • When issuing a SearchService.Request, you can pass the Searchflags.Synchronous which will search synchronously. This could have 2 consequences though: it can take a long time depending on the size of your project or the scene you are searching. If asset indexation is not finished, this operation could fail. But I have tested this workflow and it works in a Modal window.

Just out of curiosity and because I like to better understand how our users are consuming our public apis, would you care to answers these questions?

1- What are the queries you are running? Which filters are you using? I suppose you are NOT using simple queries that could be answered by the AssetDatabase.FindAssets.

2- Did you create your own Search Provider?

3- Are those queries run on assets or scene objects?

Thank,

Sebastien

I am only searching for assets and I only use the “asset” provider, at least for now. I need to search for assets in both the project and the packages (I mean UPM packages). Some of the queries use filters like such as ‘dir:[2D, TopDown]’.

But the main reason I use the search service is because I created my own indexers (using the CustomObjectIndexer attribute). I use them to index prefabs and also my own assets (ScriptableObjects). For example this allows me to search for prefabs by usage (player, enemy, 3D, 2D…).

I also have some custom assets that contains many sub assets. For example I have a TileCollection asset that contain many tiles. The custom indexer for TileCollection let me search for individual tiles by name or other properties.

Does the Synchronous flag makes SearchService.Request() blocking?

Queries are pretty fast but indexation can take some time and I need to wait for the indexer to be ready. I have progress bar in my UI to let the user know that the app didn’t freeze. This is the code I use to make sure the indexer is ready before I make a query. I also need to tick the editor for this one to work.

bool indexReady = SearchService.IsIndexReady("Indexer");
while (!indexReady)
{
  await Task.Yield();
  internalTick?.Invoke(null, new object[] {});
  indexReady = SearchService.IsIndexReady("Indexer");
}

I hope that answers your questions.

This answers my questions. Thanks.

I am glad you are suing CustomObjectIndexer. This is a feature that is REALLY powerful and that is not used a lot. I guess it is a bit difficult to discover.

Synchronous flags will make the Request function blocking. So depending on the size of the project this could be long(ish). But like you said, resolving queries is usually pretty fast.