Files
cartridges/src/game.py
2023-04-20 02:05:20 +02:00

250 lines
7.6 KiB
Python

# game.py
#
# Copyright 2022-2023 kramo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
import json
import os
from time import time
from gi.repository import Adw, Gio, GLib, Gtk
from .game_cover import GameCover
@Gtk.Template(resource_path="/hu/kramo/Cartridges/gtk/game.ui")
class Game(Gtk.Box):
__gtype_name__ = "Game"
overlay = Gtk.Template.Child()
title = Gtk.Template.Child()
play_button = Gtk.Template.Child()
cover = Gtk.Template.Child()
spinner = Gtk.Template.Child()
cover_button = Gtk.Template.Child()
menu_button = Gtk.Template.Child()
play_revealer = Gtk.Template.Child()
title_revealer = Gtk.Template.Child()
game_options = Gtk.Template.Child()
hidden_game_options = Gtk.Template.Child()
loading = 0
filtered = False
added = None
executable = None
game_id = None
source = None
hidden = None
last_played = None
name = None
developer = None
removed = None
blacklisted = None
game_cover = None
def __init__(self, win, data, **kwargs):
super().__init__(**kwargs)
self.win = win
self.app = win.get_application()
self.update_values(data)
self.win.games[self.game_id] = self
self.set_play_label()
self.overlay.set_measure_overlay(self.play_revealer, True)
self.event_contoller_motion = Gtk.EventControllerMotion.new()
self.add_controller(self.event_contoller_motion)
self.event_contoller_motion.connect("enter", self.toggle_play, False)
self.event_contoller_motion.connect("leave", self.toggle_play, None, None)
self.cover_button.connect("clicked", self.main_button_clicked, False)
self.play_button.connect("clicked", self.main_button_clicked, True)
self.win.schema.connect("changed", self.schema_changed)
def update(self):
if self.get_parent():
self.get_parent().get_parent().remove(self)
if self.get_parent():
self.get_parent().set_child()
self.menu_button.set_menu_model(
self.hidden_game_options if self.hidden else self.game_options
)
self.title.set_label(self.name)
self.menu_button.get_popover().connect(
"notify::visible", self.toggle_play, None
)
self.menu_button.get_popover().connect(
"notify::visible", self.win.set_active_game, self
)
if self.game_id in self.win.game_covers:
self.game_cover = self.win.game_covers[self.game_id]
self.game_cover.add_picture(self.cover)
else:
self.game_cover = GameCover({self.cover}, self.get_cover_path())
self.win.game_covers[self.game_id] = self.game_cover
if (
self.win.stack.get_visible_child() == self.win.details_view
and self.win.active_game == self
):
self.win.show_details_view(self)
if not self.removed and not self.blacklisted:
if self.hidden:
self.win.hidden_library.append(self)
else:
self.win.library.append(self)
self.get_parent().set_focusable(False)
self.win.set_library_child()
def update_values(self, data):
for key, value in data.items():
setattr(self, key, value)
def save(self):
self.win.games_dir.mkdir(parents=True, exist_ok=True)
attrs = (
"added",
"executable",
"game_id",
"source",
"hidden",
"last_played",
"name",
"developer",
"removed",
"blacklisted",
)
json.dump(
{attr: getattr(self, attr) for attr in attrs if attr},
(self.win.games_dir / f"{self.game_id}.json").open("w"),
indent=4,
sort_keys=True,
)
self.update()
def create_toast(self, title, action=None):
toast = Adw.Toast.new(title.format(self.name))
toast.set_priority(Adw.ToastPriority.HIGH)
if action:
toast.set_button_label(_("Undo"))
toast.connect("button-clicked", self.win.on_undo_action, self, action)
if (self, action) in self.win.toasts.keys():
# Dismiss the toast if there already is one
self.win.toasts[(self, action)].dismiss()
self.win.toasts[(self, action)] = toast
self.win.toast_overlay.add_toast(toast)
def launch(self):
self.last_played = int(time())
self.save()
argv = (
("flatpak-spawn", "--host", *self.executable) # Flatpak
if os.getenv("FLATPAK_ID") == "hu.kramo.Cartridges"
else self.executable # Others
)
GLib.spawn_async(argv, flags=GLib.SpawnFlags.SEARCH_PATH)
if Gio.Settings.new("hu.kramo.Cartridges").get_boolean("exit-after-launch"):
self.app.quit()
# The variable is the title of the game
self.create_toast(_("{} launched"))
def toggle_hidden(self, toast=True):
self.hidden = not self.hidden
self.save()
if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action()
if toast:
self.create_toast(
# The variable is the title of the game
(_("{} hidden") if self.hidden else _("{} unhidden")).format(self.name),
"hide",
)
def remove_game(self):
# Add "removed=True" to the game properties so it can be deleted on next init
self.removed = True
self.save()
if self.win.stack.get_visible_child() == self.win.details_view:
self.win.on_go_back_action()
# The variable is the title of the game
self.create_toast(_("{} removed").format(self.name), "remove")
def set_loading(self, state):
self.loading += state
loading = self.loading > 0
self.cover.set_opacity(int(not loading))
self.spinner.set_spinning(loading)
def get_cover_path(self):
cover_path = self.win.covers_dir / f"{self.game_id}.gif"
if cover_path.is_file():
return cover_path
cover_path = self.win.covers_dir / f"{self.game_id}.tiff"
if cover_path.is_file():
return cover_path
return None
def toggle_play(self, _widget, _prop1, _prop2, state=True):
if not self.menu_button.get_active():
self.title_revealer.set_reveal_child(state)
self.play_revealer.set_reveal_child(not state)
def main_button_clicked(self, _widget, button):
if self.win.schema.get_boolean("cover-launches-game") ^ button:
self.launch()
else:
self.win.show_details_view(self)
def set_play_label(self):
self.play_button.set_label(
_("Details")
if self.win.schema.get_boolean("cover-launches-game")
else _("Play")
)
def schema_changed(self, _settings, key):
if key == "cover-launches-game":
self.set_play_label()