Repository: lutris/lutris

Issue:
White flash occurs when switching between sidebar rows
### Bug description

There's an unpleasant white flash that occurs when switching between sidebar rows. It doesn't really break anything, but it's not classy.

It's actually trying to accommodate a spinner that is sometimes displayed- it's useful to do this when searching Lutris, which can take seconds to complete. But it does it even when switching between ordinary sidebar rows, which is too fast for this to be useful.

### How to Reproduce

Run Lutris. Switch between rows; there's a brief flash as the game view is visibly removed then replaced just after.

### Expected behavior

I think it should update without this visual discontinuity - when possible. When searching Lutris.net, it can take several seconds to complete the search, so the white 'flash' is needed  or the spinner looks dumb.

### Log output

```shell
INFO     2022-01-17 05:44:57,599 [startup.check_driver:53]:Using NVIDIA drivers 470.86 for x86_64
INFO     2022-01-17 05:44:57,599 [startup.check_driver:57]:GPU: NVIDIA GeForce RTX 3060
INFO     2022-01-17 05:44:57,599 [startup.check_driver:73]:GPU: 10DE:2503 196E:1377 (nvidia drivers)
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,702 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,703 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,704 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,704 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,705 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,707 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,708 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,708 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,708 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,709 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,709 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,709 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,710 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,718 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,718 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,718 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,719 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,721 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,723 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,724 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,725 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of DXVK versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of VKD3D versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of D3D Extras versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
WARNING  2022-01-17 05:44:57,726 [dll_manager.versions:38]:Loading of DXVK-NVAPI versions failed, defaulting to locally available versions
DEBUG    2022-01-17 05:44:57,749 [lutriswindow.update_store:451]:Showing 30 games
DEBUG    2022-01-17 05:44:59,671 [lutriswindow.update_store:451]:Showing 30 games
DEBUG    2022-01-17 05:45:00,969 [lutriswindow.update_store:451]:Showing 30 games
INFO     2022-01-17 05:45:03,011 [application.do_shutdown:631]:Shutting down Lutris
```


### System Information

```shell
[System]
OS:              Pop!_OS 21.10 impish
Arch:            x86_64
Kernel:          5.15.11-76051511-generic
Desktop:         GNOME
Display Server:  x11

[CPU]
Vendor:          AuthenticAMD
Model:           AMD Ryzen 7 5800X 8-Core Processor
Physical cores:  8
Logical cores:   16

[Memory]
RAM:             31.4 GB
Swap:            0.0 GB

[Graphics]
Vendor:          NVIDIA Corporation
OpenGL Renderer: NVIDIA GeForce RTX 3060/PCIe/SSE2
OpenGL Version:  4.6.0 NVIDIA 470.86
OpenGL Core:     4.6.0 NVIDIA 470.86
OpenGL ES:       OpenGL ES 3.2 NVIDIA 470.86
Vulkan:          Supported
```


### Media (optional)

_No response_

### Checklist:

- [X] I'm not asking for support with a game or the wine runner.
- [X] I have followed the above mentioned guides and have all the graphics and wine dependencies installed.
- [X] I have checked for existing issues that describe my problem prior to opening this one.
- [X] I understand that improperly formatted bug reports may be closed without explanation.


Discussion:

Resolving patch:
diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py
--- a/lutris/gui/lutriswindow.py
+++ b/lutris/gui/lutriswindow.py
@@ -117,7 +117,6 @@ def __init__(self, application, **kwargs):
 
         self.sidebar_revealer.set_reveal_child(self.side_panel_visible)
         self.sidebar_revealer.set_transition_duration(300)
-        self.tabs_box.hide()
 
         self.game_bar = None
         self.revealer_box = Gtk.HBox(visible=True)
@@ -462,17 +461,24 @@ def show_empty_label(self):
                 self.show_label(_("No games found"))
 
     def update_store(self, *_args, **_kwargs):
