1
0
Fork 0
mirror of synced 2024-06-26 18:20:50 +12:00

Library: Initialize only one view on each run

Do not create multiple library views and remove the ability to switch
between them on the fly. Add an option in settings to select the preferred
view. The view will be used the next time Rare is started.
This commit is contained in:
loathingKernel 2024-01-29 01:02:29 +02:00
parent 7c3d5dc9e8
commit b1e537af43
3 changed files with 159 additions and 150 deletions

View file

@ -13,8 +13,6 @@ from rare.shared import (
) )
from rare.shared import RareCore from rare.shared import RareCore
from rare.models.options import options from rare.models.options import options
from rare.widgets.library_layout import LibraryLayout
from rare.widgets.sliding_stack import SlidingStackedWidget
from .game_info import GameInfoTabs from .game_info import GameInfoTabs
from .game_widgets import LibraryWidgetController, LibraryFilter, LibraryOrder, LibraryView from .game_widgets import LibraryWidgetController, LibraryFilter, LibraryOrder, LibraryView
from .game_widgets.icon_game_widget import IconGameWidget from .game_widgets.icon_game_widget import IconGameWidget
@ -54,43 +52,14 @@ class GamesTab(QStackedWidget):
self.integrations_page.back_clicked.connect(lambda: self.setCurrentWidget(self.games_page)) self.integrations_page.back_clicked.connect(lambda: self.setCurrentWidget(self.games_page))
self.addWidget(self.integrations_page) self.addWidget(self.integrations_page)
self.view_stack = SlidingStackedWidget(self.games_page) self.view_scroll = QScrollArea(self.games_page)
self.view_stack.setFrameStyle(QFrame.NoFrame) self.view_scroll.setWidgetResizable(True)
self.view_scroll.setFrameShape(QFrame.StyledPanel)
self.icon_view_scroll = QScrollArea(self.view_stack) self.view_scroll.horizontalScrollBar().setDisabled(True)
self.icon_view_scroll.setWidgetResizable(True)
self.icon_view_scroll.setFrameShape(QFrame.StyledPanel)
self.icon_view_scroll.horizontalScrollBar().setDisabled(True)
self.list_view_scroll = QScrollArea(self.view_stack)
self.list_view_scroll.setWidgetResizable(True)
self.list_view_scroll.setFrameShape(QFrame.StyledPanel)
self.list_view_scroll.horizontalScrollBar().setDisabled(True)
self.icon_view = QWidget(self.icon_view_scroll)
icon_view_layout = LibraryLayout(self.icon_view)
icon_view_layout.setSpacing(9)
icon_view_layout.setContentsMargins(0, 13, 0, 13)
icon_view_layout.setAlignment(Qt.AlignTop)
self.list_view = QWidget(self.list_view_scroll)
list_view_layout = QVBoxLayout(self.list_view)
list_view_layout.setContentsMargins(3, 3, 9, 3)
list_view_layout.setAlignment(Qt.AlignTop)
self.library_controller = LibraryWidgetController(self.icon_view, self.list_view, self)
self.icon_view_scroll.setWidget(self.icon_view)
self.list_view_scroll.setWidget(self.list_view)
self.view_stack.addWidget(self.icon_view_scroll)
self.view_stack.addWidget(self.list_view_scroll)
games_page_layout.addWidget(self.view_stack)
library_view = LibraryView(self.settings.value(*options.library_view)) library_view = LibraryView(self.settings.value(*options.library_view))
self.view_stack.setCurrentWidget(self.list_view_scroll) self.library_controller = LibraryWidgetController(library_view, self.view_scroll)
if library_view == LibraryView.VLIST: games_page_layout.addWidget(self.view_scroll)
self.view_stack.setCurrentWidget(self.list_view_scroll)
else:
self.view_stack.setCurrentWidget(self.icon_view_scroll)
self.head_bar.search_bar.textChanged.connect(self.search_games) self.head_bar.search_bar.textChanged.connect(self.search_games)
self.head_bar.search_bar.textChanged.connect(self.scroll_to_top) self.head_bar.search_bar.textChanged.connect(self.scroll_to_top)
@ -99,7 +68,6 @@ class GamesTab(QStackedWidget):
self.head_bar.orderChanged.connect(self.order_games) self.head_bar.orderChanged.connect(self.order_games)
self.head_bar.orderChanged.connect(self.scroll_to_top) self.head_bar.orderChanged.connect(self.scroll_to_top)
self.head_bar.refresh_list.clicked.connect(self.library_controller.update_game_views) self.head_bar.refresh_list.clicked.connect(self.library_controller.update_game_views)
self.head_bar.viewChanged.connect(self.change_view)
# signals # signals
self.signals.game.installed.connect(self.update_count_games_label) self.signals.game.installed.connect(self.update_count_games_label)
@ -116,11 +84,8 @@ class GamesTab(QStackedWidget):
@pyqtSlot() @pyqtSlot()
def scroll_to_top(self): def scroll_to_top(self):
self.icon_view_scroll.verticalScrollBar().setSliderPosition( self.view_scroll.verticalScrollBar().setSliderPosition(
self.icon_view_scroll.verticalScrollBar().minimum() self.view_scroll.verticalScrollBar().minimum()
)
self.list_view_scroll.verticalScrollBar().setSliderPosition(
self.list_view_scroll.verticalScrollBar().minimum()
) )
@pyqtSlot() @pyqtSlot()
@ -153,24 +118,21 @@ class GamesTab(QStackedWidget):
def setup_game_list(self): def setup_game_list(self):
for rgame in self.rcore.games: for rgame in self.rcore.games:
icon_widget, list_widget = self.add_library_widget(rgame) widget = self.add_library_widget(rgame)
if not icon_widget or not list_widget: if not widget:
logger.warning("Excluding %s from the game list", rgame.app_title) logger.warning("Excluding %s from the game list", rgame.app_title)
continue continue
self.icon_view.layout().addWidget(icon_widget)
self.list_view.layout().addWidget(list_widget)
self.filter_games(self.head_bar.current_filter()) self.filter_games(self.head_bar.current_filter())
self.update_count_games_label() self.update_count_games_label()
def add_library_widget(self, rgame: RareGame): def add_library_widget(self, rgame: RareGame):
try: try:
icon_widget, list_widget = self.library_controller.add_game(rgame) widget = self.library_controller.add_game(rgame)
except Exception as e: except Exception as e:
logger.error("Could not add widget for %s to library: %s", rgame.app_name, e) logger.error("Could not add widget for %s to library: %s", rgame.app_name, e)
return None, None return None
icon_widget.show_info.connect(self.show_game_info) widget.show_info.connect(self.show_game_info)
list_widget.show_info.connect(self.show_game_info) return widget
return icon_widget, list_widget
@pyqtSlot(str) @pyqtSlot(str)
def search_games(self, search_text: str = ""): def search_games(self, search_text: str = ""):
@ -191,10 +153,3 @@ class GamesTab(QStackedWidget):
search_text = t search_text = t
self.library_controller.order_game_views(library_order, search_text.lower()) self.library_controller.order_game_views(library_order, search_text.lower())
@pyqtSlot(object)
def change_view(self, library_view: LibraryView = LibraryView.COVER):
if library_view == LibraryView.VLIST:
self.view_stack.slideInWidget(self.list_view_scroll)
else:
self.view_stack.slideInWidget(self.icon_view_scroll)

