From da07d6b7910c255ea95ef16503ad6a73ee7acb8f Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Sun, 31 Jul 2022 23:55:21 +0200 Subject: [PATCH 1/5] Move some options in install dialog to advanced options --- rare/components/dialogs/install_dialog.py | 14 +- rare/ui/components/dialogs/install_dialog.py | 202 +++++----- rare/ui/components/dialogs/install_dialog.ui | 366 ++++++++++--------- rare/widgets/collabsible_widget.py | 86 +++++ 4 files changed, 401 insertions(+), 267 deletions(-) create mode 100644 rare/widgets/collabsible_widget.py diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 65501956..683bed1c 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -10,17 +10,18 @@ from legendary.models.downloading import ConditionCheckResult from legendary.models.game import Game from legendary.utils.selective_dl import get_sdl_appname -from rare.lgndr.cli import LegendaryCLI from rare.lgndr.api_arguments import LgndrInstallGameArgs from rare.lgndr.api_exception import LgndrException from rare.lgndr.api_monkeys import LgndrIndirectStatus +from rare.lgndr.cli import LegendaryCLI from rare.lgndr.core import LegendaryCore +from rare.models.install import InstallDownloadModel, InstallQueueItemModel from rare.shared import LegendaryCoreSingleton, ApiResultsSingleton, ArgumentsSingleton from rare.ui.components.dialogs.install_dialog import Ui_InstallDialog -from rare.utils.extra_widgets import PathEdit -from rare.models.install import InstallDownloadModel, InstallQueueItemModel -from rare.utils.misc import get_size from rare.utils import config_helper +from rare.utils.extra_widgets import PathEdit +from rare.utils.misc import get_size +from rare.widgets.collabsible_widget import CollabsibleWidget class InstallDialog(QDialog, Ui_InstallDialog): @@ -41,6 +42,11 @@ class InstallDialog(QDialog, Ui_InstallDialog): if not self.dl_item.options.overlay else Game(app_name=self.app_name, app_title="Epic Overlay") ) + self.advanced_layout.setParent(None) + self.advanced_widget = CollabsibleWidget( + self.advanced_layout, self.tr("Advanced options"), parent=self + ) + self.advanced_placeholder_layout.addWidget(self.advanced_widget) self.game_path = self.game.metadata.get("customAttributes", {}).get("FolderName", {}).get("value", "") diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index dd4baa58..3696b8ed 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'rare/ui/components/dialogs/install_dialog.ui' # -# Created by: PyQt5 UI code generator 5.15.6 +# 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. @@ -14,11 +14,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_InstallDialog(object): def setupUi(self, InstallDialog): InstallDialog.setObjectName("InstallDialog") - InstallDialog.resize(406, 447) + InstallDialog.resize(526, 483) InstallDialog.setWindowTitle("Rare") self.install_dialog_layout = QtWidgets.QFormLayout(InstallDialog) self.install_dialog_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) - self.install_dialog_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + 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") @@ -29,38 +31,6 @@ class Ui_InstallDialog(object): self.install_dir_layout = QtWidgets.QHBoxLayout() self.install_dir_layout.setObjectName("install_dir_layout") self.install_dialog_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.install_dir_layout) - self.max_workers_label = QtWidgets.QLabel(InstallDialog) - self.max_workers_label.setObjectName("max_workers_label") - self.install_dialog_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.max_workers_label) - self.max_workers_layout = QtWidgets.QHBoxLayout() - self.max_workers_layout.setObjectName("max_workers_layout") - self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.max_workers_spin.sizePolicy().hasHeightForWidth()) - self.max_workers_spin.setSizePolicy(sizePolicy) - self.max_workers_spin.setObjectName("max_workers_spin") - self.max_workers_layout.addWidget(self.max_workers_spin) - self.max_workers_info_label = QtWidgets.QLabel(InstallDialog) - font = QtGui.QFont() - font.setItalic(True) - self.max_workers_info_label.setFont(font) - self.max_workers_info_label.setObjectName("max_workers_info_label") - self.max_workers_layout.addWidget(self.max_workers_info_label) - self.install_dialog_layout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.max_workers_layout) - self.force_download_label = QtWidgets.QLabel(InstallDialog) - self.force_download_label.setObjectName("force_download_label") - self.install_dialog_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.force_download_label) - self.force_download_check = QtWidgets.QCheckBox(InstallDialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.force_download_check.sizePolicy().hasHeightForWidth()) - self.force_download_check.setSizePolicy(sizePolicy) - self.force_download_check.setText("") - self.force_download_check.setObjectName("force_download_check") - self.install_dialog_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.force_download_check) self.platform_label = QtWidgets.QLabel(InstallDialog) self.platform_label.setObjectName("platform_label") self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.platform_label) @@ -72,12 +42,6 @@ class Ui_InstallDialog(object): self.platform_combo_box.setSizePolicy(sizePolicy) self.platform_combo_box.setObjectName("platform_combo_box") self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.platform_combo_box) - self.ignore_space_label = QtWidgets.QLabel(InstallDialog) - self.ignore_space_label.setObjectName("ignore_space_label") - self.install_dialog_layout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.ignore_space_label) - self.download_only_label = QtWidgets.QLabel(InstallDialog) - self.download_only_label.setObjectName("download_only_label") - self.install_dialog_layout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.download_only_label) self.shortcut_lbl = QtWidgets.QLabel(InstallDialog) self.shortcut_lbl.setObjectName("shortcut_lbl") self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.shortcut_lbl) @@ -85,6 +49,16 @@ class Ui_InstallDialog(object): self.shortcut_cb.setText("") self.shortcut_cb.setObjectName("shortcut_cb") self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.shortcut_cb) + self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog) + self.install_preqs_lbl.setObjectName("install_preqs_lbl") + self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) + self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.install_preqs_check.setFont(font) + self.install_preqs_check.setText("") + self.install_preqs_check.setObjectName("install_preqs_check") + self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) self.sdl_list_label = QtWidgets.QLabel(InstallDialog) self.sdl_list_label.setObjectName("sdl_list_label") self.install_dialog_layout.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.sdl_list_label) @@ -104,7 +78,9 @@ class Ui_InstallDialog(object): font.setItalic(True) self.download_size_info_label.setFont(font) self.download_size_info_label.setObjectName("download_size_info_label") - self.install_dialog_layout.setWidget(13, QtWidgets.QFormLayout.FieldRole, self.download_size_info_label) + self.install_dialog_layout.setWidget( + 13, QtWidgets.QFormLayout.FieldRole, self.download_size_info_label + ) self.install_size_label = QtWidgets.QLabel(InstallDialog) self.install_size_label.setObjectName("install_size_label") self.install_dialog_layout.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.install_size_label) @@ -114,7 +90,12 @@ class Ui_InstallDialog(object): self.install_size_info_label.setFont(font) self.install_size_info_label.setWordWrap(True) self.install_size_info_label.setObjectName("install_size_info_label") - self.install_dialog_layout.setWidget(14, QtWidgets.QFormLayout.FieldRole, self.install_size_info_label) + self.install_dialog_layout.setWidget( + 14, QtWidgets.QFormLayout.FieldRole, self.install_size_info_label + ) + self.warn_label = QtWidgets.QLabel(InstallDialog) + self.warn_label.setObjectName("warn_label") + self.install_dialog_layout.setWidget(15, QtWidgets.QFormLayout.LabelRole, self.warn_label) self.warn_message = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) @@ -124,12 +105,11 @@ class Ui_InstallDialog(object): self.warn_message.setWordWrap(True) self.warn_message.setObjectName("warn_message") self.install_dialog_layout.setWidget(15, QtWidgets.QFormLayout.FieldRole, self.warn_message) - self.warn_label = QtWidgets.QLabel(InstallDialog) - self.warn_label.setObjectName("warn_label") - self.install_dialog_layout.setWidget(15, QtWidgets.QFormLayout.LabelRole, self.warn_label) self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.setObjectName("button_layout") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + 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") @@ -140,42 +120,41 @@ class Ui_InstallDialog(object): self.install_button = QtWidgets.QPushButton(InstallDialog) self.install_button.setObjectName("install_button") self.button_layout.addWidget(self.install_button) - self.install_dialog_layout.setLayout(16, QtWidgets.QFormLayout.SpanningRole, self.button_layout) - self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog) - self.install_preqs_lbl.setObjectName("install_preqs_lbl") - self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) - self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog) - font = QtGui.QFont() - font.setItalic(True) - self.install_preqs_check.setFont(font) - self.install_preqs_check.setText("") - self.install_preqs_check.setObjectName("install_preqs_check") - self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) - self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog) + self.install_dialog_layout.setLayout(17, QtWidgets.QFormLayout.SpanningRole, self.button_layout) + self.advanced_layout = QtWidgets.QFormLayout() + self.advanced_layout.setLabelAlignment( + QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter + ) + self.advanced_layout.setObjectName("advanced_layout") + self.max_workers_label = QtWidgets.QLabel(InstallDialog) + self.max_workers_label.setAlignment( + QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter + ) + self.max_workers_label.setObjectName("max_workers_label") + self.advanced_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.max_workers_label) + self.max_workers_layout = QtWidgets.QHBoxLayout() + self.max_workers_layout.setObjectName("max_workers_layout") + self.max_workers_spin = QtWidgets.QSpinBox(InstallDialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.ignore_space_check.sizePolicy().hasHeightForWidth()) - self.ignore_space_check.setSizePolicy(sizePolicy) + sizePolicy.setHeightForWidth(self.max_workers_spin.sizePolicy().hasHeightForWidth()) + self.max_workers_spin.setSizePolicy(sizePolicy) + self.max_workers_spin.setObjectName("max_workers_spin") + self.max_workers_layout.addWidget(self.max_workers_spin) + self.max_workers_info_label = QtWidgets.QLabel(InstallDialog) font = QtGui.QFont() font.setItalic(True) - self.ignore_space_check.setFont(font) - self.ignore_space_check.setObjectName("ignore_space_check") - self.install_dialog_layout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.ignore_space_check) - self.download_only_check = QtWidgets.QCheckBox(InstallDialog) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.download_only_check.sizePolicy().hasHeightForWidth()) - self.download_only_check.setSizePolicy(sizePolicy) - font = QtGui.QFont() - font.setItalic(True) - self.download_only_check.setFont(font) - self.download_only_check.setObjectName("download_only_check") - self.install_dialog_layout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.download_only_check) + self.max_workers_info_label.setFont(font) + self.max_workers_info_label.setObjectName("max_workers_info_label") + self.max_workers_layout.addWidget(self.max_workers_info_label) + self.advanced_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.max_workers_layout) self.max_memory_label = QtWidgets.QLabel(InstallDialog) + self.max_memory_label.setAlignment( + QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter + ) self.max_memory_label.setObjectName("max_memory_label") - self.install_dialog_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.max_memory_label) + self.advanced_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.max_memory_label) self.max_memory_layout = QtWidgets.QHBoxLayout() self.max_memory_layout.setObjectName("max_memory_layout") self.max_memory_spin = QtWidgets.QSpinBox(InstallDialog) @@ -196,15 +175,61 @@ class Ui_InstallDialog(object): self.max_memory_info_label.setFont(font) self.max_memory_info_label.setObjectName("max_memory_info_label") self.max_memory_layout.addWidget(self.max_memory_info_label) - self.install_dialog_layout.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout) + self.advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout) self.dl_optimizations_label = QtWidgets.QLabel(InstallDialog) self.dl_optimizations_label.setObjectName("dl_optimizations_label") - self.install_dialog_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) + self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) self.dl_optimizations_check = QtWidgets.QCheckBox(InstallDialog) self.dl_optimizations_check.setText("") self.dl_optimizations_check.setChecked(False) self.dl_optimizations_check.setObjectName("dl_optimizations_check") - self.install_dialog_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.dl_optimizations_check) + self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dl_optimizations_check) + self.force_download_label = QtWidgets.QLabel(InstallDialog) + self.force_download_label.setObjectName("force_download_label") + self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.force_download_label) + self.force_download_check = QtWidgets.QCheckBox(InstallDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.force_download_check.sizePolicy().hasHeightForWidth()) + self.force_download_check.setSizePolicy(sizePolicy) + self.force_download_check.setText("") + self.force_download_check.setObjectName("force_download_check") + self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.force_download_check) + self.ignore_space_label = QtWidgets.QLabel(InstallDialog) + self.ignore_space_label.setObjectName("ignore_space_label") + self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.ignore_space_label) + self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.ignore_space_check.sizePolicy().hasHeightForWidth()) + self.ignore_space_check.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setItalic(True) + self.ignore_space_check.setFont(font) + self.ignore_space_check.setObjectName("ignore_space_check") + self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.ignore_space_check) + self.download_only_label = QtWidgets.QLabel(InstallDialog) + self.download_only_label.setObjectName("download_only_label") + self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.download_only_label) + self.download_only_check = QtWidgets.QCheckBox(InstallDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.download_only_check.sizePolicy().hasHeightForWidth()) + self.download_only_check.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setItalic(True) + self.download_only_check.setFont(font) + self.download_only_check.setObjectName("download_only_check") + self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.download_only_check) + self.install_dialog_layout.setLayout(16, QtWidgets.QFormLayout.SpanningRole, self.advanced_layout) + self.advanced_placeholder_layout = QtWidgets.QVBoxLayout() + self.advanced_placeholder_layout.setObjectName("advanced_placeholder_layout") + self.install_dialog_layout.setLayout( + 12, QtWidgets.QFormLayout.SpanningRole, self.advanced_placeholder_layout + ) self.retranslateUi(InstallDialog) QtCore.QMetaObject.connectSlotsByName(InstallDialog) @@ -213,34 +238,35 @@ class Ui_InstallDialog(object): _translate = QtCore.QCoreApplication.translate self.install_dialog_label.setText(_translate("InstallDialog", "error")) self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) - self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) - self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)")) - self.force_download_label.setText(_translate("InstallDialog", "Force redownload")) self.platform_label.setText(_translate("InstallDialog", "Platform")) - self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) - self.download_only_label.setText(_translate("InstallDialog", "Download only")) self.shortcut_lbl.setText(_translate("InstallDialog", "Create shortcut")) + self.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites")) self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs")) self.download_size_label.setText(_translate("InstallDialog", "Download size")) self.download_size_info_label.setText(_translate("InstallDialog", "Click verify...")) self.install_size_label.setText(_translate("InstallDialog", "Total install size")) self.install_size_info_label.setText(_translate("InstallDialog", "Click verify...")) - self.warn_message.setText(_translate("InstallDialog", "None")) self.warn_label.setText(_translate("InstallDialog", "Warning")) + self.warn_message.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.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites")) - self.ignore_space_check.setText(_translate("InstallDialog", "Use with caution!")) - self.download_only_check.setText(_translate("InstallDialog", "Do not try to install.")) + self.max_workers_label.setText(_translate("InstallDialog", "Max workers")) + self.max_workers_info_label.setText(_translate("InstallDialog", "Less is slower. (0: Default)")) self.max_memory_label.setText(_translate("InstallDialog", "Max shared memory")) self.max_memory_spin.setSuffix(_translate("InstallDialog", "MiB")) self.max_memory_info_label.setText(_translate("InstallDialog", "Less is slower (0: Default)")) self.dl_optimizations_label.setText(_translate("InstallDialog", "Enable reordering")) + self.force_download_label.setText(_translate("InstallDialog", "Force redownload")) + self.ignore_space_label.setText(_translate("InstallDialog", "Ignore free space")) + self.ignore_space_check.setText(_translate("InstallDialog", "Use with caution!")) + self.download_only_label.setText(_translate("InstallDialog", "Download only")) + self.download_only_check.setText(_translate("InstallDialog", "Do not try to install.")) if __name__ == "__main__": import sys + app = QtWidgets.QApplication(sys.argv) InstallDialog = QtWidgets.QDialog() ui = Ui_InstallDialog() diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index fdfd2aa3..03875d60 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 406 - 447 + 526 + 483 @@ -37,59 +37,6 @@ - - - - Max workers - - - - - - - - - - 0 - 0 - - - - - - - - - true - - - - Less is slower. (0: Default) - - - - - - - - - Force redownload - - - - - - - - 0 - 0 - - - - - - - @@ -107,20 +54,6 @@ - - - - Ignore free space - - - - - - - Download only - - - @@ -135,6 +68,25 @@ + + + + Install prerequisites + + + + + + + + true + + + + + + + @@ -198,6 +150,13 @@ + + + + Warning + + + @@ -219,14 +178,7 @@ - - - - Warning - - - - + @@ -264,125 +216,189 @@ - - - - Install prerequisites + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - - - - - - - - - - 0 - 0 - - - - - true - - - - Use with caution! - - - - - - - - 0 - 0 - - - - - true - - - - Do not try to install. - - - - - - - Max shared memory - - - - - - - + + + + Max workers + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + + + + + + true + + + + Less is slower. (0: Default) + + + + + + + + + Max shared memory + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + MiB + + + 0 + + + 10240 + + + 128 + + + 1024 + + + + + + + + true + + + + Less is slower (0: Default) + + + + + + + + + Enable reordering + + + + + + + + + + false + + + + + + + Force redownload + + + + + 0 0 - - MiB - - - 0 - - - 10240 - - - 128 - - - 1024 + + - - + + + + Ignore free space + + + + + + + + 0 + 0 + + true - Less is slower (0: Default) + Use with caution! + + + + + + + Download only + + + + + + + + 0 + 0 + + + + + true + + + + Do not try to install. - - - - Enable reordering - - - - - - - - - - false - - + + diff --git a/rare/widgets/collabsible_widget.py b/rare/widgets/collabsible_widget.py new file mode 100644 index 00000000..79726b0b --- /dev/null +++ b/rare/widgets/collabsible_widget.py @@ -0,0 +1,86 @@ +from PyQt5.QtCore import QParallelAnimationGroup, Qt, QPropertyAnimation, QAbstractAnimation +from PyQt5.QtWidgets import QWidget, QFrame, QToolButton, QGridLayout, QSizePolicy, QLayout + + +# https://newbedev.com/how-to-make-an-expandable-collapsable-section-widget-in-qt + + +class CollabsibleWidget(QWidget): + def __init__( + self, child_layout: QLayout = None, title: str = "", animation_duration: int = 200, parent=None + ): + """ + References: + # Adapted from c++ version + https://stackoverflow.com/questions/32476006/how-to-make-an-expandable-collapsable-section-widget-in-qt + """ + super(CollabsibleWidget, self).__init__(parent=parent) + + self.animationDuration = animation_duration + self.toggleAnimation = QParallelAnimationGroup() + self.contentArea = QWidget() + self.headerLine = QFrame() + self.toggleButton = QToolButton() + self.mainLayout = QGridLayout() + + toggleButton = self.toggleButton + toggleButton.setStyleSheet("QToolButton { border: none; }") + toggleButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + toggleButton.setArrowType(Qt.RightArrow) + toggleButton.setText(str(title)) + toggleButton.setCheckable(True) + toggleButton.setChecked(False) + + headerLine = self.headerLine + headerLine.setFrameShape(QFrame.HLine) + headerLine.setFrameShadow(QFrame.Sunken) + headerLine.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) + + self.contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }") + self.contentArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + # start out collapsed + self.contentArea.setMaximumHeight(0) + self.contentArea.setMinimumHeight(0) + # let the entire widget grow and shrink with its content + toggleAnimation = self.toggleAnimation + toggleAnimation.addAnimation(QPropertyAnimation(self, b"minimumHeight")) + toggleAnimation.addAnimation(QPropertyAnimation(self, b"maximumHeight")) + toggleAnimation.addAnimation(QPropertyAnimation(self.contentArea, b"maximumHeight")) + # don't waste space + mainLayout = self.mainLayout + mainLayout.setVerticalSpacing(0) + mainLayout.setContentsMargins(0, 0, 0, 0) + row = 0 + mainLayout.addWidget(self.toggleButton, row, 0, 1, 1, Qt.AlignLeft) + mainLayout.addWidget(self.headerLine, row, 2, 1, 1) + row += 1 + mainLayout.addWidget(self.contentArea, row, 0, 1, 3) + self.setLayout(self.mainLayout) + + def start_animation(checked): + arrow_type = Qt.DownArrow if checked else Qt.RightArrow + direction = QAbstractAnimation.Forward if checked else QAbstractAnimation.Backward + toggleButton.setArrowType(arrow_type) + self.toggleAnimation.setDirection(direction) + self.toggleAnimation.start() + + self.toggleButton.clicked.connect(start_animation) + + if child_layout: + self.setContentLayout(child_layout) + + def setContentLayout(self, content_layout: QLayout): + # Not sure if this is equivalent to self.contentArea.destroy() + self.contentArea.destroy() + self.contentArea.setLayout(content_layout) + collapsedHeight = self.sizeHint().height() - self.contentArea.maximumHeight() + contentHeight = content_layout.sizeHint().height() + for i in range(self.toggleAnimation.animationCount() - 1): + spoilerAnimation = self.toggleAnimation.animationAt(i) + spoilerAnimation.setDuration(self.animationDuration) + spoilerAnimation.setStartValue(collapsedHeight) + spoilerAnimation.setEndValue(collapsedHeight + contentHeight) + contentAnimation = self.toggleAnimation.animationAt(self.toggleAnimation.animationCount() - 1) + contentAnimation.setDuration(self.animationDuration) + contentAnimation.setStartValue(0) + contentAnimation.setEndValue(contentHeight) From 2797485e6994ca278f43bde6d3ad11196ca3fdd3 Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Sun, 31 Jul 2022 23:56:02 +0200 Subject: [PATCH 2/5] Fix reason for cloud save edit, if nothing wrong --- .../tabs/games/game_info/game_settings.py | 71 ++++++------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/rare/components/tabs/games/game_info/game_settings.py b/rare/components/tabs/games/game_info/game_settings.py index 3808b9cc..c3725258 100644 --- a/rare/components/tabs/games/game_info/game_settings.py +++ b/rare/components/tabs/games/game_info/game_settings.py @@ -30,54 +30,37 @@ class GameSettings(DefaultGameSettings): "", file_type=QFileDialog.DirectoryOnly, placeholder=self.tr("Cloud save path"), - edit_func=lambda text: (os.path.exists(text), text, PathEdit.reasons.dir_not_exist), + edit_func=lambda text: (True, text, None) + if os.path.exists(text) + else (False, text, PathEdit.reasons.dir_not_exist), save_func=self.save_save_path, ) - self.cloud_layout.addRow( - QLabel(self.tr("Save path")), self.cloud_save_path_edit - ) + self.cloud_layout.addRow(QLabel(self.tr("Save path")), self.cloud_save_path_edit) - self.compute_save_path_button = QPushButton( - icon("fa.magic"), self.tr("Auto compute save path") - ) - self.compute_save_path_button.setSizePolicy( - QSizePolicy.Maximum, QSizePolicy.Fixed - ) + self.compute_save_path_button = QPushButton(icon("fa.magic"), self.tr("Auto compute save path")) + self.compute_save_path_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed) self.compute_save_path_button.clicked.connect(self.compute_save_path) self.cloud_layout.addRow(None, self.compute_save_path_button) - self.offline.currentIndexChanged.connect( - lambda x: self.update_combobox(x, "offline") - ) - self.skip_update.currentIndexChanged.connect( - lambda x: self.update_combobox(x, "skip_update_check") - ) + self.offline.currentIndexChanged.connect(lambda x: self.update_combobox(x, "offline")) + self.skip_update.currentIndexChanged.connect(lambda x: self.update_combobox(x, "skip_update_check")) self.cloud_sync.stateChanged.connect( lambda: self.settings.setValue( f"{self.game.app_name}/auto_sync_cloud", self.cloud_sync.isChecked() ) ) - self.override_exe_edit.textChanged.connect( - lambda text: self.save_line_edit("override_exe", text) - ) - self.launch_params.textChanged.connect( - lambda x: self.save_line_edit("start_params", x) - ) + self.override_exe_edit.textChanged.connect(lambda text: self.save_line_edit("override_exe", text)) + self.launch_params.textChanged.connect(lambda x: self.save_line_edit("start_params", x)) self.game_settings_layout.setAlignment(Qt.AlignTop) def compute_save_path(self): - if ( - self.core.is_installed(self.game.app_name) - and self.game.supports_cloud_saves - ): + if self.core.is_installed(self.game.app_name) and self.game.supports_cloud_saves: try: new_path = self.core.get_save_path(self.game.app_name) except Exception as e: logger.warning(str(e)) - resolver = WineResolver( - get_raw_save_path(self.game), self.game.app_name - ) + resolver = WineResolver(get_raw_save_path(self.game), self.game.app_name) if not resolver.wine_env.get("WINEPREFIX"): self.cloud_save_path_edit.setText("") QMessageBox.warning(self, "Warning", "No wine prefix selected. Please set it in settings") @@ -87,18 +70,14 @@ class GameSettings(DefaultGameSettings): self.compute_save_path_button.setDisabled(True) app_name = self.game.app_name[:] - resolver.signals.result_ready.connect( - lambda x: self.wine_resolver_finished(x, app_name) - ) + resolver.signals.result_ready.connect(lambda x: self.wine_resolver_finished(x, app_name)) QThreadPool.globalInstance().start(resolver) return else: self.cloud_save_path_edit.setText(new_path) def wine_resolver_finished(self, path, app_name): - logger.info( - f"Wine resolver finished for {app_name}. Computed save path: {path}" - ) + logger.info(f"Wine resolver finished for {app_name}. Computed save path: {path}") if app_name == self.game.app_name: self.cloud_save_path_edit.setDisabled(False) self.compute_save_path_button.setDisabled(False) @@ -110,9 +89,9 @@ class GameSettings(DefaultGameSettings): QMessageBox.warning( None, "Error", - self.tr( - "Error while launching {}. No permission to create {}" - ).format(self.game.app_title, path), + self.tr("Error while launching {}. No permission to create {}").format( + self.game.app_title, path + ), ) return if not path: @@ -160,9 +139,7 @@ class GameSettings(DefaultGameSettings): self.igame = self.core.get_installed_game(self.game.app_name) if self.igame: if self.igame.can_run_offline: - offline = self.core.lgd.config.get( - self.game.app_name, "offline", fallback="unset" - ) + offline = self.core.lgd.config.get(self.game.app_name, "offline", fallback="unset") if offline == "true": self.offline.setCurrentIndex(1) elif offline == "false": @@ -176,9 +153,7 @@ class GameSettings(DefaultGameSettings): else: self.offline.setEnabled(False) - skip_update = self.core.lgd.config.get( - self.game.app_name, "skip_update_check", fallback="unset" - ) + skip_update = self.core.lgd.config.get(self.game.app_name, "skip_update_check", fallback="unset") if skip_update == "true": self.skip_update.setCurrentIndex(1) elif skip_update == "false": @@ -198,18 +173,14 @@ class GameSettings(DefaultGameSettings): self.cloud_save_path_edit.setText("") else: self.cloud_group.setEnabled(True) - sync_cloud = self.settings.value( - f"{self.game.app_name}/auto_sync_cloud", True, bool - ) + sync_cloud = self.settings.value(f"{self.game.app_name}/auto_sync_cloud", True, bool) self.cloud_sync.setChecked(sync_cloud) if self.igame.save_path: self.cloud_save_path_edit.setText(self.igame.save_path) else: self.cloud_save_path_edit.setText("") - self.launch_params.setText( - self.core.lgd.config.get(self.game.app_name, "start_params", fallback="") - ) + self.launch_params.setText(self.core.lgd.config.get(self.game.app_name, "start_params", fallback="")) self.override_exe_edit.setText( self.core.lgd.config.get(self.game.app_name, "override_exe", fallback="") ) From 3469946ee941a0df4e68dc586b272d97a996a9af Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Thu, 11 Aug 2022 00:47:55 +0200 Subject: [PATCH 3/5] Install prereqs by default on windows --- rare/components/dialogs/install_dialog.py | 5 ++ rare/ui/components/dialogs/install_dialog.py | 75 ++++++++------------ rare/ui/components/dialogs/install_dialog.ui | 65 +++++++++-------- 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index 683bed1c..0400b958 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -153,6 +153,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.verify_button.clicked.connect(self.verify_clicked) self.install_button.clicked.connect(self.install_clicked) + self.install_preqs_check.setChecked(self.dl_item.options.install_preqs) + self.install_dialog_layout.setSizeConstraint(self.install_dialog_layout.SetFixedSize) def execute(self): @@ -205,6 +207,8 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.dl_item.options.ignore_space = self.ignore_space_check.isChecked() self.dl_item.options.no_install = self.download_only_check.isChecked() self.dl_item.options.platform = self.platform_combo_box.currentText() + self.dl_item.options.install_preqs = self.install_preqs_check.isChecked() + self.dl_item.options.create_shortcut = self.shortcut_cb.isChecked() if self.sdl_list_cbs: self.dl_item.options.install_tag = [""] for cb in self.sdl_list_cbs: @@ -286,6 +290,7 @@ class InstallDialog(QDialog, Ui_InstallDialog): if dl_item.igame.prereq_info and not dl_item.igame.prereq_info.get("installed", False): self.install_preqs_check.setVisible(True) self.install_preqs_lbl.setVisible(True) + self.install_preqs_check.setChecked(True) self.install_preqs_check.setText( self.tr("Also install: {}").format(dl_item.igame.prereq_info.get("name", "")) ) diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py index 3696b8ed..1f8f4823 100644 --- a/rare/ui/components/dialogs/install_dialog.py +++ b/rare/ui/components/dialogs/install_dialog.py @@ -14,13 +14,12 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_InstallDialog(object): def setupUi(self, InstallDialog): InstallDialog.setObjectName("InstallDialog") - InstallDialog.resize(526, 483) + InstallDialog.resize(530, 489) InstallDialog.setWindowTitle("Rare") self.install_dialog_layout = QtWidgets.QFormLayout(InstallDialog) self.install_dialog_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.install_dialog_layout.setLabelAlignment( - QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter - ) + 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") @@ -43,22 +42,14 @@ class Ui_InstallDialog(object): self.platform_combo_box.setObjectName("platform_combo_box") self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.platform_combo_box) self.shortcut_lbl = QtWidgets.QLabel(InstallDialog) + self.shortcut_lbl.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.shortcut_lbl.setObjectName("shortcut_lbl") self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.shortcut_lbl) self.shortcut_cb = QtWidgets.QCheckBox(InstallDialog) self.shortcut_cb.setText("") + self.shortcut_cb.setChecked(True) self.shortcut_cb.setObjectName("shortcut_cb") self.install_dialog_layout.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.shortcut_cb) - self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog) - self.install_preqs_lbl.setObjectName("install_preqs_lbl") - self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) - self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog) - font = QtGui.QFont() - font.setItalic(True) - self.install_preqs_check.setFont(font) - self.install_preqs_check.setText("") - self.install_preqs_check.setObjectName("install_preqs_check") - self.install_dialog_layout.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) self.sdl_list_label = QtWidgets.QLabel(InstallDialog) self.sdl_list_label.setObjectName("sdl_list_label") self.install_dialog_layout.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.sdl_list_label) @@ -78,9 +69,7 @@ class Ui_InstallDialog(object): font.setItalic(True) self.download_size_info_label.setFont(font) self.download_size_info_label.setObjectName("download_size_info_label") - self.install_dialog_layout.setWidget( - 13, QtWidgets.QFormLayout.FieldRole, self.download_size_info_label - ) + self.install_dialog_layout.setWidget(13, QtWidgets.QFormLayout.FieldRole, self.download_size_info_label) self.install_size_label = QtWidgets.QLabel(InstallDialog) self.install_size_label.setObjectName("install_size_label") self.install_dialog_layout.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.install_size_label) @@ -90,9 +79,7 @@ class Ui_InstallDialog(object): self.install_size_info_label.setFont(font) self.install_size_info_label.setWordWrap(True) self.install_size_info_label.setObjectName("install_size_info_label") - self.install_dialog_layout.setWidget( - 14, QtWidgets.QFormLayout.FieldRole, self.install_size_info_label - ) + self.install_dialog_layout.setWidget(14, QtWidgets.QFormLayout.FieldRole, self.install_size_info_label) self.warn_label = QtWidgets.QLabel(InstallDialog) self.warn_label.setObjectName("warn_label") self.install_dialog_layout.setWidget(15, QtWidgets.QFormLayout.LabelRole, self.warn_label) @@ -107,9 +94,7 @@ class Ui_InstallDialog(object): self.install_dialog_layout.setWidget(15, QtWidgets.QFormLayout.FieldRole, self.warn_message) self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.setObjectName("button_layout") - spacerItem = QtWidgets.QSpacerItem( - 40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum - ) + 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") @@ -122,14 +107,10 @@ class Ui_InstallDialog(object): self.button_layout.addWidget(self.install_button) self.install_dialog_layout.setLayout(17, QtWidgets.QFormLayout.SpanningRole, self.button_layout) self.advanced_layout = QtWidgets.QFormLayout() - self.advanced_layout.setLabelAlignment( - QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter - ) + self.advanced_layout.setLabelAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.advanced_layout.setObjectName("advanced_layout") self.max_workers_label = QtWidgets.QLabel(InstallDialog) - self.max_workers_label.setAlignment( - QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter - ) + self.max_workers_label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.max_workers_label.setObjectName("max_workers_label") self.advanced_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.max_workers_label) self.max_workers_layout = QtWidgets.QHBoxLayout() @@ -150,9 +131,7 @@ class Ui_InstallDialog(object): self.max_workers_layout.addWidget(self.max_workers_info_label) self.advanced_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.max_workers_layout) self.max_memory_label = QtWidgets.QLabel(InstallDialog) - self.max_memory_label.setAlignment( - QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter - ) + self.max_memory_label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.max_memory_label.setObjectName("max_memory_label") self.advanced_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.max_memory_label) self.max_memory_layout = QtWidgets.QHBoxLayout() @@ -178,15 +157,15 @@ class Ui_InstallDialog(object): self.advanced_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.max_memory_layout) self.dl_optimizations_label = QtWidgets.QLabel(InstallDialog) self.dl_optimizations_label.setObjectName("dl_optimizations_label") - self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) + self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.dl_optimizations_label) self.dl_optimizations_check = QtWidgets.QCheckBox(InstallDialog) self.dl_optimizations_check.setText("") self.dl_optimizations_check.setChecked(False) self.dl_optimizations_check.setObjectName("dl_optimizations_check") - self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dl_optimizations_check) + self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.dl_optimizations_check) self.force_download_label = QtWidgets.QLabel(InstallDialog) self.force_download_label.setObjectName("force_download_label") - self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.force_download_label) + self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.force_download_label) self.force_download_check = QtWidgets.QCheckBox(InstallDialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -195,10 +174,10 @@ class Ui_InstallDialog(object): self.force_download_check.setSizePolicy(sizePolicy) self.force_download_check.setText("") self.force_download_check.setObjectName("force_download_check") - self.advanced_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.force_download_check) + self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.force_download_check) self.ignore_space_label = QtWidgets.QLabel(InstallDialog) self.ignore_space_label.setObjectName("ignore_space_label") - self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.ignore_space_label) + self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.ignore_space_label) self.ignore_space_check = QtWidgets.QCheckBox(InstallDialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -209,10 +188,10 @@ class Ui_InstallDialog(object): font.setItalic(True) self.ignore_space_check.setFont(font) self.ignore_space_check.setObjectName("ignore_space_check") - self.advanced_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.ignore_space_check) + self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.ignore_space_check) self.download_only_label = QtWidgets.QLabel(InstallDialog) self.download_only_label.setObjectName("download_only_label") - self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.download_only_label) + self.advanced_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.download_only_label) self.download_only_check = QtWidgets.QCheckBox(InstallDialog) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) @@ -223,13 +202,22 @@ class Ui_InstallDialog(object): font.setItalic(True) self.download_only_check.setFont(font) self.download_only_check.setObjectName("download_only_check") - self.advanced_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.download_only_check) + self.advanced_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.download_only_check) + self.install_preqs_lbl = QtWidgets.QLabel(InstallDialog) + self.install_preqs_lbl.setObjectName("install_preqs_lbl") + self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.install_preqs_lbl) + self.install_preqs_check = QtWidgets.QCheckBox(InstallDialog) + font = QtGui.QFont() + font.setItalic(True) + self.install_preqs_check.setFont(font) + self.install_preqs_check.setText("") + self.install_preqs_check.setChecked(False) + self.install_preqs_check.setObjectName("install_preqs_check") + self.advanced_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.install_preqs_check) self.install_dialog_layout.setLayout(16, QtWidgets.QFormLayout.SpanningRole, self.advanced_layout) self.advanced_placeholder_layout = QtWidgets.QVBoxLayout() self.advanced_placeholder_layout.setObjectName("advanced_placeholder_layout") - self.install_dialog_layout.setLayout( - 12, QtWidgets.QFormLayout.SpanningRole, self.advanced_placeholder_layout - ) + self.install_dialog_layout.setLayout(12, QtWidgets.QFormLayout.SpanningRole, self.advanced_placeholder_layout) self.retranslateUi(InstallDialog) QtCore.QMetaObject.connectSlotsByName(InstallDialog) @@ -240,7 +228,6 @@ class Ui_InstallDialog(object): self.install_dir_label.setText(_translate("InstallDialog", "Install directory")) self.platform_label.setText(_translate("InstallDialog", "Platform")) self.shortcut_lbl.setText(_translate("InstallDialog", "Create shortcut")) - self.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites")) self.sdl_list_label.setText(_translate("InstallDialog", "Optional packs")) self.download_size_label.setText(_translate("InstallDialog", "Download size")) self.download_size_info_label.setText(_translate("InstallDialog", "Click verify...")) @@ -262,11 +249,11 @@ class Ui_InstallDialog(object): self.ignore_space_check.setText(_translate("InstallDialog", "Use with caution!")) self.download_only_label.setText(_translate("InstallDialog", "Download only")) self.download_only_check.setText(_translate("InstallDialog", "Do not try to install.")) + self.install_preqs_lbl.setText(_translate("InstallDialog", "Install prerequisites")) if __name__ == "__main__": import sys - app = QtWidgets.QApplication(sys.argv) InstallDialog = QtWidgets.QDialog() ui = Ui_InstallDialog() diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui index 03875d60..0542d5a1 100644 --- a/rare/ui/components/dialogs/install_dialog.ui +++ b/rare/ui/components/dialogs/install_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 526 - 483 + 530 + 489 @@ -59,6 +59,9 @@ Create shortcut + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + @@ -66,24 +69,8 @@ - - - - - - Install prerequisites - - - - - - - - true - - - - + + true @@ -308,14 +295,14 @@ - + Enable reordering - + @@ -325,14 +312,14 @@ - + Force redownload - + @@ -345,14 +332,14 @@ - + Ignore free space - + @@ -370,14 +357,14 @@ - + Download only - + @@ -395,6 +382,28 @@ + + + + Install prerequisites + + + + + + + + true + + + + + + + false + + + From 625066d2da3313e3eb3ec11ca717c57b67ae52ba Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Thu, 11 Aug 2022 21:09:21 +0200 Subject: [PATCH 4/5] Fix two small errors in game launch helper --- rare/game_launch_helper/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rare/game_launch_helper/__init__.py b/rare/game_launch_helper/__init__.py index 6b26435d..6773b866 100644 --- a/rare/game_launch_helper/__init__.py +++ b/rare/game_launch_helper/__init__.py @@ -40,7 +40,7 @@ class PreLaunchThread(QRunnable): def prepare_launch(self, app_name) -> Union[LaunchArgs, None]: try: args = get_launch_args(self.core, InitArgs(app_name)) - except GameArgsError as e: + except Exception as e: self.signals.error_occurred.emit(str(e)) return None if not args: @@ -196,8 +196,11 @@ class GameProcessApp(RareApp): def stop(self): self.logger.info("Stopping server") - self.server.close() - self.server.deleteLater() + try: + self.server.close() + self.server.deleteLater() + except RuntimeError: + pass self.exit_app.emit() From 70a51902d23219e1c761fdda697f02af7120e5eb Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Thu, 11 Aug 2022 21:43:11 +0200 Subject: [PATCH 5/5] Move move game stuff to external file I know it's weird --- .../tabs/games/game_info/game_info.py | 256 +---------------- .../tabs/games/game_info/move_game.py | 258 ++++++++++++++++++ 2 files changed, 261 insertions(+), 253 deletions(-) create mode 100644 rare/components/tabs/games/game_info/move_game.py diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py index 9f8fa904..d4808378 100644 --- a/rare/components/tabs/games/game_info/game_info.py +++ b/rare/components/tabs/games/game_info/game_info.py @@ -3,31 +3,25 @@ import platform import shutil from logging import getLogger from pathlib import Path -from typing import Tuple from PyQt5.QtCore import ( - QObject, - QRunnable, Qt, pyqtSignal, QThreadPool, pyqtSlot, ) from PyQt5.QtWidgets import ( - QFileDialog, QHBoxLayout, - QLabel, QMenu, QProgressBar, QPushButton, - QVBoxLayout, QWidget, QMessageBox, QWidgetAction, ) -from legendary.models.game import Game, InstalledGame, VerifyResult -from legendary.utils.lfs import validate_files +from legendary.models.game import Game, InstalledGame +from rare.models.install import InstallOptionsModel from rare.shared import ( LegendaryCoreSingleton, GlobalSignalsSingleton, @@ -35,11 +29,9 @@ from rare.shared import ( ) from rare.shared.image_manager import ImageManagerSingleton, ImageSize from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo -from rare.utils.extra_widgets import PathEdit from rare.utils.legendary_utils import VerifyWorker -from rare.models.install import InstallOptionsModel -from rare.utils.steam_grades import SteamWorker from rare.utils.misc import get_size +from rare.utils.steam_grades import SteamWorker from rare.widgets.image_widget import ImageWidget logger = getLogger("GameInfo") @@ -401,245 +393,3 @@ class GameInfo(QWidget, Ui_GameInfo): self.move_game_pop_up.update_game(app_name) - -class MoveGamePopUp(QWidget): - move_clicked = pyqtSignal(str) - browse_done = pyqtSignal() - - def __init__(self): - super(MoveGamePopUp, self).__init__() - layout: QVBoxLayout = QVBoxLayout() - self.install_path = str() - self.core = LegendaryCoreSingleton() - self.move_path_edit = PathEdit(str(), QFileDialog.Directory, edit_func=self.edit_func_move_game) - self.move_path_edit.path_select.clicked.connect(self.emit_browse_done_signal) - - self.move_game = QPushButton(self.tr("Move")) - self.move_game.setMaximumWidth(50) - self.move_game.clicked.connect(self.emit_move_game_signal) - - self.warn_overwriting = QLabel() - - middle_layout = QHBoxLayout() - middle_layout.setAlignment(Qt.AlignRight) - middle_layout.addWidget(self.warn_overwriting, stretch=1) - middle_layout.addWidget(self.move_game) - - bottom_layout = QVBoxLayout() - self.aval_space_label = QLabel() - self.req_space_label = QLabel() - bottom_layout.addWidget(self.aval_space_label) - bottom_layout.addWidget(self.req_space_label) - - layout.addWidget(self.move_path_edit) - layout.addLayout(middle_layout) - layout.addLayout(bottom_layout) - - self.setLayout(layout) - - def emit_move_game_signal(self): - self.move_clicked.emit(self.move_path_edit.text()) - - def emit_browse_done_signal(self): - self.browse_done.emit() - - def refresh_indicator(self): - # needed so the edit_func gets run again - text = self.move_path_edit.text() - self.move_path_edit.setText(str()) - self.move_path_edit.setText(text) - - # Thanks to lk. - @staticmethod - def find_mount(path): - mount_point = path - while path != path.anchor: - if path.is_mount(): - return path - else: - path = path.parent - return mount_point - - def edit_func_move_game(self, dir_selected): - self.move_game.setEnabled(True) - self.warn_overwriting.setHidden(True) - - def helper_func(reason: str) -> Tuple[bool, str, str]: - self.move_game.setEnabled(False) - return False, dir_selected, self.tr(reason) - - if not self.install_path or not dir_selected: - return helper_func("You need to provide a directory.") - - install_path = Path(self.install_path).resolve() - dest_path = Path(dir_selected).resolve() - dest_path_with_suffix = dest_path.joinpath(install_path.stem).resolve() - - if not dest_path.is_dir(): - return helper_func("Directory doesn't exist or file selected.") - - # Get free space on drive and size of game folder - _, _, free_space = shutil.disk_usage(dest_path) - source_size = sum(f.stat().st_size for f in install_path.glob("**/*") if f.is_file()) - - # Calculate from bytes to gigabytes - free_space_dest_drive = round(free_space / 1000**3, 2) - source_size = round(source_size / 1000**3, 2) - self.aval_space_label.setText(self.tr("Available space: {}GB".format(free_space_dest_drive))) - self.req_space_label.setText(self.tr("Required space: {}GB").format(source_size)) - - if not os.access(dir_selected, os.W_OK) or not os.access(self.install_path, os.W_OK): - return helper_func("No write permission on destination path/current install path.") - - if install_path == dest_path or install_path == dest_path_with_suffix: - return helper_func("Same directory or parent directory selected.") - - if str(install_path) in str(dest_path): - return helper_func("You can't select a directory that is inside the current install path.") - - if str(dest_path_with_suffix) in str(install_path): - return helper_func("You can't select a directory which contains the game installation.") - - for game in self.core.get_installed_list(): - if game.install_path in dir_selected: - return helper_func("Game installations cannot be nested due to unintended sideeffects.") - - is_existing_dir = is_game_dir(install_path, dest_path_with_suffix) - - for i in dest_path.iterdir(): - if install_path.stem in i.stem: - if dest_path_with_suffix.is_dir(): - if not is_existing_dir: - self.warn_overwriting.setHidden(False) - elif dest_path_with_suffix.is_file(): - self.warn_overwriting.setHidden(False) - - if free_space_dest_drive <= source_size and not is_existing_dir: - return helper_func("Not enough space available on drive.") - - # Fallback - self.move_game.setEnabled(True) - return True, dir_selected, str() - - def update_game(self, app_name): - igame = self.core.get_installed_game(app_name, False) - if igame is None: - return - self.install_path = igame.install_path - self.move_path_edit.setText(igame.install_path) - self.warn_overwriting.setText( - self.tr("Moving here will overwrite the dir/file {}/").format(Path(self.install_path).stem) - ) - - -class CopyGameInstallation(QRunnable): - class Signals(QObject): - progress = pyqtSignal(int) - finished = pyqtSignal(str) - no_space_left = pyqtSignal() - - def __init__( - self, - install_path: str, - dest_path: Path, - is_existing_dir: bool, - igame: InstalledGame, - ): - super(CopyGameInstallation, self).__init__() - self.signals = self.Signals() - self.install_path = install_path - self.dest_path = dest_path - self.source_size = 0 - self.dest_size = 0 - self.is_existing_dir = is_existing_dir - self.core = LegendaryCoreSingleton() - self.igame = igame - self.file_list = None - self.total: int = 0 - - def run(self): - root_directory = Path(self.install_path) - self.source_size = sum(f.stat().st_size for f in root_directory.glob("**/*") if f.is_file()) - - # if game dir is not existing, just copying: - if not self.is_existing_dir: - shutil.copytree( - self.install_path, - self.dest_path, - copy_function=self.copy_each_file_with_progress, - dirs_exist_ok=True, - ) - else: - manifest_data, _ = self.core.get_installed_manifest(self.igame.app_name) - manifest = self.core.load_manifest(manifest_data) - files = sorted( - manifest.file_manifest_list.elements, - key=lambda a: a.filename.lower(), - ) - self.file_list = [(f.filename, f.sha_hash.hex()) for f in files] - self.total = len(self.file_list) - - # recreate dir structure - shutil.copytree( - self.install_path, - self.dest_path, - copy_function=self.copy_dir_structure, - dirs_exist_ok=True, - ) - - for i, (result, relative_path, _, _) in enumerate( - validate_files(str(self.dest_path), self.file_list) - ): - dst_path = f"{self.dest_path}/{relative_path}" - src_path = f"{self.install_path}/{relative_path}" - if Path(src_path).is_file(): - if result == VerifyResult.HASH_MISMATCH: - try: - shutil.copy(src_path, dst_path) - except IOError: - self.signals.no_space_left.emit() - return - elif result == VerifyResult.FILE_MISSING: - try: - shutil.copy(src_path, dst_path) - except (IOError, OSError): - self.signals.no_space_left.emit() - return - elif result == VerifyResult.OTHER_ERROR: - logger.warning(f"Copying file {src_path} to {dst_path} failed") - self.signals.progress.emit(int(i * 10 / self.total * 10)) - else: - logger.warning( - f"Source dir does not have file {src_path}. File will be missing in the destination " - f"dir. " - ) - - shutil.rmtree(self.install_path) - self.signals.finished.emit(str(self.dest_path)) - - def copy_each_file_with_progress(self, src, dst): - shutil.copy(src, dst) - self.dest_size += Path(src).stat().st_size - self.signals.progress.emit(int(self.dest_size * 10 / self.source_size * 10)) - - # This method is a copy_func, and only copies the src if it's a dir. - # Thus, it can be used to re-create the dir strucute. - @staticmethod - def copy_dir_structure(src, dst): - if os.path.isdir(dst): - dst = os.path.join(dst, os.path.basename(src)) - if os.path.isdir(src): - shutil.copyfile(src, dst) - shutil.copystat(src, dst) - return dst - - -def is_game_dir(install_path: Path, dest_path: Path): - # This iterates over the destination dir, then iterates over the current install dir and if the file names - # matches, we have an exisiting dir - if dest_path.is_dir(): - for file in dest_path.iterdir(): - for install_file in install_path.iterdir(): - if file.name == install_file.name: - return True - return False diff --git a/rare/components/tabs/games/game_info/move_game.py b/rare/components/tabs/games/game_info/move_game.py new file mode 100644 index 00000000..e4a71d3e --- /dev/null +++ b/rare/components/tabs/games/game_info/move_game.py @@ -0,0 +1,258 @@ +import os +import shutil +from logging import getLogger +from pathlib import Path +from typing import Tuple + +from PyQt5.QtCore import pyqtSignal, QRunnable, QObject, Qt +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog +from legendary.models.game import VerifyResult, InstalledGame +from legendary.utils.lfs import validate_files + +from rare.shared import LegendaryCoreSingleton +from rare.utils.extra_widgets import PathEdit + +logger = getLogger("MoveGame") + + +class MoveGamePopUp(QWidget): + move_clicked = pyqtSignal(str) + browse_done = pyqtSignal() + + def __init__(self): + super(MoveGamePopUp, self).__init__() + layout: QVBoxLayout = QVBoxLayout() + self.install_path = str() + self.core = LegendaryCoreSingleton() + self.move_path_edit = PathEdit(str(), QFileDialog.Directory, edit_func=self.edit_func_move_game) + self.move_path_edit.path_select.clicked.connect(self.emit_browse_done_signal) + + self.move_game = QPushButton(self.tr("Move")) + self.move_game.setMaximumWidth(50) + self.move_game.clicked.connect(self.emit_move_game_signal) + + self.warn_overwriting = QLabel() + + middle_layout = QHBoxLayout() + middle_layout.setAlignment(Qt.AlignRight) + middle_layout.addWidget(self.warn_overwriting, stretch=1) + middle_layout.addWidget(self.move_game) + + bottom_layout = QVBoxLayout() + self.aval_space_label = QLabel() + self.req_space_label = QLabel() + bottom_layout.addWidget(self.aval_space_label) + bottom_layout.addWidget(self.req_space_label) + + layout.addWidget(self.move_path_edit) + layout.addLayout(middle_layout) + layout.addLayout(bottom_layout) + + self.setLayout(layout) + + def emit_move_game_signal(self): + self.move_clicked.emit(self.move_path_edit.text()) + + def emit_browse_done_signal(self): + self.browse_done.emit() + + def refresh_indicator(self): + # needed so the edit_func gets run again + text = self.move_path_edit.text() + self.move_path_edit.setText(str()) + self.move_path_edit.setText(text) + + # Thanks to lk. + @staticmethod + def find_mount(path): + mount_point = path + while path != path.anchor: + if path.is_mount(): + return path + else: + path = path.parent + return mount_point + + def edit_func_move_game(self, dir_selected): + self.move_game.setEnabled(True) + self.warn_overwriting.setHidden(True) + + def helper_func(reason: str) -> Tuple[bool, str, str]: + self.move_game.setEnabled(False) + return False, dir_selected, self.tr(reason) + + if not self.install_path or not dir_selected: + return helper_func("You need to provide a directory.") + + install_path = Path(self.install_path).resolve() + dest_path = Path(dir_selected).resolve() + dest_path_with_suffix = dest_path.joinpath(install_path.stem).resolve() + + if not dest_path.is_dir(): + return helper_func("Directory doesn't exist or file selected.") + + # Get free space on drive and size of game folder + _, _, free_space = shutil.disk_usage(dest_path) + source_size = sum(f.stat().st_size for f in install_path.glob("**/*") if f.is_file()) + + # Calculate from bytes to gigabytes + free_space_dest_drive = round(free_space / 1000 ** 3, 2) + source_size = round(source_size / 1000 ** 3, 2) + self.aval_space_label.setText(self.tr("Available space: {}GB".format(free_space_dest_drive))) + self.req_space_label.setText(self.tr("Required space: {}GB").format(source_size)) + + if not os.access(dir_selected, os.W_OK) or not os.access(self.install_path, os.W_OK): + return helper_func("No write permission on destination path/current install path.") + + if install_path == dest_path or install_path == dest_path_with_suffix: + return helper_func("Same directory or parent directory selected.") + + if str(install_path) in str(dest_path): + return helper_func("You can't select a directory that is inside the current install path.") + + if str(dest_path_with_suffix) in str(install_path): + return helper_func("You can't select a directory which contains the game installation.") + + for game in self.core.get_installed_list(): + if game.install_path in dir_selected: + return helper_func("Game installations cannot be nested due to unintended sideeffects.") + + is_existing_dir = is_game_dir(install_path, dest_path_with_suffix) + + for i in dest_path.iterdir(): + if install_path.stem in i.stem: + if dest_path_with_suffix.is_dir(): + if not is_existing_dir: + self.warn_overwriting.setHidden(False) + elif dest_path_with_suffix.is_file(): + self.warn_overwriting.setHidden(False) + + if free_space_dest_drive <= source_size and not is_existing_dir: + return helper_func("Not enough space available on drive.") + + # Fallback + self.move_game.setEnabled(True) + return True, dir_selected, str() + + def update_game(self, app_name): + igame = self.core.get_installed_game(app_name, False) + if igame is None: + return + self.install_path = igame.install_path + self.move_path_edit.setText(igame.install_path) + self.warn_overwriting.setText( + self.tr("Moving here will overwrite the dir/file {}/").format(Path(self.install_path).stem) + ) + + +class CopyGameInstallation(QRunnable): + class Signals(QObject): + progress = pyqtSignal(int) + finished = pyqtSignal(str) + no_space_left = pyqtSignal() + + def __init__( + self, + install_path: str, + dest_path: Path, + is_existing_dir: bool, + igame: InstalledGame, + ): + super(CopyGameInstallation, self).__init__() + self.signals = CopyGameInstallation.Signals() + self.install_path = install_path + self.dest_path = dest_path + self.source_size = 0 + self.dest_size = 0 + self.is_existing_dir = is_existing_dir + self.core = LegendaryCoreSingleton() + self.igame = igame + self.file_list = None + self.total: int = 0 + + def run(self): + root_directory = Path(self.install_path) + self.source_size = sum(f.stat().st_size for f in root_directory.glob("**/*") if f.is_file()) + + # if game dir is not existing, just copying: + if not self.is_existing_dir: + shutil.copytree( + self.install_path, + self.dest_path, + copy_function=self.copy_each_file_with_progress, + dirs_exist_ok=True, + ) + else: + manifest_data, _ = self.core.get_installed_manifest(self.igame.app_name) + manifest = self.core.load_manifest(manifest_data) + files = sorted( + manifest.file_manifest_list.elements, + key=lambda a: a.filename.lower(), + ) + self.file_list = [(f.filename, f.sha_hash.hex()) for f in files] + self.total = len(self.file_list) + + # recreate dir structure + shutil.copytree( + self.install_path, + self.dest_path, + copy_function=self.copy_dir_structure, + dirs_exist_ok=True, + ) + + for i, (result, relative_path, _, _) in enumerate( + validate_files(str(self.dest_path), self.file_list) + ): + dst_path = f"{self.dest_path}/{relative_path}" + src_path = f"{self.install_path}/{relative_path}" + if Path(src_path).is_file(): + if result == VerifyResult.HASH_MISMATCH: + try: + shutil.copy(src_path, dst_path) + except IOError: + self.signals.no_space_left.emit() + return + elif result == VerifyResult.FILE_MISSING: + try: + shutil.copy(src_path, dst_path) + except (IOError, OSError): + self.signals.no_space_left.emit() + return + elif result == VerifyResult.OTHER_ERROR: + logger.warning(f"Copying file {src_path} to {dst_path} failed") + self.signals.progress.emit(int(i * 10 / self.total * 10)) + else: + logger.warning( + f"Source dir does not have file {src_path}. File will be missing in the destination " + f"dir. " + ) + + shutil.rmtree(self.install_path) + self.signals.finished.emit(str(self.dest_path)) + + def copy_each_file_with_progress(self, src, dst): + shutil.copy(src, dst) + self.dest_size += Path(src).stat().st_size + self.signals.progress.emit(int(self.dest_size * 10 / self.source_size * 10)) + + # This method is a copy_func, and only copies the src if it's a dir. + # Thus, it can be used to re-create the dir strucute. + @staticmethod + def copy_dir_structure(src, dst): + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + if os.path.isdir(src): + shutil.copyfile(src, dst) + shutil.copystat(src, dst) + return dst + + +def is_game_dir(install_path: Path, dest_path: Path): + # This iterates over the destination dir, then iterates over the current install dir and if the file names + # matches, we have an exisiting dir + if dest_path.is_dir(): + for file in dest_path.iterdir(): + for install_file in install_path.iterdir(): + if file.name == install_file.name: + return True + return False