diff --git a/Rare/Components/TabWidget.py b/Rare/Components/TabWidget.py index d383efbd..73ea4a08 100644 --- a/Rare/Components/TabWidget.py +++ b/Rare/Components/TabWidget.py @@ -1,10 +1,10 @@ -from PyQt5.QtGui import QIcon +from PyQt5.QtCore import QSize +from PyQt5.QtGui import QIcon, QFont from PyQt5.QtWidgets import QTabWidget, QTabBar, QWidget, QToolButton, QWidgetAction, QMenu from legendary.core import LegendaryCore from Rare import style_path from Rare.Components.Tabs.Account.AccountWidget import MiniWidget -from Rare.Components.Tabs.CloudSaves.CloudSaves import SyncSaves from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab from Rare.Components.Tabs.Games.GamesTab import GameTab from Rare.Components.Tabs.Settings.SettingsTab import SettingsTab @@ -25,8 +25,8 @@ class TabWidget(QTabWidget): self.game_list.default_widget.game_list.install_game.connect(lambda x: self.downloadTab.install_game(x)) # Commented, because it is not finished - #self.cloud_saves = SyncSaves(core) - #self.addTab(self.cloud_saves, "Cloud Saves") + # self.cloud_saves = SyncSaves(core) + # self.addTab(self.cloud_saves, "Cloud Saves") # Space Tab self.addTab(QWidget(), "") @@ -34,11 +34,11 @@ class TabWidget(QTabWidget): self.account = QWidget() self.addTab(self.account, "") - self.setTabEnabled(disabled_tab+1, False) + self.setTabEnabled(disabled_tab + 1, False) # self.settings = SettingsTab(core) self.addTab(self.settings, QIcon(style_path + "/Icons/settings.png"), None) - + self.setIconSize(QSize(25, 25)) self.tabBar().setTabButton(3, self.tabBar().RightSide, TabButtonWidget(core)) def resizeEvent(self, event): @@ -51,6 +51,8 @@ class TabBar(QTabBar): super(TabBar, self).__init__() self._expanded = expanded self.setObjectName("main_tab_bar") + self.setFont(QFont("Arial", 13)) + # self.setContentsMargins(0,10,0,10) def tabSizeHint(self, index): size = super(TabBar, self).tabSizeHint(index) @@ -70,6 +72,7 @@ class TabButtonWidget(QToolButton): self.setIcon(QIcon(style_path + "/Icons/account.png")) self.setToolTip("Account") + self.setIconSize(QSize(25, 25)) self.setMenu(QMenu()) action = QWidgetAction(self) action.setDefaultWidget(MiniWidget(core)) diff --git a/Rare/Components/Tabs/Games/GameList.py b/Rare/Components/Tabs/Games/GameList.py index 0bb82ec2..a7439abe 100644 --- a/Rare/Components/Tabs/Games/GameList.py +++ b/Rare/Components/Tabs/Games/GameList.py @@ -1,9 +1,11 @@ -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtCore import Qt, pyqtSignal, QSettings from PyQt5.QtWidgets import * from legendary.core import LegendaryCore from Rare.Components.Tabs.Games.GameWidgetInstalled import GameWidgetInstalled +from Rare.Components.Tabs.Games.GameWidgetListUninstalled import UninstalledGameWidget from Rare.Components.Tabs.Games.GameWidgetUninstalled import GameWidgetUninstalled +from Rare.Components.Tabs.Games.InstalledListWidget import GameWidget from Rare.utils.QtExtensions import FlowLayout @@ -20,22 +22,33 @@ class GameList(QScrollArea): self.setWidgetResizable(True) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.init_ui() - def init_ui(self): + self.settings = QSettings() + icon_view = self.settings.value("icon_view", True, bool) + self.init_ui(icon_view) + + def init_ui(self, icon_view=True): self.widget = QWidget() self.widgets = [] - self.layout = FlowLayout() + if icon_view: + self.layout = FlowLayout() + else: + self.layout = QVBoxLayout() self.updates = [] + # Installed Games for game in sorted(self.core.get_installed_list(), key=lambda x: x.title): - widget = GameWidgetInstalled(self.core, game) + if icon_view: + widget = GameWidgetInstalled(self.core, game) + widget.show_info.connect(lambda app_name: self.show_game_info.emit(app_name)) + else: + widget = GameWidget(game, self.core) if widget.update_available: self.updates.append(widget.game.app_name) self.layout.addWidget(widget) widget.update_list.connect(self.update_list) - widget.show_info.connect(lambda app_name: self.show_game_info.emit(app_name)) + uninstalled_games = [] installed = [i.app_name for i in self.core.get_installed_list()] @@ -45,7 +58,10 @@ class GameList(QScrollArea): uninstalled_games.append(game) # add uninstalled to gui for game in uninstalled_games: - widget = GameWidgetUninstalled(self.core, game) + if icon_view: + widget = GameWidgetUninstalled(self.core, game) + else: + widget = UninstalledGameWidget(self.core, game) widget.install_game.connect(lambda options: self.install_game.emit(options)) self.layout.addWidget(widget) self.widgets.append(widget) @@ -65,8 +81,9 @@ class GameList(QScrollArea): for w in self.widgets: w.setVisible(not i_o) - def update_list(self): + def update_list(self, icon_view=True): print("Updating List") + self.settings.setValue("icon_view", icon_view) self.setWidget(QWidget()) - self.init_ui() + self.init_ui(icon_view) self.update() diff --git a/Rare/Components/Tabs/Games/GameWidgetInstalled.py b/Rare/Components/Tabs/Games/GameWidgetInstalled.py index 533fc124..6d8454e7 100644 --- a/Rare/Components/Tabs/Games/GameWidgetInstalled.py +++ b/Rare/Components/Tabs/Games/GameWidgetInstalled.py @@ -122,8 +122,3 @@ class Menu(QMenu): self.addAction(self.tr("Game info"), lambda: self.action.emit("info")) self.addAction(self.tr("Uninstall"), lambda: self.action.emit("uninstall")) - def info(self): - pass - - def uninstall(self): - pass diff --git a/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py b/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py new file mode 100644 index 00000000..34186738 --- /dev/null +++ b/Rare/Components/Tabs/Games/GameWidgetListUninstalled.py @@ -0,0 +1,67 @@ +import os +import subprocess +from logging import getLogger + +from PyQt5.QtCore import QThread, pyqtSignal, QProcess, QSettings +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QMessageBox +from legendary.core import LegendaryCore + +from Rare.Components.Dialogs.InstallDialog import InstallDialog + +logger = getLogger("Game") + + +class UninstalledGameWidget(QWidget): + install_game = pyqtSignal(dict) + def __init__(self, core: LegendaryCore, game): + super(UninstalledGameWidget, self).__init__() + self.title = game.app_title + self.app_name = game.app_name + self.version = game.app_version + self.layout = QHBoxLayout() + self.game = game + self.core = core + settings = QSettings() + IMAGE_DIR = settings.value("img_dir", os.path.expanduser("~/.cache/rare"), type=str) + + if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/UninstalledArt.png") + pixmap = pixmap.scaled(120, 160) + self.image = QLabel() + self.image.setPixmap(pixmap) + self.layout.addWidget(self.image) + + self.child_layout = QVBoxLayout() + + self.title_label = QLabel(f"

