diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py index cfc16f54..68691dd1 100644 --- a/rare/components/dialogs/install_dialog.py +++ b/rare/components/dialogs/install_dialog.py @@ -1,6 +1,7 @@ import os import platform from multiprocessing import Queue as MPQueue +from typing import Tuple from PyQt5.QtCore import Qt, QObject, QRunnable, QThreadPool, pyqtSignal, pyqtSlot from PyQt5.QtGui import QCloseEvent, QKeyEvent @@ -35,6 +36,10 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.game = self.core.get_game(self.app_name) \ if not self.dl_item.options.overlay \ else Game(app_name=self.app_name, app_title="Epic Overlay") + + self.game_path = self.game.metadata.get('customAttributes', {}). \ + get('FolderName', {}).get('value', self.game.app_name) + self.update = update self.silent = silent @@ -194,13 +199,17 @@ class InstallDialog(QDialog, Ui_InstallDialog): self.get_options() self.get_download_info() - def option_changed(self, path): + def option_changed(self, path) -> Tuple[bool, str, str]: self.options_changed = True self.install_button.setEnabled(False) self.verify_button.setEnabled(not self.worker_running) - return True, path - # TODO Add check, if directory is empty + # directory is not empty + full_path = os.path.join(self.dl_item.options.base_path, self.game_path) + if not self.dl_item.options.update and os.path.exists(full_path) and os.listdir(full_path) != 0: + return False, path, PathEdit.reasons.dir_not_empty + + return True, path, "" def non_reload_option_changed(self, option: str): if option == "download_only": diff --git a/rare/components/dialogs/login/browser_login.py b/rare/components/dialogs/login/browser_login.py index ff00841b..258a55f4 100644 --- a/rare/components/dialogs/login/browser_login.py +++ b/rare/components/dialogs/login/browser_login.py @@ -46,19 +46,19 @@ class BrowserLogin(QWidget, Ui_BrowserLogin): return self.sid_edit.is_valid @staticmethod - def text_changed(text) -> Tuple[bool, str]: + def text_changed(text) -> Tuple[bool, str, str]: if text: text = text.strip() if text.startswith("{") and text.endswith("}"): try: text = json.loads(text).get("sid") except json.JSONDecodeError: - return False, text + return False, text, IndicatorLineEdit.reasons.wrong_format elif '"' in text: text = text.strip('"') - return len(text) == 32, text + return len(text) == 32, text, IndicatorLineEdit.reasons.wrong_format else: - return False, text + return False, text, "" def do_login(self): self.status_label.setText(self.tr("Logging in...")) diff --git a/rare/components/tabs/games/game_info/game_settings.py b/rare/components/tabs/games/game_info/game_settings.py index 0750cd13..255c95ac 100644 --- a/rare/components/tabs/games/game_info/game_settings.py +++ b/rare/components/tabs/games/game_info/game_settings.py @@ -66,7 +66,7 @@ class GameSettings(QWidget, Ui_GameSettings): "", file_type=QFileDialog.DirectoryOnly, ph_text=self.tr("Cloud save path"), - edit_func=lambda text: (os.path.exists(text), text), + edit_func=lambda text: (os.path.exists(text), text, PathEdit.reasons.dir_not_exist), save_func=self.save_save_path, ) self.cloud_gb.layout().addRow( @@ -296,10 +296,10 @@ class GameSettings(QWidget, Ui_GameSettings): self.core.lgd.save_config() - def proton_prefix_edit(self, text: str) -> Tuple[bool, str]: + def proton_prefix_edit(self, text: str) -> Tuple[bool, str, str]: if not text: text = os.path.expanduser("~/.proton") - return True, text + return True, text, "" parent = os.path.dirname(text) return os.path.exists(parent), text diff --git a/rare/components/tabs/games/import_sync/egl_sync_group.py b/rare/components/tabs/games/import_sync/egl_sync_group.py index be7cf3f0..b190045c 100644 --- a/rare/components/tabs/games/import_sync/egl_sync_group.py +++ b/rare/components/tabs/games/import_sync/egl_sync_group.py @@ -88,11 +88,11 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): self.egl_path_edit.setText(path) @staticmethod - def egl_path_edit_edit_cb(path) -> Tuple[bool, str]: + def egl_path_edit_edit_cb(path) -> Tuple[bool, str, str]: if not path: - return True, path + return True, path, "" if os.path.exists(os.path.join(path, "system.reg")) and os.path.exists( - os.path.join(path, "dosdevices/c:") + os.path.join(path, "dosdevices/c:") ): # path is a wine prefix path = os.path.join( @@ -101,13 +101,13 @@ class EGLSyncGroup(QGroupBox, Ui_EGLSyncGroup): "ProgramData/Epic/EpicGamesLauncher/Data/Manifests", ) elif not path.rstrip("/").endswith( - "ProgramData/Epic/EpicGamesLauncher/Data/Manifests" + "ProgramData/Epic/EpicGamesLauncher/Data/Manifests" ): # lower() might or might not be needed in the check - return False, path + return False, path, PathEdit.reasons.wrong_path if os.path.exists(path): - return True, path - return False, path + return True, path, "" + return False, path, PathEdit.reasons.dir_not_exist @staticmethod def egl_path_edit_save_cb(path): diff --git a/rare/components/tabs/games/import_sync/import_group.py b/rare/components/tabs/games/import_sync/import_group.py index 344f1ab1..522238a2 100644 --- a/rare/components/tabs/games/import_sync/import_group.py +++ b/rare/components/tabs/games/import_sync/import_group.py @@ -99,13 +99,15 @@ class ImportGroup(QGroupBox, Ui_ImportGroup): self.import_button.setEnabled(False) self.import_button.clicked.connect(self.import_game) - def path_edit_cb(self, path) -> Tuple[bool, str]: + def path_edit_cb(self, path) -> Tuple[bool, str, str]: if os.path.exists(path): if os.path.exists(os.path.join(path, ".egstore")): - return True, path + return True, path, "" elif os.path.basename(path) in self.install_dir_list: - return True, path - return False, path + return True, path, "" + else: + return False, path, PathEdit.reasons.dir_not_exist + return False, path, "" def path_changed(self, path): self.info_label.setText(str()) @@ -114,13 +116,13 @@ class ImportGroup(QGroupBox, Ui_ImportGroup): else: self.app_name.setText(str()) - def app_name_edit_cb(self, text) -> Tuple[bool, str]: + def app_name_edit_cb(self, text) -> Tuple[bool, str, str]: if not text: - return False, text + return False, text, "" if text in self.app_name_list: - return True, text + return True, text, "" else: - return False, text + return False, text, IndicatorLineEdit.reasons.game_not_installed def app_name_changed(self, text): self.info_label.setText(str()) diff --git a/rare/components/tabs/settings/legendary.py b/rare/components/tabs/settings/legendary.py index a6a47040..aded8da8 100644 --- a/rare/components/tabs/settings/legendary.py +++ b/rare/components/tabs/settings/legendary.py @@ -100,14 +100,18 @@ class LegendarySettings(QWidget, Ui_LegendarySettings): QThreadPool.globalInstance().start(worker) @staticmethod - def locale_edit_cb(text: str) -> Tuple[bool, str]: + def locale_edit_cb(text: str) -> Tuple[bool, str, str]: if text: if re.match("^[a-zA-Z]{2,3}[-_][a-zA-Z]{2,3}$", text): language, country = text.replace("_", "-").split("-") text = "-".join([language.lower(), country.upper()]) - return bool(re.match("^[a-z]{2,3}-[A-Z]{2,3}$", text)), text + if bool(re.match("^[a-z]{2,3}-[A-Z]{2,3}$", text)): + return True, text, "" + else: + return False, text, IndicatorLineEdit.reasons.wrong_format + else: - return True, text + return True, text, "" def locale_save_cb(self, text: str): if text: diff --git a/rare/utils/extra_widgets.py b/rare/utils/extra_widgets.py index b40dbc65..e97e2077 100644 --- a/rare/utils/extra_widgets.py +++ b/rare/utils/extra_widgets.py @@ -143,19 +143,28 @@ class FlowLayout(QLayout): return parent.spacing() +class IndicatorReasons: + dir_not_empty = QCoreApplication.translate("IndicatorReasons", "Directory is not empty") + wrong_format = QCoreApplication.translate("IndicatorReasons", "Given text has wrong format") + game_not_installed = QCoreApplication.translate("IndicatorReasons", "Game is not installed or does not exist") + dir_not_exist = QCoreApplication.translate("IndicatorReasons", "Directory does not exist") + wrong_path = QCoreApplication.translate("IndicatorReasons", "Wrong Directory") + + class IndicatorLineEdit(QWidget): textChanged = pyqtSignal(str) is_valid = False + reasons = IndicatorReasons() def __init__( - self, - text: str = "", - ph_text: str = "", - completer: QCompleter = None, - edit_func: Callable[[str], Tuple[bool, str]] = None, - save_func: Callable[[str], None] = None, - horiz_policy: QSizePolicy = QSizePolicy.Expanding, - parent=None, + self, + text: str = "", + ph_text: str = "", + completer: QCompleter = None, + edit_func: Callable[[str], Tuple[bool, str, str]] = None, + save_func: Callable[[str], None] = None, + horiz_policy: QSizePolicy = QSizePolicy.Expanding, + parent=None, ): super(IndicatorLineEdit, self).__init__(parent=parent) self.setObjectName("IndicatorLineEdit") @@ -219,20 +228,25 @@ class IndicatorLineEdit(QWidget): self.hint_label.setFrameRect(self.line_edit.rect()) self.hint_label.setText(text) - def __indicator(self, res): + def __indicator(self, res, reason=None): color = "green" if res else "red" self.indicator_label.setPixmap( qta_icon("ei.info-circle", color=color).pixmap(16, 16) ) + if reason: + self.indicator_label.setToolTip(reason) + else: + self.indicator_label.setToolTip("") def __edit(self, text): if self.edit_func is not None: self.line_edit.blockSignals(True) - self.is_valid, text = self.edit_func(text) + + self.is_valid, text, reason = self.edit_func(text) if text != self.line_edit.text(): self.line_edit.setText(text) self.line_edit.blockSignals(False) - self.__indicator(self.is_valid) + self.__indicator(self.is_valid, reason) if self.is_valid: self.__save(text) self.textChanged.emit(text) @@ -280,13 +294,13 @@ class PathEdit(IndicatorLineEdit): compl_model = QFileSystemModel() def __init__( - self, + self, path: str = "", file_type: QFileDialog.FileType = QFileDialog.AnyFile, type_filter: str = "", name_filter: str = "", ph_text: str = "", - edit_func: Callable[[str], Tuple[bool, str]] = None, + edit_func: Callable[[str], Tuple[bool, str, str]] = None, save_func: Callable[[str], None] = None, horiz_policy: QSizePolicy = QSizePolicy.Expanding, parent=None,