View file

@ -1,41 +1,33 @@
from typing import Tuple, List, Union from abc import abstractmethod
from typing import Tuple, List, Union, Type, TypeVar
from PyQt5.QtCore import QObject, pyqtSlot from PyQt5.QtCore import QObject, pyqtSlot, Qt
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget, QVBoxLayout, QScrollArea
from rare.lgndr.core import LegendaryCore from rare.lgndr.core import LegendaryCore
from rare.models.game import RareGame from rare.models.game import RareGame
from rare.models.signals import GlobalSignals from rare.models.signals import GlobalSignals
from rare.models.library import LibraryFilter, LibraryOrder, LibraryView from rare.models.library import LibraryFilter, LibraryOrder, LibraryView
from rare.shared import RareCore from rare.shared import RareCore
from rare.widgets.library_layout import LibraryLayout
from .icon_game_widget import IconGameWidget from .icon_game_widget import IconGameWidget
from .list_game_widget import ListGameWidget from .list_game_widget import ListGameWidget
ViewWidget = TypeVar("ViewWidget", IconGameWidget, ListGameWidget)
class LibraryWidgetController(QObject):
def __init__(self, icon_container: QWidget, list_container: QWidget, parent: QWidget = None):
super(LibraryWidgetController, self).__init__(parent=parent)
self._icon_container: QWidget = icon_container
self._list_container: QWidget = list_container
self.rcore = RareCore.instance()
self.core: LegendaryCore = self.rcore.core()
self.signals: GlobalSignals = self.rcore.signals()
self.signals.game.installed.connect(self.order_game_views) class ViewContainer(QWidget):
self.signals.game.uninstalled.connect(self.order_game_views) def __init__(self, rcore: RareCore, parent=None):
super().__init__(parent=parent)
self.rcore: RareCore = rcore
def add_game(self, rgame: RareGame): def _add_widget(self, widget_type: Type[ViewWidget], rgame: RareGame) -> ViewWidget:
return self.add_widgets(rgame) widget = widget_type(rgame, self)
self.layout().addWidget(widget)
def add_widgets(self, rgame: RareGame) -> Tuple[IconGameWidget, ListGameWidget]: return widget
icon_widget = IconGameWidget(rgame, self._icon_container)
list_widget = ListGameWidget(rgame, self._list_container)
return icon_widget, list_widget
@staticmethod @staticmethod
def __visibility( def __visibility(widget: ViewWidget, library_filter, search_text) -> Tuple[bool, float]:
widget: Union[IconGameWidget, ListGameWidget], library_filter, search_text
) -> Tuple[bool, float]:
if library_filter == LibraryFilter.HIDDEN: if library_filter == LibraryFilter.HIDDEN:
visible = "hidden" in widget.rgame.metadata.tags visible = "hidden" in widget.rgame.metadata.tags
elif "hidden" in widget.rgame.metadata.tags: elif "hidden" in widget.rgame.metadata.tags:
@ -67,85 +59,159 @@ class LibraryWidgetController(QObject):
return visible, opacity return visible, opacity
def filter_game_views(self, library_filter=LibraryFilter.ALL, search_text: str = ""): def _filter_view(self, widget_type: Type[ViewWidget], filter_by: LibraryFilter = LibraryFilter.ALL, search_text: str = ""):
icon_widgets = self._icon_container.findChildren(IconGameWidget) widgets = self.findChildren(widget_type)
list_widgets = self._list_container.findChildren(ListGameWidget) for iw in widgets:
for iw in icon_widgets: visibility, opacity = self.__visibility(iw, filter_by, search_text)
visibility, opacity = self.__visibility(iw, library_filter, search_text)
iw.setOpacity(opacity) iw.setOpacity(opacity)
iw.setVisible(visibility) iw.setVisible(visibility)
for lw in list_widgets:
visibility, opacity = self.__visibility(lw, library_filter, search_text)
lw.setOpacity(opacity)
lw.setVisible(visibility)
self.order_game_views(search_text=search_text)
@pyqtSlot() def _update_view(self, widget_type: Type[ViewWidget]):
def order_game_views(self, order_by: LibraryOrder = LibraryOrder.TITLE, search_text: str = ""): widgets = self.findChildren(widget_type)
list_widgets = self._list_container.findChildren(ListGameWidget) app_names = {iw.rgame.app_name for iw in widgets}
games = list(self.rcore.games)
game_app_names = {g.app_name for g in games}
new_app_names = game_app_names.difference(app_names)
for app_name in new_app_names:
game = self.rcore.get_game(app_name)
w = widget_type(game, self)
self.layout().addWidget(w)
def _find_widget(self, widget_type: Type[ViewWidget], app_name: str) -> ViewWidget:
w = self.findChild(widget_type, app_name)
return w
@abstractmethod
def order_view(self):
pass
class IconViewContainer(ViewContainer):
def __init__(self, rcore: RareCore, parent=None):
super().__init__(rcore, parent=parent)
view_layout = LibraryLayout(self)
view_layout.setSpacing(9)
view_layout.setContentsMargins(0, 13, 0, 13)
view_layout.setAlignment(Qt.AlignTop)
self.setLayout(view_layout)
def add_widget(self, rgame: RareGame) -> IconGameWidget:
return self._add_widget(IconGameWidget, rgame)
def filter_view(self, filter_by: LibraryFilter = LibraryFilter.ALL, search_text: str = ""):
self._filter_view(IconGameWidget, filter_by, search_text)
def update_view(self):
self._update_view(IconGameWidget)
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:
self._icon_container.layout().sort( self.layout().sort(
lambda x: (search_text not in x.widget().rgame.app_title.lower(),) lambda x: (search_text not in x.widget().rgame.app_title.lower(),)
) )
list_widgets.sort(key=lambda x: (search_text not in x.rgame.app_title.lower(),))
else: else:
if (newest := order_by == LibraryOrder.NEWEST) or order_by == LibraryOrder.OLDEST: if (newest := order_by == LibraryOrder.NEWEST) or order_by == LibraryOrder.OLDEST:
# Sort by grant date # Sort by grant date
self._icon_container.layout().sort( self.layout().sort(
key=lambda x: (x.widget().rgame.is_installed, not x.widget().rgame.is_non_asset, x.widget().rgame.grant_date()), key=lambda x: (x.widget().rgame.is_installed, not x.widget().rgame.is_non_asset, x.widget().rgame.grant_date()),
reverse=newest, reverse=newest,
) )
elif order_by == LibraryOrder.RECENT:
# Sort by recently played
self.layout().sort(
key=lambda x: (not x.widget().rgame.is_installed, x.widget().rgame.is_non_asset, x.widget().rgame.metadata.last_played),
reverse=True,
)
else:
# Sort by title
self.layout().sort(
key=lambda x: (not x.widget().rgame.is_installed, x.widget().rgame.is_non_asset, x.widget().rgame.app_title)
)
class ListViewContainer(ViewContainer):
def __init__(self, rcore, parent=None):
super().__init__(rcore, parent=parent)
view_layout = QVBoxLayout(self)
view_layout.setContentsMargins(3, 3, 9, 3)
view_layout.setAlignment(Qt.AlignTop)
self.setLayout(view_layout)
def add_widget(self, rgame: RareGame) -> ListGameWidget:
return self._add_widget(ListGameWidget, rgame)
def filter_view(self, filter_by: LibraryFilter = LibraryFilter.ALL, search_text: str = ""):
self._filter_view(ListGameWidget, filter_by, search_text)
def update_view(self):
self._update_view(ListGameWidget)
def find_widget(self, app_name: str) -> ViewWidget:
return self._find_widget(ListGameWidget, app_name)
def order_view(self, order_by: LibraryOrder = LibraryOrder.TITLE, search_text: str = ""):
list_widgets = self.findChildren(ListGameWidget)
if search_text:
list_widgets.sort(key=lambda x: (search_text not in x.rgame.app_title.lower(),))
else:
if (newest := order_by == LibraryOrder.NEWEST) or order_by == LibraryOrder.OLDEST:
list_widgets.sort( list_widgets.sort(
key=lambda x: (x.rgame.is_installed, not x.rgame.is_non_asset, x.rgame.grant_date()), key=lambda x: (x.rgame.is_installed, not x.rgame.is_non_asset, x.rgame.grant_date()),
reverse=newest, reverse=newest,
) )
elif order_by == LibraryOrder.RECENT: elif order_by == LibraryOrder.RECENT:
# Sort by recently played
self._icon_container.layout().sort(
key=lambda x: (not x.widget().rgame.is_installed, x.widget().rgame.is_non_asset, x.widget().rgame.metadata.last_played),
reverse=True,
)
list_widgets.sort( list_widgets.sort(
key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.metadata.last_played), key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.metadata.last_played),
reverse=True, reverse=True,
) )
else: else:
# Sort by title
self._icon_container.layout().sort(
key=lambda x: (not x.widget().rgame.is_installed, x.widget().rgame.is_non_asset, x.widget().rgame.app_title)
)
list_widgets.sort( list_widgets.sort(
key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.app_title) key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.app_title)
) )
for idx, wl in enumerate(list_widgets): for idx, wl in enumerate(list_widgets):
self._list_container.layout().insertWidget(idx, wl) self.layout().insertWidget(idx, wl)
class LibraryWidgetController(QObject):
def __init__(self, view: LibraryView, parent: QScrollArea = None):
super(LibraryWidgetController, self).__init__(parent=parent)
self.rcore = RareCore.instance()
self.core: LegendaryCore = self.rcore.core()
self.signals: GlobalSignals = self.rcore.signals()
if view == LibraryView.COVER:
self._container: IconViewContainer = IconViewContainer(self.rcore, parent)
else:
self._container: ListViewContainer = ListViewContainer(self.rcore, parent)
parent.setWidget(self._container)
self.signals.game.installed.connect(self.order_game_views)
self.signals.game.uninstalled.connect(self.order_game_views)
def add_game(self, rgame: RareGame):
return self.add_widgets(rgame)
def add_widgets(self, rgame: RareGame) -> ViewWidget:
return self._container.add_widget(rgame)
def filter_game_views(self, filter_by: LibraryFilter = LibraryFilter.ALL, search_text: str = ""):
self._container.filter_view(filter_by, search_text)
self.order_game_views(search_text=search_text)
@pyqtSlot()
def order_game_views(self, order_by: LibraryOrder = LibraryOrder.TITLE, search_text: str = ""):
self._container.order_view(order_by, search_text)
@pyqtSlot() @pyqtSlot()
@pyqtSlot(list) @pyqtSlot(list)
def update_game_views(self, app_names: List[str] = None): def update_game_views(self, app_names: List[str] = None):
if app_names: if app_names:
return return
# lk: base it on icon widgets, the two lists should be identical self._container.update_view()
icon_widgets = self._icon_container.findChildren(IconGameWidget)
list_widgets = self._list_container.findChildren(ListGameWidget)
icon_app_names = {iw.rgame.app_name for iw in icon_widgets}
list_app_names = {lw.rgame.app_name for lw in list_widgets}
games = list(self.rcore.games)
game_app_names = {g.app_name for g in games}
new_icon_app_names = game_app_names.difference(icon_app_names)
new_list_app_names = game_app_names.difference(list_app_names)
for app_name in new_icon_app_names:
game = self.rcore.get_game(app_name)
iw = IconGameWidget(game)
self._icon_container.layout().addWidget(iw)
for app_name in new_list_app_names:
game = self.rcore.get_game(app_name)
lw = ListGameWidget(game)
self._list_container.layout().addWidget(lw)
self.order_game_views() self.order_game_views()
def __find_widget(self, app_name: str) -> Tuple[Union[IconGameWidget, None], Union[ListGameWidget, None]]: def __find_widget(self, app_name: str) -> Union[ViewWidget, None]:
iw = self._icon_container.findChild(IconGameWidget, app_name) return self._container.find_widget(app_name)
lw = self._list_container.findChild(ListGameWidget, app_name)
return iw, lw