{self.title}

") + self.app_name_label = QLabel(f"App Name: {self.app_name}") + self.version_label = QLabel(f"Version: {self.version}") + self.install_button = QPushButton(self.tr("Install")) + self.install_button.setFixedWidth(120) + self.install_button.clicked.connect(self.install) + + self.child_layout.addWidget(self.title_label) + self.child_layout.addWidget(self.app_name_label) + self.child_layout.addWidget(self.version_label) + self.child_layout.addWidget(self.install_button) + self.child_layout.addStretch(1) + self.layout.addLayout(self.child_layout) + + self.layout.addStretch(1) + self.setLayout(self.layout) + + def install(self): + logger.info("Install " + self.game.app_title) + + infos = InstallDialog().get_information() + if infos != 0: + path, max_workers = infos + self.install_game.emit({ + "app_name": self.game.app_name, + "options": { + "path": path, + "max_workers": max_workers + } + }) + # wait for update of legendary to get progress diff --git a/Rare/Components/Tabs/Games/GamesTab.py b/Rare/Components/Tabs/Games/GamesTab.py index 475fbd10..56d5ac3d 100644 --- a/Rare/Components/Tabs/Games/GamesTab.py +++ b/Rare/Components/Tabs/Games/GamesTab.py @@ -1,3 +1,4 @@ +from PyQt5.QtCore import QSettings from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QLabel, QPushButton, QStyle, \ QStackedLayout @@ -29,6 +30,7 @@ class Games(QWidget): self.head_bar = GameListHeadBar() self.head_bar.setObjectName("head_bar") + self.game_list = GameList(core) self.head_bar.search_bar.textChanged.connect( @@ -41,6 +43,8 @@ class Games(QWidget): self.layout.addWidget(self.head_bar) self.layout.addWidget(self.game_list) # self.layout.addStretch(1) + self.head_bar.view.stateChanged.connect( + lambda: self.game_list.update_list(not self.head_bar.view.isChecked())) self.setLayout(self.layout) @@ -62,6 +66,7 @@ class GameListHeadBar(QWidget): self.list_view = QLabel(self.tr("List view")) self.layout.addWidget(self.list_view) self.view = QCheckBox(self.tr("Icon view")) + self.view.setChecked(not QSettings().value("icon_view", True, bool)) self.layout.addWidget(self.view) self.refresh_list = QPushButton() self.refresh_list.setIcon(self.style().standardIcon(getattr(QStyle, "SP_BrowserReload"))) # Reload icon diff --git a/Rare/Components/Tabs/Games/InstalledListWidget.py b/Rare/Components/Tabs/Games/InstalledListWidget.py new file mode 100644 index 00000000..4089433f --- /dev/null +++ b/Rare/Components/Tabs/Games/InstalledListWidget.py @@ -0,0 +1,110 @@ +import os +from logging import getLogger + +from PyQt5.QtCore import QProcess, pyqtSignal, QSettings +from PyQt5.QtGui import QPixmap +from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QPushButton, QStyle, QVBoxLayout +from legendary.core import LegendaryCore +from legendary.models.game import InstalledGame + +from Rare.utils import LegendaryApi + +logger = getLogger("GameWidget") + + +class GameWidget(QWidget): + proc: QProcess + signal = pyqtSignal(str) + update_list = pyqtSignal() + + # TODO Repair + def __init__(self, game: InstalledGame, core: LegendaryCore): + super(GameWidget, self).__init__() + self.core = core + self.game = game + self.dev = core.get_game(self.game.app_name).metadata["developer"] + self.title = game.title + self.app_name = game.app_name + self.version = game.version + self.size = game.install_size + self.launch_params = game.launch_parameters + self.update_available = self.core.get_asset(self.game.app_name, True).build_version != game.version + settings = QSettings() + IMAGE_DIR = settings.value("img_dir", os.path.expanduser("~/.cache/rare")) + + # self.dev = + self.game_running = False + self.layout = QHBoxLayout() + if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png") + elif os.path.exists(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png") + elif os.path.exists(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"): + pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png") + else: + logger.warning(f"No Image found: {self.game.title}") + pixmap = None + if pixmap: + pixmap = pixmap.scaled(180, 240) + self.image = QLabel() + self.image.setPixmap(pixmap) + self.layout.addWidget(self.image) + + ##Layout on the right + self.childLayout = QVBoxLayout() + play_icon = self.style().standardIcon(getattr(QStyle, 'SP_MediaPlay')) + settings_icon = self.style().standardIcon(getattr(QStyle, 'SP_DirIcon')) + self.title_widget = QLabel(f"

{self.title}

") + self.app_name_label = QLabel(self.app_name) + self.launch_button = QPushButton(play_icon, self.tr("Launch")) + self.launch_button.setObjectName("launch_game_button") + self.launch_button.setFixedWidth(120) + + self.launch_button.clicked.connect(self.launch) + if os.name != "nt": + self.wine_rating = QLabel("Wine Rating: " + self.get_rating()) + self.developer_label = QLabel(self.tr("Developer: ") + self.dev) + self.version_label = QLabel("Version: " + str(self.version)) + self.size_label = QLabel(f"{self.tr('Installed size')}: {round(self.size / (1024 ** 3), 2)} GB") + self.childLayout.addWidget(self.title_widget) + self.childLayout.addWidget(self.launch_button) + self.childLayout.addWidget(self.app_name_label) + self.childLayout.addWidget(self.developer_label) + if os.name != "nt": + self.childLayout.addWidget(self.wine_rating) + self.childLayout.addWidget(self.version_label) + self.childLayout.addWidget(self.size_label) + + self.childLayout.addStretch(1) + self.layout.addLayout(self.childLayout) + self.layout.addStretch(1) + self.setLayout(self.layout) + + def launch(self, offline=False): + if not self.game_running: + logger.info("Launching " + self.game.title) + self.proc = LegendaryApi.launch_game(self.core, self.game.app_name, offline) + if not self.proc: + logger.error("Could not start process") + return + self.proc.finished.connect(self.finished) + self.launch_button.setText(self.tr("Game running")) + self.launch_button.setDisabled(True) + self.game_running = True + else: + self.launch_button.setText("Launch") + self.launch_button.setDisabled(False) + + def finished(self, exit_code): + self.launch_button.setText("Launch") + logger.info(f"Game {self.title} finished with exit code {exit_code}") + self.game_running = False + + def kill(self): + self.proc.terminate() + self.launch_button.setText("Launch") + self.game_running = False + logger.info("Killing Game") + + def get_rating(self) -> str: + return "gold" # TODO diff --git a/Rare/Styles/RareStyle.qss b/Rare/Styles/RareStyle.qss index 66b37db4..32d0c7be 100644 --- a/Rare/Styles/RareStyle.qss +++ b/Rare/Styles/RareStyle.qss @@ -13,7 +13,7 @@ QTabBar{ QTabBar::tab#main_tab_bar { border-bottom: none; - padding: 5px; + padding: 5px } QTabBar::tab:selected{ border-bottom: 2px solid white; diff --git a/Rare/languages/de.ts b/Rare/languages/de.ts new file mode 100644 index 00000000..129175a8 --- /dev/null +++ b/Rare/languages/de.ts @@ -0,0 +1,401 @@ + + + + + About + + + Developer: + Entwickler: + + + + Legendary developer: + Legendary Entwickler: + + + + This is a beta version, so you can get bugs. If you get a bug, please report it by creating a Issue on <a href='https://github.com/Dummerle/Rare/issues'>Github</a>. You can also contact me on Discord (Dummerle#7419). If you have a feature request, please contact me + Dies ist eine beta version, dementsprechend wird es Bugs geben. Wenn du einen bug findest, berichte mir davon, indem du einen Issue auf <a href='https://github.com/Dummerle/Rare/issues'>Github</a> erstellst oder du kontaktierst mich über Discord (Dummerle#7419). Auch wenn du gerne ein neues Feature haben möchtest, kontaktiere mich + + + + DownloadTab + + + <b>WARNING</b>: The progress bar is not implemented. It is normal, if there is no progress. The progress is visible in console, because Legendary prints output to console. A pull request is active to get output + <b>Warnung</b>: Die Anzeige ist noch nicht fertig. Es wird normal sein, wenn dort kein Fortschritt angezeigt wird. Dafür musst du in die Konsole schauen, da es in Legendary noch keine Möglichkeit gibt, die Ausgabe zu verarbeiten. Ein Pull Request ist eingereicht + + + + No active Download + Keine aktiven Downloads + + + + No updates available + Keine verfügbaren Updates + + + + Download size is 0. Game already exists + Downloadgröße ist 0. Spiel exitstiert bereits + + + + Installation failed. See logs for more information + Installation fehlgeschlagen. Siehe Log für mehr Informationen + + + + Installation finished + Installation abgeschlossen + + + + Download of game + Download des Spiels + + + + Installing Game: No active download + Zu installierendes Spiel: Kein aktiver Download + + + + GameListHeadBar + + + Installed only + Nur installierte + + + + Search Game + Spiel suchen + + + + List view + Listenansicht + + + + Icon view + Iconansicht + + + + GameWidgetInstalled + + + Update available + Update verfügbar + + + + Game running + Spiel läuft + + + + Do you want to uninstall + Möchtest du das Spiel deinstallieren: + + + + GameWidgetUninstalled + + + Install Game + Spiel installieren + + + + ImportWidget + + + Appdata path: + Appdata pfad: + + + + LaunchDialog + + + Launching Rare + Starte Rare + + + + Logging in + Einloggen + + + + LegendarySettings + + + Legendary settings + Legendary Einstellungen + + + + Default installation directory + Standard Installationsordner + + + + Max workers for Download (Less: slower download)(0: Default) + Maximale Anzahl für Downloadprozesse (Weniger: Langsamer)(o: Default) + + + + LinuxSettings + + + Default Wine Prefix + Standard Wine Prefix + + + + Default Wine executable + Standard Wine zum ausführen + + + + LoginDialog + + + Select one option to Login + Wähle eine Option zum einloggen + + + + Menu + + + Game info + Spielinformation + + + + Uninstall + Deinstallieren + + + + MiniWidget + + + Logged in as + Eingeloggt als + + + + Account settings + Accounteinstellungen + + + + Logout + Ausloggen + + + + Do you really want to logout + Möchtest du dich wirklich ausloggen + + + + PathEdit + + + Select Path + Wähle Pfad + + + + Choose Path + Wähle Pfad + + + + PathInputDialog + + + Select directory + Wähle Ordner + + + + RareSettings + + + Rare settings + Rare Einstellungen + + + + Image Directory + Ordner für Bilder + + + + Restart Application to activate changes + Starte die App neu um die Änderungen zu aktivieren + + + + SyncSaves + + + Cloud Saves + Cloudspeicher + + + + Found Saves for folowing Games + Speicherstände für folgende Spiele gefunden + + + + Sync all games + Alle Spiele synchronisieren + + + + No Games Found + Keine Spiele gefunden + + + + Your games don't support cloud save + Deine Spiele unterstützen keine Cloudspeicherstände + + + + Found no savepath + Keinen Speicherort gefunden + + + + No save path was found. Please select path or skip + Kein Pfad zum speichern wurde gefunden. Wähle einen Pfad oder überspringe + + + + SyncWidget + + + Path not found + + + + + Local Save date: + + + + + No Local Save files + + + + + Cloud save date: + + + + + No Cloud saves + + + + + Game is up to date + + + + + Upload anyway + + + + + Download anyway + + + + + Cloud save is newer + + + + + Download Cloud saves + + + + + Upload Saves + + + + + Local save is newer + + + + + Upload saves + + + + + Download saves + + + + + Change path + + + + + Uploading... + + + + + Upload finished + + + + + Downloading... + + + + + Download finished + + + + + TabWidget + + + Games + Spiele + + + + UpdateWidget + + + Update Game + Spiel updaten + + +