Merge branch 'main' into pr/257
This commit is contained in:
121
cartridges/application_delegate.py
Normal file
121
cartridges/application_delegate.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# application_delegate.py
|
||||
#
|
||||
# Copyright 2024 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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""A set of methods that manage your app’s life cycle and its interaction
|
||||
with common system services."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from AppKit import NSApp, NSApplication, NSMenu, NSMenuItem # type: ignore
|
||||
from Foundation import NSObject # type: ignore
|
||||
from gi.repository import Gio # type: ignore
|
||||
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
class ApplicationDelegate(NSObject): # type: ignore
|
||||
"""A set of methods that manage your app’s life cycle and its interaction
|
||||
with common system services."""
|
||||
|
||||
def applicationDidFinishLaunching_(self, *_args: Any) -> None:
|
||||
main_menu = NSApp.mainMenu()
|
||||
|
||||
add_game_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Add Game", "add:", "n"
|
||||
)
|
||||
|
||||
import_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Import", "import:", "i"
|
||||
)
|
||||
|
||||
file_menu = NSMenu.alloc().init()
|
||||
file_menu.addItem_(add_game_menu_item)
|
||||
file_menu.addItem_(import_menu_item)
|
||||
|
||||
file_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"File", None, ""
|
||||
)
|
||||
file_menu_item.setSubmenu_(file_menu)
|
||||
main_menu.addItem_(file_menu_item)
|
||||
|
||||
show_hidden_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Show Hidden", "hidden:", "h"
|
||||
)
|
||||
|
||||
windows_menu = NSMenu.alloc().init()
|
||||
|
||||
view_menu = NSMenu.alloc().init()
|
||||
view_menu.addItem_(show_hidden_menu_item)
|
||||
|
||||
view_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"View", None, ""
|
||||
)
|
||||
view_menu_item.setSubmenu_(view_menu)
|
||||
main_menu.addItem_(view_menu_item)
|
||||
|
||||
windows_menu = NSMenu.alloc().init()
|
||||
|
||||
windows_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Window", None, ""
|
||||
)
|
||||
windows_menu_item.setSubmenu_(windows_menu)
|
||||
main_menu.addItem_(windows_menu_item)
|
||||
|
||||
NSApp.setWindowsMenu_(windows_menu)
|
||||
|
||||
keyboard_shortcuts_menu_item = (
|
||||
NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Keyboard Shortcuts", "shortcuts:", "?"
|
||||
)
|
||||
)
|
||||
|
||||
help_menu = NSMenu.alloc().init()
|
||||
help_menu.addItem_(keyboard_shortcuts_menu_item)
|
||||
|
||||
help_menu_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||
"Help", None, ""
|
||||
)
|
||||
help_menu_item.setSubmenu_(help_menu)
|
||||
main_menu.addItem_(help_menu_item)
|
||||
|
||||
NSApp.setHelpMenu_(help_menu)
|
||||
|
||||
def add_(self, *_args: Any) -> None:
|
||||
if (not shared.win) or (not (app := shared.win.get_application())):
|
||||
return
|
||||
|
||||
app.lookup_action("add_game").activate()
|
||||
|
||||
def import_(self, *_args: Any) -> None:
|
||||
if (not shared.win) or (not (app := shared.win.get_application())):
|
||||
return
|
||||
|
||||
app.lookup_action("import").activate()
|
||||
|
||||
def hidden_(self, *_args: Any) -> None:
|
||||
if not shared.win:
|
||||
return
|
||||
|
||||
shared.win.lookup_action("show_hidden").activate()
|
||||
|
||||
def shortcuts_(self, *_args: Any) -> None:
|
||||
if (not shared.win) or (not (overlay := shared.win.get_help_overlay())):
|
||||
return
|
||||
|
||||
overlay.present()
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# cartridges.in
|
||||
#
|
||||
# Copyright 2022-2023 kramo
|
||||
# Copyright 2022-2024 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
|
||||
@@ -24,6 +24,8 @@ import locale
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from platform import system
|
||||
|
||||
VERSION = "@VERSION@"
|
||||
|
||||
@@ -36,7 +38,7 @@ else:
|
||||
sys.path.insert(1, PKGDATADIR)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
if os.name != "nt":
|
||||
if system() == "Linux":
|
||||
locale.bindtextdomain("cartridges", LOCALEDIR)
|
||||
locale.textdomain("cartridges")
|
||||
gettext.install("cartridges", LOCALEDIR)
|
||||
@@ -44,9 +46,15 @@ else:
|
||||
gettext.install("cartridges")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from gi.repository import Gio
|
||||
from gi.repository import Gio, GLib
|
||||
|
||||
resource = Gio.Resource.load(os.path.join(PKGDATADIR, "cartridges.gresource"))
|
||||
try:
|
||||
# For a macOS application bundle
|
||||
resource = Gio.Resource.load(
|
||||
str(Path(__file__).parent / "Resources" / "cartridges.gresource")
|
||||
)
|
||||
except GLib.GError:
|
||||
resource = Gio.Resource.load(os.path.join(PKGDATADIR, "cartridges.gresource"))
|
||||
resource._register() # pylint: disable=protected-access
|
||||
|
||||
from cartridges import main
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
# pyright: reportAssignmentType=none
|
||||
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from sys import platform
|
||||
from time import time
|
||||
from typing import Any, Optional
|
||||
|
||||
@@ -40,28 +42,35 @@ from cartridges.utils.save_cover import convert_cover, save_cover
|
||||
class DetailsDialog(Adw.Dialog):
|
||||
__gtype_name__ = "DetailsDialog"
|
||||
|
||||
cover_overlay = Gtk.Template.Child()
|
||||
cover = Gtk.Template.Child()
|
||||
cover_button_edit = Gtk.Template.Child()
|
||||
cover_button_delete_revealer = Gtk.Template.Child()
|
||||
cover_button_delete = Gtk.Template.Child()
|
||||
spinner = Gtk.Template.Child()
|
||||
cover_overlay: Gtk.Overlay = Gtk.Template.Child()
|
||||
cover: Gtk.Picture = Gtk.Template.Child()
|
||||
cover_button_edit: Gtk.Button = Gtk.Template.Child()
|
||||
cover_button_delete_revealer: Gtk.Revealer = Gtk.Template.Child()
|
||||
cover_button_delete: Gtk.Button = Gtk.Template.Child()
|
||||
spinner: Gtk.Spinner = Gtk.Template.Child()
|
||||
|
||||
name = Gtk.Template.Child()
|
||||
developer = Gtk.Template.Child()
|
||||
executable = Gtk.Template.Child()
|
||||
name: Adw.EntryRow = Gtk.Template.Child()
|
||||
developer: Adw.EntryRow = Gtk.Template.Child()
|
||||
executable: Adw.EntryRow = Gtk.Template.Child()
|
||||
|
||||
exec_info_label = Gtk.Template.Child()
|
||||
exec_info_popover = Gtk.Template.Child()
|
||||
file_chooser_button = Gtk.Template.Child()
|
||||
exec_info_label: Gtk.Label = Gtk.Template.Child()
|
||||
exec_info_popover: Gtk.Popover = Gtk.Template.Child()
|
||||
file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
apply_button = Gtk.Template.Child()
|
||||
apply_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
cover_changed: bool = False
|
||||
|
||||
is_open: bool = False
|
||||
|
||||
def __init__(self, game: Optional[Game] = None, **kwargs: Any):
|
||||
super().__init__(**kwargs)
|
||||
self.game: Game = game
|
||||
|
||||
# Make it so only one dialog can be open at a time
|
||||
self.__class__.is_open = True
|
||||
self.connect("closed", lambda *_: self.set_is_open(False))
|
||||
|
||||
self.game: Optional[Game] = game
|
||||
self.game_cover: GameCover = GameCover({self.cover})
|
||||
|
||||
if self.game:
|
||||
@@ -106,7 +115,7 @@ class DetailsDialog(Adw.Dialog):
|
||||
# As in software
|
||||
exe_name = _("program")
|
||||
|
||||
if os.name == "nt":
|
||||
if platform == "win32":
|
||||
exe_name += ".exe"
|
||||
# Translate this string as you would translate "path to {}"
|
||||
exe_path = _("C:\\path\\to\\{}").format(exe_name)
|
||||
@@ -118,7 +127,7 @@ class DetailsDialog(Adw.Dialog):
|
||||
exe_path = _("/path/to/{}").format(exe_name)
|
||||
# Translate this string as you would translate "path to {}"
|
||||
file_path = _("/path/to/{}").format(file_name)
|
||||
command = "xdg-open"
|
||||
command = "open" if platform == "darwin" else "xdg-open"
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
exec_info_text = _(
|
||||
@@ -325,3 +334,6 @@ class DetailsDialog(Adw.Dialog):
|
||||
|
||||
def choose_cover(self, *_args: Any) -> None:
|
||||
self.image_file_dialog.open(self.get_root(), None, self.set_cover)
|
||||
|
||||
def set_is_open(self, is_open: bool) -> None:
|
||||
self.__class__.is_open = is_open
|
||||
|
||||
@@ -355,7 +355,7 @@ class HeroicSource(URLExecutableSource):
|
||||
name = _("Heroic")
|
||||
iterable_class = HeroicSourceIterable
|
||||
url_format = "heroic://launch/{runner}/{app_name}"
|
||||
available_on = {"linux", "win32"}
|
||||
available_on = {"linux", "win32", "darwin"}
|
||||
|
||||
locations: HeroicLocations
|
||||
|
||||
@@ -377,6 +377,7 @@ class HeroicSource(URLExecutableSource):
|
||||
/ "config"
|
||||
/ "heroic",
|
||||
shared.appdata_dir / "heroic",
|
||||
shared.app_support_dir / "heroic",
|
||||
),
|
||||
paths={
|
||||
"config.json": LocationSubPath("config.json"),
|
||||
|
||||
@@ -81,7 +81,7 @@ class ItchSource(URLExecutableSource):
|
||||
name = _("itch")
|
||||
iterable_class = ItchSourceIterable
|
||||
url_format = "itch://caves/{cave_id}/launch"
|
||||
available_on = {"linux", "win32"}
|
||||
available_on = {"linux", "win32", "darwin"}
|
||||
|
||||
locations: ItchLocations
|
||||
|
||||
@@ -95,6 +95,7 @@ class ItchSource(URLExecutableSource):
|
||||
shared.config_dir / "itch",
|
||||
shared.host_config_dir / "itch",
|
||||
shared.appdata_dir / "itch",
|
||||
shared.app_support_dir / "itch",
|
||||
),
|
||||
paths={
|
||||
"butler.db": LocationSubPath("db/butler.db"),
|
||||
|
||||
@@ -125,6 +125,8 @@ class URLExecutableSource(ExecutableFormatSource):
|
||||
return "start " + self.url_format
|
||||
case "linux":
|
||||
return "xdg-open " + self.url_format
|
||||
case "darwin":
|
||||
return "open " + self.url_format
|
||||
case other:
|
||||
raise NotImplementedError(
|
||||
f"No URL handler command available for {other}"
|
||||
|
||||
@@ -112,7 +112,7 @@ class SteamLocations(NamedTuple):
|
||||
class SteamSource(URLExecutableSource):
|
||||
source_id = "steam"
|
||||
name = _("Steam")
|
||||
available_on = {"linux", "win32"}
|
||||
available_on = {"linux", "win32", "darwin"}
|
||||
iterable_class = SteamSourceIterable
|
||||
url_format = "steam://rungameid/{game_id}"
|
||||
|
||||
@@ -128,6 +128,7 @@ class SteamSource(URLExecutableSource):
|
||||
shared.data_dir / "Steam",
|
||||
shared.flatpak_dir / "com.valvesoftware.Steam" / "data" / "Steam",
|
||||
shared.programfiles32_dir / "Steam",
|
||||
shared.app_support_dir / "Steam",
|
||||
),
|
||||
paths={
|
||||
"libraryfolders.vdf": LocationSubPath(
|
||||
|
||||
@@ -24,6 +24,7 @@ import shlex
|
||||
import sys
|
||||
from time import time
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
import gi
|
||||
|
||||
@@ -40,7 +41,7 @@ from cartridges.importer.bottles_source import BottlesSource
|
||||
from cartridges.importer.desktop_source import DesktopSource
|
||||
from cartridges.importer.flatpak_source import FlatpakSource
|
||||
from cartridges.importer.heroic_source import HeroicSource
|
||||
from cartridges.importer.importer import Importer
|
||||
from cartridges.importer.importer import Importer # yo dawg
|
||||
from cartridges.importer.itch_source import ItchSource
|
||||
from cartridges.importer.legendary_source import LegendarySource
|
||||
from cartridges.importer.lutris_source import LutrisSource
|
||||
@@ -57,6 +58,12 @@ from cartridges.store.store import Store
|
||||
from cartridges.utils.run_executable import run_executable
|
||||
from cartridges.window import CartridgesWindow
|
||||
|
||||
if sys.platform == "darwin":
|
||||
from AppKit import NSApp # type: ignore
|
||||
from PyObjCTools import AppHelper
|
||||
|
||||
from cartridges.application_delegate import ApplicationDelegate
|
||||
|
||||
|
||||
class CartridgesApplication(Adw.Application):
|
||||
state = shared.AppState.DEFAULT
|
||||
@@ -87,10 +94,28 @@ class CartridgesApplication(Adw.Application):
|
||||
|
||||
self.add_main_option_entries((search, launch))
|
||||
|
||||
if sys.platform == "darwin":
|
||||
if settings := Gtk.Settings.get_default():
|
||||
settings.props.gtk_decoration_layout = "close,minimize,maximize:"
|
||||
|
||||
def setup_app_delegate() -> None:
|
||||
NSApp.setDelegate_(ApplicationDelegate.alloc().init()) # type: ignore
|
||||
AppHelper.runEventLoop() # type: ignore
|
||||
|
||||
GLib.Thread.new(None, setup_app_delegate)
|
||||
|
||||
def do_activate(self) -> None: # pylint: disable=arguments-differ
|
||||
"""Called on app creation"""
|
||||
|
||||
setup_logging()
|
||||
if os.getenv("XDG_CURRENT_DESKOP") == "COSMIC":
|
||||
Gio.AppInfo.launch_default_for_uri("https://stopthemingmy.app")
|
||||
self.quit()
|
||||
|
||||
try:
|
||||
setup_logging()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
log_system_info()
|
||||
|
||||
# Set fallback icon-name
|
||||
@@ -280,7 +305,7 @@ class CartridgesApplication(Adw.Application):
|
||||
_parameter: Any = None,
|
||||
page_name: Optional[str] = None,
|
||||
expander_row: Optional[str] = None,
|
||||
) -> CartridgesWindow:
|
||||
) -> Optional[CartridgesPreferences]:
|
||||
if CartridgesPreferences.is_open:
|
||||
return
|
||||
|
||||
@@ -303,6 +328,9 @@ class CartridgesApplication(Adw.Application):
|
||||
DetailsDialog(shared.win.active_game).present(shared.win)
|
||||
|
||||
def on_add_game_action(self, *_args: Any) -> None:
|
||||
if DetailsDialog.is_open:
|
||||
return
|
||||
|
||||
DetailsDialog().present(shared.win)
|
||||
|
||||
def on_import_action(self, *_args: Any) -> None:
|
||||
@@ -345,7 +373,7 @@ class CartridgesApplication(Adw.Application):
|
||||
self.on_remove_game_action()
|
||||
|
||||
def search(self, uri: str) -> None:
|
||||
Gio.AppInfo.launch_default_for_uri(f"{uri}{shared.win.active_game.name}")
|
||||
Gio.AppInfo.launch_default_for_uri(f"{uri}{quote(shared.win.active_game.name)}")
|
||||
|
||||
def on_igdb_search_action(self, *_args: Any) -> None:
|
||||
self.search("https://www.igdb.com/search?type=1&q=")
|
||||
@@ -375,7 +403,11 @@ class CartridgesApplication(Adw.Application):
|
||||
if action[1:2]:
|
||||
self.set_accels_for_action(
|
||||
f"app.{action[0]}" if scope == self else f"win.{action[0]}",
|
||||
action[1],
|
||||
(
|
||||
tuple(s.replace("<primary>", "<meta>") for s in action[1])
|
||||
if sys.platform == "darwin"
|
||||
else action[1]
|
||||
),
|
||||
)
|
||||
|
||||
scope.add_action(simple_action)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
moduledir = join_paths(python_dir, 'cartridges')
|
||||
moduledir = python_dir / 'cartridges'
|
||||
|
||||
configure_file(
|
||||
input: 'cartridges.in',
|
||||
output: 'cartridges',
|
||||
configuration: conf,
|
||||
install: true,
|
||||
install_dir: get_option('bindir')
|
||||
install_dir: get_option('bindir'),
|
||||
)
|
||||
|
||||
install_subdir('importer', install_dir: moduledir)
|
||||
@@ -15,17 +15,14 @@ install_subdir('logging', install_dir: moduledir)
|
||||
install_subdir('errors', install_dir: moduledir)
|
||||
install_data(
|
||||
[
|
||||
'application_delegate.py',
|
||||
'main.py',
|
||||
'window.py',
|
||||
'preferences.py',
|
||||
'details_dialog.py',
|
||||
'game.py',
|
||||
'game_cover.py',
|
||||
configure_file(
|
||||
input: 'shared.py.in',
|
||||
output: 'shared.py',
|
||||
configuration: conf
|
||||
)
|
||||
configure_file(input: 'shared.py.in', output: 'shared.py', configuration: conf),
|
||||
],
|
||||
install_dir: moduledir
|
||||
install_dir: moduledir,
|
||||
)
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# pyright: reportAssignmentType=none
|
||||
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
@@ -30,6 +32,7 @@ from cartridges import shared
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.bottles_source import BottlesSource
|
||||
from cartridges.importer.desktop_source import DesktopSource
|
||||
from cartridges.importer.flatpak_source import FlatpakSource
|
||||
from cartridges.importer.heroic_source import HeroicSource
|
||||
from cartridges.importer.itch_source import ItchSource
|
||||
@@ -47,69 +50,69 @@ from cartridges.utils.create_dialog import create_dialog
|
||||
class CartridgesPreferences(Adw.PreferencesDialog):
|
||||
__gtype_name__ = "CartridgesPreferences"
|
||||
|
||||
general_page = Gtk.Template.Child()
|
||||
import_page = Gtk.Template.Child()
|
||||
sgdb_page = Gtk.Template.Child()
|
||||
general_page: Adw.PreferencesPage = Gtk.Template.Child()
|
||||
import_page: Adw.PreferencesPage = Gtk.Template.Child()
|
||||
sgdb_page: Adw.PreferencesPage = Gtk.Template.Child()
|
||||
|
||||
sources_group = Gtk.Template.Child()
|
||||
sources_group: Adw.PreferencesGroup = Gtk.Template.Child()
|
||||
|
||||
exit_after_launch_switch = Gtk.Template.Child()
|
||||
cover_launches_game_switch = Gtk.Template.Child()
|
||||
high_quality_images_switch = Gtk.Template.Child()
|
||||
exit_after_launch_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
cover_launches_game_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
high_quality_images_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
remove_missing_switch = Gtk.Template.Child()
|
||||
remove_missing_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
steam_expander_row = Gtk.Template.Child()
|
||||
steam_data_action_row = Gtk.Template.Child()
|
||||
steam_data_file_chooser_button = Gtk.Template.Child()
|
||||
steam_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
steam_data_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
steam_data_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
lutris_expander_row = Gtk.Template.Child()
|
||||
lutris_data_action_row = Gtk.Template.Child()
|
||||
lutris_data_file_chooser_button = Gtk.Template.Child()
|
||||
lutris_import_steam_switch = Gtk.Template.Child()
|
||||
lutris_import_flatpak_switch = Gtk.Template.Child()
|
||||
lutris_expander_row: Adw.ExpanderRowClass = Gtk.Template.Child()
|
||||
lutris_data_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
lutris_data_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
lutris_import_steam_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
lutris_import_flatpak_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
heroic_expander_row = Gtk.Template.Child()
|
||||
heroic_config_action_row = Gtk.Template.Child()
|
||||
heroic_config_file_chooser_button = Gtk.Template.Child()
|
||||
heroic_import_epic_switch = Gtk.Template.Child()
|
||||
heroic_import_gog_switch = Gtk.Template.Child()
|
||||
heroic_import_amazon_switch = Gtk.Template.Child()
|
||||
heroic_import_sideload_switch = Gtk.Template.Child()
|
||||
heroic_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
heroic_config_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
heroic_config_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
heroic_import_epic_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
heroic_import_gog_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
heroic_import_amazon_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
heroic_import_sideload_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
bottles_expander_row = Gtk.Template.Child()
|
||||
bottles_data_action_row = Gtk.Template.Child()
|
||||
bottles_data_file_chooser_button = Gtk.Template.Child()
|
||||
bottles_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
bottles_data_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
bottles_data_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
itch_expander_row = Gtk.Template.Child()
|
||||
itch_config_action_row = Gtk.Template.Child()
|
||||
itch_config_file_chooser_button = Gtk.Template.Child()
|
||||
itch_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
itch_config_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
itch_config_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
legendary_expander_row = Gtk.Template.Child()
|
||||
legendary_config_action_row = Gtk.Template.Child()
|
||||
legendary_config_file_chooser_button = Gtk.Template.Child()
|
||||
legendary_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
legendary_config_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
legendary_config_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
retroarch_expander_row = Gtk.Template.Child()
|
||||
retroarch_config_action_row = Gtk.Template.Child()
|
||||
retroarch_config_file_chooser_button = Gtk.Template.Child()
|
||||
retroarch_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
retroarch_config_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
retroarch_config_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
flatpak_expander_row = Gtk.Template.Child()
|
||||
flatpak_system_data_action_row = Gtk.Template.Child()
|
||||
flatpak_system_data_file_chooser_button = Gtk.Template.Child()
|
||||
flatpak_user_data_action_row = Gtk.Template.Child()
|
||||
flatpak_user_data_file_chooser_button = Gtk.Template.Child()
|
||||
flatpak_import_launchers_switch = Gtk.Template.Child()
|
||||
flatpak_expander_row: Adw.ExpanderRow = Gtk.Template.Child()
|
||||
flatpak_system_data_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
flatpak_system_data_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
flatpak_user_data_action_row: Adw.ActionRow = Gtk.Template.Child()
|
||||
flatpak_user_data_file_chooser_button: Gtk.Button = Gtk.Template.Child()
|
||||
flatpak_import_launchers_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
desktop_switch = Gtk.Template.Child()
|
||||
desktop_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
|
||||
sgdb_key_group = Gtk.Template.Child()
|
||||
sgdb_key_entry_row = Gtk.Template.Child()
|
||||
sgdb_switch = Gtk.Template.Child()
|
||||
sgdb_prefer_switch = Gtk.Template.Child()
|
||||
sgdb_animated_switch = Gtk.Template.Child()
|
||||
sgdb_fetch_button = Gtk.Template.Child()
|
||||
sgdb_stack = Gtk.Template.Child()
|
||||
sgdb_spinner = Gtk.Template.Child()
|
||||
sgdb_key_group: Adw.PreferencesGroup = Gtk.Template.Child()
|
||||
sgdb_key_entry_row: Adw.EntryRow = Gtk.Template.Child()
|
||||
sgdb_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
sgdb_prefer_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
sgdb_animated_switch: Adw.SwitchRow = Gtk.Template.Child()
|
||||
sgdb_fetch_button: Gtk.Button = Gtk.Template.Child()
|
||||
sgdb_stack: Gtk.Stack = Gtk.Template.Child()
|
||||
sgdb_spinner: Gtk.Spinner = Gtk.Template.Child()
|
||||
|
||||
danger_zone_group = Gtk.Template.Child()
|
||||
remove_all_games_button_row = Gtk.Template.Child()
|
||||
@@ -168,6 +171,10 @@ class CartridgesPreferences(Adw.PreferencesDialog):
|
||||
else:
|
||||
self.init_source_row(source)
|
||||
|
||||
# Special case for the desktop source
|
||||
if not DesktopSource().is_available:
|
||||
self.desktop_switch.set_visible(False)
|
||||
|
||||
# SteamGridDB
|
||||
def sgdb_key_changed(*_args: Any) -> None:
|
||||
shared.schema.set_string("sgdb-key", self.sgdb_key_entry_row.get_text())
|
||||
@@ -274,7 +281,7 @@ class CartridgesPreferences(Adw.PreferencesDialog):
|
||||
) -> None:
|
||||
self.file_chooser.select_folder(shared.win, None, callback, callback_data)
|
||||
|
||||
def undo_remove_all(self, *_args: Any) -> None:
|
||||
def undo_remove_all(self, *_args: Any) -> bool:
|
||||
shared.win.get_application().state = shared.AppState.UNDO_REMOVE_ALL_GAMES
|
||||
for game in self.removed_games:
|
||||
game.removed = False
|
||||
@@ -286,6 +293,8 @@ class CartridgesPreferences(Adw.PreferencesDialog):
|
||||
shared.win.get_application().state = shared.AppState.DEFAULT
|
||||
shared.win.create_source_rows()
|
||||
|
||||
return True
|
||||
|
||||
def remove_all_games(self, *_args: Any) -> None:
|
||||
shared.win.get_application().state = shared.AppState.REMOVE_ALL_GAMES
|
||||
shared.win.row_selected(None, shared.win.all_games_row_box.get_parent())
|
||||
|
||||
@@ -64,6 +64,8 @@ local_appdata_dir = Path(
|
||||
)
|
||||
programfiles32_dir = Path(getenv("programfiles(x86)") or r"C:\Program Files (x86)")
|
||||
|
||||
app_support_dir = home / "Library" / "Application Support"
|
||||
|
||||
try:
|
||||
scale_factor = max(
|
||||
monitor.get_scale_factor()
|
||||
@@ -79,4 +81,4 @@ win = None
|
||||
importer = None
|
||||
import_time = None
|
||||
store = None
|
||||
log_files = None
|
||||
log_files = []
|
||||
|
||||
78
cartridges/shared.pyi
Normal file
78
cartridges/shared.pyi
Normal file
@@ -0,0 +1,78 @@
|
||||
# shared.pyi
|
||||
#
|
||||
# Copyright 2024 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
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
from cartridges.importer.importer import Importer
|
||||
from cartridges.store.store import Store
|
||||
from cartridges.window import CartridgesWindow
|
||||
|
||||
|
||||
class AppState:
|
||||
DEFAULT: int
|
||||
LOAD_FROM_DISK: int
|
||||
IMPORT: int
|
||||
REMOVE_ALL_GAMES: int
|
||||
UNDO_REMOVE_ALL_GAMES: int
|
||||
|
||||
|
||||
APP_ID: str
|
||||
VERSION: str
|
||||
PREFIX: str
|
||||
PROFILE: str
|
||||
TIFF_COMPRESSION: str
|
||||
SPEC_VERSION: float
|
||||
|
||||
schema: Gio.Settings
|
||||
state_schema: Gio.Settings
|
||||
|
||||
home: Path
|
||||
|
||||
data_dir: Path
|
||||
host_data_dir: Path
|
||||
|
||||
config_dir: Path
|
||||
host_config_dir: Path
|
||||
|
||||
cache_dir: Path
|
||||
host_cache_dir: Path
|
||||
|
||||
flatpak_dir: Path
|
||||
|
||||
games_dir: Path
|
||||
covers_dir: Path
|
||||
|
||||
appdata_dir: Path
|
||||
local_appdata_dir: Path
|
||||
programfiles32_dir: Path
|
||||
|
||||
app_support_dir: Path
|
||||
|
||||
|
||||
scale_factor: int
|
||||
image_size: int
|
||||
|
||||
win: Optional[CartridgesWindow]
|
||||
importer: Optional[Importer]
|
||||
import_time: Optional[int]
|
||||
store = Optional[Store]
|
||||
log_files: list[Path]
|
||||
@@ -48,16 +48,10 @@ class SteamAPIManager(AsyncManager):
|
||||
appid = additional_data.get("steam_appid", None)
|
||||
if appid is None:
|
||||
return
|
||||
|
||||
# Get online metadata
|
||||
try:
|
||||
online_data = self.steam_api_helper.get_api_data(appid=appid)
|
||||
|
||||
except SteamGameNotFoundError:
|
||||
return
|
||||
|
||||
except SteamNotAGameError:
|
||||
except (SteamNotAGameError, SteamGameNotFoundError):
|
||||
game.update_values({"blacklisted": True})
|
||||
|
||||
else:
|
||||
game.update_values(online_data)
|
||||
|
||||
@@ -33,7 +33,7 @@ def create_dialog(
|
||||
dialog.add_response("dismiss", _("Dismiss"))
|
||||
|
||||
if extra_option:
|
||||
dialog.add_response(extra_option, _(extra_label))
|
||||
dialog.add_response(extra_option, extra_label or "")
|
||||
|
||||
dialog.choose(win)
|
||||
return dialog
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# pyright: reportAssignmentType=none
|
||||
|
||||
from sys import platform
|
||||
from typing import Any, Optional
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
from gi.repository import Adw, Gio, GLib, Gtk, Pango
|
||||
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
@@ -31,51 +34,52 @@ from cartridges.utils.relative_date import relative_date
|
||||
class CartridgesWindow(Adw.ApplicationWindow):
|
||||
__gtype_name__ = "CartridgesWindow"
|
||||
|
||||
overlay_split_view = Gtk.Template.Child()
|
||||
navigation_view = Gtk.Template.Child()
|
||||
sidebar = Gtk.Template.Child()
|
||||
all_games_row_box = Gtk.Template.Child()
|
||||
all_games_no_label = Gtk.Template.Child()
|
||||
added_row_box = Gtk.Template.Child()
|
||||
added_games_no_label = Gtk.Template.Child()
|
||||
toast_overlay = Gtk.Template.Child()
|
||||
primary_menu_button = Gtk.Template.Child()
|
||||
show_sidebar_button = Gtk.Template.Child()
|
||||
details_view = Gtk.Template.Child()
|
||||
library_page = Gtk.Template.Child()
|
||||
library_view = Gtk.Template.Child()
|
||||
library = Gtk.Template.Child()
|
||||
scrolledwindow = Gtk.Template.Child()
|
||||
library_overlay = Gtk.Template.Child()
|
||||
notice_empty = Gtk.Template.Child()
|
||||
notice_no_results = Gtk.Template.Child()
|
||||
search_bar = Gtk.Template.Child()
|
||||
search_entry = Gtk.Template.Child()
|
||||
search_button = Gtk.Template.Child()
|
||||
overlay_split_view: Adw.OverlaySplitView = Gtk.Template.Child()
|
||||
navigation_view: Adw.NavigationView = Gtk.Template.Child()
|
||||
sidebar_navigation_page: Adw.NavigationPage = Gtk.Template.Child()
|
||||
sidebar: Gtk.ListBox = Gtk.Template.Child()
|
||||
all_games_row_box: Gtk.Box = Gtk.Template.Child()
|
||||
all_games_no_label: Gtk.Label = Gtk.Template.Child()
|
||||
added_row_box: Gtk.Box = Gtk.Template.Child()
|
||||
added_games_no_label: Gtk.Label = Gtk.Template.Child()
|
||||
toast_overlay: Adw.ToastOverlay = Gtk.Template.Child()
|
||||
primary_menu_button: Gtk.MenuButton = Gtk.Template.Child()
|
||||
show_sidebar_button: Gtk.Button = Gtk.Template.Child()
|
||||
details_view: Gtk.Overlay = Gtk.Template.Child()
|
||||
library_page: Adw.NavigationPage = Gtk.Template.Child()
|
||||
library_view: Adw.ToolbarView = Gtk.Template.Child()
|
||||
library: Gtk.FlowBox = Gtk.Template.Child()
|
||||
scrolledwindow: Gtk.ScrolledWindow = Gtk.Template.Child()
|
||||
library_overlay: Gtk.Overlay = Gtk.Template.Child()
|
||||
notice_empty: Adw.StatusPage = Gtk.Template.Child()
|
||||
notice_no_results: Adw.StatusPage = Gtk.Template.Child()
|
||||
search_bar: Gtk.SearchBar = Gtk.Template.Child()
|
||||
search_entry: Gtk.SearchEntry = Gtk.Template.Child()
|
||||
search_button: Gtk.ToggleButton = Gtk.Template.Child()
|
||||
|
||||
details_page = Gtk.Template.Child()
|
||||
details_view_toolbar_view = Gtk.Template.Child()
|
||||
details_view_cover = Gtk.Template.Child()
|
||||
details_view_spinner = Gtk.Template.Child()
|
||||
details_view_title = Gtk.Template.Child()
|
||||
details_view_blurred_cover = Gtk.Template.Child()
|
||||
details_view_play_button = Gtk.Template.Child()
|
||||
details_view_developer = Gtk.Template.Child()
|
||||
details_view_added = Gtk.Template.Child()
|
||||
details_view_last_played = Gtk.Template.Child()
|
||||
details_view_hide_button = Gtk.Template.Child()
|
||||
details_page: Adw.NavigationPage = Gtk.Template.Child()
|
||||
details_view_toolbar_view: Adw.ToolbarView = Gtk.Template.Child()
|
||||
details_view_cover: Gtk.Picture = Gtk.Template.Child()
|
||||
details_view_spinner: Gtk.Spinner = Gtk.Template.Child()
|
||||
details_view_title: Gtk.Label = Gtk.Template.Child()
|
||||
details_view_blurred_cover: Gtk.Picture = Gtk.Template.Child()
|
||||
details_view_play_button: Gtk.Button = Gtk.Template.Child()
|
||||
details_view_developer: Gtk.Label = Gtk.Template.Child()
|
||||
details_view_added: Gtk.ShortcutLabel = Gtk.Template.Child()
|
||||
details_view_last_played: Gtk.Label = Gtk.Template.Child()
|
||||
details_view_hide_button: Gtk.Button = Gtk.Template.Child()
|
||||
|
||||
hidden_library_page = Gtk.Template.Child()
|
||||
hidden_primary_menu_button = Gtk.Template.Child()
|
||||
hidden_library = Gtk.Template.Child()
|
||||
hidden_library_view = Gtk.Template.Child()
|
||||
hidden_scrolledwindow = Gtk.Template.Child()
|
||||
hidden_library_overlay = Gtk.Template.Child()
|
||||
hidden_notice_empty = Gtk.Template.Child()
|
||||
hidden_notice_no_results = Gtk.Template.Child()
|
||||
hidden_search_bar = Gtk.Template.Child()
|
||||
hidden_search_entry = Gtk.Template.Child()
|
||||
hidden_search_button = Gtk.Template.Child()
|
||||
hidden_library_page: Adw.NavigationPage = Gtk.Template.Child()
|
||||
hidden_primary_menu_button: Gtk.MenuButton = Gtk.Template.Child()
|
||||
hidden_library: Gtk.FlowBox = Gtk.Template.Child()
|
||||
hidden_library_view: Adw.ToolbarView = Gtk.Template.Child()
|
||||
hidden_scrolledwindow: Gtk.ScrolledWindow = Gtk.Template.Child()
|
||||
hidden_library_overlay: Gtk.Overlay = Gtk.Template.Child()
|
||||
hidden_notice_empty: Adw.StatusPage = Gtk.Template.Child()
|
||||
hidden_notice_no_results: Adw.StatusPage = Gtk.Template.Child()
|
||||
hidden_search_bar: Gtk.SearchBar = Gtk.Template.Child()
|
||||
hidden_search_entry: Gtk.SearchEntry = Gtk.Template.Child()
|
||||
hidden_search_button: Gtk.ToggleButton = Gtk.Template.Child()
|
||||
|
||||
game_covers: dict = {}
|
||||
toasts: dict = {}
|
||||
@@ -152,6 +156,8 @@ class CartridgesWindow(Adw.ApplicationWindow):
|
||||
Gtk.Label(
|
||||
label=self.get_application().get_source_name(source_id),
|
||||
halign=Gtk.Align.START,
|
||||
wrap=True,
|
||||
wrap_mode=Pango.WrapMode.CHAR,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -213,6 +219,9 @@ class CartridgesWindow(Adw.ApplicationWindow):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
if platform == "darwin":
|
||||
self.sidebar_navigation_page.set_title("")
|
||||
|
||||
self.details_view.set_measure_overlay(self.details_view_toolbar_view, True)
|
||||
self.details_view.set_clip_overlay(self.details_view_toolbar_view, False)
|
||||
|
||||
@@ -443,6 +452,9 @@ class CartridgesWindow(Adw.ApplicationWindow):
|
||||
self.navigation_view.pop_to_page(self.library_page)
|
||||
|
||||
def on_show_hidden_action(self, *_args: Any) -> None:
|
||||
if self.navigation_view.get_visible_page() == self.hidden_library_page:
|
||||
return
|
||||
|
||||
self.navigation_view.push(self.hidden_library_page)
|
||||
|
||||
def on_sort_action(self, action: Gio.SimpleAction, state: GLib.Variant) -> None:
|
||||
|
||||
Reference in New Issue
Block a user