From 5d26fd10269d4fd3c38c8210baa09df840e0859c Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:46:34 +0100 Subject: [PATCH 1/7] Add settings to edit tags for games --- .../tabs/library/details/details.py | 67 ++++++++++++++- rare/models/game.py | 4 + .../tabs/games/game_info/details.py | 85 ++++++++----------- .../tabs/games/game_info/details.ui | 37 ++++---- 4 files changed, 122 insertions(+), 71 deletions(-) diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py index 81672919d..cd37d97ab 100644 --- a/rare/components/tabs/library/details/details.py +++ b/rare/components/tabs/library/details/details.py @@ -11,7 +11,7 @@ ) from PySide6.QtWidgets import ( QWidget, - QMessageBox, + QMessageBox, QLineEdit, QHBoxLayout, QPushButton ) from rare.models.install import SelectiveDownloadsModel, MoveGameModel @@ -80,8 +80,16 @@ def __init__(self, parent=None): "na": self.tr("Not applicable"), } + self.ui.hidden_check.checkStateChanged.connect(self.__on_tag_change) + self.ui.favorites_check.checkStateChanged.connect(self.__on_tag_change) + self.ui.backlog_check.checkStateChanged.connect(self.__on_tag_change) + self.ui.completed_check.checkStateChanged.connect(self.__on_tag_change) + + self.custom_tags: list[QWidget] = [] + self.ui.add_tag_button.setIcon(qta_icon("mdi.plus")) + self.ui.add_tag_button.clicked.connect(lambda: self.__on_tag_add()) + # lk: hide unfinished things - self.ui.tags_group.setVisible(False) self.ui.requirements_group.setVisible(False) @Slot() @@ -150,7 +158,8 @@ def __on_verify(self): QMessageBox.warning( self, self.tr("Error - {}").format(self.rgame.app_title), - self.tr("Installation path for {} does not exist. Cannot continue.").format(self.rgame.app_title), + self.tr("Installation path for {} does not exist. Cannot continue.").format( + self.rgame.app_title), ) return if self.rgame.sdl_name is not None: @@ -267,6 +276,44 @@ def __on_move_result(self, rgame: RareGame, dst_path: str): self.tr("{} successfully moved to {}.").format(rgame.app_title, dst_path), ) + @Slot() + def __on_tag_change(self): + tag_list = [] + if self.ui.hidden_check.isChecked(): + tag_list.append("hidden") + if self.ui.favorites_check.isChecked(): + tag_list.append("favorite") + if self.ui.backlog_check.isChecked(): + tag_list.append("backlog") + if self.ui.completed_check.isChecked(): + tag_list.append("completed") + + for w in self.custom_tags: + tag_list.append(w.layout().itemAt(0).widget().text()) + + logger.info(f"Tags: {tag_list}") + + self.rgame.set_tags(tag_list) + + + @Slot() + def __on_tag_add(self, text=""): + widget = QWidget() + layout = QHBoxLayout() + edit = QLineEdit(text) + edit.editingFinished.connect(self.__on_tag_change) + layout.addWidget(edit) + btn = QPushButton(qta_icon("mdi.trash-can"), None) + def delete_widget(): + self.custom_tags.remove(widget) + widget.deleteLater() + self.__on_tag_change() + btn.clicked.connect(delete_widget) + layout.addWidget(btn) + widget.setLayout(layout) + self.ui.custom_tag_layout.addWidget(widget) + self.custom_tags.append(widget) + @Slot() def __update_widget(self): """ React to state updates from RareGame """ @@ -357,6 +404,20 @@ def __update_widget(self): else: self.ui.game_actions_stack.setCurrentWidget(self.ui.uninstalled_page) + for w in self.custom_tags: + w.deleteLater() + self.custom_tags.clear() + + self.ui.hidden_check.setChecked("hidden" in self.rgame.metadata.tags) + self.ui.favorites_check.setChecked("favorite" in self.rgame.metadata.tags) + self.ui.backlog_check.setChecked("backlog" in self.rgame.metadata.tags) + self.ui.completed_check.setChecked("completed" in self.rgame.metadata.tags) + + for tag in self.rgame.metadata.tags: + if tag in ["hidden", "favorite", "backlog", "completed"]: + continue + self.__on_tag_add(tag) + @Slot(RareGame) def update_game(self, rgame: RareGame): if self.rgame is not None: diff --git a/rare/models/game.py b/rare/models/game.py index 756bbb238..9ec05203b 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -481,6 +481,10 @@ def grant_date(self, force=False) -> datetime: self.__save_metadata() return self.metadata.grant_date + def set_tags(self, tags: List[str]) -> None: + self.metadata.tags = tags + self.__save_metadata() + def set_origin_attributes(self, path: str, size: int = 0) -> None: self.__origin_install_path = path self.__origin_install_size = size diff --git a/rare/ui/components/tabs/games/game_info/details.py b/rare/ui/components/tabs/games/game_info/details.py index 986ea974d..7a95b0ac0 100644 --- a/rare/ui/components/tabs/games/game_info/details.py +++ b/rare/ui/components/tabs/games/game_info/details.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'details.ui' ## -## Created by: Qt User Interface Compiler version 6.7.2 +## Created by: Qt User Interface Compiler version 6.8.1 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -17,9 +17,8 @@ QPalette, QPixmap, QRadialGradient, QTransform) from PySide6.QtWidgets import (QApplication, QCheckBox, QFormLayout, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, - QLayout, QLineEdit, QProgressBar, QPushButton, - QSizePolicy, QSpacerItem, QStackedWidget, QVBoxLayout, - QWidget) + QLayout, QProgressBar, QPushButton, QSizePolicy, + QSpacerItem, QStackedWidget, QVBoxLayout, QWidget) class Ui_GameDetails(object): def setupUi(self, GameDetails): @@ -46,42 +45,31 @@ def setupUi(self, GameDetails): self.tags_layout.addWidget(self.completed_check, 3, 0, 1, 2) - self.hidden_check = QCheckBox(self.tags_group) - self.hidden_check.setObjectName(u"hidden_check") - - self.tags_layout.addWidget(self.hidden_check, 0, 0, 1, 2) - - self.custom1_edit = QLineEdit(self.tags_group) - self.custom1_edit.setObjectName(u"custom1_edit") + self.backlog_check = QCheckBox(self.tags_group) + self.backlog_check.setObjectName(u"backlog_check") - self.tags_layout.addWidget(self.custom1_edit, 4, 1, 1, 1) + self.tags_layout.addWidget(self.backlog_check, 2, 0, 1, 2) self.favorites_check = QCheckBox(self.tags_group) self.favorites_check.setObjectName(u"favorites_check") self.tags_layout.addWidget(self.favorites_check, 1, 0, 1, 2) - self.custom1_check = QCheckBox(self.tags_group) - self.custom1_check.setObjectName(u"custom1_check") - self.custom1_check.setText(u"") - - self.tags_layout.addWidget(self.custom1_check, 4, 0, 1, 1) - - self.backlog_check = QCheckBox(self.tags_group) - self.backlog_check.setObjectName(u"backlog_check") + self.hidden_check = QCheckBox(self.tags_group) + self.hidden_check.setObjectName(u"hidden_check") - self.tags_layout.addWidget(self.backlog_check, 2, 0, 1, 2) + self.tags_layout.addWidget(self.hidden_check, 0, 0, 1, 2) - self.custom2_check = QCheckBox(self.tags_group) - self.custom2_check.setObjectName(u"custom2_check") - self.custom2_check.setText(u"") + self.custom_tag_layout = QVBoxLayout() + self.custom_tag_layout.setObjectName(u"custom_tag_layout") + self.custom_tag_layout.setContentsMargins(-1, 0, -1, 0) - self.tags_layout.addWidget(self.custom2_check, 5, 0, 1, 1) + self.tags_layout.addLayout(self.custom_tag_layout, 5, 0, 1, 2) - self.custom2_edit = QLineEdit(self.tags_group) - self.custom2_edit.setObjectName(u"custom2_edit") + self.add_tag_button = QPushButton(self.tags_group) + self.add_tag_button.setObjectName(u"add_tag_button") - self.tags_layout.addWidget(self.custom2_edit, 5, 1, 1, 1) + self.tags_layout.addWidget(self.add_tag_button, 7, 0, 1, 2) self.left_layout.addWidget(self.tags_group) @@ -95,7 +83,7 @@ def setupUi(self, GameDetails): self.details_layout.setObjectName(u"details_layout") self.details_layout.setSizeConstraint(QLayout.SetFixedSize) self.details_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) - self.details_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.details_layout.setLabelAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setHorizontalSpacing(12) self.details_layout.setVerticalSpacing(12) self.details_layout.setContentsMargins(6, 6, 6, 6) @@ -109,7 +97,7 @@ def setupUi(self, GameDetails): font = QFont() font.setBold(True) self.lbl_dev.setFont(font) - self.lbl_dev.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_dev.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(0, QFormLayout.LabelRole, self.lbl_dev) @@ -118,14 +106,14 @@ def setupUi(self, GameDetails): self.dev.setText(u"error") self.dev.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.dev) + self.details_layout.setWidget(0, QFormLayout.FieldRole, self.dev) self.lbl_app_name = QLabel(GameDetails) self.lbl_app_name.setObjectName(u"lbl_app_name") sizePolicy1.setHeightForWidth(self.lbl_app_name.sizePolicy().hasHeightForWidth()) self.lbl_app_name.setSizePolicy(sizePolicy1) self.lbl_app_name.setFont(font) - self.lbl_app_name.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_app_name.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(1, QFormLayout.LabelRole, self.lbl_app_name) @@ -134,14 +122,14 @@ def setupUi(self, GameDetails): self.app_name.setText(u"error") self.app_name.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.app_name) + self.details_layout.setWidget(1, QFormLayout.FieldRole, self.app_name) self.lbl_version = QLabel(GameDetails) self.lbl_version.setObjectName(u"lbl_version") sizePolicy1.setHeightForWidth(self.lbl_version.sizePolicy().hasHeightForWidth()) self.lbl_version.setSizePolicy(sizePolicy1) self.lbl_version.setFont(font) - self.lbl_version.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_version.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(2, QFormLayout.LabelRole, self.lbl_version) @@ -150,14 +138,14 @@ def setupUi(self, GameDetails): self.version.setText(u"error") self.version.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.version) + self.details_layout.setWidget(2, QFormLayout.FieldRole, self.version) self.lbl_grade = QLabel(GameDetails) self.lbl_grade.setObjectName(u"lbl_grade") sizePolicy1.setHeightForWidth(self.lbl_grade.sizePolicy().hasHeightForWidth()) self.lbl_grade.setSizePolicy(sizePolicy1) self.lbl_grade.setFont(font) - self.lbl_grade.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_grade.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(3, QFormLayout.LabelRole, self.lbl_grade) @@ -167,14 +155,14 @@ def setupUi(self, GameDetails): self.grade.setOpenExternalLinks(True) self.grade.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.grade) + self.details_layout.setWidget(3, QFormLayout.FieldRole, self.grade) self.lbl_install_size = QLabel(GameDetails) self.lbl_install_size.setObjectName(u"lbl_install_size") sizePolicy1.setHeightForWidth(self.lbl_install_size.sizePolicy().hasHeightForWidth()) self.lbl_install_size.setSizePolicy(sizePolicy1) self.lbl_install_size.setFont(font) - self.lbl_install_size.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_install_size.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(4, QFormLayout.LabelRole, self.lbl_install_size) @@ -183,14 +171,14 @@ def setupUi(self, GameDetails): self.install_size.setText(u"error") self.install_size.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.install_size) + self.details_layout.setWidget(4, QFormLayout.FieldRole, self.install_size) self.lbl_install_path = QLabel(GameDetails) self.lbl_install_path.setObjectName(u"lbl_install_path") sizePolicy1.setHeightForWidth(self.lbl_install_path.sizePolicy().hasHeightForWidth()) self.lbl_install_path.setSizePolicy(sizePolicy1) self.lbl_install_path.setFont(font) - self.lbl_install_path.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_install_path.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(5, QFormLayout.LabelRole, self.lbl_install_path) @@ -200,14 +188,14 @@ def setupUi(self, GameDetails): self.install_path.setWordWrap(True) self.install_path.setTextInteractionFlags(Qt.LinksAccessibleByMouse|Qt.TextSelectableByMouse) - self.details_layout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.install_path) + self.details_layout.setWidget(5, QFormLayout.FieldRole, self.install_path) self.lbl_platform = QLabel(GameDetails) self.lbl_platform.setObjectName(u"lbl_platform") sizePolicy1.setHeightForWidth(self.lbl_platform.sizePolicy().hasHeightForWidth()) self.lbl_platform.setSizePolicy(sizePolicy1) self.lbl_platform.setFont(font) - self.lbl_platform.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_platform.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(6, QFormLayout.LabelRole, self.lbl_platform) @@ -215,14 +203,14 @@ def setupUi(self, GameDetails): self.platform.setObjectName(u"platform") self.platform.setText(u"error") - self.details_layout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.platform) + self.details_layout.setWidget(6, QFormLayout.FieldRole, self.platform) self.lbl_game_actions = QLabel(GameDetails) self.lbl_game_actions.setObjectName(u"lbl_game_actions") sizePolicy1.setHeightForWidth(self.lbl_game_actions.sizePolicy().hasHeightForWidth()) self.lbl_game_actions.setSizePolicy(sizePolicy1) self.lbl_game_actions.setFont(font) - self.lbl_game_actions.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + self.lbl_game_actions.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter) self.details_layout.setWidget(7, QFormLayout.LabelRole, self.lbl_game_actions) @@ -339,7 +327,7 @@ def setupUi(self, GameDetails): self.game_actions_stack.addWidget(self.uninstalled_page) - self.details_layout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.game_actions_stack) + self.details_layout.setWidget(7, QFormLayout.FieldRole, self.game_actions_stack) self.right_layout.addLayout(self.details_layout) @@ -351,7 +339,7 @@ def setupUi(self, GameDetails): sizePolicy3.setVerticalStretch(0) sizePolicy3.setHeightForWidth(self.requirements_group.sizePolicy().hasHeightForWidth()) self.requirements_group.setSizePolicy(sizePolicy3) - self.requirements_group.setFrameShape(QFrame.Shape.StyledPanel) + self.requirements_group.setFrameShape(QFrame.StyledPanel) self.requirements_group.setFrameShadow(QFrame.Sunken) self.requirements_layout = QHBoxLayout(self.requirements_group) self.requirements_layout.setObjectName(u"requirements_layout") @@ -375,9 +363,10 @@ def setupUi(self, GameDetails): def retranslateUi(self, GameDetails): self.tags_group.setTitle(QCoreApplication.translate("GameDetails", u"Tags", None)) self.completed_check.setText(QCoreApplication.translate("GameDetails", u"Completed", None)) - self.hidden_check.setText(QCoreApplication.translate("GameDetails", u"Hidden", None)) - self.favorites_check.setText(QCoreApplication.translate("GameDetails", u"Favorites", None)) self.backlog_check.setText(QCoreApplication.translate("GameDetails", u"Backlog", None)) + self.favorites_check.setText(QCoreApplication.translate("GameDetails", u"Favorites", None)) + self.hidden_check.setText(QCoreApplication.translate("GameDetails", u"Hidden", None)) + self.add_tag_button.setText("") self.lbl_dev.setText(QCoreApplication.translate("GameDetails", u"Developer", None)) self.lbl_app_name.setText(QCoreApplication.translate("GameDetails", u"Application name", None)) self.lbl_version.setText(QCoreApplication.translate("GameDetails", u"Version", None)) diff --git a/rare/ui/components/tabs/games/game_info/details.ui b/rare/ui/components/tabs/games/game_info/details.ui index ce7f9ff09..43c346783 100644 --- a/rare/ui/components/tabs/games/game_info/details.ui +++ b/rare/ui/components/tabs/games/game_info/details.ui @@ -38,16 +38,13 @@ - - + + - Hidden + Backlog - - - @@ -55,30 +52,30 @@ - - + + - + Hidden - - - - Backlog + + + + 0 - + + 0 + + - - + + - + - - - From d1e15e77aed4a3862ee93192d8d4fd7b4437a1a6 Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:12:59 +0100 Subject: [PATCH 2/7] Add hidden filter in head bar --- rare/components/tabs/library/__init__.py | 2 ++ rare/components/tabs/library/head_bar.py | 2 +- rare/ui/components/tabs/games/game_info/details.ui | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rare/components/tabs/library/__init__.py b/rare/components/tabs/library/__init__.py index 1cab607cd..4729837a2 100644 --- a/rare/components/tabs/library/__init__.py +++ b/rare/components/tabs/library/__init__.py @@ -43,6 +43,8 @@ def __init__(self, parent=None): self.game_info_page = GameInfoTabs(self) self.game_info_page.back_clicked.connect(lambda: self.setCurrentWidget(self.games_page)) + # Update visibility of hidden games + self.game_info_page.back_clicked.connect(lambda: self.filter_games(self.head_bar.current_filter())) self.game_info_page.import_clicked.connect(self.show_import) self.addWidget(self.game_info_page) diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py index b8f779b4d..ce5e23c5f 100644 --- a/rare/components/tabs/library/head_bar.py +++ b/rare/components/tabs/library/head_bar.py @@ -38,7 +38,7 @@ def __init__(self, parent=None): LibraryFilter.ALL: self.tr("All games"), LibraryFilter.INSTALLED: self.tr("Installed"), LibraryFilter.OFFLINE: self.tr("Offline"), - # LibraryFilter.HIDDEN: self.tr("Hidden"), + LibraryFilter.HIDDEN: self.tr("Hidden"), } for data, text in filters.items(): self.filter.addItem(text, data) diff --git a/rare/ui/components/tabs/games/game_info/details.ui b/rare/ui/components/tabs/games/game_info/details.ui index 43c346783..41e8243d2 100644 --- a/rare/ui/components/tabs/games/game_info/details.ui +++ b/rare/ui/components/tabs/games/game_info/details.ui @@ -71,6 +71,9 @@ + + Add custom tag + From 20f6419da3ac23d05d966e16f52c9d257c596bad Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:35:26 +0100 Subject: [PATCH 3/7] Show correct label for cloud saves in cloud saves game details --- .../tabs/library/details/cloud_saves.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rare/components/tabs/library/details/cloud_saves.py b/rare/components/tabs/library/details/cloud_saves.py index 39d3926ae..3a9686de5 100644 --- a/rare/components/tabs/library/details/cloud_saves.py +++ b/rare/components/tabs/library/details/cloud_saves.py @@ -42,6 +42,7 @@ def __init__(self, parent=None): self.sync_ui.setupUi(self.sync_widget) self.info_label = QLabel(self.tr("This game doesn't support cloud saves")) + self.info_label_not_installed = QLabel(self.tr("Install this game to see cloud saves")) self.rcore = RareCore.instance() self.core = RareCore.instance().core() @@ -86,6 +87,7 @@ def __init__(self, parent=None): layout.addWidget(self.sync_widget) layout.addWidget(self.cloud_widget) layout.addWidget(self.info_label) + layout.addWidget(self.info_label_not_installed) layout.addSpacerItem(QSpacerItem(0, 0, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)) @staticmethod @@ -169,16 +171,16 @@ def __on_wine_resolver_result(self, path, app_name): self.cloud_save_path_edit.setText(path) def __update_widget(self): - supports_saves = self.rgame.igame is not None and ( - self.rgame.game.supports_cloud_saves or self.rgame.game.supports_mac_cloud_saves - ) + supports_saves = self.rgame.game.supports_cloud_saves or self.rgame.game.supports_mac_cloud_saves + saves_ready = self.rgame.igame is not None and supports_saves self.sync_widget.setEnabled( - bool(supports_saves and self.rgame.save_path)) # and not self.rgame.is_save_up_to_date)) + bool(saves_ready and self.rgame.save_path)) # and not self.rgame.is_save_up_to_date)) - self.cloud_widget.setEnabled(supports_saves) - self.info_label.setVisible(not supports_saves) - if not supports_saves: + self.cloud_widget.setEnabled(saves_ready) + self.info_label.setVisible(not saves_ready and not supports_saves) + self.info_label_not_installed.setVisible(supports_saves and not self.rgame.is_installed) + if not saves_ready: self.sync_ui.date_info_local.setText("None") self.sync_ui.age_label_local.setText("None") self.sync_ui.date_info_remote.setText("None") From 733d10319a653f03a0680cc02f4043572cb895cd Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:42:51 +0100 Subject: [PATCH 4/7] Add search bar filter by tag --- rare/components/tabs/library/details/details.py | 10 +++++++++- rare/components/tabs/library/head_bar.py | 13 +++++++++++-- rare/components/tabs/library/widgets/__init__.py | 12 ++++++++++-- rare/models/game.py | 3 ++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py index cd37d97ab..13902cba7 100644 --- a/rare/components/tabs/library/details/details.py +++ b/rare/components/tabs/library/details/details.py @@ -80,6 +80,8 @@ def __init__(self, parent=None): "na": self.tr("Not applicable"), } + self.editing = False + self.ui.hidden_check.checkStateChanged.connect(self.__on_tag_change) self.ui.favorites_check.checkStateChanged.connect(self.__on_tag_change) self.ui.backlog_check.checkStateChanged.connect(self.__on_tag_change) @@ -278,6 +280,8 @@ def __on_move_result(self, rgame: RareGame, dst_path: str): @Slot() def __on_tag_change(self): + if self.editing: + return tag_list = [] if self.ui.hidden_check.isChecked(): tag_list.append("hidden") @@ -291,7 +295,7 @@ def __on_tag_change(self): for w in self.custom_tags: tag_list.append(w.layout().itemAt(0).widget().text()) - logger.info(f"Tags: {tag_list}") + logger.info(f"Saving Tags for {self.rgame.game.app_title}: {tag_list}") self.rgame.set_tags(tag_list) @@ -408,10 +412,14 @@ def __update_widget(self): w.deleteLater() self.custom_tags.clear() + self.editing = True self.ui.hidden_check.setChecked("hidden" in self.rgame.metadata.tags) self.ui.favorites_check.setChecked("favorite" in self.rgame.metadata.tags) self.ui.backlog_check.setChecked("backlog" in self.rgame.metadata.tags) self.ui.completed_check.setChecked("completed" in self.rgame.metadata.tags) + self.editing = False + + logger.info(f"Tags: {self.rgame.metadata.tags}") for tag in self.rgame.metadata.tags: if tag in ["hidden", "favorite", "backlog", "completed"]: diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py index ce5e23c5f..635bd53f5 100644 --- a/rare/components/tabs/library/head_bar.py +++ b/rare/components/tabs/library/head_bar.py @@ -10,7 +10,7 @@ QComboBox, QMenu, QSpacerItem, - QSizePolicy, + QSizePolicy, QCompleter, ) from rare.models.options import options, LibraryFilter, LibraryOrder @@ -110,11 +110,20 @@ def __init__(self, parent=None): integrations.setIcon(qta_icon("mdi.tools")) integrations.setMenu(integrations_menu) - self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search")) + self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search (use :: to filter by tag)")) self.search_bar.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred) self.search_bar.setObjectName("SearchBar") self.search_bar.setMinimumWidth(250) + wordlist = {"hidden", "favorite", "backlog", "completed"} + for game in self.rcore.games: + wordlist.update(game.metadata.tags) + + wordlist = list(map(lambda x: "::" + x.lower(), wordlist)) + completer = QCompleter(wordlist, self.search_bar) + completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) + self.search_bar.setCompleter(completer) + installed_tooltip = self.tr("Installed games") self.installed_icon = QLabel(parent=self) self.installed_icon.setPixmap(qta_icon("ph.floppy-disk-back-fill").pixmap(QSize(16, 16))) diff --git a/rare/components/tabs/library/widgets/__init__.py b/rare/components/tabs/library/widgets/__init__.py index adbbee1ba..85ca4ec78 100644 --- a/rare/components/tabs/library/widgets/__init__.py +++ b/rare/components/tabs/library/widgets/__init__.py @@ -28,7 +28,10 @@ def _add_widget(self, widget_type: Type[ViewWidget], rgame: RareGame) -> ViewWid @staticmethod def __visibility(widget: ViewWidget, library_filter, search_text) -> Tuple[bool, float]: - if library_filter == LibraryFilter.HIDDEN: + if search_text.startswith("::"): + search_text = search_text[2:] + visible = search_text in widget.rgame.metadata.tags + elif library_filter == LibraryFilter.HIDDEN: visible = "hidden" in widget.rgame.metadata.tags elif "hidden" in widget.rgame.metadata.tags: visible = False @@ -52,6 +55,7 @@ def __visibility(widget: ViewWidget, library_filter, search_text) -> Tuple[bool, if ( search_text not in widget.rgame.app_name.lower() and search_text not in widget.rgame.app_title.lower() + and search_text not in widget.rgame.metadata.tags ): opacity = 0.25 else: @@ -108,10 +112,14 @@ def find_widget(self, app_name: str) -> ViewWidget: return self._find_widget(IconGameWidget, app_name) def order_view(self, order_by: LibraryOrder = LibraryOrder.TITLE, search_text: str = ""): - if search_text: + if search_text and not search_text.startswith("::"): self.layout().sort( lambda x: (search_text not in x.widget().rgame.app_title.lower(),) ) + elif search_text and search_text.startswith("::"): + self.layout().sort( + lambda x: (search_text in x.widget().rgame.metadata.tags,) + ) else: if (newest := order_by == LibraryOrder.NEWEST) or order_by == LibraryOrder.OLDEST: # Sort by grant date diff --git a/rare/models/game.py b/rare/models/game.py index 9ec05203b..40a218714 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -482,7 +482,8 @@ def grant_date(self, force=False) -> datetime: return self.metadata.grant_date def set_tags(self, tags: List[str]) -> None: - self.metadata.tags = tags + self.metadata.tags.clear() + self.metadata.tags.extend(tags) self.__save_metadata() def set_origin_attributes(self, path: str, size: int = 0) -> None: From 220711f015a877fb29d04e96a8d87f92afc4d6c2 Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:46:08 +0100 Subject: [PATCH 5/7] Add filter by favorites --- rare/components/tabs/library/head_bar.py | 1 + rare/components/tabs/library/widgets/__init__.py | 2 ++ rare/models/library.py | 1 + 3 files changed, 4 insertions(+) diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py index 635bd53f5..89dcb91b3 100644 --- a/rare/components/tabs/library/head_bar.py +++ b/rare/components/tabs/library/head_bar.py @@ -39,6 +39,7 @@ def __init__(self, parent=None): LibraryFilter.INSTALLED: self.tr("Installed"), LibraryFilter.OFFLINE: self.tr("Offline"), LibraryFilter.HIDDEN: self.tr("Hidden"), + LibraryFilter.FAVORITES: self.tr("Favorites"), } for data, text in filters.items(): self.filter.addItem(text, data) diff --git a/rare/components/tabs/library/widgets/__init__.py b/rare/components/tabs/library/widgets/__init__.py index 85ca4ec78..1af81df60 100644 --- a/rare/components/tabs/library/widgets/__init__.py +++ b/rare/components/tabs/library/widgets/__init__.py @@ -33,6 +33,8 @@ def __visibility(widget: ViewWidget, library_filter, search_text) -> Tuple[bool, visible = search_text in widget.rgame.metadata.tags elif library_filter == LibraryFilter.HIDDEN: visible = "hidden" in widget.rgame.metadata.tags + elif library_filter == LibraryFilter.FAVORITES: + visible = "favorite" in widget.rgame.metadata.tags elif "hidden" in widget.rgame.metadata.tags: visible = False elif library_filter == LibraryFilter.INSTALLED: diff --git a/rare/models/library.py b/rare/models/library.py index c1fef2ac7..b7979e6ef 100644 --- a/rare/models/library.py +++ b/rare/models/library.py @@ -15,6 +15,7 @@ class LibraryFilter(IntEnum): MAC = 6 INSTALLABLE = 7 INCLUDE_UE = 8 + FAVORITES = 9 class LibraryOrder(IntEnum): From 269e841c5a9a05e8747864559c796ad5b944e7c4 Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:21:45 +0100 Subject: [PATCH 6/7] Add autocompletion for search bar when searching for tags --- rare/components/tabs/library/__init__.py | 1 + rare/components/tabs/library/head_bar.py | 16 +++++++++------- rare/models/game.py | 2 +- rare/models/signals.py | 1 + rare/shared/rare_core.py | 9 +++++++++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/rare/components/tabs/library/__init__.py b/rare/components/tabs/library/__init__.py index 4729837a2..3ae363873 100644 --- a/rare/components/tabs/library/__init__.py +++ b/rare/components/tabs/library/__init__.py @@ -45,6 +45,7 @@ def __init__(self, parent=None): self.game_info_page.back_clicked.connect(lambda: self.setCurrentWidget(self.games_page)) # Update visibility of hidden games self.game_info_page.back_clicked.connect(lambda: self.filter_games(self.head_bar.current_filter())) + self.game_info_page.back_clicked.connect(lambda: self.signals.application.update_tag_list.emit()) self.game_info_page.import_clicked.connect(self.show_import) self.addWidget(self.game_info_page) diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py index 89dcb91b3..6d8d36a32 100644 --- a/rare/components/tabs/library/head_bar.py +++ b/rare/components/tabs/library/head_bar.py @@ -31,6 +31,7 @@ def __init__(self, parent=None): super(LibraryHeadBar, self).__init__(parent=parent) self.logger = logging.getLogger(type(self).__name__) self.rcore = RareCore.instance() + self.signals = RareCore.instance().signals() self.settings = QSettings(self) self.filter = QComboBox(self) @@ -115,15 +116,11 @@ def __init__(self, parent=None): self.search_bar.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred) self.search_bar.setObjectName("SearchBar") self.search_bar.setMinimumWidth(250) - - wordlist = {"hidden", "favorite", "backlog", "completed"} - for game in self.rcore.games: - wordlist.update(game.metadata.tags) - - wordlist = list(map(lambda x: "::" + x.lower(), wordlist)) - completer = QCompleter(wordlist, self.search_bar) + completer = QCompleter([], self.search_bar) completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) self.search_bar.setCompleter(completer) + self.signals.application.update_tag_list.connect(self.tag_updated) + self.tag_updated() installed_tooltip = self.tr("Installed games") self.installed_icon = QLabel(parent=self) @@ -159,6 +156,11 @@ def __init__(self, parent=None): layout.addWidget(integrations) layout.addWidget(self.refresh_list) + def tag_updated(self): + wordlist = list(map(lambda x: "::" + x, self.rcore.tag_list)) + print(wordlist) + self.search_bar.completer().model().setStringList(wordlist) + def set_games_count(self, inst: int, avail: int) -> None: self.installed_label.setText(str(inst)) self.available_label.setText(str(avail)) diff --git a/rare/models/game.py b/rare/models/game.py index 40a218714..627884bcd 100644 --- a/rare/models/game.py +++ b/rare/models/game.py @@ -483,7 +483,7 @@ def grant_date(self, force=False) -> datetime: def set_tags(self, tags: List[str]) -> None: self.metadata.tags.clear() - self.metadata.tags.extend(tags) + self.metadata.tags.extend(map(lambda x: x.lower() ,tags)) self.__save_metadata() def set_origin_attributes(self, path: str, size: int = 0) -> None: diff --git a/rare/models/signals.py b/rare/models/signals.py index ae770bc5f..137bb2529 100644 --- a/rare/models/signals.py +++ b/rare/models/signals.py @@ -19,6 +19,7 @@ class ApplicationSignals(QObject): update_statusbar = Signal() # str: locale # change_translation = Signal(str) + update_tag_list = Signal() class GameSignals(QObject): # model diff --git a/rare/shared/rare_core.py b/rare/shared/rare_core.py index a9b3a710d..9fd9c53be 100644 --- a/rare/shared/rare_core.py +++ b/rare/shared/rare_core.py @@ -2,6 +2,7 @@ import os import time from argparse import Namespace +from collections import defaultdict from itertools import chain from logging import getLogger from typing import Dict, Iterator, Callable, Optional, List, Union, Iterable, Tuple, Set @@ -297,6 +298,7 @@ def __add_game(self, rgame: RareGame) -> None: rgame.signals.game.finished.connect(self.__signals.discord_rpc.remove_presence) self.__library[rgame.app_name] = rgame + self.__signals.application.update_tag_list.emit() def __filter_games(self, condition: Callable[[RareGame], bool]) -> Iterator[RareGame]: return filter(condition, self.__library.values()) @@ -363,6 +365,13 @@ def __on_fetch_result(self, result: Tuple, result_type: int): self.completed.emit() QTimer.singleShot(100, self.__post_init) + @property + def tag_list(self) -> list[str]: + tags = {"hidden", "favorite", "backlog", "completed"} + for rgame in self.games: + tags.update(map(lambda x: x.lower(), rgame.metadata.tags)) + return sorted(tags) + def fetch(self): self.__start_time = time.perf_counter() From 087f7a5bb3ac3ce0b706d9be6f3d34a50cfa6207 Mon Sep 17 00:00:00 2001 From: lennard <44114474+Dummerle@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:28:43 +0100 Subject: [PATCH 7/7] Remove debug prints --- rare/components/tabs/library/details/details.py | 4 +--- rare/components/tabs/library/head_bar.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/rare/components/tabs/library/details/details.py b/rare/components/tabs/library/details/details.py index 13902cba7..9cbc0845c 100644 --- a/rare/components/tabs/library/details/details.py +++ b/rare/components/tabs/library/details/details.py @@ -295,7 +295,7 @@ def __on_tag_change(self): for w in self.custom_tags: tag_list.append(w.layout().itemAt(0).widget().text()) - logger.info(f"Saving Tags for {self.rgame.game.app_title}: {tag_list}") + logger.debug(f"Saving Tags for {self.rgame.game.app_title}: {tag_list}") self.rgame.set_tags(tag_list) @@ -419,8 +419,6 @@ def __update_widget(self): self.ui.completed_check.setChecked("completed" in self.rgame.metadata.tags) self.editing = False - logger.info(f"Tags: {self.rgame.metadata.tags}") - for tag in self.rgame.metadata.tags: if tag in ["hidden", "favorite", "backlog", "completed"]: continue diff --git a/rare/components/tabs/library/head_bar.py b/rare/components/tabs/library/head_bar.py index 6d8d36a32..822474933 100644 --- a/rare/components/tabs/library/head_bar.py +++ b/rare/components/tabs/library/head_bar.py @@ -158,7 +158,6 @@ def __init__(self, parent=None): def tag_updated(self): wordlist = list(map(lambda x: "::" + x, self.rcore.tag_list)) - print(wordlist) self.search_bar.completer().model().setStringList(wordlist) def set_games_count(self, inst: int, avail: int) -> None: