1
0
Fork 0
mirror of synced 2024-05-18 19:42:54 +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.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_widgets import LibraryWidgetController, LibraryFilter, LibraryOrder, LibraryView
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.addWidget(self.integrations_page)
self.view_stack = SlidingStackedWidget(self.games_page)
self.view_stack.setFrameStyle(QFrame.NoFrame)
self.icon_view_scroll = QScrollArea(self.view_stack)
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)
self.view_scroll = QScrollArea(self.games_page)
self.view_scroll.setWidgetResizable(True)
self.view_scroll.setFrameShape(QFrame.StyledPanel)
self.view_scroll.horizontalScrollBar().setDisabled(True)
library_view = LibraryView(self.settings.value(*options.library_view))
self.view_stack.setCurrentWidget(self.list_view_scroll)
if library_view == LibraryView.VLIST:
self.view_stack.setCurrentWidget(self.list_view_scroll)
else:
self.view_stack.setCurrentWidget(self.icon_view_scroll)
self.library_controller = LibraryWidgetController(library_view, self.view_scroll)
games_page_layout.addWidget(self.view_scroll)
self.head_bar.search_bar.textChanged.connect(self.search_games)
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.scroll_to_top)
self.head_bar.refresh_list.clicked.connect(self.library_controller.update_game_views)
self.head_bar.viewChanged.connect(self.change_view)
# signals
self.signals.game.installed.connect(self.update_count_games_label)
@ -116,11 +84,8 @@ class GamesTab(QStackedWidget):
@pyqtSlot()
def scroll_to_top(self):
self.icon_view_scroll.verticalScrollBar().setSliderPosition(
self.icon_view_scroll.verticalScrollBar().minimum()
)
self.list_view_scroll.verticalScrollBar().setSliderPosition(
self.list_view_scroll.verticalScrollBar().minimum()
self.view_scroll.verticalScrollBar().setSliderPosition(
self.view_scroll.verticalScrollBar().minimum()
)
@pyqtSlot()
@ -153,24 +118,21 @@ class GamesTab(QStackedWidget):
def setup_game_list(self):
for rgame in self.rcore.games:
icon_widget, list_widget = self.add_library_widget(rgame)
if not icon_widget or not list_widget:
widget = self.add_library_widget(rgame)
if not widget:
logger.warning("Excluding %s from the game list", rgame.app_title)
continue
self.icon_view.layout().addWidget(icon_widget)
self.list_view.layout().addWidget(list_widget)
self.filter_games(self.head_bar.current_filter())
self.update_count_games_label()
def add_library_widget(self, rgame: RareGame):
try:
icon_widget, list_widget = self.library_controller.add_game(rgame)
widget = self.library_controller.add_game(rgame)
except Exception as e:
logger.error("Could not add widget for %s to library: %s", rgame.app_name, e)
return None, None
icon_widget.show_info.connect(self.show_game_info)
list_widget.show_info.connect(self.show_game_info)
return icon_widget, list_widget
return None
widget.show_info.connect(self.show_game_info)
return widget
@pyqtSlot(str)
def search_games(self, search_text: str = ""):
@ -191,10 +153,3 @@ class GamesTab(QStackedWidget):
search_text = t
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.QtWidgets import QWidget
from PyQt5.QtCore import QObject, pyqtSlot, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QScrollArea
from rare.lgndr.core import LegendaryCore
from rare.models.game import RareGame
from rare.models.signals import GlobalSignals
from rare.models.library import LibraryFilter, LibraryOrder, LibraryView
from rare.shared import RareCore
from rare.widgets.library_layout import LibraryLayout
from .icon_game_widget import IconGameWidget
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)
self.signals.game.uninstalled.connect(self.order_game_views)
class ViewContainer(QWidget):
def __init__(self, rcore: RareCore, parent=None):
super().__init__(parent=parent)
self.rcore: RareCore = rcore
def add_game(self, rgame: RareGame):
return self.add_widgets(rgame)
def add_widgets(self, rgame: RareGame) -> Tuple[IconGameWidget, ListGameWidget]:
icon_widget = IconGameWidget(rgame, self._icon_container)
list_widget = ListGameWidget(rgame, self._list_container)
return icon_widget, list_widget
def _add_widget(self, widget_type: Type[ViewWidget], rgame: RareGame) -> ViewWidget:
widget = widget_type(rgame, self)
self.layout().addWidget(widget)
return widget
@staticmethod
def __visibility(
widget: Union[IconGameWidget, ListGameWidget], library_filter, search_text
) -> Tuple[bool, float]:
def __visibility(widget: ViewWidget, library_filter, search_text) -> Tuple[bool, float]:
if library_filter == LibraryFilter.HIDDEN:
visible = "hidden" in widget.rgame.metadata.tags
elif "hidden" in widget.rgame.metadata.tags:
@ -67,85 +59,159 @@ class LibraryWidgetController(QObject):
return visible, opacity
def filter_game_views(self, library_filter=LibraryFilter.ALL, search_text: str = ""):
icon_widgets = self._icon_container.findChildren(IconGameWidget)
list_widgets = self._list_container.findChildren(ListGameWidget)
for iw in icon_widgets:
visibility, opacity = self.__visibility(iw, library_filter, search_text)
def _filter_view(self, widget_type: Type[ViewWidget], filter_by: LibraryFilter = LibraryFilter.ALL, search_text: str = ""):
widgets = self.findChildren(widget_type)
for iw in widgets:
visibility, opacity = self.__visibility(iw, filter_by, search_text)
iw.setOpacity(opacity)
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 order_game_views(self, order_by: LibraryOrder = LibraryOrder.TITLE, search_text: str = ""):
list_widgets = self._list_container.findChildren(ListGameWidget)
def _update_view(self, widget_type: Type[ViewWidget]):
widgets = self.findChildren(widget_type)
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:
self._icon_container.layout().sort(
self.layout().sort(
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:
if (newest := order_by == LibraryOrder.NEWEST) or order_by == LibraryOrder.OLDEST:
# 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()),
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(
key=lambda x: (x.rgame.is_installed, not x.rgame.is_non_asset, x.rgame.grant_date()),
reverse=newest,
)
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(
key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.metadata.last_played),
reverse=True,
)
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(
key=lambda x: (not x.rgame.is_installed, x.rgame.is_non_asset, x.rgame.app_title)
)
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(list)
def update_game_views(self, app_names: List[str] = None):
if app_names:
return
# lk: base it on icon widgets, the two lists should be identical
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._container.update_view()
self.order_game_views()
def __find_widget(self, app_name: str) -> Tuple[Union[IconGameWidget, None], Union[ListGameWidget, None]]:
iw = self._icon_container.findChild(IconGameWidget, app_name)
lw = self._list_container.findChild(ListGameWidget, app_name)
return iw, lw
def __find_widget(self, app_name: str) -> Union[ViewWidget, None]:
return self._container.find_widget(app_name)

View file

@ -11,8 +11,8 @@ from PyQt5.QtWidgets import (
)
from rare.shared import RareCore
from rare.models.options import options, LibraryFilter, LibraryOrder, LibraryView
from rare.utils.extra_widgets import SelectViewWidget, ButtonLineEdit
from rare.models.options import options, LibraryFilter, LibraryOrder
from rare.utils.extra_widgets import ButtonLineEdit
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.setObjectName("SearchBar")
self.search_bar.setFrame(False)
self.search_bar.setMinimumWidth(200)
self.search_bar.setMinimumWidth(250)
installed_tooltip = self.tr("Installed games")
self.installed_icon = QLabel(parent=self)
@ -125,10 +125,6 @@ class GameListHeadBar(QWidget):
self.available_label = QLabel(parent=self)
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.setIcon(icon("fa.refresh")) # Reload icon
self.refresh_list.clicked.connect(self.__refresh_clicked)
@ -139,16 +135,14 @@ class GameListHeadBar(QWidget):
layout.addWidget(self.order)
layout.addStretch(0)
layout.addWidget(integrations)
layout.addStretch(2)
layout.addStretch(3)
layout.addWidget(self.search_bar)
layout.addStretch(2)
layout.addStretch(4)
layout.addWidget(self.installed_icon)
layout.addWidget(self.installed_label)
layout.addWidget(self.available_icon)
layout.addWidget(self.available_label)
layout.addStretch(2)
layout.addWidget(self.library_view)
layout.addStretch(2)
layout.addStretch(4)
layout.addWidget(self.refresh_list)
def set_games_count(self, inst: int, avail: int) -> None:
@ -160,7 +154,7 @@ class GameListHeadBar(QWidget):
self.rcore.fetch()
def current_filter(self) -> LibraryFilter:
return LibraryFilter(self.filter.currentData(Qt.UserRole))
return self.filter.currentData(Qt.UserRole)
@pyqtSlot(int)
def __filter_changed(self, index: int):
@ -169,16 +163,10 @@ class GameListHeadBar(QWidget):
self.settings.setValue(options.library_filter.key, int(data))
def current_order(self) -> LibraryOrder:
return LibraryOrder(self.order.currentData(Qt.UserRole))
return self.order.currentData(Qt.UserRole)
@pyqtSlot(int)
def __order_changed(self, index: int):
data = self.order.itemData(index, Qt.UserRole)
self.orderChanged.emit(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))