Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add settings for tags #510

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions rare/components/tabs/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ 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.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)

Expand Down
16 changes: 9 additions & 7 deletions rare/components/tabs/library/details/cloud_saves.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init__(self, parent=None):
self.sync_ui.setupUi(self.sync_widget)

self.info_label = QLabel(self.tr("<b>This game doesn't support cloud saves</b>"))
self.info_label_not_installed = QLabel(self.tr("<b>Install this game to see cloud saves</b>"))

self.rcore = RareCore.instance()
self.core = RareCore.instance().core()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
73 changes: 70 additions & 3 deletions rare/components/tabs/library/details/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
)
from PySide6.QtWidgets import (
QWidget,
QMessageBox,
QMessageBox, QLineEdit, QHBoxLayout, QPushButton
)

from rare.models.install import SelectiveDownloadsModel, MoveGameModel
Expand Down Expand Up @@ -80,8 +80,18 @@ 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)
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()
Expand Down Expand Up @@ -150,7 +160,8 @@ def __on_verify(self):
QMessageBox.warning(
self,
self.tr("Error - {}").format(self.rgame.app_title),
self.tr("Installation path for <b>{}</b> does not exist. Cannot continue.").format(self.rgame.app_title),
self.tr("Installation path for <b>{}</b> does not exist. Cannot continue.").format(
self.rgame.app_title),
)
return
if self.rgame.sdl_name is not None:
Expand Down Expand Up @@ -267,6 +278,46 @@ def __on_move_result(self, rgame: RareGame, dst_path: str):
self.tr("<b>{}</b> successfully moved to <b>{}<b>.").format(rgame.app_title, dst_path),
)

@Slot()
def __on_tag_change(self):
if self.editing:
return
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.debug(f"Saving Tags for {self.rgame.game.app_title}: {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 """
Expand Down Expand Up @@ -357,6 +408,22 @@ 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.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

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:
Expand Down
17 changes: 14 additions & 3 deletions rare/components/tabs/library/head_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
QComboBox,
QMenu,
QSpacerItem,
QSizePolicy,
QSizePolicy, QCompleter,
)

from rare.models.options import options, LibraryFilter, LibraryOrder
Expand All @@ -31,14 +31,16 @@ 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)
filters = {
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"),
LibraryFilter.FAVORITES: self.tr("Favorites"),
}
for data, text in filters.items():
self.filter.addItem(text, data)
Expand Down Expand Up @@ -110,10 +112,15 @@ 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)
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)
Expand Down Expand Up @@ -149,6 +156,10 @@ 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))
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))
Expand Down
14 changes: 12 additions & 2 deletions rare/components/tabs/library/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ 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 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:
Expand All @@ -52,6 +57,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:
Expand Down Expand Up @@ -108,10 +114,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
Expand Down
5 changes: 5 additions & 0 deletions rare/models/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ 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.clear()
self.metadata.tags.extend(map(lambda x: x.lower() ,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
Expand Down
1 change: 1 addition & 0 deletions rare/models/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class LibraryFilter(IntEnum):
MAC = 6
INSTALLABLE = 7
INCLUDE_UE = 8
FAVORITES = 9


class LibraryOrder(IntEnum):
Expand Down
1 change: 1 addition & 0 deletions rare/models/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ApplicationSignals(QObject):
update_statusbar = Signal()
# str: locale
# change_translation = Signal(str)
update_tag_list = Signal()

class GameSignals(QObject):
# model
Expand Down
9 changes: 9 additions & 0 deletions rare/shared/rare_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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()

Expand Down
Loading
Loading