Move DetailsWindow to its own class
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/game.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/preferences.ui</file>
|
||||
<file preprocess="xml-stripblanks">gtk/details_window.ui</file>
|
||||
<file alias="style.css">gtk/style.css</file>
|
||||
<file alias="style-dark.css">gtk/style-dark.css</file>
|
||||
<file>library_placeholder.svg</file>
|
||||
|
||||
136
data/gtk/details_window.blp
Normal file
136
data/gtk/details_window.blp
Normal file
@@ -0,0 +1,136 @@
|
||||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template DetailsWindow : Adw.Window {
|
||||
default-width: 500;
|
||||
default-height: -1;
|
||||
modal: true;
|
||||
|
||||
ShortcutController {
|
||||
Shortcut {
|
||||
trigger: "Escape";
|
||||
action: "action(window.close)";
|
||||
}
|
||||
}
|
||||
|
||||
Box {
|
||||
orientation: vertical;
|
||||
|
||||
Adw.HeaderBar HeaderBar {
|
||||
show-start-title-buttons: false;
|
||||
show-end-title-buttons: false;
|
||||
|
||||
[start]
|
||||
Button cancel_button {
|
||||
label: _("Cancel");
|
||||
action-name: "window.close";
|
||||
}
|
||||
|
||||
[end]
|
||||
Button apply_button {
|
||||
styles [
|
||||
"suggested-action"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Adw.PreferencesPage {
|
||||
vexpand: true;
|
||||
|
||||
Adw.PreferencesGroup cover_group {
|
||||
Adw.Clamp cover_clamp {
|
||||
maximum-size: 200;
|
||||
Overlay cover_overlay {
|
||||
halign: center;
|
||||
valign: center;
|
||||
|
||||
[overlay]
|
||||
Button cover_button_edit {
|
||||
icon-name: "document-edit-symbolic";
|
||||
halign: end;
|
||||
valign: end;
|
||||
margin-bottom: 6;
|
||||
margin-end: 6;
|
||||
|
||||
styles [
|
||||
"circular", "osd"
|
||||
]
|
||||
}
|
||||
|
||||
[overlay]
|
||||
Revealer cover_button_delete_revealer {
|
||||
transition-type: crossfade;
|
||||
margin-end: 40;
|
||||
|
||||
Button cover_button_delete {
|
||||
icon-name: "user-trash-symbolic";
|
||||
halign: end;
|
||||
valign: end;
|
||||
margin-bottom: 6;
|
||||
margin-end: 6;
|
||||
|
||||
styles [
|
||||
"circular", "osd"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Picture cover {
|
||||
width-request: 200;
|
||||
height-request: 300;
|
||||
|
||||
styles [
|
||||
"card"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Adw.PreferencesGroup title_group {
|
||||
title: _("Title");
|
||||
description: _("The title of the game");
|
||||
|
||||
Entry name {}
|
||||
}
|
||||
|
||||
Adw.PreferencesGroup developer_group {
|
||||
title: _("Developer");
|
||||
description: _("The developer or publisher (optional)");
|
||||
|
||||
Entry developer {}
|
||||
}
|
||||
|
||||
Adw.PreferencesGroup exec_group {
|
||||
title: _("Executable");
|
||||
description: _("File to open or command to run when launching the game");
|
||||
|
||||
[header-suffix]
|
||||
Gtk.MenuButton exec-info-button {
|
||||
valign: center;
|
||||
icon-name: "help-about-symbolic";
|
||||
|
||||
popover: Popover {
|
||||
visible: bind exec-info-button.active bidirectional;
|
||||
|
||||
Label exec_info_label {
|
||||
use-markup: true;
|
||||
wrap: true;
|
||||
max-width-chars: 30;
|
||||
margin-top: 6;
|
||||
margin-bottom: 12;
|
||||
margin-start: 6;
|
||||
margin-end: 6;
|
||||
}
|
||||
};
|
||||
|
||||
styles [
|
||||
"flat"
|
||||
]
|
||||
}
|
||||
|
||||
Entry executable {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ blueprints = custom_target('blueprints',
|
||||
'gtk/help-overlay.blp',
|
||||
'gtk/window.blp',
|
||||
'gtk/game.blp',
|
||||
'gtk/preferences.blp'
|
||||
'gtk/preferences.blp',
|
||||
'gtk/details_window.blp'
|
||||
),
|
||||
output: '.',
|
||||
command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],
|
||||
|
||||
236
src/details_window.py
Normal file
236
src/details_window.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# details_window.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 os
|
||||
import shlex
|
||||
from time import time
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
from PIL import Image
|
||||
|
||||
from .create_dialog import create_dialog
|
||||
from .game import Game
|
||||
from .game_cover import GameCover
|
||||
from .save_cover import resize_cover, save_cover
|
||||
from .steamgriddb import SGDBSave
|
||||
|
||||
|
||||
@Gtk.Template(resource_path="/hu/kramo/Cartridges/gtk/details_window.ui")
|
||||
class DetailsWindow(Adw.Window):
|
||||
__gtype_name__ = "DetailsWindow"
|
||||
|
||||
cover = Gtk.Template.Child()
|
||||
cover_button_edit = Gtk.Template.Child()
|
||||
cover_button_delete_revealer = Gtk.Template.Child()
|
||||
cover_button_delete = Gtk.Template.Child()
|
||||
|
||||
name = Gtk.Template.Child()
|
||||
developer = Gtk.Template.Child()
|
||||
executable = Gtk.Template.Child()
|
||||
|
||||
exec_info_label = Gtk.Template.Child()
|
||||
|
||||
apply_button = Gtk.Template.Child()
|
||||
|
||||
cover_changed = False
|
||||
|
||||
def delete_pixbuf(self, *_args):
|
||||
self.game_cover.new_cover()
|
||||
|
||||
self.cover_button_delete_revealer.set_reveal_child(False)
|
||||
self.cover_changed = True
|
||||
|
||||
def __init__(self, win, game=None, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_transient_for(win)
|
||||
|
||||
self.win = win
|
||||
self.game = game
|
||||
self.game_cover = GameCover({self.cover})
|
||||
|
||||
if self.game:
|
||||
self.set_title(_("Edit Game Details"))
|
||||
self.name.set_text(self.game.name)
|
||||
self.developer.set_text(self.game.developer)
|
||||
self.executable.set_text(shlex.join(self.game.executable))
|
||||
self.apply_button.set_label(_("Apply"))
|
||||
|
||||
self.game_cover.new_cover(self.game.get_cover_path())
|
||||
if self.game_cover.get_pixbuf():
|
||||
self.cover_button_delete_revealer.set_reveal_child(True)
|
||||
else:
|
||||
self.set_title(_("Add New Game"))
|
||||
self.apply_button.set_label(_("Confirm"))
|
||||
|
||||
image_filter = Gtk.FileFilter(name=_("Images"))
|
||||
for extension in Image.registered_extensions():
|
||||
image_filter.add_suffix(extension[1:])
|
||||
|
||||
file_filters = Gio.ListStore.new(Gtk.FileFilter)
|
||||
file_filters.append(image_filter)
|
||||
self.file_dialog = Gtk.FileDialog()
|
||||
self.file_dialog.set_filters(file_filters)
|
||||
|
||||
# Translate this string as you would translate "file"
|
||||
file_name = _("file.txt")
|
||||
# As in software
|
||||
exe_name = _("program")
|
||||
|
||||
if os.name == "nt":
|
||||
exe_name += ".exe"
|
||||
# Translate this string as you would translate "path to {}"
|
||||
exe_path = _("C:\\path\\to\\{}").format(exe_name)
|
||||
# Translate this string as you would translate "path to {}"
|
||||
file_path = _("C:\\path\\to\\{}").format(file_name)
|
||||
command = "start"
|
||||
else:
|
||||
# Translate this string as you would translate "path to {}"
|
||||
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"
|
||||
|
||||
exec_info_text = _(
|
||||
'To launch the executable "{}", use the command:\n\n<tt>"{}"</tt>\n\nTo open the file "{}" with the default application, use:\n\n<tt>{} "{}"</tt>\n\nIf the path contains spaces, make sure to wrap it in double quotes!'
|
||||
).format(exe_name, exe_path, file_name, command, file_path)
|
||||
|
||||
self.exec_info_label.set_label(exec_info_text)
|
||||
|
||||
self.cover_button_delete.connect("clicked", self.delete_pixbuf)
|
||||
self.cover_button_edit.connect("clicked", self.choose_cover)
|
||||
self.apply_button.connect("clicked", self.apply_preferences)
|
||||
|
||||
self.name.connect("activate", self.focus_executable)
|
||||
self.developer.connect("activate", self.focus_executable)
|
||||
self.executable.connect("activate", self.apply_preferences)
|
||||
|
||||
self.set_focus(self.name)
|
||||
self.present()
|
||||
|
||||
def apply_preferences(self, *_args):
|
||||
final_name = self.name.get_text()
|
||||
final_developer = self.developer.get_text()
|
||||
final_executable = self.executable.get_text()
|
||||
|
||||
try:
|
||||
# Attempt to parse using shell parsing rules (doesn't verify executable existence).
|
||||
final_executable_split = shlex.split(
|
||||
final_executable, comments=False, posix=True
|
||||
)
|
||||
except ValueError as exception:
|
||||
create_dialog(
|
||||
self,
|
||||
_("Couldn't Add Game")
|
||||
if not self.game
|
||||
else _("Couldn't Apply Preferences"),
|
||||
f'{_("Executable")}: {exception}.',
|
||||
)
|
||||
return
|
||||
|
||||
if not self.game:
|
||||
if final_name == "":
|
||||
create_dialog(
|
||||
self, _("Couldn't Add Game"), _("Game title cannot be empty.")
|
||||
)
|
||||
return
|
||||
|
||||
if final_executable == "":
|
||||
create_dialog(
|
||||
self, _("Couldn't Add Game"), _("Executable cannot be empty.")
|
||||
)
|
||||
return
|
||||
|
||||
# Increment the number after the game id (eg. imported_1, imported_2)
|
||||
|
||||
numbers = [0]
|
||||
|
||||
for current_game in self.win.games:
|
||||
if "imported_" in current_game:
|
||||
numbers.append(int(current_game.replace("imported_", "")))
|
||||
|
||||
self.game = Game(
|
||||
self.win,
|
||||
{
|
||||
"game_id": f"imported_{str(max(numbers) + 1)}",
|
||||
"hidden": False,
|
||||
"source": "imported",
|
||||
"added": int(time()),
|
||||
"last_played": 0,
|
||||
},
|
||||
)
|
||||
|
||||
else:
|
||||
if final_name == "":
|
||||
create_dialog(
|
||||
self,
|
||||
_("Couldn't Apply Preferences"),
|
||||
_("Game title cannot be empty."),
|
||||
)
|
||||
return
|
||||
|
||||
if final_executable == "":
|
||||
create_dialog(
|
||||
self,
|
||||
_("Couldn't Apply Preferences"),
|
||||
_("Executable cannot be empty."),
|
||||
)
|
||||
return
|
||||
|
||||
self.game.name = final_name
|
||||
self.game.developer = final_developer or None
|
||||
self.game.executable = final_executable_split
|
||||
|
||||
if self.game.game_id in self.win.game_covers.keys():
|
||||
self.win.game_covers[self.game.game_id].animation = None
|
||||
|
||||
self.win.game_covers[self.game.game_id] = self.game_cover
|
||||
|
||||
if self.cover_changed:
|
||||
save_cover(
|
||||
self.win,
|
||||
self.game.game_id,
|
||||
self.game_cover.path,
|
||||
)
|
||||
|
||||
self.game.save()
|
||||
|
||||
if not self.game_cover.get_pixbuf():
|
||||
SGDBSave(self.win, {self.game})
|
||||
|
||||
self.game_cover.pictures.remove(self.cover)
|
||||
|
||||
self.close()
|
||||
self.win.show_details_view(self.game)
|
||||
|
||||
def focus_executable(self):
|
||||
self.set_focus(self.executable)
|
||||
|
||||
def set_cover(self, _source, result, *_args):
|
||||
try:
|
||||
path = self.file_dialog.open_finish(result).get_path()
|
||||
except GLib.GError:
|
||||
return
|
||||
|
||||
self.cover_button_delete_revealer.set_reveal_child(True)
|
||||
self.cover_changed = True
|
||||
|
||||
self.game_cover.new_cover(resize_cover(self.win, path))
|
||||
|
||||
def choose_cover(self, *_args):
|
||||
self.file_dialog.open(self, None, self.set_cover)
|
||||
@@ -28,7 +28,7 @@ gi.require_version("Adw", "1")
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from .bottles_importer import bottles_importer
|
||||
from .create_details_window import create_details_window
|
||||
from .details_window import DetailsWindow
|
||||
from .heroic_importer import heroic_importer
|
||||
from .importer import Importer
|
||||
from .itch_importer import itch_importer
|
||||
@@ -143,10 +143,10 @@ class CartridgesApplication(Adw.Application):
|
||||
self.win.active_game.toggle_hidden()
|
||||
|
||||
def on_edit_game_action(self, *_args):
|
||||
create_details_window(self.win, self.win.active_game)
|
||||
DetailsWindow(self.win, self.win.active_game)
|
||||
|
||||
def on_add_game_action(self, *_args):
|
||||
create_details_window(self.win)
|
||||
DetailsWindow(self.win)
|
||||
|
||||
def on_import_action(self, *_args):
|
||||
self.win.importer = Importer(self.win)
|
||||
|
||||
@@ -21,6 +21,7 @@ cartridges_sources = [
|
||||
'main.py',
|
||||
'window.py',
|
||||
'preferences.py',
|
||||
'details_window.py',
|
||||
'game.py',
|
||||
'game_cover.py',
|
||||
'importers/steam_importer.py',
|
||||
@@ -32,7 +33,6 @@ cartridges_sources = [
|
||||
'utils/steamgriddb.py',
|
||||
'utils/save_cover.py',
|
||||
'utils/create_dialog.py',
|
||||
'utils/create_details_window.py',
|
||||
'utils/check_install.py'
|
||||
]
|
||||
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
# create_details_window.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 os
|
||||
import shlex
|
||||
from time import time
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, GObject, Gtk
|
||||
from PIL import Image
|
||||
|
||||
from .create_dialog import create_dialog
|
||||
from .game import Game
|
||||
from .game_cover import GameCover
|
||||
from .save_cover import resize_cover, save_cover
|
||||
from .steamgriddb import SGDBSave
|
||||
|
||||
|
||||
def create_details_window(win, game=None):
|
||||
window = Adw.Window(
|
||||
modal=True, default_width=500, default_height=-1, transient_for=win
|
||||
)
|
||||
|
||||
cover_changed = False
|
||||
|
||||
cover_button_edit = Gtk.Button(
|
||||
icon_name="document-edit-symbolic",
|
||||
halign=Gtk.Align.END,
|
||||
valign=Gtk.Align.END,
|
||||
margin_bottom=6,
|
||||
margin_end=6,
|
||||
css_classes=["circular", "osd"],
|
||||
)
|
||||
|
||||
cover_button_delete = Gtk.Button(
|
||||
icon_name="user-trash-symbolic",
|
||||
css_classes=["circular", "osd"],
|
||||
halign=Gtk.Align.END,
|
||||
valign=Gtk.Align.END,
|
||||
margin_bottom=6,
|
||||
margin_end=6,
|
||||
)
|
||||
|
||||
def delete_pixbuf(*_args):
|
||||
nonlocal game_cover
|
||||
nonlocal cover_changed
|
||||
|
||||
game_cover.new_cover()
|
||||
|
||||
cover_button_delete_revealer.set_reveal_child(False)
|
||||
cover_changed = True
|
||||
|
||||
cover_button_delete.connect("clicked", delete_pixbuf)
|
||||
|
||||
cover_button_delete_revealer = Gtk.Revealer(
|
||||
child=cover_button_delete,
|
||||
transition_type=Gtk.RevealerTransitionType.CROSSFADE,
|
||||
margin_end=40,
|
||||
)
|
||||
|
||||
cover = Gtk.Picture.new()
|
||||
game_cover = GameCover({cover})
|
||||
|
||||
if game:
|
||||
window.set_title(_("Edit Game Details"))
|
||||
developer = Gtk.Entry.new_with_buffer(Gtk.EntryBuffer.new(game.developer, -1))
|
||||
name = Gtk.Entry.new_with_buffer(Gtk.EntryBuffer.new(game.name, -1))
|
||||
executable = Gtk.Entry.new_with_buffer(
|
||||
Gtk.EntryBuffer.new(shlex.join(game.executable), -1)
|
||||
)
|
||||
apply_button = Gtk.Button.new_with_label(_("Apply"))
|
||||
|
||||
game_cover.new_cover(game.get_cover_path())
|
||||
if game_cover.pixbuf:
|
||||
cover_button_delete_revealer.set_reveal_child(True)
|
||||
else:
|
||||
window.set_title(_("Add New Game"))
|
||||
name = Gtk.Entry()
|
||||
developer = Gtk.Entry()
|
||||
executable = Gtk.Entry()
|
||||
apply_button = Gtk.Button.new_with_label(_("Confirm"))
|
||||
|
||||
image_filter = Gtk.FileFilter(name=_("Images"))
|
||||
for extension in Image.registered_extensions():
|
||||
image_filter.add_suffix(extension[1:])
|
||||
|
||||
file_filters = Gio.ListStore.new(Gtk.FileFilter)
|
||||
file_filters.append(image_filter)
|
||||
filechooser = Gtk.FileDialog()
|
||||
filechooser.set_filters(file_filters)
|
||||
|
||||
cover.add_css_class("card")
|
||||
cover.set_size_request(200, 300)
|
||||
|
||||
cover_overlay = Gtk.Overlay(
|
||||
child=cover,
|
||||
halign=Gtk.Align.CENTER,
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
|
||||
cover_overlay.add_overlay(cover_button_edit)
|
||||
cover_overlay.add_overlay(cover_button_delete_revealer)
|
||||
|
||||
cover_clamp = Adw.Clamp(
|
||||
maximum_size=200,
|
||||
child=cover_overlay,
|
||||
)
|
||||
|
||||
cover_group = Adw.PreferencesGroup()
|
||||
cover_group.add(cover_clamp)
|
||||
|
||||
title_group = Adw.PreferencesGroup(
|
||||
title=_("Title"),
|
||||
description=_("The title of the game"),
|
||||
)
|
||||
title_group.add(name)
|
||||
|
||||
developer_group = Adw.PreferencesGroup(
|
||||
title=_("Developer"),
|
||||
description=_("The developer or publisher (optional)"),
|
||||
)
|
||||
developer_group.add(developer)
|
||||
|
||||
exec_info_button = Gtk.ToggleButton(
|
||||
icon_name="help-about-symbolic",
|
||||
valign=Gtk.Align.CENTER,
|
||||
css_classes=["flat"],
|
||||
)
|
||||
|
||||
# Translate this string as you would translate "file"
|
||||
file_name = _("file.txt")
|
||||
# As in software
|
||||
exe_name = _("program")
|
||||
|
||||
if os.name == "nt":
|
||||
exe_name += ".exe"
|
||||
# Translate this string as you would translate "path to {}"
|
||||
exe_path = _("C:\\path\\to\\{}").format(exe_name)
|
||||
# Translate this string as you would translate "path to {}"
|
||||
file_path = _("C:\\path\\to\\{}").format(file_name)
|
||||
command = "start"
|
||||
else:
|
||||
# Translate this string as you would translate "path to {}"
|
||||
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"
|
||||
|
||||
exec_info_text = _(
|
||||
'To launch the executable "{}", use the command:\n\n<tt>"{}"</tt>\n\nTo open the file "{}" with the default application, use:\n\n<tt>{} "{}"</tt>\n\nIf the path contains spaces, make sure to wrap it in double quotes!'
|
||||
).format(exe_name, exe_path, file_name, command, file_path)
|
||||
|
||||
exec_info_label = Gtk.Label(
|
||||
label=exec_info_text,
|
||||
use_markup=True,
|
||||
wrap=True,
|
||||
max_width_chars=30,
|
||||
margin_top=6,
|
||||
margin_bottom=12,
|
||||
margin_start=6,
|
||||
margin_end=6,
|
||||
)
|
||||
|
||||
exec_info_popover = Gtk.Popover(
|
||||
position=Gtk.PositionType.TOP, child=exec_info_label
|
||||
)
|
||||
|
||||
exec_info_popover.bind_property(
|
||||
"visible", exec_info_button, "active", GObject.BindingFlags.BIDIRECTIONAL
|
||||
)
|
||||
|
||||
exec_group = Adw.PreferencesGroup(
|
||||
title=_("Executable"),
|
||||
description=_("File to open or command to run when launching the game"),
|
||||
header_suffix=exec_info_button,
|
||||
)
|
||||
exec_info_popover.set_parent(exec_group.get_header_suffix())
|
||||
exec_group.add(executable)
|
||||
|
||||
general_page = Adw.PreferencesPage(vexpand=True)
|
||||
general_page.add(cover_group)
|
||||
general_page.add(title_group)
|
||||
general_page.add(developer_group)
|
||||
general_page.add(exec_group)
|
||||
|
||||
cancel_button = Gtk.Button.new_with_label(_("Cancel"))
|
||||
|
||||
apply_button.add_css_class("suggested-action")
|
||||
|
||||
header_bar = Adw.HeaderBar(
|
||||
show_start_title_buttons=False,
|
||||
show_end_title_buttons=False,
|
||||
)
|
||||
header_bar.pack_start(cancel_button)
|
||||
header_bar.pack_end(apply_button)
|
||||
|
||||
main_box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
||||
main_box.append(header_bar)
|
||||
main_box.append(general_page)
|
||||
window.set_content(main_box)
|
||||
|
||||
def choose_cover(*_args):
|
||||
filechooser.open(window, None, set_cover, None)
|
||||
|
||||
def set_cover(_source, result, *_args):
|
||||
nonlocal game_cover
|
||||
nonlocal cover_changed
|
||||
|
||||
try:
|
||||
path = filechooser.open_finish(result).get_path()
|
||||
except GLib.GError:
|
||||
return
|
||||
|
||||
cover_button_delete_revealer.set_reveal_child(True)
|
||||
cover_changed = True
|
||||
|
||||
game_cover.new_cover(resize_cover(win, path))
|
||||
|
||||
def close_window(*_args):
|
||||
window.close()
|
||||
|
||||
def apply_preferences(*_args):
|
||||
nonlocal cover_changed
|
||||
nonlocal game
|
||||
nonlocal cover
|
||||
|
||||
final_name = name.get_buffer().get_text()
|
||||
final_developer = developer.get_buffer().get_text()
|
||||
final_executable = executable.get_buffer().get_text()
|
||||
|
||||
try:
|
||||
# Attempt to parse using shell parsing rules (doesn't verify executable existence).
|
||||
final_executable_split = shlex.split(
|
||||
final_executable, comments=False, posix=True
|
||||
)
|
||||
except ValueError as exception:
|
||||
create_dialog(
|
||||
window,
|
||||
_("Couldn't Add Game") if not game else _("Couldn't Apply Preferences"),
|
||||
f'{_("Executable")}: {exception}.',
|
||||
)
|
||||
return
|
||||
|
||||
if not game:
|
||||
if final_name == "":
|
||||
create_dialog(
|
||||
window, _("Couldn't Add Game"), _("Game title cannot be empty.")
|
||||
)
|
||||
return
|
||||
|
||||
if final_executable == "":
|
||||
create_dialog(
|
||||
window, _("Couldn't Add Game"), _("Executable cannot be empty.")
|
||||
)
|
||||
return
|
||||
|
||||
# Increment the number after the game id (eg. imported_1, imported_2)
|
||||
|
||||
numbers = [0]
|
||||
|
||||
for current_game in win.games:
|
||||
if "imported_" in current_game:
|
||||
numbers.append(int(current_game.replace("imported_", "")))
|
||||
|
||||
game = Game(
|
||||
win,
|
||||
{
|
||||
"game_id": f"imported_{str(max(numbers) + 1)}",
|
||||
"hidden": False,
|
||||
"source": "imported",
|
||||
"added": int(time()),
|
||||
"last_played": 0,
|
||||
},
|
||||
)
|
||||
|
||||
else:
|
||||
if final_name == "":
|
||||
create_dialog(
|
||||
window,
|
||||
_("Couldn't Apply Preferences"),
|
||||
_("Game title cannot be empty."),
|
||||
)
|
||||
return
|
||||
|
||||
if final_executable == "":
|
||||
create_dialog(
|
||||
window,
|
||||
_("Couldn't Apply Preferences"),
|
||||
_("Executable cannot be empty."),
|
||||
)
|
||||
return
|
||||
|
||||
game.name = final_name
|
||||
game.developer = final_developer or None
|
||||
game.executable = final_executable_split
|
||||
|
||||
win.game_covers[game.game_id] = game_cover
|
||||
|
||||
if cover_changed:
|
||||
save_cover(
|
||||
win,
|
||||
game.game_id,
|
||||
game_cover.path,
|
||||
)
|
||||
|
||||
game.save()
|
||||
|
||||
if not game_cover.pixbuf:
|
||||
SGDBSave(win, {game})
|
||||
|
||||
game.game_cover.pictures.remove(cover)
|
||||
|
||||
window.close()
|
||||
win.show_details_view(game)
|
||||
|
||||
def focus_executable(*_args):
|
||||
window.set_focus(executable)
|
||||
|
||||
cover_button_edit.connect("clicked", choose_cover)
|
||||
cancel_button.connect("clicked", close_window)
|
||||
apply_button.connect("clicked", apply_preferences)
|
||||
name.connect("activate", focus_executable)
|
||||
developer.connect("activate", focus_executable)
|
||||
executable.connect("activate", apply_preferences)
|
||||
|
||||
shortcut_controller = Gtk.ShortcutController()
|
||||
shortcut_controller.add_shortcut(
|
||||
Gtk.Shortcut.new(
|
||||
Gtk.ShortcutTrigger.parse_string("Escape"),
|
||||
Gtk.CallbackAction.new(close_window),
|
||||
)
|
||||
)
|
||||
|
||||
window.add_controller(shortcut_controller)
|
||||
window.set_focus(name)
|
||||
window.present()
|
||||
@@ -24,7 +24,7 @@ from gi.repository import Adw, Gtk
|
||||
from .create_dialog import create_dialog
|
||||
from .game import Game
|
||||
from .save_cover import resize_cover, save_cover
|
||||
from .steamgriddb import SGDBSave, needs_cover
|
||||
from .steamgriddb import SGDBSave
|
||||
|
||||
|
||||
class Importer:
|
||||
@@ -58,7 +58,7 @@ class Importer:
|
||||
if values:
|
||||
game = Game(self.win, values)
|
||||
|
||||
if not needs_cover(self.win.schema, cover_path):
|
||||
if save_cover:
|
||||
save_cover(self.win, game.game_id, resize_cover(self.win, cover_path))
|
||||
|
||||
self.games.add(game)
|
||||
|
||||
@@ -7,12 +7,6 @@ from .create_dialog import create_dialog
|
||||
from .save_cover import save_cover, resize_cover
|
||||
|
||||
|
||||
def needs_cover(schema, previous):
|
||||
return schema.get_boolean("sgdb") and (
|
||||
(schema.get_boolean("sgdb-prefer")) or not previous
|
||||
)
|
||||
|
||||
|
||||
class SGDBSave:
|
||||
def __init__(self, win, games, importer=None):
|
||||
self.win = win
|
||||
@@ -34,10 +28,15 @@ class SGDBSave:
|
||||
|
||||
def update_cover(self, task, game):
|
||||
if (
|
||||
not needs_cover(
|
||||
self.win.schema,
|
||||
(self.win.covers_dir / f"{game.game_id}.gif").is_file()
|
||||
or (self.win.covers_dir / f"{game.game_id}.tiff").is_file(),
|
||||
not (
|
||||
self.win.schema.get_boolean("sgdb")
|
||||
and (
|
||||
(self.win.schema.get_boolean("sgdb-prefer"))
|
||||
or not (
|
||||
(self.win.covers_dir / f"{game.game_id}.gif").is_file()
|
||||
or (self.win.covers_dir / f"{game.game_id}.tiff").is_file()
|
||||
)
|
||||
)
|
||||
)
|
||||
or game.blacklisted
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user