From e0e741c1b837d1c347b2da0c263782d11b9c81ac Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Fri, 10 Nov 2023 06:08:25 -0500 Subject: [PATCH] Break out a base class for game actions, and use it for service games. We want a real service games menu, but this blocks access to inappropriate commands at least. --- lutris/game_actions.py | 90 +++++++++++++++++++++++++++------- lutris/gui/views/base.py | 4 +- lutris/gui/widgets/game_bar.py | 7 ++- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/lutris/game_actions.py b/lutris/game_actions.py index d8f02b6b1..b76355f2b 100644 --- a/lutris/game_actions.py +++ b/lutris/game_actions.py @@ -28,29 +28,91 @@ from lutris.util.system import path_exists from lutris.util.wine.shader_cache import update_shader_cache -class GameActions: - """Regroup a list of callbacks for a game""" +def get_game_actions(game, window, application=None): + if game.is_db_stored: + return GameActions(game, window, application) + else: + return BaseGameActions(game, window, application) + +class BaseGameActions: def __init__(self, game, window, application=None): self.application = application or Gio.Application.get_default() self.window = window # also used as a LaunchUIDelegate self.game = game + def get_game_actions(self): + """Return a list of game actions and their callbacks""" + return [] + + def get_displayed_entries(self): + """Return a dictionary of actions that should be shown for a game""" + return {} + + @property + def is_game_launchable(self): + return False + + def on_game_launch(self, *_args): + """Launch a game""" + pass + + @property + def is_game_running(self): + return False + + def on_game_stop(self, *_args): + """Stops the game""" + pass + + @property + def is_installable(self): + return False + + def on_install_clicked(self, *_args): + """Install a game""" + pass + + def on_locate_installed_game(self, _button, game): + """Show the user a dialog to import an existing install to a DRM free service + + Params: + game (Game): Game instance without a database ID, populated with a fields the service can provides + """ + AddGameDialog(self.window, game=game) + + @property + def is_game_removable(self): + return False + + def on_remove_game(self, *_args): + """Callback that present the uninstall dialog to the user""" + pass + + +class GameActions(BaseGameActions): + """Regroup a list of callbacks for a game""" + + def __init__(self, game, window, application=None): + super().__init__(game, window, application) + + @property + def is_game_launchable(self): + return self.game and self.game.is_installed and not self.is_game_running + @property def is_game_running(self): return self.game and self.game.is_db_stored and bool(self.application.get_running_game_by_id(self.game.id)) + @property + def is_installable(self): + return not self.game.is_installed + @property def is_game_removable(self): return self.game and (self.game.is_installed or self.game.is_db_stored) - def on_game_state_changed(self, game): - """Handler called when the game has changed state""" - if self.game and game.id == self.game.get_safe_id(): - self.game = game - def get_game_actions(self): - """Return a list of game actions and their callbacks""" return [ ("play", _("Play"), self.on_game_launch), ("stop", _("Stop"), self.on_game_stop), @@ -94,8 +156,8 @@ class GameActions: return { "add": not self.game.is_installed, "duplicate": self.game.is_installed, - "install": not self.game.is_installed, - "play": self.game.is_installed and not self.is_game_running, + "install": self.is_installable, + "play": self.is_game_launchable, "update": self.game.is_updatable, "update-shader-cache": self.game.is_cache_managed, "install_dlcs": self.game.is_updatable, @@ -190,14 +252,6 @@ class GameActions: def on_update_shader_cache(self, _widget): update_shader_cache(self.game) - def on_locate_installed_game(self, _button, game): - """Show the user a dialog to import an existing install to a DRM free service - - Params: - game (Game): Game instance without a database ID, populated with a fields the service can provides - """ - AddGameDialog(self.window, game=game) - def on_add_manually(self, _widget, *_args): """Callback that presents the Add game dialog""" return AddGameDialog(self.window, game=self.game, runner=self.game.runner_name) diff --git a/lutris/gui/views/base.py b/lutris/gui/views/base.py index 44c5073c9..aa5b7646b 100644 --- a/lutris/gui/views/base.py +++ b/lutris/gui/views/base.py @@ -3,7 +3,7 @@ from gi.repository import Gdk, Gio, GObject, Gtk from lutris.database.games import get_game_for_service from lutris.database.services import ServiceGameCollection from lutris.game import Game -from lutris.game_actions import GameActions +from lutris.game_actions import GameActions, get_game_actions from lutris.gui.views import COL_ID from lutris.gui.widgets.contextual_menu import ContextualMenu @@ -56,7 +56,7 @@ class GameView: else: return None - return GameActions(game, window=self.get_toplevel()) + return get_game_actions(game, window=self.get_toplevel()) def get_game_by_id(self, game_id): application = Gio.Application.get_default() diff --git a/lutris/gui/widgets/game_bar.py b/lutris/gui/widgets/game_bar.py index 3f83d128d..5ff927752 100644 --- a/lutris/gui/widgets/game_bar.py +++ b/lutris/gui/widgets/game_bar.py @@ -7,7 +7,7 @@ from lutris import runners, services from lutris.database.games import get_game_for_service from lutris.exceptions import watch_errors from lutris.game import Game -from lutris.game_actions import GameActions +from lutris.game_actions import GameActions, get_game_actions from lutris.gui.dialogs import ErrorDialog from lutris.gui.widgets.contextual_menu import update_action_widget_visibility from lutris.util.strings import gtk_safe @@ -74,7 +74,7 @@ class GameBar(Gtk.Box): def update_view(self): """Populate the view with widgets""" - game_actions = GameActions(self.game, window=self.window, application=self.application) + game_actions = get_game_actions(self.game, window=self.window, application=self.application) game_label = self.get_game_name_label() game_label.set_halign(Gtk.Align.START) @@ -212,15 +212,18 @@ class GameBar(Gtk.Box): if self.game.state == self.game.STATE_STOPPED: button.set_label(_("Play")) button.connect("clicked", game_actions.on_game_launch) + button.set_sensitive(game_actions.is_game_launchable) elif self.game.state == self.game.STATE_LAUNCHING: button.set_label(_("Launching")) button.set_sensitive(False) else: button.set_label(_("Stop")) button.connect("clicked", game_actions.on_game_stop) + button.set_sensitive(game_actions.is_game_running) else: button.set_label(_("Install")) button.connect("clicked", game_actions.on_install_clicked) + button.set_sensitive(game_actions.is_installable) if self.service: if self.service.local: # Local services don't show an install dialog, they can be launched directly