View file

@ -11,8 +11,8 @@ from PyQt5.QtWidgets import (
) )
from rare.shared import RareCore from rare.shared import RareCore
from rare.models.options import options, LibraryFilter, LibraryOrder, LibraryView from rare.models.options import options, LibraryFilter, LibraryOrder
from rare.utils.extra_widgets import SelectViewWidget, ButtonLineEdit from rare.utils.extra_widgets import ButtonLineEdit
from rare.utils.misc import icon from rare.utils.misc import icon
@ -107,7 +107,7 @@ class GameListHeadBar(QWidget):
self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search Game")) self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search Game"))
self.search_bar.setObjectName("SearchBar") self.search_bar.setObjectName("SearchBar")
self.search_bar.setFrame(False) self.search_bar.setFrame(False)
self.search_bar.setMinimumWidth(200) self.search_bar.setMinimumWidth(250)
installed_tooltip = self.tr("Installed games") installed_tooltip = self.tr("Installed games")
self.installed_icon = QLabel(parent=self) self.installed_icon = QLabel(parent=self)
@ -125,10 +125,6 @@ class GameListHeadBar(QWidget):
self.available_label = QLabel(parent=self) self.available_label = QLabel(parent=self)
self.available_label.setToolTip(available_tooltip) self.available_label.setToolTip(available_tooltip)
view = LibraryView(QSettings(self).value(*options.library_view))
self.library_view = SelectViewWidget(view == LibraryView.COVER)
self.library_view.toggled.connect(self.__view_changed)
self.refresh_list = QPushButton(parent=self) self.refresh_list = QPushButton(parent=self)
self.refresh_list.setIcon(icon("fa.refresh")) # Reload icon self.refresh_list.setIcon(icon("fa.refresh")) # Reload icon
self.refresh_list.clicked.connect(self.__refresh_clicked) self.refresh_list.clicked.connect(self.__refresh_clicked)
@ -139,16 +135,14 @@ class GameListHeadBar(QWidget):
layout.addWidget(self.order) layout.addWidget(self.order)
layout.addStretch(0) layout.addStretch(0)
layout.addWidget(integrations) layout.addWidget(integrations)
layout.addStretch(2) layout.addStretch(3)
layout.addWidget(self.search_bar) layout.addWidget(self.search_bar)
layout.addStretch(2) layout.addStretch(4)
layout.addWidget(self.installed_icon) layout.addWidget(self.installed_icon)
layout.addWidget(self.installed_label) layout.addWidget(self.installed_label)
layout.addWidget(self.available_icon) layout.addWidget(self.available_icon)
layout.addWidget(self.available_label) layout.addWidget(self.available_label)
layout.addStretch(2) layout.addStretch(4)
layout.addWidget(self.library_view)
layout.addStretch(2)
layout.addWidget(self.refresh_list) layout.addWidget(self.refresh_list)
def set_games_count(self, inst: int, avail: int) -> None: def set_games_count(self, inst: int, avail: int) -> None:
@ -160,7 +154,7 @@ class GameListHeadBar(QWidget):
self.rcore.fetch() self.rcore.fetch()
def current_filter(self) -> LibraryFilter: def current_filter(self) -> LibraryFilter:
return LibraryFilter(self.filter.currentData(Qt.UserRole)) return self.filter.currentData(Qt.UserRole)
@pyqtSlot(int) @pyqtSlot(int)
def __filter_changed(self, index: int): def __filter_changed(self, index: int):
@ -169,16 +163,10 @@ class GameListHeadBar(QWidget):
self.settings.setValue(options.library_filter.key, int(data)) self.settings.setValue(options.library_filter.key, int(data))
def current_order(self) -> LibraryOrder: def current_order(self) -> LibraryOrder:
return LibraryOrder(self.order.currentData(Qt.UserRole)) return self.order.currentData(Qt.UserRole)
@pyqtSlot(int) @pyqtSlot(int)
def __order_changed(self, index: int): def __order_changed(self, index: int):
data = self.order.itemData(index, Qt.UserRole) data = self.order.itemData(index, Qt.UserRole)
self.orderChanged.emit(data) self.orderChanged.emit(data)
self.settings.setValue(options.library_order.key, int(data)) self.settings.setValue(options.library_order.key, int(data))
@pyqtSlot(bool)
def __view_changed(self, icon_view: bool):
view = LibraryView.COVER if icon_view else LibraryView.VLIST
self.viewChanged.emit(view)
self.settings.setValue(options.library_view.key, int(view))