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(