From d7692df70629f8f7b451d937e6cbeadd4c4b82dc Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:22:21 +0200 Subject: [PATCH 1/6] DownloadsTab: add slot for RareEosOverlay in __add_update --- rare/components/tabs/downloads/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rare/components/tabs/downloads/__init__.py b/rare/components/tabs/downloads/__init__.py index 9856b4889..a42e8c7ad 100644 --- a/rare/components/tabs/downloads/__init__.py +++ b/rare/components/tabs/downloads/__init__.py @@ -17,7 +17,7 @@ from rare.components.dialogs.install import InstallDialog from rare.components.dialogs.uninstall import UninstallDialog from rare.lgndr.models.downloading import UIUpdate -from rare.models.game import RareGame +from rare.models.game import RareEosOverlay, RareGame from rare.models.image import ImageSize from rare.models.install import ( InstallOptionsModel, @@ -114,7 +114,8 @@ def __check_updates(self): @Slot(str) @Slot(RareGame) - def __add_update(self, update: Union[str, RareGame]): + @Slot(RareEosOverlay) + def __add_update(self, update: Union[str, RareGame, RareEosOverlay]): if isinstance(update, str): update = self.rcore.get_game(update) From 2982f4d9dba6a4e428cf4d2638e7947589bf1132 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:23:06 +0200 Subject: [PATCH 2/6] chore: fix some monospaced font not applying --- rare/commands/launcher/console_dialog.py | 4 +--- rare/components/dialogs/install/file_filters.py | 4 +--- rare/components/dialogs/install/selective.py | 4 +--- rare/components/dialogs/move.py | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/rare/commands/launcher/console_dialog.py b/rare/commands/launcher/console_dialog.py index 5d1e33852..95fb12e36 100644 --- a/rare/commands/launcher/console_dialog.py +++ b/rare/commands/launcher/console_dialog.py @@ -165,9 +165,7 @@ class ConsoleEdit(QPlainTextEdit): def __init__(self, parent=None): super(ConsoleEdit, self).__init__(parent=parent) self.setReadOnly(True) - font = self.font() - font.setStyleHint(QFont.StyleHint.Monospace) - self.setFont(font) + self.setFont(QFont("monospace")) def scroll_to_last_line(self): cursor = self.textCursor() diff --git a/rare/components/dialogs/install/file_filters.py b/rare/components/dialogs/install/file_filters.py index c958d5e1a..4ea756480 100644 --- a/rare/components/dialogs/install/file_filters.py +++ b/rare/components/dialogs/install/file_filters.py @@ -27,9 +27,7 @@ def clear(self): def add_item(self, data: str): li = QListWidgetItem(data, self.ui.exclude_list) - font = self.font() - font.setStyleHint(QFont.StyleHint.Monospace) - li.setFont(font) + li.setFont(QFont("monospace")) li.setCheckState(Qt.CheckState.Unchecked) self.ui.exclude_list.addItem(li) diff --git a/rare/components/dialogs/install/selective.py b/rare/components/dialogs/install/selective.py index 516339213..8a8f5ff36 100644 --- a/rare/components/dialogs/install/selective.py +++ b/rare/components/dialogs/install/selective.py @@ -12,9 +12,7 @@ class InstallTagCheckBox(QCheckBox): def __init__(self, text, desc, tags: List[str], parent=None): super(InstallTagCheckBox, self).__init__(parent) - font = self.font() - font.setStyleHint(QFont.StyleHint.Monospace) - self.setFont(font) + self.setFont(QFont("monospace")) self.setText(text) self.setToolTip(desc) self.tags = tags diff --git a/rare/components/dialogs/move.py b/rare/components/dialogs/move.py index c4797abd7..76653f2c6 100644 --- a/rare/components/dialogs/move.py +++ b/rare/components/dialogs/move.py @@ -61,9 +61,7 @@ def __init__(self, rcore: RareCore, rgame: RareGame, parent=None): ) self.full_path_info = ElideLabel(parent=self) - font = self.font() - font.setStyleHint(QFont.StyleHint.Monospace) - self.full_path_info.setFont(font) + self.full_path_info.setFont(QFont("monospace")) self.ui.main_layout.setWidget( self.ui.main_layout.getWidgetPosition(self.ui.full_path_label)[0], QFormLayout.ItemRole.FieldRole, From 65e5c1c125e8bc39268e6d1581e67cb05382d995 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:25:57 +0200 Subject: [PATCH 3/6] UninstallDialog: always keep folder and configuration when uninstalling a DLC --- rare/components/dialogs/uninstall.py | 10 ++++++---- rare/models/game.py | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rare/components/dialogs/uninstall.py b/rare/components/dialogs/uninstall.py index 0ec3e158e..f2327f068 100644 --- a/rare/components/dialogs/uninstall.py +++ b/rare/components/dialogs/uninstall.py @@ -1,10 +1,12 @@ +from typing import Union + from PySide6.QtCore import Qt, Signal, Slot from PySide6.QtWidgets import ( QCheckBox, QVBoxLayout, ) -from rare.models.game import RareGame +from rare.models.game import RareEosOverlay, RareGame from rare.models.install import UninstallOptionsModel from rare.utils.misc import qta_icon from rare.widgets.dialogs import ButtonDialog, game_title @@ -13,7 +15,7 @@ class UninstallDialog(ButtonDialog): result_ready = Signal(UninstallOptionsModel) - def __init__(self, rgame: RareGame, options: UninstallOptionsModel, parent=None): + def __init__(self, rgame: Union[RareGame, RareEosOverlay], options: UninstallOptionsModel, parent=None): super(UninstallDialog, self).__init__(parent=parent) header = self.tr("Uninstall") self.setWindowTitle(game_title(header, rgame.app_title)) @@ -25,11 +27,11 @@ def __init__(self, rgame: RareGame, options: UninstallOptionsModel, parent=None) self.keep_folder = QCheckBox(self.tr("Keep game folder")) self.keep_folder.setChecked(bool(options.keep_folder)) - self.keep_folder.setEnabled(not rgame.is_overlay) + self.keep_folder.setEnabled(not rgame.is_overlay and not rgame.is_dlc) self.keep_config = QCheckBox(self.tr("Keep configuation")) self.keep_config.setChecked(bool(options.keep_config)) - self.keep_config.setEnabled(not rgame.is_overlay) + self.keep_config.setEnabled(not rgame.is_overlay and not rgame.is_dlc) self.keep_overlay_keys = QCheckBox(self.tr("Keep EOS Overlay registry keys")) self.keep_overlay_keys.setChecked(bool(options.keep_overlay_keys)) diff --git a/rare/models/game.py b/rare/models/game.py index 549d78476..f27266ea1 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -586,7 +586,8 @@ def uninstall(self) -> bool: self.signals.game.uninstall.emit( UninstallOptionsModel( app_name=self.app_name, - keep_config=self.sdl_name is not None or platform.system() not in {"Windows"}, + keep_folder=self.is_dlc, + keep_config=self.sdl_name is not None or self.is_dlc or platform.system() not in {"Windows"}, )) return True From 5724bb0566f92dbad4dbfb1a28ec371917314d0b Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:27:17 +0200 Subject: [PATCH 4/6] RareGame: do not remove .egstore folder by mistake when removing a DLC --- rare/models/game.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rare/models/game.py b/rare/models/game.py index f27266ea1..ce172a9a4 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -310,7 +310,8 @@ def set_installed(self, installed: bool) -> None: else: if self.has_update: self.signals.download.dequeue.emit(self.app_name) - self.core.egstore_delete(self.igame) + if not self.is_dlc: + self.core.egstore_delete(self.igame) self.igame = None self.signals.game.uninstalled.emit(self.app_name) self.__update_pixmap() From 480ea191114e0f61db3cf40099795507289992dd Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Fri, 12 Dec 2025 22:52:35 +0200 Subject: [PATCH 5/6] RareGame/InstallDialog: pre-set and disable some options when installing a DLC Add handling for InstallOptionsModel's base_path and platform in RareGame to replace the equivalent handling that in InstallDialog. Default to the game's install directory and use the same platform when installing a DLC. Also disable some options that should remain immutable in the dialog. --- rare/components/dialogs/install/dialog.py | 32 +++++++--------- rare/models/game.py | 46 ++++++++++++++++++++++- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/rare/components/dialogs/install/dialog.py b/rare/components/dialogs/install/dialog.py index 48f7b9fc8..d7f952e3c 100644 --- a/rare/components/dialogs/install/dialog.py +++ b/rare/components/dialogs/install/dialog.py @@ -83,15 +83,8 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal self.threadpool = QThreadPool(self) self.threadpool.setMaxThreadCount(1) - if options.base_path: - base_path = options.base_path - elif rgame.is_installed: - base_path = rgame.install_path - else: - base_path = self.core.get_default_install_dir(rgame.default_platform) - self.install_dir_edit = PathEdit( - path=base_path, + path=options.base_path, file_mode=QFileDialog.FileMode.Directory, edit_func=self.__install_dir_edit_callback, save_func=self.__install_dir_save_callback, @@ -104,25 +97,24 @@ def __init__(self, settings: RareAppSettings, rgame: "RareGame", options: Instal self.install_dir_edit, ) - self.install_dir_edit.setDisabled(rgame.is_installed) - self.ui.install_dir_label.setDisabled(rgame.is_installed) - self.ui.shortcut_label.setDisabled(rgame.is_installed) - self.ui.shortcut_check.setDisabled(rgame.is_installed) + self.install_dir_edit.setDisabled(rgame.is_installed or rgame.is_dlc) + self.ui.install_dir_label.setDisabled(rgame.is_installed or rgame.is_dlc) + self.ui.shortcut_label.setDisabled(rgame.is_installed or rgame.is_dlc) + self.ui.shortcut_check.setDisabled(rgame.is_installed or rgame.is_dlc) self.ui.shortcut_check.setChecked(not rgame.is_installed and self.settings.get_value(app_settings.create_shortcut)) self.ui.shortcut_check.checkStateChanged.connect(self.__on_option_changed_no_reload) self.set_error_labels() self.ui.platform_combo.addItems(reversed(rgame.platforms)) - combo_text = rgame.igame.platform if rgame.is_installed else rgame.default_platform - self.ui.platform_combo.setCurrentIndex(self.ui.platform_combo.findText(combo_text)) + self.ui.platform_combo.setCurrentIndex(self.ui.platform_combo.findText(options.platform)) self.ui.platform_combo.currentIndexChanged.connect(self.__on_option_changed) self.ui.platform_combo.currentIndexChanged.connect(self.check_incompatible_platform) self.ui.platform_combo.currentIndexChanged.connect(self.reset_install_dir) self.ui.platform_combo.currentTextChanged.connect(self.selectable.update_list) - self.ui.platform_label.setDisabled(rgame.is_installed) - self.ui.platform_combo.setDisabled(rgame.is_installed) + self.ui.platform_label.setDisabled(rgame.is_installed or rgame.is_dlc) + self.ui.platform_combo.setDisabled(rgame.is_installed or rgame.is_dlc) # 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) @@ -195,7 +187,7 @@ def execute(self): @Slot(int) def reset_install_dir(self, index: int): - if not self.rgame.is_installed: + if not self.rgame.is_installed and not self.rgame.is_dlc: platform = self.ui.platform_combo.itemText(index) default_dir = self.core.get_default_install_dir(platform) self.install_dir_edit.setText(default_dir) @@ -213,7 +205,11 @@ def check_incompatible_platform(self, index: int): def get_options(self): base_path = os.path.join(self.install_dir_edit.text(), ".overlay" if self.__options.overlay else "") - self.__options.base_path = "" if self.rgame.is_installed else base_path + # TODO: investigate if this check is needed + if self.rgame.is_installed or self.rgame.is_dlc: + self.__options.base_path = "" + else: + self.__options.base_path = base_path self.__options.platform = self.ui.platform_combo.currentText() self.__options.create_shortcut = self.ui.shortcut_check.isChecked() self.__options.max_workers = self.advanced.ui.max_workers_spin.value() diff --git a/rare/models/game.py b/rare/models/game.py index ce172a9a4..267a2563d 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -97,6 +97,7 @@ def __init__( self.grant_date() self.owned_dlcs: Set[RareGame] = set() + self.__parent_rgame: Optional[RareGame] = None if self.has_update: self.logger.info(f"Update available for game: {self.app_name} ({self.app_title})") @@ -121,8 +122,18 @@ def add_dlc(self, dlc) -> None: dlc.signals.progress.start.connect(self.signals.progress.start) dlc.signals.progress.update.connect(self.signals.progress.update) dlc.signals.progress.finish.connect(self.signals.progress.finish) + dlc.parent_rgame = self self.owned_dlcs.add(dlc) + @property + def parent_rgame(self) -> Optional["RareGame"]: + return self.__parent_rgame if self.is_dlc else None + + @parent_rgame.setter + def parent_rgame(self, rgame: "RareGame") -> None: + if self.is_dlc: + self.__parent_rgame = rgame + def __on_progress_update(self, progress: int): self.progress = progress @@ -556,16 +567,45 @@ def load_pixmaps(self): def refresh_pixmap(self): self.image_manager.download_image(self.game, self.__update_pixmap, 0, True) + @property + def __install_base_path(self) -> str: + if self.is_installed: + return self.install_path + if self.parent_rgame and self.parent_rgame.is_installed: + return self.parent_rgame.install_path + return self.core.get_default_install_dir(self.default_platform) + + @property + def __install_platform(self) -> str: + if self.is_installed: + return self.igame.platform + if self.parent_rgame and self.parent_rgame.is_installed: + return self.parent_rgame.igame.platform + return self.default_platform + def install(self) -> bool: if not self.is_idle: return False - self.signals.game.install.emit(InstallOptionsModel(app_name=self.app_name)) + self.signals.game.install.emit( + InstallOptionsModel( + app_name=self.app_name, + base_path=self.__install_base_path, + platform=self.__install_platform, + ) + ) return True def modify(self) -> bool: if not self.is_idle: return False - self.signals.game.install.emit(InstallOptionsModel(app_name=self.app_name, reset_sdl=True)) + self.signals.game.install.emit( + InstallOptionsModel( + app_name=self.app_name, + base_path=self.__install_base_path, + platform=self.igame.platform, + reset_sdl=True + ) + ) return True def repair(self, repair_and_update) -> bool: @@ -574,6 +614,8 @@ def repair(self, repair_and_update) -> bool: self.signals.game.install.emit( InstallOptionsModel( app_name=self.app_name, + base_path=self.__install_base_path, + platform=self.igame.platform, repair_mode=True, repair_and_update=repair_and_update, update=repair_and_update, From caa190accecf8370d263907e76bc5d3bb8924b7a Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:11:01 +0200 Subject: [PATCH 6/6] chore: add a colon between prefix and app_title for queued workers --- rare/components/main_window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rare/components/main_window.py b/rare/components/main_window.py index 4c78681cc..41fe9ee55 100644 --- a/rare/components/main_window.py +++ b/rare/components/main_window.py @@ -180,7 +180,7 @@ def update_statusbar(self): self.queued_container.layout().removeWidget(label) label.deleteLater() for info in self.rcore.queue_info(): - label = ElideLabel(f"{info.prefix} {info.app_title}") + label = ElideLabel(f"{info.prefix}: {info.app_title}") label.setObjectName("QueueWorkerLabel") label.setToolTip(f"{info.prefix}: {info.app_title}") label.setProperty("workertype", info.type)