Repository: lutris/lutris

Issue:
Modal dialog in main window blocks auxiliary window interaction
A modal dialog, e.g. "about Lutris" in the main window inexplicably blocks all interaction in e.g. the download window. Given that the latter can — and usually will —  be on top of and visually block the former, I'm left with no clue as to why I can't interact with the application, and will conclude that its frozen. 

In Windows I seem to recall they "solve" this by flashing the titlebar, to at least indicate the app is alive while still leaving you clueless. MacOS solves it by making the dialogs part of the parent window, and be done with it — the problem doesn't exits there. 

I don't know what gnomes solution is, and I can't guess what they're thinking considering they seem to treat modal dialogs both as independent windows (by giving them a separate titlebar, etc) as well as part of the parent window (by blocking interaction and locking their positions to the parent). 

Anyway, the proper solution from my perspective is to not block interaction in the windows that don't own the modal dialog. 


Discussion:
this is standard gnome behavior, even [gedit](https://en.wikipedia.org/wiki/Gedit) has this problem (try opening "Tools->Document Statistics" in a text editor window and then clicking "About Text Editor"

i agree that it's odd behavior, but it seems as though it'll be best addressed [upstream](https://wiki.gnome.org/Community/GettingInTouch/BugReportingGuidelines)
Sounds reasonable, if it's something clients have no control over 

Resolving patch:
diff --git a/lutris/gui/config/common.py b/lutris/gui/config/common.py
--- a/lutris/gui/config/common.py
+++ b/lutris/gui/config/common.py
@@ -52,6 +52,11 @@ def __init__(self, title, parent=None):
         self.runner_index = None
         self.lutris_config = None
 
+        # These are independent windows, but start centered over
+        # a parent like a dialog. Not modal, not really transient,
+        # and does not share modality with other windows - so it
+        # needs its own window group.
+        Gtk.WindowGroup().add_window(self)
         GLib.idle_add(self.clear_transient_for)
 
     def clear_transient_for(self):
@@ -254,7 +259,8 @@ def change_game_slug(self):
         self.slug_change_button.set_label(_("Change"))
 
     def on_move_clicked(self, _button):
-        new_location = DirectoryDialog("Select new location for the game", default_path=self.game.directory)
+        new_location = DirectoryDialog("Select new location for the game",
+                                       default_path=self.game.directory, parent=self)
         if not new_location.folder or new_location.folder == self.game.directory:
             return
         move_dialog = dialogs.MoveDialog(self.game, new_location.folder)
@@ -357,6 +363,7 @@ def on_runner_changed(self, widget):
         if self.runner_index and new_runner_index != self.runner_index:
             dlg = QuestionDialog(
                 {
+                    "parent": self,
                     "question":
                     _("Are you sure you want to change the runner for this game ? "
                       "This will reset the full configuration for this game and "
@@ -410,13 +417,13 @@ def on_cancel_clicked(self, _widget=None, _event=None):
 
     def is_valid(self):
         if not self.runner_name:
-            ErrorDialog(_("Runner not provided"))
+            ErrorDialog(_("Runner not provided"), parent=self)
             return False
         if not self.name_entry.get_text():
-            ErrorDialog(_("Please fill in the name"))
+            ErrorDialog(_("Please fill in the name"), parent=self)
             return False
         if self.runner_name == "steam" and not self.lutris_config.game_config.get("appid"):
-            ErrorDialog(_("Steam AppID not provided"))
+            ErrorDialog(_("Steam AppID not provided"), parent=self)
             return False
         invalid_fields = []
         runner_class = import_runner(self.runner_name)
@@ -434,7 +441,7 @@ def is_valid(self):
                     except Exception:
                         invalid_fields.append(option.get("label"))
         if invalid_fields:
-            ErrorDialog(_("The following fields have invalid values: ") + ", ".join(invalid_fields))
+            ErrorDialog(_("The following fields have invalid values: ") + ", ".join(invalid_fields), parent=self)
             return False
         return True
 
diff --git a/lutris/gui/dialogs/__init__.py b/lutris/gui/dialogs/__init__.py
--- a/lutris/gui/dialogs/__init__.py
+++ b/lutris/gui/dialogs/__init__.py
@@ -111,6 +111,8 @@ def __init__(self, dialog_settings):
         super().__init__(message_type=Gtk.MessageType.QUESTION, buttons=Gtk.ButtonsType.YES_NO)
         self.set_markup(dialog_settings["question"])
         self.set_title(dialog_settings["title"])
+        if "parent" in dialog_settings:
+            self.set_transient_for(dialog_settings["parent"])
         if "widgets" in dialog_settings:
             for widget in dialog_settings["widgets"]:
                 self.get_message_area().add(widget)


Test patch: