From 62e19a8be222fca63d8f8d96c7f2fc1792fa22fe Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 19 Dec 2023 20:53:21 +0200 Subject: [PATCH 01/17] VerifyGame: Present a dialog to select selective downloads in the game supports them. --- rare/components/dialogs/cloud_save_dialog.py | 2 +- rare/components/dialogs/install_dialog.py | 13 +- rare/components/dialogs/selective_dialog.py | 133 ++++++++++++++++++ rare/components/dialogs/uninstall_dialog.py | 15 +- .../tabs/games/game_info/game_info.py | 20 ++- rare/models/install.py | 7 + rare/ui/components/dialogs/install_dialog.py | 8 +- rare/ui/components/dialogs/install_dialog.ui | 2 +- 8 files changed, 180 insertions(+), 20 deletions(-) create mode 100644 rare/components/dialogs/selective_dialog.py diff --git a/rare/components/dialogs/cloud_save_dialog.py b/rare/components/dialogs/cloud_save_dialog.py index 7d64c318..56067950 100644 --- a/rare/components/dialogs/cloud_save_dialog.py +++ b/rare/components/dialogs/cloud_save_dialog.py @@ -40,7 +40,7 @@ class CloudSaveDialog(QDialog, Ui_SyncSaveDialog): self.status = self.CANCEL - self.title_label.setText(f"{self.title_label.text()} {igame.title}") + self.title_label.setText(f"

{self.title_label.text()} {igame.title}

") newer = self.tr("Newer") if dt_remote and dt_local: diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index a3042281..61621694 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -23,7 +23,7 @@ from rare.widgets.indicator_edit import PathEdit, IndicatorReasonsCommon class InstallDialogAdvanced(CollapsibleFrame): def __init__(self, parent=None): - widget = QWidget() + widget = QWidget(parent) title = widget.tr("Advanced options") self.ui = Ui_InstallDialogAdvanced() self.ui.setupUi(widget) @@ -42,7 +42,7 @@ class InstallDialog(QDialog): # lk: set object names for CSS properties self.ui.install_button.setObjectName("InstallButton") - self.core = LegendaryCoreSingleton() + self.core = rgame.core self.rgame = rgame self.options = options self.__download: Optional[InstallDownloadModel] = None @@ -70,7 +70,7 @@ class InstallDialog(QDialog): header = self.tr("Modify") else: header = self.tr("Install") - self.ui.install_dialog_label.setText(f'

{header} "{self.rgame.app_title}"

') + self.ui.title_label.setText(f'

{header} "{self.rgame.app_title}"

') self.setWindowTitle(f'{header} "{self.rgame.app_title}" - {QCoreApplication.instance().applicationName()}') if options.base_path: @@ -111,6 +111,11 @@ class InstallDialog(QDialog): self.ui.platform_label.setDisabled(rgame.is_installed) self.ui.platform_combo.setDisabled(rgame.is_installed) + # if we are repairing, disable the SDL selection and open the dialog frame to be visible + self.selectable.setDisabled(options.repair_mode and not options.repair_and_update) + if options.repair_mode and not options.repair_and_update: + self.selectable.click() + self.advanced.ui.max_workers_spin.setValue(self.core.lgd.config.getint("Legendary", "max_workers", fallback=0)) self.advanced.ui.max_workers_spin.valueChanged.connect(self.option_changed) @@ -210,7 +215,7 @@ class InstallDialog(QDialog): layout = QVBoxLayout(widget) layout.setSpacing(0) for tag, info in sdl_data.items(): - cb = TagCheckBox(info["name"], info["description"], info["tags"]) + cb = TagCheckBox(info["name"].strip(), info["description"].strip(), info["tags"]) if tag == "__required": cb.setChecked(True) cb.setDisabled(True) diff --git a/rare/components/dialogs/selective_dialog.py b/rare/components/dialogs/selective_dialog.py new file mode 100644 index 00000000..bafe2afe --- /dev/null +++ b/rare/components/dialogs/selective_dialog.py @@ -0,0 +1,133 @@ +from typing import List, Union, Optional + +from PyQt5.QtCore import Qt, pyqtSignal, QCoreApplication +from PyQt5.QtGui import QCloseEvent, QKeyEvent +from PyQt5.QtWidgets import ( + QDialog, + QLabel, + QVBoxLayout, + QCheckBox, + QHBoxLayout, + QPushButton, + QLayout, QGroupBox, +) +from legendary.utils.selective_dl import get_sdl_appname + +from rare.models.game import RareGame +from rare.models.install import SelectiveDownloadsModel + + +class SelectiveDownloadsDialog(QDialog): + result_ready = pyqtSignal(RareGame, SelectiveDownloadsModel) + + def __init__(self, rgame: RareGame, parent=None): + super(SelectiveDownloadsDialog, self).__init__(parent=parent) + self.setAttribute(Qt.WA_DeleteOnClose, True) + self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) + header = self.tr("Optional downloads for") + self.setWindowTitle(f'{header} "{rgame.app_title}" - {QCoreApplication.instance().applicationName()}') + self.title_label = QLabel( + self.tr("

Select the optional downloads for {} to verify with.

").format(rgame.app_title) + ) + + self.core = rgame.core + self.rgame = rgame + + self.selectable = QGroupBox(self.tr("Optional downloads"), self) + self.selectable_layout = QVBoxLayout(self.selectable) + self.selectable_layout.setSpacing(0) + + self.selectable_checks: List[TagCheckBox] = [] + self.config_tags: Optional[List[str]] = None + + self.verify_button = QPushButton(self.tr("Verify")) + self.verify_button.clicked.connect(self.__on_verify) + + self.cancel_button = QPushButton(self.tr("Cancel")) + self.cancel_button.clicked.connect(self.__on_cancel) + + button_layout = QHBoxLayout() + button_layout.addWidget(self.cancel_button) + button_layout.addStretch(1) + button_layout.addWidget(self.verify_button) + + layout = QVBoxLayout(self) + layout.setSizeConstraint(QLayout.SetFixedSize) + layout.addWidget(self.title_label) + layout.addWidget(self.selectable) + layout.addLayout(button_layout) + + self.options: SelectiveDownloadsModel = SelectiveDownloadsModel(rgame.app_name) + + config_disable_sdl = self.core.lgd.config.getboolean(self.rgame.app_name, "disable_sdl", fallback=False) + sdl_name = get_sdl_appname(self.rgame.app_name) + if not config_disable_sdl and sdl_name is not None: + self.reset_sdl_list() + else: + self.options.accepted = True + self.close() + + def reset_sdl_list(self): + platform = self.rgame.igame.platform + for cb in self.selectable_checks: + cb.disconnect() + cb.deleteLater() + self.selectable_checks.clear() + + if config_tags := self.core.lgd.config.get(self.rgame.app_name, "install_tags", fallback=None): + self.config_tags = config_tags.split(",") + config_disable_sdl = self.core.lgd.config.getboolean(self.rgame.app_name, "disable_sdl", fallback=False) + sdl_name = get_sdl_appname(self.rgame.app_name) + if not config_disable_sdl and sdl_name is not None: + sdl_data = self.core.get_sdl_data(sdl_name, platform=platform) + if sdl_data: + for tag, info in sdl_data.items(): + cb = TagCheckBox(info["name"].strip(), info["description"].strip(), info["tags"]) + if tag == "__required": + cb.setChecked(True) + cb.setDisabled(True) + if self.config_tags is not None: + if all(elem in self.config_tags for elem in info["tags"]): + cb.setChecked(True) + self.selectable_layout.addWidget(cb) + self.selectable_checks.append(cb) + # for cb in self.selectable_checks: + # cb.stateChanged.connect(self.option_changed) + # self.selectable.setWidget(widget) + else: + self.selectable.setDisabled(True) + + def closeEvent(self, a0: QCloseEvent) -> None: + self.result_ready.emit(self.rgame, self.options) + super(SelectiveDownloadsDialog, self).closeEvent(a0) + + def __on_verify(self): + install_tag = [""] + for cb in self.selectable_checks: + if data := cb.isChecked(): + # noinspection PyTypeChecker + install_tag.extend(data) + self.options.accepted = True + self.options.install_tag = install_tag + self.close() + + def __on_cancel(self): + self.options.accepted = False + self.options.install_tag = None + self.close() + + def keyPressEvent(self, e: QKeyEvent) -> None: + if e.key() == Qt.Key_Escape: + e.accept() + self.__on_cancel() + + +class TagCheckBox(QCheckBox): + def __init__(self, text, desc, tags: List[str], parent=None): + super(TagCheckBox, self).__init__(parent) + self.setText(text) + self.setToolTip(desc) + self.tags = tags + + def isChecked(self) -> Union[bool, List[str]]: + return self.tags if super(TagCheckBox, self).isChecked() else False diff --git a/rare/components/dialogs/uninstall_dialog.py b/rare/components/dialogs/uninstall_dialog.py index 76cee75e..f87f2f00 100644 --- a/rare/components/dialogs/uninstall_dialog.py +++ b/rare/components/dialogs/uninstall_dialog.py @@ -6,7 +6,7 @@ from PyQt5.QtWidgets import ( QVBoxLayout, QCheckBox, QHBoxLayout, - QPushButton, + QPushButton, QLayout, ) from legendary.utils.selective_dl import get_sdl_appname @@ -24,8 +24,8 @@ class UninstallDialog(QDialog): self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) header = self.tr("Uninstall") self.setWindowTitle(f'{header} "{rgame.app_title}" - {QCoreApplication.instance().applicationName()}') - self.info_text = QLabel( - self.tr("Do you really want to uninstall {}?").format(rgame.app_title) + self.title_label = QLabel( + self.tr("

Do you really want to uninstall {}?

").format(rgame.app_title) ) self.keep_files = QCheckBox(self.tr("Keep game files.")) @@ -52,14 +52,13 @@ class UninstallDialog(QDialog): button_layout.addStretch(1) button_layout.addWidget(self.uninstall_button) - layout = QVBoxLayout() - layout.addWidget(self.info_text) + layout = QVBoxLayout(self) + layout.setSizeConstraint(QLayout.SetFixedSize) + layout.addWidget(self.title_label) layout.addLayout(form_layout) layout.addLayout(button_layout) - self.setLayout(layout) - - if get_sdl_appname(rgame.app_name) is not None: + if rgame.sdl_name is not None: self.keep_config.setChecked(True) self.options: UninstallOptionsModel = options diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index f0507021..9eb2242d 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -16,6 +16,8 @@ from PyQt5.QtWidgets import ( QWidgetAction, ) +from rare.models.install import SelectiveDownloadsModel +from rare.components.dialogs.selective_dialog import SelectiveDownloadsDialog from rare.models.game import RareGame from rare.shared import RareCore from rare.shared.workers import VerifyWorker, MoveWorker @@ -162,9 +164,23 @@ class GameInfo(QWidget, SideTabContents): self.tr("Installation path for {} does not exist. Cannot continue.").format(self.rgame.app_title), ) return - self.verify_game(self.rgame) + if self.rgame.sdl_name is not None: + selective_dialog = SelectiveDownloadsDialog( + self.rgame, parent=self + ) + selective_dialog.result_ready.connect(self.verify_game) + selective_dialog.exec() + else: + self.verify_game(self.rgame) - def verify_game(self, rgame: RareGame): + @pyqtSlot(RareGame, SelectiveDownloadsModel) + def verify_game(self, rgame: RareGame, sdl_model: SelectiveDownloadsModel = None): + if sdl_model: + if sdl_model.accepted: + self.core.lgd.config.set(rgame.app_name, "install_tags", ','.join(sdl_model.install_tag)) + self.core.lgd.save_config() + else: + return worker = VerifyWorker(self.core, self.args, rgame) worker.signals.progress.connect(self.__on_verify_progress) worker.signals.result.connect(self.__on_verify_result) diff --git a/rare/models/install.py b/rare/models/install.py index efd076f9..1118377a 100644 --- a/rare/models/install.py +++ b/rare/models/install.py @@ -117,3 +117,10 @@ class UninstallOptionsModel: self.accepted = values[0] self.keep_files = values[1] self.keep_config = values[2] + + +@dataclass +class SelectiveDownloadsModel: + app_name: str + accepted: bool = None + install_tag: Optional[List[str]] = None diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index de10c4ad..65e0dbcb 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -19,9 +19,9 @@ class Ui_InstallDialog(object): self.install_dialog_layout = QtWidgets.QFormLayout(InstallDialog) self.install_dialog_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.install_dialog_layout.setObjectName("install_dialog_layout") - self.install_dialog_label = QtWidgets.QLabel(InstallDialog) - self.install_dialog_label.setObjectName("install_dialog_label") - self.install_dialog_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.install_dialog_label) + self.title_label = QtWidgets.QLabel(InstallDialog) + self.title_label.setObjectName("title_label") + self.install_dialog_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label) self.install_dir_label = QtWidgets.QLabel(InstallDialog) self.install_dir_label.setObjectName("install_dir_label") self.install_dialog_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.install_dir_label) @@ -112,7 +112,7 @@ class Ui_InstallDialog(object): def retranslateUi(self, InstallDialog): _translate = QtCore.QCoreApplication.translate - self.install_dialog_label.setText(_translate("InstallDialog", "error")) + self.title_label.setText(_translate("InstallDialog", "error")) self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) self.platform_label.setText(_translate("InstallDialog", "Platform")) self.shortcut_label.setText(_translate("InstallDialog", "Create shortcut")) diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index 6db58a64..d75569f5 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -18,7 +18,7 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + error From e421d02a4f54bc4800d9bddebf1985dcee8060a4 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 26 Dec 2023 00:05:11 +0200 Subject: [PATCH 02/17] Update SDL dialog --- rare/components/dialogs/selective_dialog.py | 73 ++++++------------- .../tabs/games/game_info/game_info.py | 15 ++-- rare/models/install.py | 8 ++ 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/rare/components/dialogs/selective_dialog.py b/rare/components/dialogs/selective_dialog.py index bafe2afe..864eb657 100644 --- a/rare/components/dialogs/selective_dialog.py +++ b/rare/components/dialogs/selective_dialog.py @@ -1,73 +1,61 @@ from typing import List, Union, Optional -from PyQt5.QtCore import Qt, pyqtSignal, QCoreApplication -from PyQt5.QtGui import QCloseEvent, QKeyEvent +from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import ( - QDialog, QLabel, QVBoxLayout, QCheckBox, - QHBoxLayout, - QPushButton, QLayout, QGroupBox, ) from legendary.utils.selective_dl import get_sdl_appname from rare.models.game import RareGame from rare.models.install import SelectiveDownloadsModel +from rare.widgets.dialogs import ButtonDialog, dialog_title_game +from rare.utils.misc import icon -class SelectiveDownloadsDialog(QDialog): +class SelectiveDialog(ButtonDialog): result_ready = pyqtSignal(RareGame, SelectiveDownloadsModel) def __init__(self, rgame: RareGame, parent=None): - super(SelectiveDownloadsDialog, self).__init__(parent=parent) - self.setAttribute(Qt.WA_DeleteOnClose, True) - self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) + super(SelectiveDialog, self).__init__(parent=parent) header = self.tr("Optional downloads for") - self.setWindowTitle(f'{header} "{rgame.app_title}" - {QCoreApplication.instance().applicationName()}') - self.title_label = QLabel( - self.tr("

Select the optional downloads for {} to verify with.

").format(rgame.app_title) - ) + self.setWindowTitle(dialog_title_game(header, rgame.app_title)) + + title_label = QLabel(f"

{dialog_title_game(header, rgame.app_title)}

", self) self.core = rgame.core self.rgame = rgame - self.selectable = QGroupBox(self.tr("Optional downloads"), self) - self.selectable_layout = QVBoxLayout(self.selectable) + selectable_group = QGroupBox(self.tr("Optional downloads"), self) + self.selectable_layout = QVBoxLayout(selectable_group) self.selectable_layout.setSpacing(0) self.selectable_checks: List[TagCheckBox] = [] self.config_tags: Optional[List[str]] = None - self.verify_button = QPushButton(self.tr("Verify")) - self.verify_button.clicked.connect(self.__on_verify) - - self.cancel_button = QPushButton(self.tr("Cancel")) - self.cancel_button.clicked.connect(self.__on_cancel) - - button_layout = QHBoxLayout() - button_layout.addWidget(self.cancel_button) - button_layout.addStretch(1) - button_layout.addWidget(self.verify_button) - - layout = QVBoxLayout(self) + layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetFixedSize) - layout.addWidget(self.title_label) - layout.addWidget(self.selectable) - layout.addLayout(button_layout) + layout.addWidget(title_label) + layout.addWidget(selectable_group) + + self.setCentralLayout(layout) + + self.accept_button.setText(self.tr("Verify")) + self.accept_button.setIcon(icon("fa.check")) self.options: SelectiveDownloadsModel = SelectiveDownloadsModel(rgame.app_name) config_disable_sdl = self.core.lgd.config.getboolean(self.rgame.app_name, "disable_sdl", fallback=False) sdl_name = get_sdl_appname(self.rgame.app_name) if not config_disable_sdl and sdl_name is not None: - self.reset_sdl_list() + self.create_sdl_list() else: self.options.accepted = True - self.close() + self.accept() - def reset_sdl_list(self): + def create_sdl_list(self): platform = self.rgame.igame.platform for cb in self.selectable_checks: cb.disconnect() @@ -91,17 +79,11 @@ class SelectiveDownloadsDialog(QDialog): cb.setChecked(True) self.selectable_layout.addWidget(cb) self.selectable_checks.append(cb) - # for cb in self.selectable_checks: - # cb.stateChanged.connect(self.option_changed) - # self.selectable.setWidget(widget) - else: - self.selectable.setDisabled(True) - def closeEvent(self, a0: QCloseEvent) -> None: + def done_handler(self): self.result_ready.emit(self.rgame, self.options) - super(SelectiveDownloadsDialog, self).closeEvent(a0) - def __on_verify(self): + def accept_handler(self): install_tag = [""] for cb in self.selectable_checks: if data := cb.isChecked(): @@ -109,17 +91,10 @@ class SelectiveDownloadsDialog(QDialog): install_tag.extend(data) self.options.accepted = True self.options.install_tag = install_tag - self.close() - def __on_cancel(self): + def reject_handler(self): self.options.accepted = False self.options.install_tag = None - self.close() - - def keyPressEvent(self, e: QKeyEvent) -> None: - if e.key() == Qt.Key_Escape: - e.accept() - self.__on_cancel() class TagCheckBox(QCheckBox): diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index 9eb2242d..ed490ad0 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -17,7 +17,7 @@ from PyQt5.QtWidgets import ( ) from rare.models.install import SelectiveDownloadsModel -from rare.components.dialogs.selective_dialog import SelectiveDownloadsDialog +from rare.components.dialogs.selective_dialog import SelectiveDialog from rare.models.game import RareGame from rare.shared import RareCore from rare.shared.workers import VerifyWorker, MoveWorker @@ -165,22 +165,21 @@ class GameInfo(QWidget, SideTabContents): ) return if self.rgame.sdl_name is not None: - selective_dialog = SelectiveDownloadsDialog( + selective_dialog = SelectiveDialog( self.rgame, parent=self ) selective_dialog.result_ready.connect(self.verify_game) - selective_dialog.exec() + selective_dialog.open() else: self.verify_game(self.rgame) @pyqtSlot(RareGame, SelectiveDownloadsModel) def verify_game(self, rgame: RareGame, sdl_model: SelectiveDownloadsModel = None): - if sdl_model: - if sdl_model.accepted: - self.core.lgd.config.set(rgame.app_name, "install_tags", ','.join(sdl_model.install_tag)) - self.core.lgd.save_config() - else: + if sdl_model is not None: + if not sdl_model.accepted or sdl_model.install_tag is None: return + self.core.lgd.config.set(rgame.app_name, "install_tags", ','.join(sdl_model.install_tag)) + self.core.lgd.save_config() worker = VerifyWorker(self.core, self.args, rgame) worker.signals.progress.connect(self.__on_verify_progress) worker.signals.result.connect(self.__on_verify_result) diff --git a/rare/models/install.py b/rare/models/install.py index 1118377a..05ef39ef 100644 --- a/rare/models/install.py +++ b/rare/models/install.py @@ -124,3 +124,11 @@ class SelectiveDownloadsModel: app_name: str accepted: bool = None install_tag: Optional[List[str]] = None + + def __bool__(self): + return ( + bool(self.app_name) + and (self.accepted is not None) + and (self.install_tag is not None) + ) + From e467dc996c4ad855361ac8d0f44e4e20e7a15263 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Thu, 28 Dec 2023 14:27:31 +0200 Subject: [PATCH 03/17] GameDlcWidget: Dynamically load DLC cover image on show --- rare/components/tabs/games/game_info/game_dlc.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rare/components/tabs/games/game_info/game_dlc.py b/rare/components/tabs/games/game_info/game_dlc.py index 9c786e7b..77b20115 100644 --- a/rare/components/tabs/games/game_info/game_dlc.py +++ b/rare/components/tabs/games/game_info/game_dlc.py @@ -1,6 +1,7 @@ from typing import Optional, List from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal +from PyQt5.QtGui import QShowEvent from PyQt5.QtWidgets import QFrame, QMessageBox, QToolBox from rare.models.game import RareGame @@ -37,6 +38,14 @@ class GameDlcWidget(QFrame): @pyqtSlot() def __update(self): self.ui.action_button.setEnabled(self.rdlc.is_idle) + self.image.setPixmap(self.rdlc.pixmap) + + def showEvent(self, a0: QShowEvent) -> None: + if a0.spontaneous(): + return super().showEvent(a0) + if self.rdlc.pixmap.isNull(): + self.rdlc.load_pixmap() + super().showEvent(a0) class InstalledGameDlcWidget(GameDlcWidget): From 1914beb5e642b0afed0c25ab97da94a84ff4fef5 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sat, 30 Dec 2023 18:35:37 +0200 Subject: [PATCH 04/17] RareGameBase: Also look for `the EA app` when identifying origin games --- rare/models/base_game.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rare/models/base_game.py b/rare/models/base_game.py index 0a1ee7ab..7da087c5 100644 --- a/rare/models/base_game.py +++ b/rare/models/base_game.py @@ -154,9 +154,7 @@ class RareGameBase(QObject): @return bool If the game is an Origin game """ - return ( - self.game.metadata.get("customAttributes", {}).get("ThirdPartyManagedApp", {}).get("value") == "Origin" - ) + return self.game.third_party_store in {"Origin", "the EA app"} @property def is_overlay(self): From e2bdbbab4030e6cc1a486126e48ad4bfd3bb5020 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sat, 30 Dec 2023 18:36:25 +0200 Subject: [PATCH 05/17] RareGame: Use Game property instead of looking into metadata --- rare/models/game.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rare/models/game.py b/rare/models/game.py index e3b19044..77f93d72 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -411,9 +411,7 @@ class RareGame(RareGameSlim): @property def is_ubisoft(self) -> bool: - return ( - self.game.metadata.get("customAttributes", {}).get("partnerLinkType", {}).get("value") == "ubisoft" - ) + return self.game.partner_link_type == "ubisoft" @property def folder_name(self) -> str: From ce97ec90868bb0de615f82217a4226182cb333a8 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Mon, 25 Dec 2023 12:34:22 +0200 Subject: [PATCH 06/17] Paths: Check for membership --- rare/utils/paths.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rare/utils/paths.py b/rare/utils/paths.py index 83083b37..119ef1e1 100644 --- a/rare/utils/paths.py +++ b/rare/utils/paths.py @@ -141,7 +141,7 @@ def get_rare_executable() -> List[str]: executable = [sys.executable] elif sys.argv[0].endswith("__main__.py"): executable = [sys.executable, "-m", "rare"] - elif platform.system() == "Linux" or platform.system() == "Darwin" or platform.system() == "FreeBSD": + elif platform.system() in {"Linux", "FreeBSD", "Darwin"}: if p := os.environ.get("APPIMAGE"): executable = [p] else: @@ -214,7 +214,7 @@ def create_desktop_link(app_name: str, app_title: str = "", link_name: str = "", else: logger.info(f"Creating shortcut for {app_title} at {shortcut_path}") - if platform.system() == "Linux" or platform.system() == "FreeBSD": + if platform.system() in {"Linux", "FreeBSD"}: executable = get_rare_executable() executable = shlex.join(executable) if not for_rare: From ef09354b62738fbbac053db25a9cbaf5ed141653 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:11:53 +0200 Subject: [PATCH 07/17] Launcher: cherry-pick some sourcery suggestions --- rare/launcher/lgd_helper.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rare/launcher/lgd_helper.py b/rare/launcher/lgd_helper.py index 38a79aa0..c7e5b412 100644 --- a/rare/launcher/lgd_helper.py +++ b/rare/launcher/lgd_helper.py @@ -75,8 +75,7 @@ def get_origin_params(core: LegendaryCore, app_name, offline: bool, if os.environ.get("container") == "flatpak": flatpak_command = ["flatpak-spawn", "--host"] - for name, value in env.items(): - flatpak_command.append(f"--env={name}={value}") + flatpak_command.extend(f"--env={name}={value}" for name, value in env.items()) command = flatpak_command + command else: for name, value in env.items(): @@ -113,8 +112,10 @@ def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs, if os.environ.get("container") == "flatpak": full_params.extend(["flatpak-spawn", "--host"]) - for name, value in params.environment.items(): - full_params.append(f"--env={name}={value}") + full_params.extend( + f"--env={name}={value}" + for name, value in params.environment.items() + ) else: for name, value in params.environment.items(): launch_args.environment.insert(name, value) @@ -141,7 +142,7 @@ def get_launch_args(core: LegendaryCore, args: InitArgs = None) -> LaunchArgs: resp = LaunchArgs() if not game: - raise GameArgsError(f"Could not find metadata for ") + raise GameArgsError("Could not find metadata for ") if game.third_party_store == "Origin": args.offline = False From 2cbe6cadd96aa1d9352a69adbe4c64d69e569cf8 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sun, 31 Dec 2023 17:51:13 +0200 Subject: [PATCH 08/17] Rare: Add launchable addons as games in the library --- rare/launcher/__init__.py | 5 ++- rare/launcher/lgd_helper.py | 62 ++++++++++++++++++------------------- rare/models/base_game.py | 22 ++++++++++++- rare/models/game.py | 9 ------ rare/shared/rare_core.py | 2 +- requirements-full.txt | 4 ++- requirements.txt | 3 +- 7 files changed, 60 insertions(+), 47 deletions(-) diff --git a/rare/launcher/__init__.py b/rare/launcher/__init__.py index 07d4052d..a453bd6f 100644 --- a/rare/launcher/__init__.py +++ b/rare/launcher/__init__.py @@ -37,9 +37,8 @@ class PreLaunchThread(QRunnable): def __init__(self, core: LegendaryCore, args: InitArgs, rgame: RareGameSlim, sync_action=None): super(PreLaunchThread, self).__init__() - self.logger = getLogger(type(self).__name__) - self.core = core self.signals = self.Signals() + self.logger = getLogger(type(self).__name__) self.args = args self.rgame = rgame self.sync_action = sync_action @@ -60,7 +59,7 @@ class PreLaunchThread(QRunnable): def prepare_launch(self, args: InitArgs) -> Optional[LaunchArgs]: try: - launch_args = get_launch_args(self.core, args) + launch_args = get_launch_args(self.rgame, args) except Exception as e: self.signals.error_occurred.emit(str(e)) return None diff --git a/rare/launcher/lgd_helper.py b/rare/launcher/lgd_helper.py index c7e5b412..38c21254 100644 --- a/rare/launcher/lgd_helper.py +++ b/rare/launcher/lgd_helper.py @@ -7,10 +7,9 @@ from logging import getLogger from typing import List from PyQt5.QtCore import QProcess, QProcessEnvironment -from legendary.models.game import InstalledGame, LaunchParameters - -from rare.lgndr.core import LegendaryCore +from legendary.models.game import LaunchParameters +from rare.models.base_game import RareGameSlim logger = getLogger("Helper") @@ -55,9 +54,11 @@ class LaunchArgs: return bool(self.executable) -def get_origin_params(core: LegendaryCore, app_name, offline: bool, - launch_args: LaunchArgs) -> LaunchArgs: - origin_uri = core.get_origin_uri(app_name, offline) +def get_origin_params(rgame: RareGameSlim, init_args: InitArgs, launch_args: LaunchArgs) -> LaunchArgs: + core = rgame.core + app_name = rgame.app_name + + origin_uri = core.get_origin_uri(app_name, init_args.offline) if platform.system() == "Windows": launch_args.executable = origin_uri launch_args.arguments = [] @@ -87,24 +88,25 @@ def get_origin_params(core: LegendaryCore, app_name, offline: bool, return launch_args -def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs, - launch_args: LaunchArgs) -> LaunchArgs: +def get_game_params(rgame: RareGameSlim, args: InitArgs, launch_args: LaunchArgs) -> LaunchArgs: if not args.offline: # skip for update - if not args.skip_update_check and not core.is_noupdate_game(igame.app_name): - # print("Checking for updates...") - # check updates + if not args.skip_update_check and not rgame.core.is_noupdate_game(rgame.app_name): try: - latest = core.get_asset( - igame.app_name, igame.platform, update=False - ) + latest = rgame.core.get_asset(rgame.app_name, rgame.igame.platform, update=False) except ValueError: raise GameArgsError("Metadata doesn't exist") else: - if latest.build_version != igame.version: + if latest.build_version != rgame.igame.version: raise GameArgsError("Game is not up to date. Please update first") - params: LaunchParameters = core.get_launch_parameters( - app_name=igame.app_name, offline=args.offline + if (not rgame.igame or not rgame.igame.executable) and rgame.game is not None: + # override installed game with base title + if rgame.is_launchable_addon: + app_name = rgame.game.metadata['mainGameItem']['releaseInfo'][0]['appId'] + rgame.igame = rgame.core.get_installed_game(app_name) + + params: LaunchParameters = rgame.core.get_launch_parameters( + app_name=rgame.game.app_name, offline=args.offline, addon_app_name=rgame.igame.app_name ) full_params = [] @@ -135,32 +137,30 @@ def get_game_params(core: LegendaryCore, igame: InstalledGame, args: InitArgs, return launch_args -def get_launch_args(core: LegendaryCore, args: InitArgs = None) -> LaunchArgs: - game = core.get_game(args.app_name) - igame = core.get_installed_game(args.app_name) +def get_launch_args(rgame: RareGameSlim, init_args: InitArgs = None) -> LaunchArgs: resp = LaunchArgs() - if not game: - raise GameArgsError("Could not find metadata for ") + if not rgame.game: + raise GameArgsError(f"Could not find metadata for {rgame.app_title}") - if game.third_party_store == "Origin": - args.offline = False + if rgame.is_origin: + init_args.offline = False else: - if not igame: + if not rgame.is_installed: raise GameArgsError("Game is not installed or has unsupported format") - if game.is_dlc: + if rgame.is_dlc: raise GameArgsError("Game is a DLC") - if not os.path.exists(igame.install_path): + if not os.path.exists(rgame.install_path): raise GameArgsError("Game path does not exist") - if game.third_party_store == "Origin": - resp = get_origin_params(core, args.app_name, args.offline, resp) + if rgame.is_origin: + resp = get_origin_params(rgame, init_args, resp) else: - resp = get_game_params(core, igame, args, resp) + resp = get_game_params(rgame, init_args, resp) - pre_cmd, wait = core.get_pre_launch_command(args.app_name) + pre_cmd, wait = rgame.core.get_pre_launch_command(init_args.app_name) resp.pre_launch_command, resp.pre_launch_wait = pre_cmd, wait return resp diff --git a/rare/models/base_game.py b/rare/models/base_game.py index 7da087c5..878fffda 100644 --- a/rare/models/base_game.py +++ b/rare/models/base_game.py @@ -160,6 +160,24 @@ class RareGameBase(QObject): def is_overlay(self): return self.app_name == eos.EOSOverlayApp.app_name + @property + def is_dlc(self) -> bool: + """! + @brief Property to report if Game is a dlc + + @return bool + """ + return self.game.is_dlc + + @property + def is_launchable_addon(self) -> bool: + # lk: the attribute doesn't exist in the currently released version + # FIXME: remove after legendary >= 0.20.35 + try: + return self.game.is_launchable_addon + except AttributeError: + return False + @property def version(self) -> str: """! @@ -190,7 +208,9 @@ class RareGameSlim(RareGameBase): @property def is_installed(self) -> bool: - return True + if self.is_origin: + return True + return self.igame is not None def set_installed(self, installed: bool) -> None: pass diff --git a/rare/models/game.py b/rare/models/game.py index 77f93d72..ea701015 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -376,15 +376,6 @@ class RareGame(RareGameSlim): if not needs and os.path.exists(self.repair_file): os.unlink(self.repair_file) - @property - def is_dlc(self) -> bool: - """! - @brief Property to report if Game is a dlc - - @return bool - """ - return self.game.is_dlc - @property def is_unreal(self) -> bool: """! diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index 25c46bf3..6000a3b9 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -374,7 +374,7 @@ class RareCore(QObject): @property def games(self) -> Iterator[RareGame]: - return self.__filter_games(lambda game: not game.is_dlc) + return self.__filter_games(lambda game: not game.is_dlc or game.is_launchable_addon) @property def installed_games(self) -> Iterator[RareGame]: diff --git a/requirements-full.txt b/requirements-full.txt index 93436a61..f8dbdeb8 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -2,8 +2,10 @@ requests PyQt5 QtAwesome setuptools -legendary-gl>=0.20.33 +legendary-gl>=0.20.34; platform_system != "Windows" or platform_system != "Darwin" +legendary-gl @ git+https://github.com/derrod/legendary@96e07ff ; platform_system == "Windows" or platform_system == "Darwin" orjson +vdf; platform_system != "Windows" pywin32; platform_system == "Windows" pywebview[qt]; platform_system == "Linux" pywebview[qt]; platform_system == "FreeBSD" diff --git a/requirements.txt b/requirements.txt index 7301263b..b8298181 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,8 @@ requests PyQt5 QtAwesome setuptools -legendary-gl>=0.20.34 +legendary-gl>=0.20.34; platform_system != "Windows" or platform_system != "Darwin" +legendary-gl @ git+https://github.com/derrod/legendary@96e07ff ; platform_system == "Windows" or platform_system == "Darwin" orjson vdf; platform_system != "Windows" pywin32; platform_system == "Windows" From 456050c91c8cf94b96d5027d8f0f4675f1862123 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Mon, 25 Dec 2023 22:11:06 +0200 Subject: [PATCH 09/17] RareException: Use `quit()` instead of `exit()` --- rare/components/__init__.py | 2 +- rare/widgets/rare_app.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rare/components/__init__.py b/rare/components/__init__.py index e120412f..841b3af2 100644 --- a/rare/components/__init__.py +++ b/rare/components/__init__.py @@ -31,7 +31,7 @@ class RareException(RareAppException): except Exception as e: self.logger.fatal(str(e)) QMessageBox.warning(None, "Error", self.tr("Failed to login")) - QApplication.exit(1) + QApplication.quit() return False diff --git a/rare/widgets/rare_app.py b/rare/widgets/rare_app.py index 7a731958..673bec37 100644 --- a/rare/widgets/rare_app.py +++ b/rare/widgets/rare_app.py @@ -39,11 +39,11 @@ class RareAppException(QObject): self.logger.fatal(message) action = QMessageBox.warning( None, exc_type.__name__, message, - buttons=QMessageBox.Ignore | QMessageBox.Close, - defaultButton=QMessageBox.Ignore + buttons=QMessageBox.Ignore | QMessageBox.Abort, + defaultButton=QMessageBox.Abort ) - if action == QMessageBox.RejectRole: - QApplication.exit(1) + if action == QMessageBox.Abort: + QApplication.quit() class RareApp(QApplication): From fd22d831ebd5a43467bd096cf1639cd1bb920e15 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:21:41 +0200 Subject: [PATCH 10/17] Rare: refactor timer names --- rare/components/__init__.py | 23 ++++++++++++----------- rare/components/main_window.py | 12 ++++++------ rare/shared/game_process.py | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/rare/components/__init__.py b/rare/components/__init__.py index 841b3af2..e76dacf1 100644 --- a/rare/components/__init__.py +++ b/rare/components/__init__.py @@ -51,7 +51,7 @@ class Rare(RareApp): # set Application name for settings self.main_window: Optional[MainWindow] = None self.launch_dialog: Optional[LaunchDialog] = None - self.timer: Optional[QTimer] = None + self.relogin_timer: Optional[QTimer] = None # This launches the application after it has been instantiated. # The timer's signal will be serviced once we call `exec()` on the application @@ -61,15 +61,15 @@ class Rare(RareApp): dt_exp = datetime.fromisoformat(self.core.lgd.userdata['expires_at'][:-1]).replace(tzinfo=timezone.utc) dt_now = datetime.utcnow().replace(tzinfo=timezone.utc) td = abs(dt_exp - dt_now) - self.timer.start(int(td.total_seconds() - 60) * 1000) + self.relogin_timer.start(int(td.total_seconds() - 60) * 1000) self.logger.info(f"Renewed session expires at {self.core.lgd.userdata['expires_at']}") - def re_login(self): + def relogin(self): self.logger.info("Session expires shortly. Renew session") try: - self.core.login() + self.core.login(force_refresh=True) except requests.exceptions.ConnectionError: - self.timer.start(3000) # try again if no connection + self.relogin_timer.start(3000) # try again if no connection return self.poke_timer() @@ -85,8 +85,9 @@ class Rare(RareApp): @pyqtSlot() def __on_start_app(self): - self.timer = QTimer() - self.timer.timeout.connect(self.re_login) + self.relogin_timer = QTimer(self) + self.relogin_timer.setTimerType(Qt.VeryCoarseTimer) + self.relogin_timer.timeout.connect(self.relogin) self.poke_timer() self.main_window = MainWindow() @@ -105,10 +106,10 @@ class Rare(RareApp): def __on_exit_app(self, exit_code=0): threadpool = QThreadPool.globalInstance() threadpool.waitForDone() - if self.timer is not None: - self.timer.stop() - self.timer.deleteLater() - self.timer = None + if self.relogin_timer is not None: + self.relogin_timer.stop() + self.relogin_timer.deleteLater() + self.relogin_timer = None self.rcore.deleteLater() del self.rcore self.processEvents() diff --git a/rare/components/main_window.py b/rare/components/main_window.py index 318fc6b6..6c998336 100644 --- a/rare/components/main_window.py +++ b/rare/components/main_window.py @@ -106,10 +106,10 @@ class MainWindow(QMainWindow): except ModuleNotFoundError: logger.warning("Discord RPC module not found") - self.timer = QTimer() - self.timer.setInterval(1000) - self.timer.timeout.connect(self.timer_finished) - self.timer.start() + self.singleton_timer = QTimer(self) + self.singleton_timer.setInterval(1000) + self.singleton_timer.timeout.connect(self.timer_finished) + self.singleton_timer.start() self.tray_icon: TrayIcon = TrayIcon(self) self.tray_icon.exit_app.connect(self.__on_exit_app) @@ -199,7 +199,7 @@ class MainWindow(QMainWindow): if action.startswith("show"): self.show() os.remove(file_path) - self.timer.start() + self.singleton_timer.start() @pyqtSlot() @pyqtSlot(int) @@ -258,7 +258,7 @@ class MainWindow(QMainWindow): e.ignore() return # FIXME: End of FIXME - self.timer.stop() + self.singleton_timer.stop() self.tray_icon.deleteLater() self.hide() self.exit_app.emit(self.__exit_code) diff --git a/rare/shared/game_process.py b/rare/shared/game_process.py index be69f335..b12b690a 100644 --- a/rare/shared/game_process.py +++ b/rare/shared/game_process.py @@ -2,7 +2,7 @@ import json import logging from enum import IntEnum -from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtSlot +from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtSlot, Qt from PyQt5.QtNetwork import QLocalSocket from PyQt5.QtWidgets import QMessageBox from legendary.models.game import Game From c7336ad04a0d13b8f45f3b3ae59f352ec4d90ba9 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:12:07 +0200 Subject: [PATCH 11/17] InstallDialog: Use ActionDialog base class --- rare/components/dialogs/install_dialog.py | 197 +++++++++---------- rare/ui/components/dialogs/install_dialog.py | 81 +++----- rare/ui/components/dialogs/install_dialog.ui | 88 +++------ 3 files changed, 146 insertions(+), 220 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 61621694..49c5f10a 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -3,20 +3,19 @@ import platform as pf import shutil from typing import Tuple, List, Union, Optional -from PyQt5.QtCore import Qt, QThreadPool, QSettings, QCoreApplication +from PyQt5.QtCore import QThreadPool, QSettings from PyQt5.QtCore import pyqtSignal, pyqtSlot -from PyQt5.QtGui import QCloseEvent, QKeyEvent, QShowEvent -from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox, QLayout, QWidget, QVBoxLayout, QFormLayout +from PyQt5.QtGui import QShowEvent +from PyQt5.QtWidgets import QFileDialog, QCheckBox, QWidget, QVBoxLayout, QFormLayout from legendary.utils.selective_dl import get_sdl_appname from rare.models.game import RareGame from rare.models.install import InstallDownloadModel, InstallQueueItemModel, InstallOptionsModel -from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton from rare.shared.workers.install_info import InstallInfoWorker from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog from rare.ui.components.dialogs.install_dialog_advanced import Ui_InstallDialogAdvanced -from rare.utils import config_helper -from rare.utils.misc import format_size +from rare.utils.misc import format_size, icon +from rare.widgets.dialogs import ActionDialog, dialog_title_game from rare.widgets.collapsible_widget import CollapsibleFrame from rare.widgets.indicator_edit import PathEdit, IndicatorReasonsCommon @@ -30,22 +29,37 @@ class InstallDialogAdvanced(CollapsibleFrame): super(InstallDialogAdvanced, self).__init__(widget=widget, title=title, parent=parent) -class InstallDialog(QDialog): +class InstallDialog(ActionDialog): result_ready = pyqtSignal(InstallQueueItemModel) def __init__(self, rgame: RareGame, options: InstallOptionsModel, parent=None): super(InstallDialog, self).__init__(parent=parent) + + header = self.tr("Install") + bicon = icon("ri.install-line") + if options.repair_mode: + header = self.tr("Repair") + bicon = icon("fa.wrench") + if options.repair_and_update: + header = self.tr("Repair and update") + elif options.update: + header = self.tr("Update") + elif options.reset_sdl: + header = self.tr("Modify") + bicon = icon("fa.gear") + self.setWindowTitle(dialog_title_game(header, rgame.app_title)) + + install_widget = QWidget(self) self.ui = Ui_InstallDialog() - self.ui.setupUi(self) - self.setAttribute(Qt.WA_DeleteOnClose, True) - self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) - # lk: set object names for CSS properties - self.ui.install_button.setObjectName("InstallButton") + self.ui.setupUi(install_widget) + + self.ui.title_label.setText(f"

{dialog_title_game(header, rgame.app_title)}

") self.core = rgame.core self.rgame = rgame - self.options = options + self.__options: InstallOptionsModel = options self.__download: Optional[InstallDownloadModel] = None + self.__queue_item: Optional[InstallQueueItemModel] = None self.advanced = InstallDialogAdvanced(parent=self) self.ui.advanced_layout.addWidget(self.advanced) @@ -54,25 +68,10 @@ class InstallDialog(QDialog): self.ui.selectable_layout.addWidget(self.selectable) self.options_changed = False - self.worker_running = False - self.reject_close = True self.threadpool = QThreadPool(self) self.threadpool.setMaxThreadCount(1) - if options.repair_mode: - header = self.tr("Repair") - if options.repair_and_update: - header = self.tr("Repair and update") - elif options.update: - header = self.tr("Update") - elif options.reset_sdl: - header = self.tr("Modify") - else: - header = self.tr("Install") - self.ui.title_label.setText(f'

{header} "{self.rgame.app_title}"

') - self.setWindowTitle(f'{header} "{self.rgame.app_title}" - {QCoreApplication.instance().applicationName()}') - if options.base_path: base_path = options.base_path elif rgame.is_installed: @@ -87,8 +86,8 @@ class InstallDialog(QDialog): save_func=self.save_install_edit, parent=self, ) - self.ui.install_dialog_layout.setWidget( - self.ui.install_dialog_layout.getWidgetPosition(self.ui.install_dir_label)[0], + self.ui.main_layout.setWidget( + self.ui.main_layout.getWidgetPosition(self.ui.install_dir_label)[0], QFormLayout.FieldRole, self.install_dir_edit ) @@ -139,9 +138,9 @@ class InstallDialog(QDialog): self.reset_sdl_list(self.ui.platform_combo.currentIndex()) self.check_incompatible_platform(self.ui.platform_combo.currentIndex()) - self.ui.install_button.setEnabled(False) + self.accept_button.setEnabled(False) - if self.options.overlay: + if self.__options.overlay: self.ui.platform_label.setEnabled(False) self.ui.platform_combo.setEnabled(False) self.advanced.ui.ignore_space_label.setEnabled(False) @@ -166,13 +165,17 @@ class InstallDialog(QDialog): self.non_reload_option_changed("shortcut") - self.ui.cancel_button.clicked.connect(self.__on_cancel) - self.ui.verify_button.clicked.connect(self.__on_verify) - self.ui.install_button.clicked.connect(self.__on_install) + self.advanced.ui.install_prereqs_check.setChecked(self.__options.install_prereqs) - self.advanced.ui.install_prereqs_check.setChecked(self.options.install_prereqs) + # lk: set object names for CSS properties + self.accept_button.setText(header) + self.accept_button.setIcon(bicon) + self.accept_button.setObjectName("InstallButton") - self.ui.install_dialog_layout.setSizeConstraint(QLayout.SetFixedSize) + self.action_button.setText(self.tr("Verify")) + self.action_button.setIcon(icon("fa.check")) + + self.setCentralWidget(install_widget) def showEvent(self, a0: QShowEvent) -> None: if a0.spontaneous(): @@ -181,13 +184,11 @@ class InstallDialog(QDialog): super().showEvent(a0) def execute(self): - if self.options.silent: - self.reject_close = False + if self.__options.silent: self.get_download_info() else: - self.setModal(True) - self.__on_verify() - self.show() + self.action_handler() + self.open() @pyqtSlot(int) def reset_install_dir(self, index: int): @@ -242,50 +243,46 @@ class InstallDialog(QDialog): self.error_box() def get_options(self): - self.options.base_path = "" if self.rgame.is_installed else self.install_dir_edit.text() - self.options.max_workers = self.advanced.ui.max_workers_spin.value() - self.options.shared_memory = self.advanced.ui.max_memory_spin.value() - self.options.order_opt = self.advanced.ui.dl_optimizations_check.isChecked() - self.options.force = self.advanced.ui.force_download_check.isChecked() - self.options.ignore_space = self.advanced.ui.ignore_space_check.isChecked() - self.options.no_install = self.advanced.ui.download_only_check.isChecked() - self.options.platform = self.ui.platform_combo.currentText() - self.options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked() - self.options.create_shortcut = self.ui.shortcut_check.isChecked() + self.__options.base_path = "" if self.rgame.is_installed else self.install_dir_edit.text() + self.__options.max_workers = self.advanced.ui.max_workers_spin.value() + self.__options.shared_memory = self.advanced.ui.max_memory_spin.value() + self.__options.order_opt = self.advanced.ui.dl_optimizations_check.isChecked() + self.__options.force = self.advanced.ui.force_download_check.isChecked() + self.__options.ignore_space = self.advanced.ui.ignore_space_check.isChecked() + self.__options.no_install = self.advanced.ui.download_only_check.isChecked() + self.__options.platform = self.ui.platform_combo.currentText() + self.__options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked() + self.__options.create_shortcut = self.ui.shortcut_check.isChecked() if self.selectable_checks: - self.options.install_tag = [""] + self.__options.install_tag = [""] for cb in self.selectable_checks: if data := cb.isChecked(): # noinspection PyTypeChecker - self.options.install_tag.extend(data) + self.__options.install_tag.extend(data) def get_download_info(self): self.__download = None - info_worker = InstallInfoWorker(self.core, self.options) + info_worker = InstallInfoWorker(self.core, self.__options) info_worker.signals.result.connect(self.on_worker_result) info_worker.signals.failed.connect(self.on_worker_failed) - info_worker.signals.finished.connect(self.on_worker_finished) - self.worker_running = True self.threadpool.start(info_worker) - def __on_verify(self): + def action_handler(self): self.error_box() message = self.tr("Updating...") self.ui.download_size_text.setText(message) self.ui.download_size_text.setStyleSheet("font-style: italic; font-weight: normal") self.ui.install_size_text.setText(message) self.ui.install_size_text.setStyleSheet("font-style: italic; font-weight: normal") - self.ui.cancel_button.setEnabled(False) - self.ui.verify_button.setEnabled(False) - self.ui.install_button.setEnabled(False) + self.setActive(True) self.options_changed = False self.get_options() self.get_download_info() def option_changed(self, path) -> Tuple[bool, str, int]: self.options_changed = True - self.ui.install_button.setEnabled(False) - self.ui.verify_button.setEnabled(not self.worker_running) + self.accept_button.setEnabled(False) + self.action_button.setEnabled(not self.active()) return True, path, IndicatorReasonsCommon.VALID def save_install_edit(self, path: str): @@ -296,33 +293,18 @@ class InstallDialog(QDialog): def non_reload_option_changed(self, option: str): if option == "download_only": - self.options.no_install = self.advanced.ui.download_only_check.isChecked() + self.__options.no_install = self.advanced.ui.download_only_check.isChecked() elif option == "shortcut": QSettings().setValue("create_shortcut", self.ui.shortcut_check.isChecked()) - self.options.create_shortcut = self.ui.shortcut_check.isChecked() + self.__options.create_shortcut = self.ui.shortcut_check.isChecked() elif option == "install_prereqs": - self.options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked() - - def __on_cancel(self): - if self.config_tags is not None: - config_helper.add_option(self.rgame.app_name, 'install_tags', ','.join(self.config_tags)) - else: - # lk: this is purely for cleaning any install tags we might have added erroneously to the config - config_helper.remove_option(self.rgame.app_name, 'install_tags') - - self.__download = None - self.reject_close = False - self.close() - - def __on_install(self): - self.reject_close = False - self.close() + self.__options.install_prereqs = self.advanced.ui.install_prereqs_check.isChecked() @staticmethod def same_platform(download: InstallDownloadModel) -> bool: platform = download.igame.platform if pf.system() == "Windows": - return platform == "Windows" or platform == "Win32" + return platform in {"Windows", "Win32"} elif pf.system() == "Darwin": return platform == "Mac" else: @@ -330,6 +312,7 @@ class InstallDialog(QDialog): @pyqtSlot(InstallDownloadModel) def on_worker_result(self, download: InstallDownloadModel): + self.setActive(False) self.__download = download download_size = download.analysis.dl_size install_size = download.analysis.install_size @@ -337,14 +320,13 @@ class InstallDialog(QDialog): if download_size or (not download_size and (download.game.is_dlc or download.repair)): self.ui.download_size_text.setText(format_size(download_size)) self.ui.download_size_text.setStyleSheet("font-style: normal; font-weight: bold") - self.ui.install_button.setEnabled(not self.options_changed) + self.accept_button.setEnabled(not self.options_changed) else: self.ui.install_size_text.setText(self.tr("Game already installed")) self.ui.install_size_text.setStyleSheet("font-style: italics; font-weight: normal") self.ui.install_size_text.setText(format_size(install_size)) self.ui.install_size_text.setStyleSheet("font-style: normal; font-weight: bold") - self.ui.verify_button.setEnabled(self.options_changed) - self.ui.cancel_button.setEnabled(True) + self.action_button.setEnabled(self.options_changed) has_prereqs = bool(download.igame.prereq_info) and not download.igame.prereq_info.get("installed", False) if has_prereqs: prereq_name = download.igame.prereq_info.get("name", "") @@ -357,18 +339,19 @@ class InstallDialog(QDialog): self.advanced.ui.install_prereqs_label.setEnabled(has_prereqs) self.advanced.ui.install_prereqs_check.setEnabled(has_prereqs) self.advanced.ui.install_prereqs_check.setChecked(has_prereqs and self.same_platform(download)) - if self.options.silent: - self.close() + if self.__options.silent: + self.accept() def on_worker_failed(self, message: str): + self.setActive(False) error_text = self.tr("Error") self.ui.download_size_text.setText(error_text) self.ui.install_size_text.setText(error_text) self.error_box(error_text, message) - self.ui.verify_button.setEnabled(self.options_changed) - self.ui.cancel_button.setEnabled(True) - if self.options.silent: - self.show() + self.action_button.setEnabled(self.options_changed) + self.accept_button.setEnabled(False) + if self.__options.silent: + self.open() def error_box(self, label: str = "", message: str = ""): self.ui.warning_label.setVisible(bool(label)) @@ -376,25 +359,23 @@ class InstallDialog(QDialog): self.ui.warning_text.setVisible(bool(message)) self.ui.warning_text.setText(message) - def on_worker_finished(self): - self.worker_running = False + def done_handler(self): + self.threadpool.clear() + self.threadpool.waitForDone() + self.result_ready.emit(self.__queue_item) - # lk: happens when close() is called, also when top right 'X' is pressed. - # lk: reject any events not coming from the buttons in case the WM - # lk: doesn't honor the window hints - def closeEvent(self, a0: QCloseEvent) -> None: - if self.reject_close: - a0.ignore() - else: - self.threadpool.clear() - self.threadpool.waitForDone() - self.result_ready.emit(InstallQueueItemModel(options=self.options, download=self.__download)) - super(InstallDialog, self).closeEvent(a0) + # lk: __download is already set at this point so just do nothing. + def accept_handler(self): + self.__queue_item = InstallQueueItemModel(options=self.__options, download=self.__download) - def keyPressEvent(self, e: QKeyEvent) -> None: - if e.key() == Qt.Key_Escape: - e.accept() - self.__on_cancel() + def reject_handler(self): + # FIXME: This is implemented through the selective downloads dialog now. remove soon + # if self.config_tags is not None: + # config_helper.set_option(self.rgame.app_name, 'install_tags', ','.join(self.config_tags)) + # else: + # # lk: this is purely for cleaning any install tags we might have added erroneously to the config + # config_helper.remove_option(self.rgame.app_name, 'install_tags') + self.__queue_item = InstallQueueItemModel(options=self.__options, download=None) class TagCheckBox(QCheckBox): diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index 65e0dbcb..10c5d871 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -14,20 +14,20 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_InstallDialog(object): def setupUi(self, InstallDialog): InstallDialog.setObjectName("InstallDialog") - InstallDialog.resize(272, 238) + InstallDialog.resize(179, 204) InstallDialog.setWindowTitle("InstallDialog") - self.install_dialog_layout = QtWidgets.QFormLayout(InstallDialog) - self.install_dialog_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) - self.install_dialog_layout.setObjectName("install_dialog_layout") + self.main_layout = QtWidgets.QFormLayout(InstallDialog) + self.main_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.main_layout.setObjectName("main_layout") self.title_label = QtWidgets.QLabel(InstallDialog) self.title_label.setObjectName("title_label") - self.install_dialog_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label) + self.main_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label) self.install_dir_label = QtWidgets.QLabel(InstallDialog) self.install_dir_label.setObjectName("install_dir_label") - self.install_dialog_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.install_dir_label) + self.main_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.install_dir_label) self.platform_label = QtWidgets.QLabel(InstallDialog) self.platform_label.setObjectName("platform_label") - self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.platform_label) + self.main_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.platform_label) self.platform_combo = QtWidgets.QComboBox(InstallDialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -35,43 +35,54 @@ class Ui_InstallDialog(object): sizePolicy.setHeightForWidth(self.platform_combo.sizePolicy().hasHeightForWidth()) self.platform_combo.setSizePolicy(sizePolicy) self.platform_combo.setObjectName("platform_combo") - self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.platform_combo) + self.main_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.platform_combo) self.shortcut_label = QtWidgets.QLabel(InstallDialog) self.shortcut_label.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) self.shortcut_label.setObjectName("shortcut_label") - self.install_dialog_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.shortcut_label) + self.main_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.shortcut_label) self.shortcut_check = QtWidgets.QCheckBox(InstallDialog) self.shortcut_check.setText("") self.shortcut_check.setObjectName("shortcut_check") - self.install_dialog_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.shortcut_check) + self.main_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.shortcut_check) self.selectable_layout = QtWidgets.QVBoxLayout() self.selectable_layout.setObjectName("selectable_layout") - self.install_dialog_layout.setLayout(4, QtWidgets.QFormLayout.SpanningRole, self.selectable_layout) + self.main_layout.setLayout(4, QtWidgets.QFormLayout.SpanningRole, self.selectable_layout) self.advanced_layout = QtWidgets.QVBoxLayout() self.advanced_layout.setObjectName("advanced_layout") - self.install_dialog_layout.setLayout(5, QtWidgets.QFormLayout.SpanningRole, self.advanced_layout) + self.main_layout.setLayout(5, QtWidgets.QFormLayout.SpanningRole, self.advanced_layout) self.download_size_label = QtWidgets.QLabel(InstallDialog) self.download_size_label.setObjectName("download_size_label") - self.install_dialog_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.download_size_label) + self.main_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.download_size_label) self.download_size_text = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) self.download_size_text.setFont(font) self.download_size_text.setObjectName("download_size_text") - self.install_dialog_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.download_size_text) + self.main_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.download_size_text) self.install_size_label = QtWidgets.QLabel(InstallDialog) self.install_size_label.setObjectName("install_size_label") - self.install_dialog_layout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.install_size_label) + self.main_layout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.install_size_label) self.install_size_text = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) self.install_size_text.setFont(font) self.install_size_text.setWordWrap(True) self.install_size_text.setObjectName("install_size_text") - self.install_dialog_layout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.install_size_text) + self.main_layout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.install_size_text) + self.avail_space_label = QtWidgets.QLabel(InstallDialog) + self.avail_space_label.setObjectName("avail_space_label") + self.main_layout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.avail_space_label) + self.avail_space = QtWidgets.QLabel(InstallDialog) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.avail_space.setFont(font) + self.avail_space.setText("") + self.avail_space.setObjectName("avail_space") + self.main_layout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.avail_space) self.warning_label = QtWidgets.QLabel(InstallDialog) self.warning_label.setObjectName("warning_label") - self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.warning_label) + self.main_layout.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.warning_label) self.warning_text = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) @@ -81,57 +92,29 @@ class Ui_InstallDialog(object): self.warning_text.setWordWrap(True) self.warning_text.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) self.warning_text.setObjectName("warning_text") - self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.warning_text) - self.button_layout = QtWidgets.QHBoxLayout() - self.button_layout.setObjectName("button_layout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.button_layout.addItem(spacerItem) - self.cancel_button = QtWidgets.QPushButton(InstallDialog) - self.cancel_button.setObjectName("cancel_button") - self.button_layout.addWidget(self.cancel_button) - self.verify_button = QtWidgets.QPushButton(InstallDialog) - self.verify_button.setObjectName("verify_button") - self.button_layout.addWidget(self.verify_button) - self.install_button = QtWidgets.QPushButton(InstallDialog) - self.install_button.setObjectName("install_button") - self.button_layout.addWidget(self.install_button) - self.install_dialog_layout.setLayout(10, QtWidgets.QFormLayout.SpanningRole, self.button_layout) - self.avail_space_lbl = QtWidgets.QLabel(InstallDialog) - self.avail_space_lbl.setObjectName("avail_space_lbl") - self.install_dialog_layout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.avail_space_lbl) - self.avail_space = QtWidgets.QLabel(InstallDialog) - font = QtGui.QFont() - font.setBold(True) - font.setWeight(75) - self.avail_space.setFont(font) - self.avail_space.setText("") - self.avail_space.setObjectName("avail_space") - self.install_dialog_layout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.avail_space) + self.main_layout.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.warning_text) self.retranslateUi(InstallDialog) def retranslateUi(self, InstallDialog): _translate = QtCore.QCoreApplication.translate self.title_label.setText(_translate("InstallDialog", "error")) - self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) + self.install_dir_label.setText(_translate("InstallDialog", "Install folder")) self.platform_label.setText(_translate("InstallDialog", "Platform")) self.shortcut_label.setText(_translate("InstallDialog", "Create shortcut")) self.download_size_label.setText(_translate("InstallDialog", "Download size")) self.download_size_text.setText(_translate("InstallDialog", "Click verify...")) self.install_size_label.setText(_translate("InstallDialog", "Total install size")) self.install_size_text.setText(_translate("InstallDialog", "Click verify...")) + self.avail_space_label.setText(_translate("InstallDialog", "Available space")) self.warning_label.setText(_translate("InstallDialog", "Warning")) self.warning_text.setText(_translate("InstallDialog", "None")) - self.cancel_button.setText(_translate("InstallDialog", "Cancel")) - self.verify_button.setText(_translate("InstallDialog", "Verify")) - self.install_button.setText(_translate("InstallDialog", "Install")) - self.avail_space_lbl.setText(_translate("InstallDialog", "Available space")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - InstallDialog = QtWidgets.QDialog() + InstallDialog = QtWidgets.QWidget() ui = Ui_InstallDialog() ui.setupUi(InstallDialog) InstallDialog.show() diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index d75569f5..e47e3eaf 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -1,19 +1,19 @@ InstallDialog - + 0 0 - 272 - 238 + 179 + 204 InstallDialog - + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -27,7 +27,7 @@ - Install directory + Install folder @@ -112,6 +112,26 @@
+ + + + Available space + + + + + + + + 75 + true + + + + + + + @@ -143,64 +163,6 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - Verify - - - - - - - Install - - - - - - - - - Available space - - - - - - - - 75 - true - - - - - - - From 534c45818a142902f8cc3c33c9f752b3d9f53f51 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:34:35 +0200 Subject: [PATCH 12/17] GameInfo: Update button description strings and icons --- .../components/tabs/games/game_info/game_info.py | 6 +++--- .../components/tabs/games/game_info/game_info.py | 16 ++++++++-------- .../components/tabs/games/game_info/game_info.ui | 14 +++++++------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index ed490ad0..aed40fea 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -44,12 +44,12 @@ class GameInfo(QWidget, SideTabContents): self.ui.uninstall_button.setObjectName("UninstallButton") self.ui.install_button.setIcon(icon("ri.install-line")) - self.ui.import_button.setIcon(icon("mdi.file-import")) + self.ui.import_button.setIcon(icon("mdi.application-import")) self.ui.modify_button.setIcon(icon("fa.gear")) self.ui.verify_button.setIcon(icon("fa.check")) self.ui.repair_button.setIcon(icon("fa.wrench")) - self.ui.move_button.setIcon(icon("mdi.folder-move")) + self.ui.move_button.setIcon(icon("mdi.folder-move-outline")) self.ui.uninstall_button.setIcon(icon("ri.uninstall-line")) self.rcore = RareCore.instance() @@ -393,7 +393,7 @@ class GameInfo(QWidget, SideTabContents): self.ui.install_button.setText(self.tr("Link/Launch")) self.ui.game_actions_stack.setCurrentWidget(self.ui.uninstalled_page) else: - self.ui.install_button.setText(self.tr("Install Game")) + self.ui.install_button.setText(self.tr("Install")) self.move_game_pop_up.update_game(rgame) diff --git a/rare/ui/components/tabs/games/game_info/game_info.py b/rare/ui/components/tabs/games/game_info/game_info.py index 983b97b4..bce4a9d5 100644 --- a/rare/ui/components/tabs/games/game_info/game_info.py +++ b/rare/ui/components/tabs/games/game_info/game_info.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'rare/ui/components/tabs/games/game_info/game_info.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -336,13 +336,13 @@ class Ui_GameInfo(object): self.lbl_install_path.setText(_translate("GameInfo", "Installation Path")) self.lbl_platform.setText(_translate("GameInfo", "Platform")) self.lbl_game_actions.setText(_translate("GameInfo", "Actions")) - self.modify_button.setText(_translate("GameInfo", "Modify Installation")) - self.verify_button.setText(_translate("GameInfo", "Verify Installation")) - self.repair_button.setText(_translate("GameInfo", "Repair Installation")) - self.move_button.setText(_translate("GameInfo", "Move Installation")) - self.uninstall_button.setText(_translate("GameInfo", "Uninstall Game")) - self.install_button.setText(_translate("GameInfo", "Install Game")) - self.import_button.setText(_translate("GameInfo", "Import Game")) + self.modify_button.setText(_translate("GameInfo", "Modify")) + self.verify_button.setText(_translate("GameInfo", "Verify")) + self.repair_button.setText(_translate("GameInfo", "Repair")) + self.move_button.setText(_translate("GameInfo", "Move")) + self.uninstall_button.setText(_translate("GameInfo", "Uninstall")) + self.install_button.setText(_translate("GameInfo", "Install")) + self.import_button.setText(_translate("GameInfo", "Import")) if __name__ == "__main__": diff --git a/rare/ui/components/tabs/games/game_info/game_info.ui b/rare/ui/components/tabs/games/game_info/game_info.ui index 23921888..64796002 100644 --- a/rare/ui/components/tabs/games/game_info/game_info.ui +++ b/rare/ui/components/tabs/games/game_info/game_info.ui @@ -395,7 +395,7 @@ - Modify Installation + Modify @@ -424,7 +424,7 @@ - Verify Installation + Verify @@ -461,7 +461,7 @@ - Repair Installation + Repair @@ -496,7 +496,7 @@ - Move Installation + Move QToolButton::InstantPopup @@ -542,7 +542,7 @@ - Uninstall Game + Uninstall @@ -565,14 +565,14 @@ - Install Game + Install - Import Game + Import From 08f2d50d4fcdfcc97698f1cc8d1cd0aaf9cb4d25 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:18:19 +0200 Subject: [PATCH 13/17] RareApp: Always collect verbose version information at the start of the log --- rare/widgets/rare_app.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/rare/widgets/rare_app.py b/rare/widgets/rare_app.py index 673bec37..c3c86c66 100644 --- a/rare/widgets/rare_app.py +++ b/rare/widgets/rare_app.py @@ -85,13 +85,6 @@ class RareApp(QApplication): logging.getLogger("requests").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("asyncio").setLevel(logging.WARNING) - self.logger.info( - f"Launching Rare version {rare.__version__} Codename: {rare.__codename__}\n" - f" - Using Legendary {legendary.__version__} Codename: {legendary.__codename__} as backend\n" - f" - Operating System: {platform.system()}, Python version: {platform.python_version()}\n" - f" - Running {sys.executable} {' '.join(sys.argv)}\n" - f" - Qt version: {QT_VERSION_STR}, PyQt version: {PYQT_VERSION_STR}" - ) else: logging.basicConfig( format="[%(name)s] %(levelname)s: %(message)s", @@ -100,8 +93,13 @@ class RareApp(QApplication): ) file_handler.setLevel(logging.DEBUG) logging.root.addHandler(file_handler) - self.logger.info(f"Launching Rare version {rare.__version__}") - self.logger.info(f"Operating System: {platform.system()}") + self.logger.info( + f"Launching Rare version {rare.__version__} Codename: {rare.__codename__}\n" + f" - Using Legendary {legendary.__version__} Codename: {legendary.__codename__} as backend\n" + f" - Operating System: {platform.system()}, Python version: {platform.python_version()}\n" + f" - Running {sys.executable} {' '.join(sys.argv)}\n" + f" - Qt version: {QT_VERSION_STR}, PyQt version: {PYQT_VERSION_STR}" + ) self.settings = QSettings() From 89c1a4eaf4c16b9b68dec1a8a8eae58f2d41fe51 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:38:51 +0200 Subject: [PATCH 14/17] Launcher: Refactor cloud sync dialog to use ButtonDialog base class --- rare/components/dialogs/cloud_save_dialog.py | 94 ---------------- .../tabs/games/game_info/cloud_saves.py | 30 ++--- rare/launcher/__init__.py | 37 +++++-- rare/launcher/cloud_sync_dialog.py | 103 ++++++++++++++++++ rare/launcher/console.py | 2 +- .../ui/components/dialogs/sync_save_dialog.py | 56 ---------- .../ui/components/dialogs/sync_save_dialog.ui | 61 ----------- .../games/game_info/cloud_settings_widget.py | 52 +++++++++ .../games/game_info/cloud_settings_widget.ui | 53 +++++++++ .../{sync_widget.py => cloud_sync_widget.py} | 76 ++++++------- .../{sync_widget.ui => cloud_sync_widget.ui} | 10 +- .../tabs/games/game_info/cloud_widget.py | 44 -------- .../tabs/games/game_info/cloud_widget.ui | 38 ------- .../extra => launcher}/__init__.py | 0 .../extra => launcher}/console_env.py | 0 .../extra => launcher}/console_env.ui | 0 16 files changed, 294 insertions(+), 362 deletions(-) delete mode 100644 rare/components/dialogs/cloud_save_dialog.py create mode 100644 rare/launcher/cloud_sync_dialog.py delete mode 100644 rare/ui/components/dialogs/sync_save_dialog.py delete mode 100644 rare/ui/components/dialogs/sync_save_dialog.ui create mode 100644 rare/ui/components/tabs/games/game_info/cloud_settings_widget.py create mode 100644 rare/ui/components/tabs/games/game_info/cloud_settings_widget.ui rename rare/ui/components/tabs/games/game_info/{sync_widget.py => cloud_sync_widget.py} (60%) rename rare/ui/components/tabs/games/game_info/{sync_widget.ui => cloud_sync_widget.ui} (94%) delete mode 100644 rare/ui/components/tabs/games/game_info/cloud_widget.py delete mode 100644 rare/ui/components/tabs/games/game_info/cloud_widget.ui rename rare/ui/{components/extra => launcher}/__init__.py (100%) rename rare/ui/{components/extra => launcher}/console_env.py (100%) rename rare/ui/{components/extra => launcher}/console_env.ui (100%) diff --git a/rare/components/dialogs/cloud_save_dialog.py b/rare/components/dialogs/cloud_save_dialog.py deleted file mode 100644 index 56067950..00000000 --- a/rare/components/dialogs/cloud_save_dialog.py +++ /dev/null @@ -1,94 +0,0 @@ -import datetime -import sys -from logging import getLogger - -from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QDialog, QSizePolicy, QLayout, QApplication, QWidget -from legendary.core import LegendaryCore -from legendary.models.game import InstalledGame - -from rare.ui.components.dialogs.sync_save_dialog import Ui_SyncSaveDialog -from rare.ui.components.tabs.games.game_info.sync_widget import Ui_SyncWidget -from rare.utils.misc import icon - -logger = getLogger("Cloud Saves") - - -class CloudSaveDialog(QDialog, Ui_SyncSaveDialog): - DOWNLOAD = 2 - UPLOAD = 1 - CANCEL = 0 - SKIP = 3 - - def __init__( - self, - igame: InstalledGame, - dt_local: datetime.datetime, - dt_remote: datetime.datetime, - ): - super(CloudSaveDialog, self).__init__() - self.setupUi(self) - - self.sync_widget = QWidget() - self.sync_ui = Ui_SyncWidget() - self.sync_ui.setupUi(self.sync_widget) - - self.sync_widget_layout.addWidget(self.sync_widget) - - self.setAttribute(Qt.WA_DeleteOnClose, True) - self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint) - - self.status = self.CANCEL - - self.title_label.setText(f"

{self.title_label.text()} {igame.title}

") - - newer = self.tr("Newer") - if dt_remote and dt_local: - self.sync_ui.age_label_local.setText( - f"{newer}" if dt_remote < dt_local else " " - ) - self.sync_ui.age_label_remote.setText( - f"{newer}" if dt_remote > dt_local else " " - ) - # Set status, if one of them is None - elif dt_remote and not dt_local: - self.status = self.DOWNLOAD - elif not dt_remote and dt_local: - self.status = self.UPLOAD - else: - self.status = self.SKIP - - self.sync_ui.date_info_local.setText(dt_local.strftime("%A, %d. %B %Y %X") if dt_local else "None") - self.sync_ui.date_info_remote.setText(dt_remote.strftime("%A, %d. %B %Y %X") if dt_remote else "None") - - self.sync_ui.icon_local.setPixmap(icon("mdi.harddisk", "fa.desktop").pixmap(128, 128)) - self.sync_ui.icon_remote.setPixmap(icon("mdi.cloud-outline", "ei.cloud").pixmap(128, 128)) - - self.sync_ui.upload_button.clicked.connect(lambda: self.btn_clicked(self.UPLOAD)) - self.sync_ui.download_button.clicked.connect(lambda: self.btn_clicked(self.DOWNLOAD)) - self.cancel_button.clicked.connect(self.close) - - self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) - self.layout().setSizeConstraint(QLayout.SetFixedSize) - - def get_action(self): - if self.status == self.SKIP: - return self.SKIP - self.exec_() - return self.status - - def btn_clicked(self, status): - self.status = status - self.close() - - -def test_dialog(): - app = QApplication(sys.argv) - core = LegendaryCore() - dlg = CloudSaveDialog(core.get_installed_list()[0], datetime.datetime.now(), - datetime.datetime.strptime("2021,1", "%Y,%M")) - print(dlg.get_action()) - - -if __name__ == '__main__': - test_dialog() diff --git a/rare/components/tabs/games/game_info/cloud_saves.py b/rare/components/tabs/games/game_info/cloud_saves.py index d9cd9f23..87143f71 100644 --- a/rare/components/tabs/games/game_info/cloud_saves.py +++ b/rare/components/tabs/games/game_info/cloud_saves.py @@ -13,15 +13,15 @@ from PyQt5.QtWidgets import ( QMessageBox, QGroupBox, QVBoxLayout, - QSpacerItem, + QSpacerItem, QFormLayout, ) from legendary.models.game import SaveGameStatus from rare.models.game import RareGame from rare.shared import RareCore from rare.shared.workers.wine_resolver import WineResolver -from rare.ui.components.tabs.games.game_info.cloud_widget import Ui_CloudWidget -from rare.ui.components.tabs.games.game_info.sync_widget import Ui_SyncWidget +from rare.ui.components.tabs.games.game_info.cloud_settings_widget import Ui_CloudSettingsWidget +from rare.ui.components.tabs.games.game_info.cloud_sync_widget import Ui_CloudSyncWidget from rare.utils.misc import icon from rare.widgets.indicator_edit import PathEdit, IndicatorReasonsCommon from rare.widgets.loading_widget import LoadingWidget @@ -35,7 +35,7 @@ class CloudSaves(QWidget, SideTabContents): super(CloudSaves, self).__init__(parent=parent) self.sync_widget = QWidget(self) - self.sync_ui = Ui_SyncWidget() + self.sync_ui = Ui_CloudSyncWidget() self.sync_ui.setupUi(self.sync_widget) self.info_label = QLabel(self.tr("This game doesn't support cloud saves")) @@ -56,7 +56,7 @@ class CloudSaves(QWidget, SideTabContents): self.rgame: RareGame = None self.cloud_widget = QGroupBox(self) - self.cloud_ui = Ui_CloudWidget() + self.cloud_ui = Ui_CloudSettingsWidget() self.cloud_ui.setupUi(self.cloud_widget) self.cloud_save_path_edit = PathEdit( @@ -66,16 +66,20 @@ class CloudSaves(QWidget, SideTabContents): edit_func=self.edit_save_path, save_func=self.save_save_path, ) - self.cloud_ui.cloud_layout.addRow(QLabel(self.tr("Save path")), self.cloud_save_path_edit) + self.cloud_ui.main_layout.setWidget( + self.cloud_ui.main_layout.getWidgetPosition(self.cloud_ui.path_label)[0], + QFormLayout.FieldRole, + self.cloud_save_path_edit + ) self.compute_save_path_button = QPushButton(icon("fa.magic"), self.tr("Calculate path")) self.compute_save_path_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) self.compute_save_path_button.clicked.connect(self.compute_save_path) - self.cloud_ui.cloud_layout.addRow(None, self.compute_save_path_button) + self.cloud_ui.main_layout.addRow(None, self.compute_save_path_button) - self.cloud_ui.cloud_sync.stateChanged.connect( + self.cloud_ui.sync_check.stateChanged.connect( lambda: self.settings.setValue( - f"{self.rgame.app_name}/auto_sync_cloud", self.cloud_ui.cloud_sync.isChecked() + f"{self.rgame.app_name}/auto_sync_cloud", self.cloud_ui.sync_check.isChecked() ) ) @@ -172,7 +176,7 @@ class CloudSaves(QWidget, SideTabContents): self.sync_ui.age_label_local.setText("None") self.sync_ui.date_info_remote.setText("None") self.sync_ui.age_label_remote.setText("None") - self.cloud_ui.cloud_sync.setChecked(False) + self.cloud_ui.sync_check.setChecked(False) self.cloud_save_path_edit.setText("") return @@ -203,9 +207,9 @@ class CloudSaves(QWidget, SideTabContents): self.sync_ui.upload_button.setDisabled(not dt_local) self.sync_ui.download_button.setDisabled(not dt_remote) - self.cloud_ui.cloud_sync.blockSignals(True) - self.cloud_ui.cloud_sync.setChecked(self.rgame.auto_sync_saves) - self.cloud_ui.cloud_sync.blockSignals(False) + self.cloud_ui.sync_check.blockSignals(True) + self.cloud_ui.sync_check.setChecked(self.rgame.auto_sync_saves) + self.cloud_ui.sync_check.blockSignals(False) self.cloud_save_path_edit.setText(self.rgame.save_path if self.rgame.save_path else "") if platform.system() == "Windows" and not self.rgame.save_path: diff --git a/rare/launcher/__init__.py b/rare/launcher/__init__.py index a453bd6f..13a258ba 100644 --- a/rare/launcher/__init__.py +++ b/rare/launcher/__init__.py @@ -14,11 +14,11 @@ from PyQt5.QtNetwork import QLocalServer, QLocalSocket from PyQt5.QtWidgets import QApplication from legendary.models.game import SaveGameStatus -from rare.components.dialogs.cloud_save_dialog import CloudSaveDialog from rare.lgndr.core import LegendaryCore from rare.models.base_game import RareGameSlim from rare.models.launcher import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel from rare.widgets.rare_app import RareApp, RareAppException +from .cloud_sync_dialog import CloudSyncDialog, CloudSyncDialogResult from .console import Console from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError @@ -45,9 +45,9 @@ class PreLaunchThread(QRunnable): def run(self) -> None: self.logger.info(f"Sync action: {self.sync_action}") - if self.sync_action == CloudSaveDialog.UPLOAD: + if self.sync_action == CloudSyncDialogResult.UPLOAD: self.rgame.upload_saves(False) - elif self.sync_action == CloudSaveDialog.DOWNLOAD: + elif self.sync_action == CloudSyncDialogResult.DOWNLOAD: self.rgame.download_saves(False) else: self.logger.info("No sync action") @@ -212,15 +212,22 @@ class RareLauncher(RareApp): state, (dt_local, dt_remote) = self.rgame.save_game_state if state == SaveGameStatus.LOCAL_NEWER and not self.no_sync_on_exit: - action = CloudSaveDialog.UPLOAD + action = CloudSyncDialogResult.UPLOAD + self.__check_saved_finished(exit_code, action) else: - action = CloudSaveDialog(self.rgame.igame, dt_local, dt_remote).get_action() + sync_dialog = CloudSyncDialog(self.rgame.igame, dt_local, dt_remote) + sync_dialog.result_ready.connect(lambda a: self.__check_saved_finished(exit_code, a)) + sync_dialog.open() - if action == CloudSaveDialog.UPLOAD: + @pyqtSlot(int, int) + @pyqtSlot(int, CloudSyncDialogResult) + def __check_saved_finished(self, exit_code, action): + action = CloudSyncDialogResult(action) + if action == CloudSyncDialogResult.UPLOAD: if self.console: self.console.log("Uploading saves...") self.rgame.upload_saves() - elif action == CloudSaveDialog.DOWNLOAD: + elif action == CloudSyncDialogResult.DOWNLOAD: if self.console: self.console.log("Downloading saves...") self.rgame.download_saves() @@ -325,14 +332,20 @@ class RareLauncher(RareApp): return _, (dt_local, dt_remote) = self.rgame.save_game_state - dlg = CloudSaveDialog(self.rgame.igame, dt_local, dt_remote) - action = dlg.get_action() - if action == CloudSaveDialog.CANCEL: + sync_dialog = CloudSyncDialog(self.rgame.igame, dt_local, dt_remote) + sync_dialog.result_ready.connect(self.__sync_ready) + sync_dialog.open() + + @pyqtSlot(int) + @pyqtSlot(CloudSyncDialogResult) + def __sync_ready(self, action: CloudSyncDialogResult): + action = CloudSyncDialogResult(action) + if action == CloudSyncDialogResult.CANCEL: self.no_sync_on_exit = True if self.console: - if action == CloudSaveDialog.DOWNLOAD: + if action == CloudSyncDialogResult.DOWNLOAD: self.console.log("Downloading saves...") - elif action == CloudSaveDialog.UPLOAD: + elif action == CloudSyncDialogResult.UPLOAD: self.console.log("Uploading saves...") self.start_prepare(action) diff --git a/rare/launcher/cloud_sync_dialog.py b/rare/launcher/cloud_sync_dialog.py new file mode 100644 index 00000000..741298c1 --- /dev/null +++ b/rare/launcher/cloud_sync_dialog.py @@ -0,0 +1,103 @@ +import sys +from datetime import datetime +from enum import IntEnum +from logging import getLogger + +from PyQt5.QtCore import pyqtSignal, pyqtSlot +from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QDialog +from legendary.core import LegendaryCore +from legendary.models.game import InstalledGame + +from rare.ui.components.tabs.games.game_info.cloud_sync_widget import Ui_CloudSyncWidget +from rare.utils.misc import icon +from rare.widgets.dialogs import ButtonDialog, dialog_title_game + +logger = getLogger("CloudSyncDialog") + + +class CloudSyncDialogResult(IntEnum): + DOWNLOAD = 2 + UPLOAD = 1 + CANCEL = 0 + SKIP = 3 + + +class CloudSyncDialog(ButtonDialog): + result_ready: pyqtSignal = pyqtSignal(CloudSyncDialogResult) + + def __init__(self, igame: InstalledGame, dt_local: datetime, dt_remote: datetime, parent=None): + super(CloudSyncDialog, self).__init__(parent=parent) + header = self.tr("Cloud saves for") + self.setWindowTitle(dialog_title_game(header, igame.title)) + + title_label = QLabel(f"

{dialog_title_game(header, igame.title)}

", self) + + sync_widget = QWidget(self) + self.sync_ui = Ui_CloudSyncWidget() + self.sync_ui.setupUi(sync_widget) + + layout = QVBoxLayout() + layout.addWidget(title_label) + layout.addWidget(sync_widget) + + self.accept_button.setText(self.tr("Skip")) + self.accept_button.setIcon(icon("fa.chevron-right")) + + self.setCentralLayout(layout) + + self.status = CloudSyncDialogResult.CANCEL + + newer = self.tr("Newer") + if dt_remote and dt_local: + self.sync_ui.age_label_local.setText(f"{newer}" if dt_remote < dt_local else " ") + self.sync_ui.age_label_remote.setText(f"{newer}" if dt_remote > dt_local else " ") + # Set status, if one of them is None + elif dt_remote and not dt_local: + self.status = CloudSyncDialogResult.DOWNLOAD + elif not dt_remote and dt_local: + self.status = CloudSyncDialogResult.UPLOAD + else: + self.status = CloudSyncDialogResult.SKIP + + self.sync_ui.date_info_local.setText(dt_local.strftime("%A, %d. %B %Y %X") if dt_local else "None") + self.sync_ui.date_info_remote.setText(dt_remote.strftime("%A, %d. %B %Y %X") if dt_remote else "None") + + self.sync_ui.icon_local.setPixmap(icon("mdi.harddisk", "fa.desktop").pixmap(128, 128)) + self.sync_ui.icon_remote.setPixmap(icon("mdi.cloud-outline", "ei.cloud").pixmap(128, 128)) + + self.sync_ui.upload_button.clicked.connect(self.__on_upload) + self.sync_ui.download_button.clicked.connect(self.__on_download) + + if self.status == CloudSyncDialogResult.SKIP: + self.accept() + + def __on_upload(self): + self.status = CloudSyncDialogResult.UPLOAD + self.done(QDialog.Accepted) + + def __on_download(self): + self.status = CloudSyncDialogResult.DOWNLOAD + self.done(QDialog.Accepted) + + def done_handler(self): + self.result_ready.emit(self.status) + + def accept_handler(self): + self.status = CloudSyncDialogResult.SKIP + + def reject_handler(self): + self.status = CloudSyncDialogResult.CANCEL + + +if __name__ == "__main__": + app = QApplication(sys.argv) + core = LegendaryCore() + + @pyqtSlot(int) + def __callback(status: int): + print(repr(CloudSyncDialogResult(status))) + + dlg = CloudSyncDialog(core.get_installed_list()[0], datetime.now(), datetime.strptime("2021,1", "%Y,%M")) + dlg.result_ready.connect(__callback) + dlg.open() + app.exec() diff --git a/rare/launcher/console.py b/rare/launcher/console.py index 06412fb1..44d3797b 100644 --- a/rare/launcher/console.py +++ b/rare/launcher/console.py @@ -14,7 +14,7 @@ from PyQt5.QtWidgets import ( QSizePolicy, QTableWidgetItem, QHeaderView, QApplication, ) -from rare.ui.components.extra.console_env import Ui_ConsoleEnv +from rare.ui.launcher.console_env import Ui_ConsoleEnv class Console(QDialog): diff --git a/rare/ui/components/dialogs/sync_save_dialog.py b/rare/ui/components/dialogs/sync_save_dialog.py deleted file mode 100644 index 1db591aa..00000000 --- a/rare/ui/components/dialogs/sync_save_dialog.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'rare/ui/components/dialogs/sync_save_dialog.ui' -# -# Created by: PyQt5 UI code generator 5.15.7 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_SyncSaveDialog(object): - def setupUi(self, SyncSaveDialog): - SyncSaveDialog.setObjectName("SyncSaveDialog") - SyncSaveDialog.resize(648, 394) - self.verticalLayout = QtWidgets.QVBoxLayout(SyncSaveDialog) - self.verticalLayout.setObjectName("verticalLayout") - self.title_label = QtWidgets.QLabel(SyncSaveDialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.title_label.sizePolicy().hasHeightForWidth()) - self.title_label.setSizePolicy(sizePolicy) - self.title_label.setObjectName("title_label") - self.verticalLayout.addWidget(self.title_label) - self.sync_widget_layout = QtWidgets.QHBoxLayout() - self.sync_widget_layout.setObjectName("sync_widget_layout") - self.verticalLayout.addLayout(self.sync_widget_layout) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_2.addItem(spacerItem) - self.cancel_button = QtWidgets.QPushButton(SyncSaveDialog) - self.cancel_button.setObjectName("cancel_button") - self.horizontalLayout_2.addWidget(self.cancel_button) - self.verticalLayout.addLayout(self.horizontalLayout_2) - - self.retranslateUi(SyncSaveDialog) - - def retranslateUi(self, SyncSaveDialog): - _translate = QtCore.QCoreApplication.translate - SyncSaveDialog.setWindowTitle(_translate("SyncSaveDialog", "Sync saves with cloud")) - self.title_label.setText(_translate("SyncSaveDialog", "Select save, you want to use for ")) - self.cancel_button.setText(_translate("SyncSaveDialog", "Cancel")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - SyncSaveDialog = QtWidgets.QDialog() - ui = Ui_SyncSaveDialog() - ui.setupUi(SyncSaveDialog) - SyncSaveDialog.show() - sys.exit(app.exec_()) diff --git a/rare/ui/components/dialogs/sync_save_dialog.ui b/rare/ui/components/dialogs/sync_save_dialog.ui deleted file mode 100644 index d7c86479..00000000 --- a/rare/ui/components/dialogs/sync_save_dialog.ui +++ /dev/null @@ -1,61 +0,0 @@ - - - SyncSaveDialog - - - - 0 - 0 - 648 - 394 - - - - Sync saves with cloud - - - - - - - 0 - 0 - - - - Select save, you want to use for - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - - - - diff --git a/rare/ui/components/tabs/games/game_info/cloud_settings_widget.py b/rare/ui/components/tabs/games/game_info/cloud_settings_widget.py new file mode 100644 index 00000000..12036c80 --- /dev/null +++ b/rare/ui/components/tabs/games/game_info/cloud_settings_widget.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/game_info/cloud_settings_widget.ui' +# +# Created by: PyQt5 UI code generator 5.15.10 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_CloudSettingsWidget(object): + def setupUi(self, CloudSettingsWidget): + CloudSettingsWidget.setObjectName("CloudSettingsWidget") + CloudSettingsWidget.resize(388, 78) + CloudSettingsWidget.setWindowTitle("CloudSettingsWidget") + self.main_layout = QtWidgets.QFormLayout(CloudSettingsWidget) + self.main_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.main_layout.setObjectName("main_layout") + self.sync_label = QtWidgets.QLabel(CloudSettingsWidget) + self.sync_label.setObjectName("sync_label") + self.main_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.sync_label) + self.sync_check = QtWidgets.QCheckBox(CloudSettingsWidget) + font = QtGui.QFont() + font.setItalic(True) + self.sync_check.setFont(font) + self.sync_check.setText("Automatically synchronize saves with the cloud") + self.sync_check.setObjectName("sync_check") + self.main_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.sync_check) + self.path_label = QtWidgets.QLabel(CloudSettingsWidget) + self.path_label.setObjectName("path_label") + self.main_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.path_label) + + self.retranslateUi(CloudSettingsWidget) + + def retranslateUi(self, CloudSettingsWidget): + _translate = QtCore.QCoreApplication.translate + CloudSettingsWidget.setTitle(_translate("CloudSettingsWidget", "Settings")) + self.sync_label.setText(_translate("CloudSettingsWidget", "Enable sync")) + self.path_label.setText(_translate("CloudSettingsWidget", "Saves path")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + CloudSettingsWidget = QtWidgets.QGroupBox() + ui = Ui_CloudSettingsWidget() + ui.setupUi(CloudSettingsWidget) + CloudSettingsWidget.show() + sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/games/game_info/cloud_settings_widget.ui b/rare/ui/components/tabs/games/game_info/cloud_settings_widget.ui new file mode 100644 index 00000000..b4bbb26d --- /dev/null +++ b/rare/ui/components/tabs/games/game_info/cloud_settings_widget.ui @@ -0,0 +1,53 @@ + + + CloudSettingsWidget + + + + 0 + 0 + 388 + 78 + + + + CloudSettingsWidget + + + Settings + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Enable sync + + + + + + + + true + + + + Automatically synchronize saves with the cloud + + + + + + + Saves path + + + + + + + + diff --git a/rare/ui/components/tabs/games/game_info/sync_widget.py b/rare/ui/components/tabs/games/game_info/cloud_sync_widget.py similarity index 60% rename from rare/ui/components/tabs/games/game_info/sync_widget.py rename to rare/ui/components/tabs/games/game_info/cloud_sync_widget.py index 6a53cbbc..c368a9aa 100644 --- a/rare/ui/components/tabs/games/game_info/sync_widget.py +++ b/rare/ui/components/tabs/games/game_info/cloud_sync_widget.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/game_info/sync_widget.ui' +# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/game_info/cloud_sync_widget.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -11,29 +11,29 @@ from PyQt5 import QtCore, QtGui, QtWidgets -class Ui_SyncWidget(object): - def setupUi(self, SyncWidget): - SyncWidget.setObjectName("SyncWidget") - SyncWidget.resize(438, 137) +class Ui_CloudSyncWidget(object): + def setupUi(self, CloudSyncWidget): + CloudSyncWidget.setObjectName("CloudSyncWidget") + CloudSyncWidget.resize(438, 137) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(SyncWidget.sizePolicy().hasHeightForWidth()) - SyncWidget.setSizePolicy(sizePolicy) - SyncWidget.setWindowTitle("SyncWidget") - self.sync_layout = QtWidgets.QHBoxLayout(SyncWidget) - self.sync_layout.setContentsMargins(0, 0, 0, 0) - self.sync_layout.setObjectName("sync_layout") - self.local_gb = QtWidgets.QGroupBox(SyncWidget) - self.local_gb.setObjectName("local_gb") - self.local_layout = QtWidgets.QVBoxLayout(self.local_gb) + sizePolicy.setHeightForWidth(CloudSyncWidget.sizePolicy().hasHeightForWidth()) + CloudSyncWidget.setSizePolicy(sizePolicy) + CloudSyncWidget.setWindowTitle("SyncWidget") + self.main_layout = QtWidgets.QHBoxLayout(CloudSyncWidget) + self.main_layout.setContentsMargins(0, 0, 0, 0) + self.main_layout.setObjectName("main_layout") + self.local_group = QtWidgets.QGroupBox(CloudSyncWidget) + self.local_group.setObjectName("local_group") + self.local_layout = QtWidgets.QVBoxLayout(self.local_group) self.local_layout.setObjectName("local_layout") - self.date_info_local = QtWidgets.QLabel(self.local_gb) + self.date_info_local = QtWidgets.QLabel(self.local_group) self.date_info_local.setText("") self.date_info_local.setAlignment(QtCore.Qt.AlignCenter) self.date_info_local.setObjectName("date_info_local") self.local_layout.addWidget(self.date_info_local) - self.icon_local = QtWidgets.QLabel(self.local_gb) + self.icon_local = QtWidgets.QLabel(self.local_group) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -43,26 +43,26 @@ class Ui_SyncWidget(object): self.icon_local.setAlignment(QtCore.Qt.AlignCenter) self.icon_local.setObjectName("icon_local") self.local_layout.addWidget(self.icon_local) - self.age_label_local = QtWidgets.QLabel(self.local_gb) + self.age_label_local = QtWidgets.QLabel(self.local_group) self.age_label_local.setText("") self.age_label_local.setAlignment(QtCore.Qt.AlignCenter) self.age_label_local.setObjectName("age_label_local") self.local_layout.addWidget(self.age_label_local) - self.upload_button = QtWidgets.QPushButton(self.local_gb) + self.upload_button = QtWidgets.QPushButton(self.local_group) self.upload_button.setMinimumSize(QtCore.QSize(192, 0)) self.upload_button.setObjectName("upload_button") self.local_layout.addWidget(self.upload_button) - self.sync_layout.addWidget(self.local_gb) - self.cloud_gb = QtWidgets.QGroupBox(SyncWidget) - self.cloud_gb.setObjectName("cloud_gb") - self.cloud_layout = QtWidgets.QVBoxLayout(self.cloud_gb) + self.main_layout.addWidget(self.local_group) + self.cloud_group = QtWidgets.QGroupBox(CloudSyncWidget) + self.cloud_group.setObjectName("cloud_group") + self.cloud_layout = QtWidgets.QVBoxLayout(self.cloud_group) self.cloud_layout.setObjectName("cloud_layout") - self.date_info_remote = QtWidgets.QLabel(self.cloud_gb) + self.date_info_remote = QtWidgets.QLabel(self.cloud_group) self.date_info_remote.setText("") self.date_info_remote.setAlignment(QtCore.Qt.AlignCenter) self.date_info_remote.setObjectName("date_info_remote") self.cloud_layout.addWidget(self.date_info_remote) - self.icon_remote = QtWidgets.QLabel(self.cloud_gb) + self.icon_remote = QtWidgets.QLabel(self.cloud_group) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -72,32 +72,32 @@ class Ui_SyncWidget(object): self.icon_remote.setAlignment(QtCore.Qt.AlignCenter) self.icon_remote.setObjectName("icon_remote") self.cloud_layout.addWidget(self.icon_remote) - self.age_label_remote = QtWidgets.QLabel(self.cloud_gb) + self.age_label_remote = QtWidgets.QLabel(self.cloud_group) self.age_label_remote.setText("") self.age_label_remote.setAlignment(QtCore.Qt.AlignCenter) self.age_label_remote.setObjectName("age_label_remote") self.cloud_layout.addWidget(self.age_label_remote) - self.download_button = QtWidgets.QPushButton(self.cloud_gb) + self.download_button = QtWidgets.QPushButton(self.cloud_group) self.download_button.setMinimumSize(QtCore.QSize(192, 0)) self.download_button.setObjectName("download_button") self.cloud_layout.addWidget(self.download_button) - self.sync_layout.addWidget(self.cloud_gb) + self.main_layout.addWidget(self.cloud_group) - self.retranslateUi(SyncWidget) + self.retranslateUi(CloudSyncWidget) - def retranslateUi(self, SyncWidget): + def retranslateUi(self, CloudSyncWidget): _translate = QtCore.QCoreApplication.translate - self.local_gb.setTitle(_translate("SyncWidget", "Local")) - self.upload_button.setText(_translate("SyncWidget", "Upload")) - self.cloud_gb.setTitle(_translate("SyncWidget", "Cloud")) - self.download_button.setText(_translate("SyncWidget", "Download")) + self.local_group.setTitle(_translate("CloudSyncWidget", "Local")) + self.upload_button.setText(_translate("CloudSyncWidget", "Upload")) + self.cloud_group.setTitle(_translate("CloudSyncWidget", "Cloud")) + self.download_button.setText(_translate("CloudSyncWidget", "Download")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - SyncWidget = QtWidgets.QWidget() - ui = Ui_SyncWidget() - ui.setupUi(SyncWidget) - SyncWidget.show() + CloudSyncWidget = QtWidgets.QWidget() + ui = Ui_CloudSyncWidget() + ui.setupUi(CloudSyncWidget) + CloudSyncWidget.show() sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/games/game_info/sync_widget.ui b/rare/ui/components/tabs/games/game_info/cloud_sync_widget.ui similarity index 94% rename from rare/ui/components/tabs/games/game_info/sync_widget.ui rename to rare/ui/components/tabs/games/game_info/cloud_sync_widget.ui index f67b8614..72c1d910 100644 --- a/rare/ui/components/tabs/games/game_info/sync_widget.ui +++ b/rare/ui/components/tabs/games/game_info/cloud_sync_widget.ui @@ -1,7 +1,7 @@ - SyncWidget - + CloudSyncWidget + 0 @@ -19,7 +19,7 @@ SyncWidget - + 0 @@ -33,7 +33,7 @@ 0 - + Local @@ -91,7 +91,7 @@ - + Cloud diff --git a/rare/ui/components/tabs/games/game_info/cloud_widget.py b/rare/ui/components/tabs/games/game_info/cloud_widget.py deleted file mode 100644 index aa8ab79e..00000000 --- a/rare/ui/components/tabs/games/game_info/cloud_widget.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/game_info/cloud_widget.ui' -# -# Created by: PyQt5 UI code generator 5.15.7 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_CloudWidget(object): - def setupUi(self, CloudWidget): - CloudWidget.setObjectName("CloudWidget") - CloudWidget.resize(251, 93) - CloudWidget.setWindowTitle("GroupBox") - self.cloud_layout = QtWidgets.QFormLayout(CloudWidget) - self.cloud_layout.setObjectName("cloud_layout") - self.cloud_sync_label = QtWidgets.QLabel(CloudWidget) - self.cloud_sync_label.setObjectName("cloud_sync_label") - self.cloud_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.cloud_sync_label) - self.cloud_sync = QtWidgets.QCheckBox(CloudWidget) - self.cloud_sync.setText("") - self.cloud_sync.setObjectName("cloud_sync") - self.cloud_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cloud_sync) - - self.retranslateUi(CloudWidget) - - def retranslateUi(self, CloudWidget): - _translate = QtCore.QCoreApplication.translate - CloudWidget.setTitle(_translate("CloudWidget", "Options")) - self.cloud_sync_label.setText(_translate("CloudWidget", "Sync with cloud")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - CloudWidget = QtWidgets.QGroupBox() - ui = Ui_CloudWidget() - ui.setupUi(CloudWidget) - CloudWidget.show() - sys.exit(app.exec_()) diff --git a/rare/ui/components/tabs/games/game_info/cloud_widget.ui b/rare/ui/components/tabs/games/game_info/cloud_widget.ui deleted file mode 100644 index ee753f6b..00000000 --- a/rare/ui/components/tabs/games/game_info/cloud_widget.ui +++ /dev/null @@ -1,38 +0,0 @@ - - - CloudWidget - - - - 0 - 0 - 251 - 93 - - - - GroupBox - - - Options - - - - - - Sync with cloud - - - - - - - - - - - - - - - diff --git a/rare/ui/components/extra/__init__.py b/rare/ui/launcher/__init__.py similarity index 100% rename from rare/ui/components/extra/__init__.py rename to rare/ui/launcher/__init__.py diff --git a/rare/ui/components/extra/console_env.py b/rare/ui/launcher/console_env.py similarity index 100% rename from rare/ui/components/extra/console_env.py rename to rare/ui/launcher/console_env.py diff --git a/rare/ui/components/extra/console_env.ui b/rare/ui/launcher/console_env.ui similarity index 100% rename from rare/ui/components/extra/console_env.ui rename to rare/ui/launcher/console_env.ui From 80ac9296fc06a2162dc84d89e01ac39dec50aad9 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:57:02 +0200 Subject: [PATCH 15/17] Rare: cherry-pick some sourcery suggestions --- rare/components/dialogs/launch_dialog.py | 11 ++++------- rare/components/dialogs/login/__init__.py | 7 +++---- rare/components/dialogs/login/import_login.py | 4 +--- rare/components/main_window.py | 5 ++--- rare/components/tabs/games/__init__.py | 2 +- rare/components/tabs/settings/widgets/env_vars.py | 2 +- .../tabs/settings/widgets/env_vars_model.py | 2 +- rare/launcher/__init__.py | 6 +++--- rare/main.py | 2 +- rare/models/base_game.py | 3 +-- 10 files changed, 18 insertions(+), 26 deletions(-) diff --git a/rare/components/dialogs/launch_dialog.py b/rare/components/dialogs/launch_dialog.py index e63d7a1b..2de1f85d 100644 --- a/rare/components/dialogs/launch_dialog.py +++ b/rare/components/dialogs/launch_dialog.py @@ -49,17 +49,14 @@ class LaunchDialog(BaseDialog): def login(self): can_launch = True try: - if self.args.offline: - pass - else: + if not self.args.offline: # Force an update check and notice in case there are API changes # self.core.check_for_updates(force=True) # self.core.force_show_update = True - if self.core.login(force_refresh=True): - logger.info("You are logged in") - self.login_dialog.close() - else: + if not self.core.login(force_refresh=True): raise ValueError("You are not logged in. Opening login window.") + logger.info("You are logged in") + self.login_dialog.close() except ValueError as e: logger.info(str(e)) # Do not set parent, because it won't show a task bar icon diff --git a/rare/components/dialogs/login/__init__.py b/rare/components/dialogs/login/__init__.py index 5c2158e7..ba876b58 100644 --- a/rare/components/dialogs/login/__init__.py +++ b/rare/components/dialogs/login/__init__.py @@ -143,11 +143,10 @@ class LoginDialog(BaseDialog): def login_successful(self): try: - if self.core.login(): - self.logged_in = True - self.accept() - else: + if not self.core.login(): raise ValueError("Login failed.") + self.logged_in = True + self.accept() except Exception as e: logger.error(str(e)) self.core.lgd.invalidate_userdata() diff --git a/rare/components/dialogs/login/import_login.py b/rare/components/dialogs/login/import_login.py index 7cbe0a9e..9aa4980c 100644 --- a/rare/components/dialogs/login/import_login.py +++ b/rare/components/dialogs/login/import_login.py @@ -92,9 +92,7 @@ class ImportLogin(QFrame): def do_login(self): self.ui.status_label.setText(self.tr("Loading...")) - if os.name == "nt": - pass - else: + if os.name != "nt": logger.info("Using EGL appdata path at %s", {self.egl_appdata}) self.core.egl.appdata_path = self.egl_appdata try: diff --git a/rare/components/main_window.py b/rare/components/main_window.py index 6c998336..b26928bd 100644 --- a/rare/components/main_window.py +++ b/rare/components/main_window.py @@ -193,9 +193,8 @@ class MainWindow(QMainWindow): def timer_finished(self): file_path = lock_file() if os.path.exists(file_path): - file = open(file_path, "r") - action = file.read() - file.close() + with open(file_path, "r") as file: + action = file.read() if action.startswith("show"): self.show() os.remove(file_path) diff --git a/rare/components/tabs/games/__init__.py b/rare/components/tabs/games/__init__.py index 2cb3876d..2e183fd4 100644 --- a/rare/components/tabs/games/__init__.py +++ b/rare/components/tabs/games/__init__.py @@ -146,7 +146,7 @@ class GamesTab(QStackedWidget): def update_count_games_label(self): self.head_bar.set_games_count( len([game for game in self.rcore.games if game.is_installed]), - len([game for game in self.rcore.games]) + len(list(self.rcore.games)), ) def setup_game_list(self): diff --git a/rare/components/tabs/settings/widgets/env_vars.py b/rare/components/tabs/settings/widgets/env_vars.py index 362e62a0..901d4c90 100644 --- a/rare/components/tabs/settings/widgets/env_vars.py +++ b/rare/components/tabs/settings/widgets/env_vars.py @@ -45,7 +45,7 @@ class EnvVars(QGroupBox): layout.addWidget(self.table_view) def keyPressEvent(self, e): - if e.key() == Qt.Key_Delete or e.key() == Qt.Key_Backspace: + if e.key() in {Qt.Key_Delete, Qt.Key_Backspace}: indexes = self.table_view.selectedIndexes() if not len(indexes): return diff --git a/rare/components/tabs/settings/widgets/env_vars_model.py b/rare/components/tabs/settings/widgets/env_vars_model.py index d8f5d8c1..08ba8a9c 100644 --- a/rare/components/tabs/settings/widgets/env_vars_model.py +++ b/rare/components/tabs/settings/widgets/env_vars_model.py @@ -97,7 +97,7 @@ class EnvVarsTableModel(QAbstractTableModel): return value == match.group(0) def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> Any: - if role == Qt.DisplayRole or role == Qt.EditRole: + if role in {Qt.DisplayRole, Qt.EditRole}: if index.row() == self.__data_length(): return "" if index.column() == 0: diff --git a/rare/launcher/__init__.py b/rare/launcher/__init__.py index 13a258ba..8d6c992d 100644 --- a/rare/launcher/__init__.py +++ b/rare/launcher/__init__.py @@ -52,10 +52,10 @@ class PreLaunchThread(QRunnable): else: self.logger.info("No sync action") - args = self.prepare_launch(self.args) - if not args: + if args := self.prepare_launch(self.args): + self.signals.ready_to_launch.emit(args) + else: return - self.signals.ready_to_launch.emit(args) def prepare_launch(self, args: InitArgs) -> Optional[LaunchArgs]: try: diff --git a/rare/main.py b/rare/main.py index 5bf89d58..243095d7 100755 --- a/rare/main.py +++ b/rare/main.py @@ -97,7 +97,7 @@ def main() -> int: print(f"Rare {__version__} Codename: {__codename__}") return 0 - if args.subparser == "start" or args.subparser == "launch": + if args.subparser in {"start", "launch"}: from rare.launcher import launch return launch(args) diff --git a/rare/models/base_game.py b/rare/models/base_game.py index 878fffda..0cd1611c 100644 --- a/rare/models/base_game.py +++ b/rare/models/base_game.py @@ -318,8 +318,7 @@ class RareGameSlim(RareGameBase): @property def is_save_up_to_date(self): status, (_, _) = self.save_game_state - return (status == SaveGameStatus.SAME_AGE) \ - or (status == SaveGameStatus.NO_SAVE) + return status in {SaveGameStatus.SAME_AGE, SaveGameStatus.NO_SAVE} @property def raw_save_path(self) -> str: From fa158bd4aaf4f2d4d76d30c3b26ec521fa69731e Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sun, 31 Dec 2023 14:53:48 +0200 Subject: [PATCH 16/17] Launcher: Rename Console to ConsoleDialog Add more games to be launched as detached processes. Signed-off-by: loathingKernel <142770+loathingKernel@users.noreply.github.com> --- rare/launcher/__init__.py | 18 +++++++++++------- .../launcher/{console.py => console_dialog.py} | 8 ++++---- 2 files changed, 15 insertions(+), 11 deletions(-) rename rare/launcher/{console.py => console_dialog.py} (97%) diff --git a/rare/launcher/__init__.py b/rare/launcher/__init__.py index 8d6c992d..ef1d5a53 100644 --- a/rare/launcher/__init__.py +++ b/rare/launcher/__init__.py @@ -19,13 +19,17 @@ from rare.models.base_game import RareGameSlim from rare.models.launcher import ErrorModel, Actions, FinishedModel, BaseModel, StateChangedModel from rare.widgets.rare_app import RareApp, RareAppException from .cloud_sync_dialog import CloudSyncDialog, CloudSyncDialogResult -from .console import Console +from .console_dialog import ConsoleDialog from .lgd_helper import get_launch_args, InitArgs, get_configured_process, LaunchArgs, GameArgsError -DETACHED_APP_NAMES = [ - "0a2d9f6403244d12969e11da6713137b" # Fall Guys - "Fortnite" -] +DETACHED_APP_NAMES = { + "0a2d9f6403244d12969e11da6713137b", # Fall Guys + "Fortnite", + "afdb5a85efcc45d8ae8e406e2121d81c", # Fortnite Battle Royale + "09e442f830a341f698b4da42abd98c9b", # Fortnite Festival + "d8f7763e07d74c209d760a679f9ed6ac", # Lego Fortnite + "Fortnite_Studio", # Unreal Editor for Fortnite +} class PreLaunchThread(QRunnable): @@ -120,7 +124,7 @@ class RareLauncher(RareApp): def __init__(self, args: InitArgs): super(RareLauncher, self).__init__(args, f"{type(self).__name__}_{args.app_name}_{{0}}.log") self.socket: Optional[QLocalSocket] = None - self.console: Optional[Console] = None + self.console: Optional[ConsoleDialog] = None self.game_process: QProcess = QProcess(self) self.server: QLocalServer = QLocalServer(self) @@ -141,7 +145,7 @@ class RareLauncher(RareApp): self.load_translator(lang) if QSettings().value("show_console", False, bool): - self.console = Console() + self.console = ConsoleDialog() self.console.show() self.game_process.finished.connect(self.__process_finished) diff --git a/rare/launcher/console.py b/rare/launcher/console_dialog.py similarity index 97% rename from rare/launcher/console.py rename to rare/launcher/console_dialog.py index 44d3797b..24f45274 100644 --- a/rare/launcher/console.py +++ b/rare/launcher/console_dialog.py @@ -17,13 +17,13 @@ from PyQt5.QtWidgets import ( from rare.ui.launcher.console_env import Ui_ConsoleEnv -class Console(QDialog): +class ConsoleDialog(QDialog): term = pyqtSignal() kill = pyqtSignal() env: QProcessEnvironment def __init__(self, parent=None): - super(Console, self).__init__(parent=parent) + super(ConsoleDialog, self).__init__(parent=parent) self.setAttribute(Qt.WA_DeleteOnClose, True) self.setWindowTitle("Rare - Console") self.setGeometry(0, 0, 640, 480) @@ -68,7 +68,7 @@ class Console(QDialog): self.accept_close = False def show(self) -> None: - super(Console, self).show() + super(ConsoleDialog, self).show() self.center_window() def center_window(self): @@ -131,7 +131,7 @@ class Console(QDialog): def closeEvent(self, a0: QCloseEvent) -> None: if self.accept_close: - super(Console, self).closeEvent(a0) + super(ConsoleDialog, self).closeEvent(a0) a0.accept() else: self.showMinimized() From 339fec2bcac215c839cb7272d52c2b3e8a5e49e3 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 2 Jan 2024 18:03:41 +0200 Subject: [PATCH 17/17] CloudSaves: don't error if the saves directory already exists --- rare/components/tabs/games/game_info/cloud_saves.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rare/components/tabs/games/game_info/cloud_saves.py b/rare/components/tabs/games/game_info/cloud_saves.py index 87143f71..7006eb6a 100644 --- a/rare/components/tabs/games/game_info/cloud_saves.py +++ b/rare/components/tabs/games/game_info/cloud_saves.py @@ -143,7 +143,7 @@ class CloudSaves(QWidget, SideTabContents): self.compute_save_path_button.setDisabled(False) if path and not os.path.exists(path): try: - os.makedirs(path) + os.makedirs(path, exist_ok=True) except PermissionError: self.cloud_save_path_edit.setText("") QMessageBox.warning(