mirror of
https://github.com/lutris/lutris
synced 2024-09-16 06:20:38 +00:00
Clean up dialog 'response' handling - ensure dialogs can be closed.
This fixes the bug where the Wine app-usage dialog can't be closed with its 'OK' button. This commit moves on_response() up to the 'Dialog' base class and override it in a few places where this is required. 'response' is how dialogs close, 'delete-event' doesn't even fire for them, so I've removed this event handler from the dialogs.
This commit is contained in:
parent
b9970ff33d
commit
37fc312800
|
@ -18,22 +18,47 @@ from lutris.util.strings import gtk_safe
|
|||
|
||||
|
||||
class Dialog(Gtk.Dialog):
|
||||
"""A base class for dialogs that provides handling for the response signal;
|
||||
you can override its on_response() methods, but that method will record
|
||||
the response for you via 'response_type' or 'confirmed' and destory this
|
||||
dialog if it isn't NONE."""
|
||||
|
||||
def __init__(self, title=None, parent=None, flags=0, buttons=None, **kwargs):
|
||||
def __init__(self, title: str = None, parent: Gtk.Widget = None,
|
||||
flags: Gtk.DialogFlags = 0, buttons: Gtk.ButtonsType = None,
|
||||
**kwargs):
|
||||
super().__init__(title, parent, flags, buttons, **kwargs)
|
||||
self.connect("delete-event", self.on_destroy)
|
||||
self._response_type = Gtk.ResponseType.NONE
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
def on_destroy(self, _widget, _data=None):
|
||||
self.destroy()
|
||||
@property
|
||||
def response_type(self) -> Gtk.ResponseType:
|
||||
"""The response type of the response that occurred; initially this is NONE.
|
||||
Use the GTK response() method to artificially generate a response, rather than
|
||||
setting this."""
|
||||
return self._response_type
|
||||
|
||||
def add_styled_button(self, button_text, response_id, css_class):
|
||||
@property
|
||||
def confirmed(self) -> bool:
|
||||
"""True if 'response_type' is OK or YES."""
|
||||
return self.response_type in (Gtk.ResponseType.OK, Gtk.ResponseType.YES)
|
||||
|
||||
def on_response(self, _dialog, response: Gtk.ResponseType) -> None:
|
||||
"""Handles the dialog response; you can override this but by default
|
||||
this records the response for 'response_type' and destroys the dialog."""
|
||||
self._response_type = response
|
||||
if response != Gtk.ResponseType.NONE:
|
||||
self.destroy()
|
||||
|
||||
def add_styled_button(self, button_text: str, response_id: Gtk.ResponseType,
|
||||
css_class: str):
|
||||
button = self.add_button(button_text, response_id)
|
||||
if css_class:
|
||||
style_context = button.get_style_context()
|
||||
style_context.add_class(css_class)
|
||||
return button
|
||||
|
||||
def add_default_button(self, button_text, response_id, css_class="suggested-action"):
|
||||
def add_default_button(self, button_text: str, response_id: Gtk.ResponseType,
|
||||
css_class: str = "suggested-action"):
|
||||
"""Adds a button to the dialog with a particular response id, but
|
||||
also makes it the default and styles it as the suggested action."""
|
||||
button = self.add_styled_button(button_text, response_id, css_class)
|
||||
|
@ -44,7 +69,9 @@ class Dialog(Gtk.Dialog):
|
|||
class ModalDialog(Dialog):
|
||||
"""A base class of modal dialogs, which sets the flag for you."""
|
||||
|
||||
def __init__(self, title=None, parent=None, flags=0, buttons=None, **kwargs):
|
||||
def __init__(self, title: str = None, parent: Gtk.Widget = None,
|
||||
flags: Gtk.DialogFlags = 0, buttons: Gtk.ButtonsType = None,
|
||||
**kwargs):
|
||||
super().__init__(title, parent, flags | Gtk.DialogFlags.MODAL, buttons, **kwargs)
|
||||
self.set_destroy_with_parent(True)
|
||||
|
||||
|
@ -55,7 +82,9 @@ class ModelessDialog(Dialog):
|
|||
its own window group, so it treats its own modal dialogs separately, and it resets
|
||||
its transient-for after being created."""
|
||||
|
||||
def __init__(self, title=None, parent=None, flags=0, buttons=None, **kwargs):
|
||||
def __init__(self, title: str = None, parent: Gtk.Widget = None,
|
||||
flags: Gtk.DialogFlags = 0, buttons: Gtk.ButtonsType = None,
|
||||
**kwargs):
|
||||
super().__init__(title, parent, flags, buttons, **kwargs)
|
||||
# These are not stuck above the 'main' window, but can be
|
||||
# re-ordered freely.
|
||||
|
@ -80,7 +109,7 @@ class SavableModelessDialog(ModelessDialog):
|
|||
"""This is a modeless dialog that has a Cancel and a Save button in the header-bar,
|
||||
with a ctrl-S keyboard shortcut to save."""
|
||||
|
||||
def __init__(self, title, parent=None, **kwargs):
|
||||
def __init__(self, title: str, parent: Gtk.Widget = None, **kwargs):
|
||||
super().__init__(title, parent=parent, use_header_bar=True, **kwargs)
|
||||
|
||||
self.cancel_button = self.add_button(_("Cancel"), Gtk.ResponseType.CANCEL)
|
||||
|
@ -95,12 +124,6 @@ class SavableModelessDialog(ModelessDialog):
|
|||
key, mod = Gtk.accelerator_parse("<Primary>s")
|
||||
self.save_button.add_accelerator("clicked", self.accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
|
||||
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
def on_response(self, _widget, response):
|
||||
if response != Gtk.ResponseType.NONE:
|
||||
self.destroy()
|
||||
|
||||
def on_save(self, _button):
|
||||
pass
|
||||
|
||||
|
@ -320,7 +343,6 @@ class InstallOrPlayDialog(ModalDialog):
|
|||
|
||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||
self.add_default_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.set_size_request(320, 120)
|
||||
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 6)
|
||||
|
@ -341,10 +363,9 @@ class InstallOrPlayDialog(ModalDialog):
|
|||
self.action = action
|
||||
|
||||
def on_response(self, _widget, response):
|
||||
logger.debug("Dialog response %s", response)
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
self.action = None
|
||||
self.destroy()
|
||||
super().on_response(_widget, response)
|
||||
|
||||
|
||||
class LaunchConfigSelectDialog(ModalDialog):
|
||||
|
@ -352,11 +373,9 @@ class LaunchConfigSelectDialog(ModalDialog):
|
|||
super().__init__(title=title, parent=parent, border_width=10)
|
||||
self.config_index = 0
|
||||
self.dont_show_again = False
|
||||
self.confirmed = False
|
||||
|
||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||
self.add_default_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.set_size_request(320, 120)
|
||||
vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 6)
|
||||
|
@ -384,10 +403,6 @@ class LaunchConfigSelectDialog(ModalDialog):
|
|||
def on_dont_show_checkbutton_toggled(self, _button):
|
||||
self.dont_show_again = _button.get_active()
|
||||
|
||||
def on_response(self, _widget, response):
|
||||
self.confirmed = response == Gtk.ResponseType.OK
|
||||
self.destroy()
|
||||
|
||||
|
||||
class ClientLoginDialog(GtkBuilderDialog):
|
||||
glade_file = "dialog-lutris-login.ui"
|
||||
|
@ -445,7 +460,6 @@ class InstallerSourceDialog(ModelessDialog):
|
|||
|
||||
ok_button = self.add_default_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
ok_button.set_border_width(10)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.scrolled_window = Gtk.ScrolledWindow()
|
||||
self.scrolled_window.set_hexpand(True)
|
||||
|
@ -462,9 +476,6 @@ class InstallerSourceDialog(ModelessDialog):
|
|||
|
||||
self.show_all()
|
||||
|
||||
def on_response(self, *args):
|
||||
self.destroy()
|
||||
|
||||
|
||||
class WarningMessageDialog(Gtk.MessageDialog):
|
||||
def __init__(self, message, secondary_message="", parent=None):
|
||||
|
@ -526,7 +537,6 @@ class HumbleBundleCookiesDialog(ModalDialog):
|
|||
self.cookies_content = None
|
||||
self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
|
||||
self.add_default_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.set_size_request(640, 512)
|
||||
|
||||
|
@ -561,10 +571,11 @@ class HumbleBundleCookiesDialog(ModalDialog):
|
|||
self.show_all()
|
||||
self.run()
|
||||
|
||||
def on_response(self, _widget, response):
|
||||
def on_response(self, dialog, response):
|
||||
if response == Gtk.ResponseType.CANCEL:
|
||||
self.cookies_content = None
|
||||
else:
|
||||
buffer = self.textview.get_buffer()
|
||||
self.cookies_content = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
|
||||
self.destroy()
|
||||
|
||||
super().on_response(dialog, response)
|
||||
|
|
|
@ -17,7 +17,6 @@ class DownloadDialog(ModalDialog):
|
|||
|
||||
self.dialog_progress_box.connect("complete", self.download_complete)
|
||||
self.dialog_progress_box.connect("cancel", self.download_cancelled)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.get_content_area().add(self.dialog_progress_box)
|
||||
self.show_all()
|
||||
|
@ -29,13 +28,11 @@ class DownloadDialog(ModalDialog):
|
|||
|
||||
def download_complete(self, _widget, _data):
|
||||
self.response(Gtk.ResponseType.OK)
|
||||
self.destroy()
|
||||
|
||||
def download_cancelled(self, _widget):
|
||||
self.response(Gtk.ResponseType.CANCEL)
|
||||
self.destroy()
|
||||
|
||||
def on_response(self, _dialog, response):
|
||||
if response == Gtk.ResponseType.DELETE_EVENT:
|
||||
def on_response(self, dialog, response):
|
||||
if response in (Gtk.ResponseType.DELETE_EVENT, Gtk.ResponseType.CANCEL):
|
||||
self.dialog_progress_box.downloader.cancel()
|
||||
self.destroy()
|
||||
super().on_response(dialog, response)
|
||||
|
|
|
@ -49,17 +49,17 @@ class ImportGameDialog(ModelessDialog):
|
|||
self.close_button = self.add_button(Gtk.STOCK_STOP, Gtk.ResponseType.CANCEL)
|
||||
key, mod = Gtk.accelerator_parse("Escape")
|
||||
self.close_button.add_accelerator("clicked", self.accelerators, key, mod, Gtk.AccelFlags.VISIBLE)
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
self.show_all()
|
||||
self.search_call = AsyncCall(self.search_checksums, self.search_result_finished)
|
||||
|
||||
def on_response(self, _widget, response):
|
||||
def on_response(self, dialog, response: Gtk.ResponseType) -> None:
|
||||
if response in (Gtk.ResponseType.CLOSE, Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||
if self.search_call:
|
||||
self.search_call.stop_request.set()
|
||||
else:
|
||||
self.destroy()
|
||||
return # don't actually close the dialog
|
||||
|
||||
super().on_response(dialog, response)
|
||||
|
||||
def get_file_labels_listbox(self, files):
|
||||
listbox = Gtk.ListBox(vexpand=True)
|
||||
|
|
|
@ -11,7 +11,6 @@ from lutris.util.linux import gather_system_info
|
|||
|
||||
|
||||
class IssueReportWindow(BaseApplicationWindow):
|
||||
|
||||
"""Window for collecting and sending issue reports"""
|
||||
|
||||
def __init__(self, application):
|
||||
|
@ -46,7 +45,7 @@ class IssueReportWindow(BaseApplicationWindow):
|
|||
action_buttons_alignment.add(self.action_buttons)
|
||||
self.vbox.pack_start(action_buttons_alignment, False, True, 0)
|
||||
|
||||
cancel_button = self.get_action_button(_("C_ancel"), handler=self.on_destroy)
|
||||
cancel_button = self.get_action_button(_("C_ancel"), handler=lambda *x: self.destroy())
|
||||
self.action_buttons.add(cancel_button)
|
||||
|
||||
save_button = self.get_action_button(_("_Save"), handler=self.on_save)
|
||||
|
|
|
@ -125,7 +125,6 @@ class RunnerInstallDialog(ModelessDialog):
|
|||
label = Gtk.Label.new(_("%s version management") % self.runner_info["name"])
|
||||
self.vbox.add(label)
|
||||
self.installing = {}
|
||||
self.connect("response", self.on_destroy)
|
||||
|
||||
scrolled_listbox = Gtk.ScrolledWindow()
|
||||
self.listbox = Gtk.ListBox()
|
||||
|
@ -410,9 +409,7 @@ class RunnerInstallDialog(ModelessDialog):
|
|||
from lutris.util.wine.wine import get_installed_wine_versions
|
||||
get_installed_wine_versions.cache_clear()
|
||||
|
||||
def on_destroy(self, _dialog, _data=None):
|
||||
"""Override delete handler to prevent closing while downloads are active"""
|
||||
def on_response(self, dialog, response: Gtk.ResponseType) -> None:
|
||||
if self.installing:
|
||||
return True
|
||||
self.destroy()
|
||||
return True
|
||||
return
|
||||
super().on_response(dialog, response)
|
||||
|
|
|
@ -121,7 +121,6 @@ class UninstallMultipleGamesDialog(Gtk.Dialog):
|
|||
|
||||
self.update_all_checkboxes()
|
||||
self.show_all()
|
||||
self.connect("response", self.on_response)
|
||||
|
||||
def update_all_checkboxes(self) -> None:
|
||||
"""Sets the state of the checkboxes at the button that are used to control all
|
||||
|
|
|
@ -200,9 +200,6 @@ class InstallerWindow(ModelessDialog,
|
|||
def on_navigate_home(self, _accel_group, _window, _keyval, _modifier):
|
||||
self.stack.navigate_home()
|
||||
|
||||
def on_destroy(self, _widget, _data=None):
|
||||
self.on_cancel_clicked()
|
||||
|
||||
@watch_errors()
|
||||
def on_cancel_clicked(self, _button=None):
|
||||
"""Ask a confirmation before cancelling the installation, if it has started."""
|
||||
|
|
|
@ -3,7 +3,6 @@ from gi.repository import Gtk
|
|||
|
||||
|
||||
class BaseApplicationWindow(Gtk.ApplicationWindow):
|
||||
|
||||
"""Window used to guide the user through a issue reporting process"""
|
||||
|
||||
def __init__(self, application):
|
||||
|
@ -12,7 +11,6 @@ class BaseApplicationWindow(Gtk.ApplicationWindow):
|
|||
self.set_show_menubar(False)
|
||||
|
||||
self.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.connect("delete-event", self.on_destroy)
|
||||
|
||||
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12, visible=True)
|
||||
self.vbox.set_margin_top(18)
|
||||
|
@ -32,10 +30,6 @@ class BaseApplicationWindow(Gtk.ApplicationWindow):
|
|||
button.set_tooltip_text(tooltip)
|
||||
return button
|
||||
|
||||
def on_destroy(self, _widget=None, _data=None):
|
||||
"""Destroy callback"""
|
||||
self.destroy()
|
||||
|
||||
def present(self): # pylint: disable=arguments-differ
|
||||
"""The base implementation doesn't always work, this one does."""
|
||||
self.set_keep_above(True)
|
||||
|
|
|
@ -252,7 +252,7 @@ class ScriptInterpreter(GObject.Object, CommandsMixin):
|
|||
"""Install required runners for a game"""
|
||||
if self.runners_to_install:
|
||||
self.install_runner(self.runners_to_install.pop(0), ui_delegate)
|
||||
return
|
||||
|
||||
self.emit("runners-installed")
|
||||
|
||||
def install_runner(self, runner, ui_delegate):
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<property name="default-width">800</property>
|
||||
<property name="default-height">600</property>
|
||||
<property name="type-hint">dialog</property>
|
||||
<signal name="response" handler="on_response" swapped="no"/>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
|
|
Loading…
Reference in a new issue