Add SlidingStackedWidget from #196
This commit is contained in:
parent
dc9b8e6cb2
commit
5eb3ae7f80
|
@ -46,11 +46,6 @@ class ImageWorker(LaunchWorker):
|
|||
na_dlc_list = [dlc[0] for dlc in na_dlcs.values()]
|
||||
|
||||
game_list = games + dlc_list + na_games + na_dlc_list
|
||||
fetched = [False] * len(game_list)
|
||||
|
||||
def set_fetched(idx):
|
||||
fetched[idx] = True
|
||||
self.signals.progress.emit(sum(fetched))
|
||||
|
||||
for i, game in enumerate(game_list):
|
||||
if game.app_title == "Unreal Engine":
|
||||
|
@ -58,13 +53,6 @@ class ImageWorker(LaunchWorker):
|
|||
self.core.lgd.set_game_meta(game.app_name, game)
|
||||
self.image_manager.download_image_blocking(game)
|
||||
self.signals.progress.emit(int(i / len(game_list) * 100))
|
||||
# self.image_manager.download_image(
|
||||
# game,
|
||||
# load_callback=lambda: set_fetched(i),
|
||||
# priority=i
|
||||
# )
|
||||
# while not all(fetched):
|
||||
# continue
|
||||
|
||||
self.signals.progress.emit(100)
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ class UpdateWidget(QWidget):
|
|||
+ self.game.version
|
||||
+ " -> "
|
||||
+ self.core.get_asset(
|
||||
self.game.app_name, self.game.platform, True
|
||||
self.game.app_name, self.game.platform, False
|
||||
).build_version
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from logging import getLogger
|
||||
from typing import Tuple, Dict, Union, List
|
||||
|
||||
from PyQt5.QtCore import QSettings, QObjectCleanupHandler
|
||||
from PyQt5.QtWidgets import QStackedWidget, QVBoxLayout, QWidget
|
||||
from PyQt5.QtCore import QSettings, Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import QStackedWidget, QVBoxLayout, QWidget, QScrollArea, QFrame
|
||||
from legendary.models.game import InstalledGame, Game
|
||||
|
||||
from rare.shared import (
|
||||
|
@ -14,6 +14,7 @@ from rare.shared import (
|
|||
from rare.shared.image_manager import ImageManagerSingleton
|
||||
from rare.ui.components.tabs.games.games_tab import Ui_GamesTab
|
||||
from rare.widgets.library_layout import LibraryLayout
|
||||
from rare.widgets.sliding_stack import SlidingStackedWidget
|
||||
from .cloud_save_utils import CloudSaveUtils
|
||||
from .game_info import GameInfoTabs
|
||||
from .game_info.uninstalled_info import UninstalledInfoTabs
|
||||
|
@ -23,24 +24,26 @@ from .game_widgets.base_uninstalled_widget import BaseUninstalledWidget
|
|||
from .game_widgets.installed_icon_widget import InstalledIconWidget
|
||||
from .game_widgets.installed_list_widget import InstalledListWidget
|
||||
from .game_widgets.installing_game_widget import InstallingGameWidget
|
||||
from .game_widgets.uninstalled_icon_widget import IconWidgetUninstalled
|
||||
from .game_widgets.uninstalled_list_widget import ListWidgetUninstalled
|
||||
from .game_widgets.uninstalled_icon_widget import UninstalledIconWidget
|
||||
from .game_widgets.uninstalled_list_widget import UninstalledListWidget
|
||||
from .head_bar import GameListHeadBar
|
||||
from .import_sync import ImportSyncTabs
|
||||
|
||||
logger = getLogger("GamesTab")
|
||||
|
||||
|
||||
class GamesTab(QStackedWidget, Ui_GamesTab):
|
||||
class GamesTab(QStackedWidget):
|
||||
widgets: Dict[str, Tuple[
|
||||
Union[InstalledIconWidget, IconWidgetUninstalled], Union[InstalledListWidget, ListWidgetUninstalled]]] = dict()
|
||||
Union[InstalledIconWidget, UninstalledIconWidget], Union[InstalledListWidget, UninstalledListWidget]]] = dict()
|
||||
running_games = list()
|
||||
updates = set()
|
||||
active_filter = 0
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(GamesTab, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.ui = Ui_GamesTab()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.signals = GlobalSignalsSingleton()
|
||||
self.args = ArgumentsSingleton()
|
||||
|
@ -59,7 +62,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
self.head_bar = GameListHeadBar(self)
|
||||
self.head_bar.import_clicked.connect(self.show_import)
|
||||
self.head_bar.egl_sync_clicked.connect(self.show_egl_sync)
|
||||
self.games.layout().insertWidget(0, self.head_bar)
|
||||
self.ui.games.layout().insertWidget(0, self.head_bar)
|
||||
|
||||
self.game_info_tabs = GameInfoTabs(self.dlcs, self.game_utils, self)
|
||||
self.game_info_tabs.back_clicked.connect(lambda: self.setCurrentIndex(0))
|
||||
|
@ -101,13 +104,41 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
self.no_assets = []
|
||||
|
||||
self.installed = self.core.get_installed_list()
|
||||
self.setup_game_list()
|
||||
|
||||
self.view_stack = SlidingStackedWidget(self.ui.library_frame)
|
||||
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.NoFrame)
|
||||
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.NoFrame)
|
||||
self.list_view_scroll.horizontalScrollBar().setDisabled(True)
|
||||
self.icon_view = QWidget(self.icon_view_scroll)
|
||||
self.icon_view.setLayout(LibraryLayout(self.icon_view))
|
||||
self.icon_view.layout().setContentsMargins(0, 0, 0, 0)
|
||||
self.icon_view.layout().setAlignment(Qt.AlignTop)
|
||||
self.list_view = QWidget(self.list_view_scroll)
|
||||
self.list_view.setLayout(QVBoxLayout(self.list_view))
|
||||
self.list_view.layout().setContentsMargins(3, 3, 9, 3)
|
||||
self.list_view.layout().setAlignment(Qt.AlignTop)
|
||||
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)
|
||||
self.ui.library_frame_layout.addWidget(self.view_stack)
|
||||
|
||||
# add installing game widget for icon view: List view not supported
|
||||
self.installing_widget = InstallingGameWidget()
|
||||
self.icon_view.layout().addWidget(self.installing_widget)
|
||||
self.installing_widget.setVisible(False)
|
||||
|
||||
if not self.settings.value("icon_view", True, bool):
|
||||
self.scroll_widget.layout().insertWidget(1, self.list_view)
|
||||
self.view_stack.setCurrentWidget(self.list_view_scroll)
|
||||
self.head_bar.view.list()
|
||||
else:
|
||||
self.scroll_widget.layout().insertWidget(1, self.icon_view)
|
||||
self.view_stack.setCurrentWidget(self.icon_view_scroll)
|
||||
|
||||
self.head_bar.search_bar.textChanged.connect(lambda x: self.filter_games("", x))
|
||||
self.head_bar.filterChanged.connect(self.filter_games)
|
||||
|
@ -118,7 +149,6 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
if f >= len(self.head_bar.available_filters):
|
||||
f = 0
|
||||
self.active_filter = self.head_bar.available_filters[f]
|
||||
self.filter_games(self.active_filter)
|
||||
|
||||
# signals
|
||||
self.signals.dl_progress.connect(self.installing_widget.set_status)
|
||||
|
@ -131,7 +161,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
|
||||
self.game_utils.update_list.connect(self.update_list)
|
||||
|
||||
self.game_list_scroll_area.horizontalScrollBar().setDisabled(True)
|
||||
self.setup_game_list()
|
||||
|
||||
def installation_finished(self, app_name: str):
|
||||
self.installing_widget.setVisible(False)
|
||||
|
@ -178,19 +208,17 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
self.uninstalled_info_tabs.update_game(game)
|
||||
self.setCurrentIndex(3)
|
||||
|
||||
@pyqtSlot()
|
||||
def update_count_games_label(self):
|
||||
self.ui.count_games_label.setText(
|
||||
self.tr("Installed Games: {}\tAvailable Games: {}").format(
|
||||
len(self.core.get_installed_list()), len(self.game_list)
|
||||
)
|
||||
)
|
||||
|
||||
def setup_game_list(self):
|
||||
self.icon_view = QWidget()
|
||||
self.icon_view.setLayout(LibraryLayout())
|
||||
self.list_view = QWidget()
|
||||
self.list_view.setLayout(QVBoxLayout())
|
||||
|
||||
self.update_count_games_label()
|
||||
|
||||
# add installing game widget for icon view: List view not supported
|
||||
self.installing_widget = InstallingGameWidget()
|
||||
self.icon_view.layout().addWidget(self.installing_widget)
|
||||
self.installing_widget.setVisible(False)
|
||||
|
||||
# add installed games
|
||||
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
|
||||
icon_widget, list_widget = self.add_installed_widget(igame.app_name)
|
||||
|
@ -212,13 +240,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
icon_widget, list_widget = self.add_uninstalled_widget(game)
|
||||
self.icon_view.layout().addWidget(icon_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
|
||||
def update_count_games_label(self):
|
||||
self.count_games_label.setText(
|
||||
self.tr("Installed Games: {} Available Games: {}").format(
|
||||
len(self.core.get_installed_list()), len(self.game_list)
|
||||
)
|
||||
)
|
||||
self.filter_games(self.active_filter)
|
||||
|
||||
def add_installed_widget(self, app_name):
|
||||
pixmap = self.image_manager.get_pixmap(app_name)
|
||||
|
@ -257,8 +279,8 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
elif self.ue_name:
|
||||
pixmap = self.image_manager.get_pixmap(self.ue_name, color=False)
|
||||
|
||||
icon_widget = IconWidgetUninstalled(game, self.core, pixmap)
|
||||
list_widget = ListWidgetUninstalled(self.core, game, pixmap)
|
||||
icon_widget = UninstalledIconWidget(game, self.core, pixmap)
|
||||
list_widget = UninstalledListWidget(self.core, game, pixmap)
|
||||
except Exception as e:
|
||||
logger.error(f"{game.app_name} is broken. Don't add it to game list: {e}")
|
||||
return None, None
|
||||
|
@ -279,7 +301,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
if not filter_name and (t := self.active_filter):
|
||||
filter_name = t
|
||||
|
||||
def get_visibility(widget):
|
||||
def get_visibility(widget) -> Tuple[bool, float]:
|
||||
app_name = widget.game.app_name
|
||||
|
||||
if not isinstance(widget,
|
||||
|
@ -310,19 +332,50 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
visible = True
|
||||
|
||||
if (
|
||||
search_text.lower() not in app_name.lower()
|
||||
and search_text.lower() not in widget.game.app_title.lower()
|
||||
search_text not in widget.game.app_name.lower()
|
||||
and search_text not in widget.game.app_title.lower()
|
||||
):
|
||||
visible = False
|
||||
return visible
|
||||
opacity = 0.25
|
||||
else:
|
||||
opacity = 1.0
|
||||
return visible, opacity
|
||||
|
||||
for t in self.widgets.values():
|
||||
visible = get_visibility(t[0])
|
||||
visible, opacity = get_visibility(t[0])
|
||||
for w in t:
|
||||
w.setVisible(visible)
|
||||
w.image.setOpacity(opacity)
|
||||
|
||||
self.sort_list(search_text)
|
||||
|
||||
if self.installing_widget.game:
|
||||
self.installing_widget.setVisible(get_visibility(self.installing_widget))
|
||||
self.installing_widget.setVisible(get_visibility(self.installing_widget)[0])
|
||||
|
||||
@pyqtSlot()
|
||||
def sort_list(self, sort_by: str = ""):
|
||||
# lk: this is the existing sorting implemenation
|
||||
# lk: it sorts by installed then by title
|
||||
installing_widget = self.icon_view.layout().remove(type(self.installing_widget).__name__)
|
||||
if sort_by:
|
||||
self.icon_view.layout().sort(lambda x: (sort_by not in x.widget().game.app_title.lower(),))
|
||||
else:
|
||||
self.icon_view.layout().sort(
|
||||
lambda x: (
|
||||
not x.widget().is_installed,
|
||||
x.widget().is_non_asset,
|
||||
x.widget().app_title,
|
||||
)
|
||||
)
|
||||
self.icon_view.layout().insert(0, installing_widget)
|
||||
list_widgets = self.list_view.findChildren(InstalledListWidget) + self.list_view.findChildren(UninstalledListWidget)
|
||||
if sort_by:
|
||||
list_widgets.sort(key=lambda x: (sort_by not in x.game.app_title.lower(),))
|
||||
else:
|
||||
list_widgets.sort(
|
||||
key=lambda x: (not x.is_installed, x.is_non_asset, x.app_title)
|
||||
)
|
||||
for idx, wl in enumerate(list_widgets):
|
||||
self.list_view.layout().insertWidget(idx, wl)
|
||||
|
||||
def update_list(self, app_names: list = None):
|
||||
logger.debug(f"Updating list for {app_names}")
|
||||
|
@ -351,11 +404,15 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
widgets[0], BaseUninstalledWidget
|
||||
):
|
||||
logger.debug(f"Update Gamelist: New installed {app_name}")
|
||||
self.icon_view.layout().removeWidget(self.widgets[app_name][0])
|
||||
self.list_view.layout().removeWidget(self.widgets[app_name][1])
|
||||
self.widgets[app_name][0].deleteLater()
|
||||
self.widgets[app_name][1].deleteLater()
|
||||
self.widgets.pop(app_name)
|
||||
|
||||
self.add_installed_widget(app_name)
|
||||
icon_widget, list_widget = self.add_installed_widget(app_name)
|
||||
self.icon_view.layout().addWidget(icon_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
update_list = True
|
||||
|
||||
# uninstalled
|
||||
|
@ -363,6 +420,8 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
widgets[0].game.app_name
|
||||
) and isinstance(widgets[0], BaseInstalledWidget):
|
||||
logger.debug(f"Update list: Uninstalled: {app_name}")
|
||||
self.icon_view.layout().removeWidget(self.widgets[app_name][0])
|
||||
self.list_view.layout().removeWidget(self.widgets[app_name][1])
|
||||
self.widgets[app_name][0].deleteLater()
|
||||
self.widgets[app_name][1].deleteLater()
|
||||
|
||||
|
@ -370,14 +429,16 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
|
||||
game = self.core.get_game(app_name, False)
|
||||
try:
|
||||
self.add_uninstalled_widget(game)
|
||||
icon_widget, list_widget = self.add_uninstalled_widget(game)
|
||||
self.icon_view.layout().addWidget(icon_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
except Exception:
|
||||
pass
|
||||
update_list = True
|
||||
|
||||
# do not update, if only update finished
|
||||
if update_list:
|
||||
self._update_games()
|
||||
self.sort_list()
|
||||
|
||||
else:
|
||||
installed_names = [i.app_name for i in self.core.get_installed_list()]
|
||||
|
@ -401,11 +462,15 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
|
||||
if new_installed_games:
|
||||
for name in new_installed_games:
|
||||
self.icon_view.layout().removeWidget(self.widgets[name][0])
|
||||
self.list_view.layout().removeWidget(self.widgets[name][1])
|
||||
self.widgets[name][0].deleteLater()
|
||||
self.widgets[name][1].deleteLater()
|
||||
self.widgets.pop(name)
|
||||
|
||||
self.add_installed_widget(name)
|
||||
icon_widget, list_widget = self.add_installed_widget(name)
|
||||
self.icon_view.layout().addWidget(icon_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
|
||||
for name in new_uninstalled_games:
|
||||
self.icon_view.layout().removeWidget(self.widgets[name][0])
|
||||
|
@ -418,104 +483,19 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
|
|||
|
||||
game = self.core.get_game(name, False)
|
||||
try:
|
||||
self.add_uninstalled_widget(game)
|
||||
icon_widget, list_widget = self.add_uninstalled_widget(game)
|
||||
self.icon_view.layout().addWidget(icon_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for igame in sorted(
|
||||
self.core.get_installed_list(), key=lambda x: x.title
|
||||
):
|
||||
i_widget, list_widget = self.widgets[igame.app_name]
|
||||
|
||||
self.icon_view.layout().addWidget(i_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
self.installed = self.core.get_installed_list()
|
||||
|
||||
for game in self.no_assets:
|
||||
i_widget, list_widget = self.widgets[game.app_name]
|
||||
self.icon_view.layout().addWidget(i_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
|
||||
# get Uninstalled games
|
||||
self.uninstalled_games = []
|
||||
games, self.dlcs = self.core.get_game_and_dlc_list()
|
||||
for game in sorted(games, key=lambda x: x.app_title):
|
||||
if (
|
||||
not self.core.is_installed(game.app_name)
|
||||
and game.app_name not in self.no_asset_names
|
||||
):
|
||||
i_widget, list_widget = self.widgets[game.app_name]
|
||||
self.icon_view.layout().addWidget(i_widget)
|
||||
self.list_view.layout().addWidget(list_widget)
|
||||
self.uninstalled_games.append(game)
|
||||
self.sort_list()
|
||||
self.update_count_games_label()
|
||||
|
||||
def _update_games(self):
|
||||
icon_layout = FlowLayout()
|
||||
list_layout = QVBoxLayout()
|
||||
|
||||
icon_layout.addWidget(self.installing_widget)
|
||||
|
||||
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title) + self.no_assets:
|
||||
i_widget, l_widget = self.widgets.get(igame.app_name, (None, None))
|
||||
if i_widget and l_widget:
|
||||
icon_layout.addWidget(i_widget)
|
||||
list_layout.addWidget(l_widget)
|
||||
else:
|
||||
logger.warning("Found installed game, without widget. Generating widget... ")
|
||||
try:
|
||||
i_widget, l_widget = self.add_installed_widget(igame.app_name)
|
||||
icon_layout.addWidget(i_widget)
|
||||
list_layout.addWidget(l_widget)
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
continue
|
||||
|
||||
# get Uninstalled games
|
||||
self.game_list, self.dlcs = self.core.get_game_and_dlc_list(update_assets=False)
|
||||
# add uninstalled games
|
||||
for game in sorted(self.game_list, key=lambda x: x.app_title):
|
||||
if self.core.is_installed(game.app_name) or game.app_name in self.no_asset_names:
|
||||
continue
|
||||
i_widget, list_widget = self.widgets.get(game.app_name, (None, None))
|
||||
if i_widget and list_widget:
|
||||
icon_layout.addWidget(i_widget)
|
||||
list_layout.addWidget(list_widget)
|
||||
else:
|
||||
logger.warning("Found installed game, without widget. Generating widget... ")
|
||||
try:
|
||||
i_widget, l_widget = self.add_uninstalled_widget(game)
|
||||
if not i_widget or not l_widget:
|
||||
logger.warning(f"Ignoring {game.app_name}")
|
||||
continue
|
||||
icon_layout.addWidget(i_widget)
|
||||
list_layout.addWidget(l_widget)
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
continue
|
||||
|
||||
QObjectCleanupHandler().add(self.icon_view.layout())
|
||||
QObjectCleanupHandler().add(self.list_view.layout())
|
||||
|
||||
self.icon_view.setLayout(icon_layout)
|
||||
self.list_view.setLayout(list_layout)
|
||||
|
||||
self.icon_view.setParent(None)
|
||||
self.list_view.setParent(None)
|
||||
|
||||
# insert widget in layout
|
||||
self.scroll_widget.layout().insertWidget(
|
||||
1, self.icon_view if self.head_bar.view.isChecked() else self.list_view
|
||||
)
|
||||
|
||||
def toggle_view(self):
|
||||
self.settings.setValue("icon_view", not self.head_bar.view.isChecked())
|
||||
|
||||
if not self.head_bar.view.isChecked():
|
||||
self.scroll_widget.layout().replaceWidget(self.list_view, self.icon_view)
|
||||
self.list_view.setParent(None)
|
||||
self.view_stack.slideInWidget(self.icon_view_scroll)
|
||||
else:
|
||||
self.scroll_widget.layout().replaceWidget(self.icon_view, self.list_view)
|
||||
self.icon_view.setParent(None)
|
||||
|
||||
self.game_list_scroll_area.verticalScrollBar().setValue(0)
|
||||
self.view_stack.slideInWidget(self.list_view_scroll)
|
||||
|
|
|
@ -217,3 +217,33 @@ class BaseInstalledWidget(QFrame):
|
|||
if error:
|
||||
QMessageBox.warning(self, "Error", error)
|
||||
self.game_running = False
|
||||
|
||||
# From RareGame, added from sorting to work
|
||||
@property
|
||||
def is_non_asset(self) -> bool:
|
||||
"""!
|
||||
@brief Property to report if a Game doesn't have assets
|
||||
|
||||
Typically, games have assets, however some games that require
|
||||
other launchers do not have them. Rare treats these games as installed
|
||||
offering to execute their launcher.
|
||||
|
||||
@return bool If the game doesn't have assets
|
||||
"""
|
||||
return not self.game.asset_infos
|
||||
|
||||
@property
|
||||
def is_installed(self) -> bool:
|
||||
"""!
|
||||
@brief Property to report if a game is installed
|
||||
|
||||
This returns True if InstalledGame data have been loaded for the game
|
||||
or if the game is a game without assets, for example an Origin game.
|
||||
|
||||
@return bool If the game should be considered installed
|
||||
"""
|
||||
return (self.igame is not None) or self.is_non_asset
|
||||
|
||||
@property
|
||||
def app_title(self) -> str:
|
||||
return self.igame.title if self.igame is not None else self.game.app_title
|
||||
|
|
|
@ -39,3 +39,33 @@ class BaseUninstalledWidget(QFrame):
|
|||
|
||||
def install(self):
|
||||
self.show_uninstalled_info.emit(self.game)
|
||||
|
||||
# From RareGame, added from sorting to work
|
||||
@property
|
||||
def is_non_asset(self) -> bool:
|
||||
"""!
|
||||
@brief Property to report if a Game doesn't have assets
|
||||
|
||||
Typically, games have assets, however some games that require
|
||||
other launchers do not have them. Rare treats these games as installed
|
||||
offering to execute their launcher.
|
||||
|
||||
@return bool If the game doesn't have assets
|
||||
"""
|
||||
return not self.game.asset_infos
|
||||
|
||||
@property
|
||||
def is_installed(self) -> bool:
|
||||
"""!
|
||||
@brief Property to report if a game is installed
|
||||
|
||||
This returns True if InstalledGame data have been loaded for the game
|
||||
or if the game is a game without assets, for example an Origin game.
|
||||
|
||||
@return bool If the game should be considered installed
|
||||
"""
|
||||
return False or self.is_non_asset
|
||||
|
||||
@property
|
||||
def app_title(self) -> str:
|
||||
return self.game.app_title
|
||||
|
|
|
@ -20,7 +20,7 @@ class InstalledIconWidget(BaseInstalledWidget):
|
|||
|
||||
def __init__(self, app_name, pixmap, game_utils):
|
||||
super(InstalledIconWidget, self).__init__(app_name, pixmap, game_utils)
|
||||
self.setObjectName("game_widget_icon")
|
||||
self.setObjectName(type(self).__name__)
|
||||
|
||||
self.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
layout = QVBoxLayout()
|
||||
|
|
|
@ -17,7 +17,7 @@ class InstallingGameWidget(QFrame):
|
|||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.setFixedWidth(ImageSize.Display.size.width())
|
||||
self.setObjectName("game_widget_icon")
|
||||
self.setObjectName(type(self).__name__)
|
||||
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.image_manager = ImageManagerSingleton()
|
||||
|
|
|
@ -14,12 +14,12 @@ from rare.widgets.elide_label import ElideLabel
|
|||
logger = getLogger("Uninstalled")
|
||||
|
||||
|
||||
class IconWidgetUninstalled(BaseUninstalledWidget):
|
||||
class UninstalledIconWidget(BaseUninstalledWidget):
|
||||
def __init__(self, game: Game, core: LegendaryCore, pixmap):
|
||||
super(IconWidgetUninstalled, self).__init__(game, core, pixmap)
|
||||
super(UninstalledIconWidget, self).__init__(game, core, pixmap)
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.setObjectName("game_widget_icon")
|
||||
self.setObjectName(type(self).__name__)
|
||||
layout.addWidget(self.image)
|
||||
|
||||
miniwidget = QWidget(self)
|
||||
|
|
|
@ -11,9 +11,9 @@ from rare.components.tabs.games.game_widgets.base_uninstalled_widget import (
|
|||
logger = getLogger("Game")
|
||||
|
||||
|
||||
class ListWidgetUninstalled(BaseUninstalledWidget):
|
||||
class UninstalledListWidget(BaseUninstalledWidget):
|
||||
def __init__(self, core: LegendaryCore, game, pixmap):
|
||||
super(ListWidgetUninstalled, self).__init__(game, core, pixmap)
|
||||
super(UninstalledListWidget, self).__init__(game, core, pixmap)
|
||||
self.setFrameStyle(self.StyledPanel)
|
||||
layout = QHBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
|
|
@ -12,7 +12,8 @@ from PyQt5.QtWidgets import (
|
|||
QStackedWidget,
|
||||
)
|
||||
|
||||
from rare.utils.extra_widgets import ImageLabel, FlowLayout, WaitingSpinner
|
||||
from rare.utils.extra_widgets import ImageLabel, WaitingSpinner
|
||||
from rare.widgets.flow_layout import FlowLayout
|
||||
|
||||
|
||||
class SearchResults(QStackedWidget):
|
||||
|
|
|
@ -13,7 +13,8 @@ from PyQt5.QtWidgets import (
|
|||
|
||||
from legendary.core import LegendaryCore
|
||||
from rare.ui.components.tabs.store.store import Ui_ShopWidget
|
||||
from rare.utils.extra_widgets import WaitingSpinner, FlowLayout, ButtonLineEdit
|
||||
from rare.utils.extra_widgets import WaitingSpinner, ButtonLineEdit
|
||||
from rare.widgets.flow_layout import FlowLayout
|
||||
from .constants import Constants
|
||||
from .game_widgets import GameWidget
|
||||
from .shop_api_core import ShopApiCore
|
||||
|
|
|
@ -2,43 +2,44 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/games_tab.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.4
|
||||
# Created by: PyQt5 UI code generator 5.15.6
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_GamesTab(object):
|
||||
def setupUi(self, GamesTab):
|
||||
GamesTab.setObjectName("GamesTab")
|
||||
GamesTab.resize(1071, 678)
|
||||
GamesTab.setWindowTitle("StackedWidget")
|
||||
self.games = QtWidgets.QWidget()
|
||||
self.games.setObjectName("games")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.games)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.game_list_scroll_area = QtWidgets.QScrollArea(self.games)
|
||||
self.game_list_scroll_area.setWidgetResizable(True)
|
||||
self.game_list_scroll_area.setObjectName("game_list_scroll_area")
|
||||
self.scroll_widget = QtWidgets.QWidget()
|
||||
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 1051, 658))
|
||||
self.scroll_widget.setObjectName("scroll_widget")
|
||||
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.scroll_widget)
|
||||
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||
self.count_games_label = QtWidgets.QLabel(self.scroll_widget)
|
||||
self.count_games_label.setText("")
|
||||
self.library_frame = QtWidgets.QFrame(self.games)
|
||||
self.library_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.library_frame.setFrameShadow(QtWidgets.QFrame.Plain)
|
||||
self.library_frame.setObjectName("library_frame")
|
||||
self.library_frame_layout = QtWidgets.QVBoxLayout(self.library_frame)
|
||||
self.library_frame_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.library_frame_layout.setObjectName("library_frame_layout")
|
||||
self.games_count_layout = QtWidgets.QHBoxLayout()
|
||||
self.games_count_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
|
||||
self.games_count_layout.setContentsMargins(6, 6, 6, 6)
|
||||
self.games_count_layout.setObjectName("games_count_layout")
|
||||
self.count_games_label = QtWidgets.QLabel(self.library_frame)
|
||||
self.count_games_label.setText("error")
|
||||
self.count_games_label.setObjectName("count_games_label")
|
||||
self.verticalLayout_4.addWidget(self.count_games_label)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.verticalLayout_4.addItem(spacerItem)
|
||||
self.game_list_scroll_area.setWidget(self.scroll_widget)
|
||||
self.verticalLayout.addWidget(self.game_list_scroll_area)
|
||||
self.games_count_layout.addWidget(self.count_games_label, 0, QtCore.Qt.AlignTop)
|
||||
self.library_frame_layout.addLayout(self.games_count_layout)
|
||||
self.verticalLayout.addWidget(self.library_frame)
|
||||
GamesTab.addWidget(self.games)
|
||||
|
||||
self.retranslateUi(GamesTab)
|
||||
GamesTab.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(GamesTab)
|
||||
|
||||
def retranslateUi(self, GamesTab):
|
||||
|
|
|
@ -2,56 +2,62 @@
|
|||
<ui version="4.0">
|
||||
<class>GamesTab</class>
|
||||
<widget class="QStackedWidget" name="GamesTab">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1071</width>
|
||||
<height>678</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">StackedWidget</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="games">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="game_list_scroll_area">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
<widget class="QFrame" name="library_frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="scroll_widget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1051</width>
|
||||
<height>658</height>
|
||||
</rect>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="library_frame_layout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="count_games_label">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="games_count_layout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QLabel" name="count_games_label">
|
||||
<property name="text">
|
||||
<string notr="true">error</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -13,7 +13,6 @@ from PyQt5.QtCore import (
|
|||
)
|
||||
from PyQt5.QtGui import QMovie, QPixmap, QFontMetrics, QImage
|
||||
from PyQt5.QtWidgets import (
|
||||
QLayout,
|
||||
QStyle,
|
||||
QSizePolicy,
|
||||
QLabel,
|
||||
|
@ -42,110 +41,6 @@ from rare.utils.utils import icon as qta_icon
|
|||
logger = getLogger("ExtraWidgets")
|
||||
|
||||
|
||||
class FlowLayout(QLayout):
|
||||
def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
|
||||
super(FlowLayout, self).__init__(parent)
|
||||
self._hspacing = hspacing
|
||||
self._vspacing = vspacing
|
||||
self._items = []
|
||||
self.setContentsMargins(margin, margin, margin, margin)
|
||||
self.setObjectName(type(self).__name__)
|
||||
|
||||
def __del__(self):
|
||||
del self._items[:]
|
||||
|
||||
def addItem(self, item):
|
||||
self._items.append(item)
|
||||
|
||||
def horizontalSpacing(self):
|
||||
if self._hspacing >= 0:
|
||||
return self._hspacing
|
||||
else:
|
||||
return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
|
||||
|
||||
def verticalSpacing(self):
|
||||
if self._vspacing >= 0:
|
||||
return self._vspacing
|
||||
else:
|
||||
return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
|
||||
|
||||
def count(self):
|
||||
return len(self._items)
|
||||
|
||||
def itemAt(self, index):
|
||||
if 0 <= index < len(self._items):
|
||||
return self._items[index]
|
||||
|
||||
def takeAt(self, index):
|
||||
if 0 <= index < len(self._items):
|
||||
return self._items.pop(index)
|
||||
|
||||
def expandingDirections(self):
|
||||
return Qt.Orientations(0)
|
||||
|
||||
def hasHeightForWidth(self):
|
||||
return True
|
||||
|
||||
def heightForWidth(self, width):
|
||||
return self.doLayout(QRect(0, 0, width, 0), True)
|
||||
|
||||
def setGeometry(self, rect):
|
||||
super(FlowLayout, self).setGeometry(rect)
|
||||
self.doLayout(rect, False)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
||||
def minimumSize(self):
|
||||
size = QSize()
|
||||
for item in self._items:
|
||||
size = size.expandedTo(item.minimumSize())
|
||||
left, top, right, bottom = self.getContentsMargins()
|
||||
size += QSize(left + right, top + bottom)
|
||||
return size
|
||||
|
||||
def doLayout(self, rect, testonly):
|
||||
left, top, right, bottom = self.getContentsMargins()
|
||||
effective = rect.adjusted(+left, +top, -right, -bottom)
|
||||
x = effective.x()
|
||||
y = effective.y()
|
||||
lineheight = 0
|
||||
for item in self._items:
|
||||
widget = item.widget()
|
||||
if not widget.isVisible():
|
||||
continue
|
||||
hspace = self.horizontalSpacing()
|
||||
if hspace == -1:
|
||||
hspace = widget.style().layoutSpacing(
|
||||
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
|
||||
)
|
||||
vspace = self.verticalSpacing()
|
||||
if vspace == -1:
|
||||
vspace = widget.style().layoutSpacing(
|
||||
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical
|
||||
)
|
||||
nextX = x + item.sizeHint().width() + hspace
|
||||
if nextX - hspace > effective.right() and lineheight > 0:
|
||||
x = effective.x()
|
||||
y = y + lineheight + vspace
|
||||
nextX = x + item.sizeHint().width() + hspace
|
||||
lineheight = 0
|
||||
if not testonly:
|
||||
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
||||
x = nextX
|
||||
lineheight = max(lineheight, item.sizeHint().height())
|
||||
return y + lineheight - rect.y() + bottom
|
||||
|
||||
def smartSpacing(self, pm):
|
||||
parent = self.parent()
|
||||
if parent is None:
|
||||
return -1
|
||||
elif parent.isWidgetType():
|
||||
return parent.style().pixelMetric(pm, None, parent)
|
||||
else:
|
||||
return parent.spacing()
|
||||
|
||||
|
||||
class IndicatorReasons:
|
||||
dir_not_empty = QCoreApplication.translate("IndicatorReasons", "Directory is not empty")
|
||||
wrong_format = QCoreApplication.translate("IndicatorReasons", "Given text has wrong format")
|
||||
|
|
115
rare/widgets/flow_layout.py
Normal file
115
rare/widgets/flow_layout.py
Normal file
|
@ -0,0 +1,115 @@
|
|||
from PyQt5.QtCore import (
|
||||
Qt,
|
||||
QRect,
|
||||
QSize,
|
||||
QPoint,
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
QLayout,
|
||||
QStyle,
|
||||
QSizePolicy,
|
||||
)
|
||||
|
||||
|
||||
class FlowLayout(QLayout):
|
||||
def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
|
||||
super(FlowLayout, self).__init__(parent)
|
||||
self._hspacing = hspacing
|
||||
self._vspacing = vspacing
|
||||
self._items = []
|
||||
self.setContentsMargins(margin, margin, margin, margin)
|
||||
self.setObjectName(type(self).__name__)
|
||||
|
||||
def __del__(self):
|
||||
del self._items[:]
|
||||
|
||||
def addItem(self, item):
|
||||
self._items.append(item)
|
||||
|
||||
def horizontalSpacing(self):
|
||||
if self._hspacing >= 0:
|
||||
return self._hspacing
|
||||
else:
|
||||
return self.smartSpacing(QStyle.PM_LayoutHorizontalSpacing)
|
||||
|
||||
def verticalSpacing(self):
|
||||
if self._vspacing >= 0:
|
||||
return self._vspacing
|
||||
else:
|
||||
return self.smartSpacing(QStyle.PM_LayoutVerticalSpacing)
|
||||
|
||||
def count(self):
|
||||
return len(self._items)
|
||||
|
||||
def itemAt(self, index):
|
||||
if 0 <= index < len(self._items):
|
||||
return self._items[index]
|
||||
|
||||
def takeAt(self, index):
|
||||
if 0 <= index < len(self._items):
|
||||
return self._items.pop(index)
|
||||
|
||||
def expandingDirections(self):
|
||||
return Qt.Orientations(0)
|
||||
|
||||
def hasHeightForWidth(self):
|
||||
return True
|
||||
|
||||
def heightForWidth(self, width):
|
||||
return self.doLayout(QRect(0, 0, width, 0), True)
|
||||
|
||||
def setGeometry(self, rect):
|
||||
super(FlowLayout, self).setGeometry(rect)
|
||||
self.doLayout(rect, False)
|
||||
|
||||
def sizeHint(self):
|
||||
return self.minimumSize()
|
||||
|
||||
def minimumSize(self):
|
||||
size = QSize()
|
||||
for item in self._items:
|
||||
size = size.expandedTo(item.minimumSize())
|
||||
left, top, right, bottom = self.getContentsMargins()
|
||||
size += QSize(left + right, top + bottom)
|
||||
return size
|
||||
|
||||
def doLayout(self, rect, testonly):
|
||||
left, top, right, bottom = self.getContentsMargins()
|
||||
effective = rect.adjusted(+left, +top, -right, -bottom)
|
||||
x = effective.x()
|
||||
y = effective.y()
|
||||
lineheight = 0
|
||||
for item in self._items:
|
||||
widget = item.widget()
|
||||
if not widget.isVisible():
|
||||
continue
|
||||
hspace = self.horizontalSpacing()
|
||||
if hspace == -1:
|
||||
hspace = widget.style().layoutSpacing(
|
||||
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
|
||||
)
|
||||
vspace = self.verticalSpacing()
|
||||
if vspace == -1:
|
||||
vspace = widget.style().layoutSpacing(
|
||||
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical
|
||||
)
|
||||
nextX = x + item.sizeHint().width() + hspace
|
||||
if nextX - hspace > effective.right() and lineheight > 0:
|
||||
x = effective.x()
|
||||
y = y + lineheight + vspace
|
||||
nextX = x + item.sizeHint().width() + hspace
|
||||
lineheight = 0
|
||||
if not testonly:
|
||||
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
|
||||
x = nextX
|
||||
lineheight = max(lineheight, item.sizeHint().height())
|
||||
return y + lineheight - rect.y() + bottom
|
||||
|
||||
def smartSpacing(self, pm):
|
||||
parent = self.parent()
|
||||
if parent is None:
|
||||
return -1
|
||||
elif parent.isWidgetType():
|
||||
return parent.style().pixelMetric(pm, None, parent)
|
||||
else:
|
||||
return parent.spacing()
|
|
@ -7,9 +7,10 @@ from PyQt5.QtCore import (
|
|||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
QSizePolicy,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from rare.utils.extra_widgets import FlowLayout
|
||||
from .flow_layout import FlowLayout
|
||||
|
||||
|
||||
class LibraryLayout(FlowLayout):
|
||||
|
@ -133,3 +134,14 @@ class LibraryLayout(FlowLayout):
|
|||
def sort(self, key: Callable):
|
||||
self._items.sort(key=key)
|
||||
self.setGeometry(self.parent().rect())
|
||||
|
||||
# These are used to pop and insert the installing widget, remove them when no longer needed
|
||||
def remove(self, name: str) -> QWidget:
|
||||
widget = next(filter(lambda x: x.widget().objectName() == name, self._items), None)
|
||||
self._items.remove(widget)
|
||||
self.setGeometry(self.parent().rect())
|
||||
return widget
|
||||
|
||||
def insert(self, index: int, widget: QWidget):
|
||||
self._items.insert(index, widget)
|
||||
self.setGeometry(self.parent().rect())
|
||||
|
|
138
rare/widgets/sliding_stack.py
Normal file
138
rare/widgets/sliding_stack.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
from PyQt5.QtCore import (
|
||||
pyqtSlot,
|
||||
QEvent,
|
||||
QEasingCurve,
|
||||
QParallelAnimationGroup,
|
||||
QAbstractAnimation,
|
||||
QPropertyAnimation,
|
||||
Qt,
|
||||
QPoint,
|
||||
)
|
||||
from PyQt5.QtWidgets import QStackedWidget, QGestureEvent, QSwipeGesture
|
||||
|
||||
|
||||
class SlidingStackedWidget(QStackedWidget):
|
||||
"""
|
||||
Taken from: https://stackoverflow.com/a/52597972
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(SlidingStackedWidget, self).__init__(parent)
|
||||
|
||||
self.m_direction = Qt.Horizontal
|
||||
self.m_speed = 500
|
||||
self.m_animationtype = QEasingCurve.OutBack
|
||||
self.m_now = 0
|
||||
self.m_next = 0
|
||||
self.m_wrap = False
|
||||
self.m_pnow = QPoint(0, 0)
|
||||
self.m_active = False
|
||||
|
||||
def setDirection(self, direction: Qt.Orientation) -> None:
|
||||
self.m_direction = direction
|
||||
|
||||
def setSpeed(self, speed: int) -> None:
|
||||
self.m_speed = speed
|
||||
|
||||
def setAnimation(self, animationtype: QEasingCurve.Type) -> None:
|
||||
self.m_animationtype = animationtype
|
||||
|
||||
def setWrap(self, wrap: bool) -> None:
|
||||
self.m_wrap = wrap
|
||||
|
||||
@pyqtSlot()
|
||||
def slideInPrev(self):
|
||||
now = self.currentIndex()
|
||||
if self.m_wrap or now > 0:
|
||||
self.slideInIndex(now - 1)
|
||||
|
||||
@pyqtSlot()
|
||||
def slideInNext(self):
|
||||
now = self.currentIndex()
|
||||
if self.m_wrap or now < (self.count() - 1):
|
||||
self.slideInIndex(now + 1)
|
||||
|
||||
def slideInIndex(self, idx):
|
||||
if idx > (self.count() - 1):
|
||||
idx = idx % self.count()
|
||||
elif idx < 0:
|
||||
idx = (idx + self.count()) % self.count()
|
||||
self.slideInWidget(self.widget(idx))
|
||||
|
||||
def slideInWidget(self, newwidget):
|
||||
if self.m_active:
|
||||
return
|
||||
|
||||
self.m_active = True
|
||||
|
||||
_now = self.currentIndex()
|
||||
_next = self.indexOf(newwidget)
|
||||
|
||||
if _now == _next:
|
||||
self.m_active = False
|
||||
return
|
||||
|
||||
offsetx, offsety = self.frameRect().width(), self.frameRect().height()
|
||||
self.widget(_next).setGeometry(self.frameRect())
|
||||
|
||||
if not self.m_direction == Qt.Horizontal:
|
||||
if _now < _next:
|
||||
offsetx, offsety = 0, -offsety
|
||||
else:
|
||||
offsetx = 0
|
||||
else:
|
||||
if _now < _next:
|
||||
offsetx, offsety = -offsetx, 0
|
||||
else:
|
||||
offsety = 0
|
||||
|
||||
pnext = self.widget(_next).pos()
|
||||
pnow = self.widget(_now).pos()
|
||||
self.m_pnow = pnow
|
||||
|
||||
offset = QPoint(offsetx, offsety)
|
||||
self.widget(_next).move(pnext - offset)
|
||||
self.widget(_next).show()
|
||||
self.widget(_next).raise_()
|
||||
|
||||
animgroup = QParallelAnimationGroup(self, finished=self.animationDoneSlot)
|
||||
|
||||
for index, start, end in zip((_now, _next), (pnow, pnext - offset), (pnow + offset, pnext)):
|
||||
animation = QPropertyAnimation(
|
||||
self.widget(index),
|
||||
b"pos",
|
||||
duration=self.m_speed,
|
||||
easingCurve=self.m_animationtype,
|
||||
startValue=start,
|
||||
endValue=end,
|
||||
)
|
||||
animgroup.addAnimation(animation)
|
||||
|
||||
self.m_next = _next
|
||||
self.m_now = _now
|
||||
self.m_active = True
|
||||
animgroup.start(QAbstractAnimation.DeleteWhenStopped)
|
||||
|
||||
@pyqtSlot()
|
||||
def animationDoneSlot(self):
|
||||
self.setCurrentIndex(self.m_next)
|
||||
self.widget(self.m_now).hide()
|
||||
self.widget(self.m_now).move(self.m_pnow)
|
||||
self.m_active = False
|
||||
|
||||
def event(self, e: QEvent):
|
||||
if e.type() == QEvent.Gesture:
|
||||
return self.gestureEvent(QGestureEvent(e))
|
||||
return super(SlidingStackedWidget, self).event(e)
|
||||
|
||||
def gestureEvent(self, e: QGestureEvent):
|
||||
if swipe := e.gesture(Qt.SwipeGesture):
|
||||
self.swipeTriggered(swipe)
|
||||
return True
|
||||
|
||||
def swipeTriggered(self, g: QSwipeGesture):
|
||||
if g.state() == Qt.GestureFinished:
|
||||
if g.horizontalDirection() == QSwipeGesture.Left:
|
||||
self.slideInPrev()
|
||||
else:
|
||||
self.slideInNext()
|
Loading…
Reference in a new issue