-        self.game_store.store.clear()
-        for child in self.blank_overlay.get_children():
-            child.destroy()
-        games = self.get_games_from_filters()
-        logger.debug("Showing %d games", len(games))
-        self.view.service = self.service.id if self.service else None
-        GLib.idle_add(self.update_revealer)
-        for game in games:
-            self.game_store.add_game(game)
-        if not games:
-            self.show_empty_label()
+        thread = AsyncCall(self.get_games_from_filters, self.update_store_cb)
+        if not thread.await_completion(.25):
+            self.show_spinner()
+
+    def update_store_cb(self, games, error):
+        self.hide_overlay()
+
+        if error:
+            dialogs.ErrorDialog(str(error))
+        else:
+            self.game_store.store.clear()
+            logger.debug("Showing %d games", len(games))
+            self.view.service = self.service.id if self.service else None
+            GLib.idle_add(self.update_revealer)
+            for game in games:
+                self.game_store.add_game(game)
+            if not games:
+                self.show_empty_label()
         self.search_timer_id = None
         return False
 
@@ -509,26 +515,30 @@ def on_zoom_changed(self, adjustment):
         icon_type = media_services[media_index]
         if icon_type != self.icon_type:
             self.save_icon_type(icon_type)
-            self.show_spinner()
 
     def show_label(self, message):
-        """Display a label in the middle of the UI"""
+        """Display a label in the middle of the UI, instead of the game view"""
+        label = Gtk.Label(message, visible=True)
         for child in self.blank_overlay.get_children():
             child.destroy()
-        label = Gtk.Label(message, visible=True)
         self.blank_overlay.add(label)
-        self.blank_overlay.props.visible = True
+        self.blank_overlay.show()
+        self.games_scrollwindow.hide()
 
     def show_spinner(self):
+        """Display a spinner in the middle of the UI, instead of the game view"""
         spinner = Gtk.Spinner(visible=True)
         spinner.start()
         for child in self.blank_overlay.get_children():
             child.destroy()
         self.blank_overlay.add(spinner)
-        self.blank_overlay.props.visible = True
+        self.blank_overlay.show()
+        self.games_scrollwindow.hide()
 
     def hide_overlay(self):
-        self.blank_overlay.props.visible = False
+        """Restore the game view, and remove the spinner or label."""
+        self.games_scrollwindow.show()
+        self.blank_overlay.hide()
         for child in self.blank_overlay.get_children():
             child.destroy()
 
@@ -603,7 +613,7 @@ def redraw_view(self):
         self.games_scrollwindow.add(self.view)
 
         self.view.show_all()
-        GLib.idle_add(self.update_store)
+        self.update_store()
 
     def set_viewtype_icon(self, view_type):
         self.viewtype_icon.set_from_icon_name("view-%s-symbolic" % view_type, Gtk.IconSize.BUTTON)
diff --git a/lutris/util/jobs.py b/lutris/util/jobs.py
--- a/lutris/util/jobs.py
+++ b/lutris/util/jobs.py
@@ -23,9 +23,20 @@ def __init__(self, func, callback, *args, **kwargs):
         self.function = func
         self.callback = callback if callback else lambda r, e: None
         self.daemon = kwargs.pop("daemon", True)
-
+        self.completion_callback_args = None
         self.start()
 
+    def await_completion(self, timeout):
+        """Block waiting for the call to complete for a time.
+        If it completes in time,  invokes the callback synchronously
+        and returns True. Returns False if it times out."""
+        self.join(timeout)
+        if not self.is_alive():
+            GLib.source_remove(self.source_id)
+            self.complete()
+            return True
+        return False
+
     def target(self, *args, **kwargs):
         result = None
         error = None
@@ -38,9 +49,14 @@ def target(self, *args, **kwargs):
             _ex_type, _ex_value, trace = sys.exc_info()
             traceback.print_tb(trace)
 
-        self.source_id = GLib.idle_add(self.callback, result, error)
+        self.completion_callback_args = [result, error]
+        self.source_id = GLib.idle_add(self.complete)
         return self.source_id
 
+    def complete(self):
+        callback_args = self.completion_callback_args
+        self.callback(*callback_args)
+
 
 def synchronized_call(func, event, result):
     """Calls func, stores the result by reference, set an event when finished"""


Test patch: