1
0
Fork 0
mirror of synced 2024-06-02 02:34:40 +12:00

Merge pull request #335 from loathingKernel/next

Improve the login dialog
This commit is contained in:
Stelios Tsampas 2023-12-13 15:23:36 +02:00 committed by GitHub
commit d36582bf6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 716 additions and 501 deletions

View file

@ -6,7 +6,7 @@ from typing import Tuple, List, Union, Optional
from PyQt5.QtCore import Qt, QThreadPool, QSettings, QCoreApplication
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QKeyEvent, QShowEvent
from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox, QLayout, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QDialog, QFileDialog, QCheckBox, QLayout, QWidget, QVBoxLayout, QFormLayout
from legendary.utils.selective_dl import get_sdl_appname
from rare.models.game import RareGame
@ -85,7 +85,10 @@ class InstallDialog(QDialog):
save_func=self.save_install_edit,
parent=self,
)
self.ui.install_dir_layout.addWidget(self.install_dir_edit)
self.ui.install_dialog_layout.setWidget(
self.ui.install_dialog_layout.getWidgetPosition(self.ui.install_dir_label)[0],
QFormLayout.FieldRole, self.install_dir_edit
)
if self.options.update:
self.ui.install_dir_label.setEnabled(False)

View file

@ -1,6 +1,6 @@
from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QLayout, QDialog, QMessageBox, QFrame
from legendary.core import LegendaryCore
@ -48,7 +48,7 @@ class LoginDialog(QDialog):
self.args = ArgumentsSingleton()
self.login_stack = SlidingStackedWidget(parent=self)
self.login_stack.setMinimumSize(480, 180)
self.login_stack.setMinimumWidth(480)
self.ui.login_stack_layout.addWidget(self.login_stack)
self.landing_page = LandingPage(self.login_stack)
@ -65,6 +65,26 @@ class LoginDialog(QDialog):
self.import_page.success.connect(self.login_successful)
self.import_page.changed.connect(lambda: self.ui.next_button.setEnabled(self.import_page.is_valid()))
# # NOTE: The real problem is that the BrowserLogin page has a huge QLabel with word-wrapping enabled.
# # That forces the whole form to vertically expand instead of horizontally. Since the form is not shown
# # on the first page, the internal Qt calculation for the size of that form calculates it by expanding it
# # vertically. Once the form becomes visible, the correct calculation takes place and that is why the
# # dialog reduces in height. To avoid that, calculate the bounding size of all forms and set it as the
# # minumum size
# self.login_stack.setMinimumSize(
# self.landing_page.sizeHint().expandedTo(
# self.browser_page.sizeHint().expandedTo(self.import_page.sizeHint())
# )
# )
self.login_stack.setFixedHeight(
max(
self.landing_page.heightForWidth(self.login_stack.minimumWidth()),
self.browser_page.heightForWidth(self.login_stack.minimumWidth()),
self.import_page.heightForWidth(self.login_stack.minimumWidth()),
)
)
self.ui.next_button.setEnabled(False)
self.ui.back_button.setEnabled(False)

View file

@ -4,7 +4,7 @@ from typing import Tuple
from PyQt5.QtCore import pyqtSignal, QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QFrame, qApp
from PyQt5.QtWidgets import QFrame, qApp, QFormLayout, QLineEdit
from legendary.core import LegendaryCore
from legendary.utils import webview_login
@ -31,11 +31,14 @@ class BrowserLogin(QFrame):
self.sid_edit = IndicatorLineEdit(
placeholder=self.tr("Insert authorizationCode here"), edit_func=self.text_changed, parent=self
)
self.sid_edit.line_edit.setEchoMode(QLineEdit.Password)
self.ui.link_text.setText(self.login_url)
self.ui.copy_button.setIcon(icon("mdi.content-copy", "fa.copy"))
self.ui.copy_button.clicked.connect(self.copy_link)
self.ui.sid_layout.addWidget(self.sid_edit)
self.ui.form_layout.setWidget(
self.ui.form_layout.getWidgetPosition(self.ui.sid_label)[0],
QFormLayout.FieldRole, self.sid_edit
)
self.ui.open_button.clicked.connect(self.open_browser)
self.sid_edit.textChanged.connect(self.changed.emit)
@ -68,7 +71,7 @@ class BrowserLogin(QFrame):
auth_code = self.sid_edit.text()
try:
if self.core.auth_code(auth_code):
logger.info(f"Successfully logged in as {self.core.lgd.userdata['displayName']}")
logger.info("Successfully logged in as %s", self.core.lgd.userdata['displayName'])
self.success.emit()
else:
self.ui.status_label.setText(self.tr("Login failed."))
@ -82,7 +85,7 @@ class BrowserLogin(QFrame):
QDesktopServices.openUrl(QUrl(self.login_url))
else:
if webview_login.do_webview_login(callback_code=self.core.auth_ex_token):
logger.info("Successfully logged in as " f"{self.core.lgd.userdata['displayName']}")
logger.info("Successfully logged in as %s", {self.core.lgd.userdata['displayName']})
self.success.emit()
else:
logger.warning("Failed to login through browser.")

View file

@ -15,6 +15,8 @@ logger = getLogger("ImportLogin")
class ImportLogin(QFrame):
success = pyqtSignal()
changed = pyqtSignal()
# FIXME: Use pathspec instead of duplicated code
if os.name == "nt":
localappdata = os.path.expandvars("%LOCALAPPDATA%")
else:
@ -45,10 +47,6 @@ class ImportLogin(QFrame):
if programdata_path := self.core.egl.programdata_path:
if wine_pfx := programdata_path.split("drive_c")[0]:
self.ui.prefix_combo.addItem(wine_pfx)
self.ui.info_label.setText(
self.tr("Please select the Wine prefix where Epic Games Launcher is installed. ")
+ self.ui.info_label.text()
)
prefixes = self.get_wine_prefixes()
if len(prefixes):
self.ui.prefix_combo.addItems(prefixes)
@ -97,15 +95,15 @@ class ImportLogin(QFrame):
if os.name == "nt":
pass
else:
logger.info(f'Using EGL appdata path at "{self.egl_appdata}"')
logger.info("Using EGL appdata path at %s", {self.egl_appdata})
self.core.egl.appdata_path = self.egl_appdata
try:
if self.core.auth_import():
logger.info(f"Logged in as {self.core.lgd.userdata['displayName']}")
logger.info("Logged in as %s", {self.core.lgd.userdata['displayName']})
self.success.emit()
else:
self.ui.status_label.setText(self.tr("Login failed."))
logger.warning("Failed to import existing session.")
except Exception as e:
self.ui.status_label.setText(self.tr("Login failed. {}").format(str(e)))
logger.warning(f"Failed to import existing session: {e}")
logger.warning("Failed to import existing session: %s", e)

View file

@ -8,7 +8,7 @@ from .downloads import DownloadsTab
from .games import GamesTab
from .settings import SettingsTab
from .settings.debug import DebugSettings
from .shop import Shop
from .store import Shop
from .tab_widgets import MainTabBar, TabButtonWidget
@ -86,9 +86,6 @@ class MainTabWidget(QTabWidget):
if index == self.games_index:
self.games_tab.setCurrentWidget(self.games_tab.games_page)
if not self.args.offline and index == self.store_index:
self.store_tab.load()
def resizeEvent(self, event):
self.tab_bar.setMinimumWidth(self.width())
super(MainTabWidget, self).resizeEvent(event)

View file

@ -72,6 +72,7 @@ class GameInfoTabs(SideTabWidget):
class GameMetadataView(QTreeView, SideTabContents):
def __init__(self, parent=None):
super(GameMetadataView, self).__init__(parent=parent)
self.implements_scrollarea = True
self.setColumnWidth(0, 300)
self.setWordWrap(True)
self.model = QJsonModel()

View file

@ -44,9 +44,8 @@ class GameSettings(DefaultGameSettings, SideTabContents):
parent=self
)
self.ui.launch_settings_layout.insertRow(
int(self.ui.launch_settings_layout.indexOf(self.ui.launch_params)/2) + 1,
QLabel(self.tr("Override executable"), self),
self.override_exe_edit
self.ui.launch_settings_layout.getWidgetPosition(self.ui.launch_params)[0] + 1,
QLabel(self.tr("Override executable"), self), self.override_exe_edit
)
self.ui.game_settings_layout.setAlignment(Qt.AlignTop)

View file

@ -6,7 +6,7 @@ from typing import Tuple, Iterable, List, Union
from PyQt5.QtCore import Qt, QThreadPool, QRunnable, pyqtSlot, pyqtSignal
from PyQt5.QtGui import QShowEvent
from PyQt5.QtWidgets import QGroupBox, QListWidgetItem, QFileDialog, QMessageBox, QFrame, QLabel
from PyQt5.QtWidgets import QGroupBox, QListWidgetItem, QFileDialog, QMessageBox, QFrame, QFormLayout
from legendary.models.egl import EGLManifest
from legendary.models.game import InstalledGame
@ -39,25 +39,27 @@ class EGLSyncGroup(QGroupBox):
save_func=self.egl_path_edit_save_cb,
parent=self,
)
self.ui.egl_path_edit_layout.addWidget(self.egl_path_edit)
self.ui.egl_sync_layout.setWidget(
self.ui.egl_sync_layout.getWidgetPosition(self.ui.egl_path_edit_label)[0],
QFormLayout.FieldRole, self.egl_path_edit
)
self.egl_path_info_label = QLabel(self.tr("Estimated path"), self)
self.egl_path_info = ElideLabel(parent=self)
self.egl_path_info.setProperty("infoLabel", 1)
self.ui.egl_sync_layout.insertRow(
self.ui.egl_sync_layout.indexOf(self.ui.egl_path_edit_label) + 1,
self.egl_path_info_label, self.egl_path_info
self.ui.egl_sync_layout.setWidget(
self.ui.egl_sync_layout.getWidgetPosition(self.ui.egl_path_info_label)[0],
QFormLayout.FieldRole, self.egl_path_info
)
if platform.system() == "Windows":
self.ui.egl_path_edit_label.setEnabled(False)
self.egl_path_edit.setEnabled(False)
self.egl_path_info_label.setEnabled(False)
self.ui.egl_path_info_label.setEnabled(False)
self.egl_path_info.setEnabled(False)
else:
self.egl_path_edit.textChanged.connect(self.egl_path_changed)
if self.core.egl.programdata_path:
self.egl_path_info_label.setEnabled(True)
self.ui.egl_path_info_label.setEnabled(True)
self.egl_path_info.setEnabled(True)
self.ui.egl_sync_check.setChecked(self.core.egl_sync_enabled)

View file

@ -199,7 +199,8 @@ class ImportGroup(QGroupBox):
)
self.path_edit.textChanged.connect(self.path_changed)
self.ui.import_layout.setWidget(
self.ui.import_layout.indexOf(self.ui.path_edit_label), QFormLayout.FieldRole, self.path_edit
self.ui.import_layout.getWidgetPosition(self.ui.path_edit_label)[0],
QFormLayout.FieldRole, self.path_edit
)
self.app_name_edit = IndicatorLineEdit(
@ -209,7 +210,8 @@ class ImportGroup(QGroupBox):
)
self.app_name_edit.textChanged.connect(self.app_name_changed)
self.ui.import_layout.setWidget(
self.ui.import_layout.indexOf(self.ui.app_name_label), QFormLayout.FieldRole, self.app_name_edit
self.ui.import_layout.getWidgetPosition(self.ui.app_name_label)[0],
QFormLayout.FieldRole, self.app_name_edit
)
self.ui.import_folder_check.stateChanged.connect(self.import_folder_changed)

View file

@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QWidget
from rare import __version__, __codename__
from rare.ui.components.tabs.settings.about import Ui_About
from rare.utils.qt_requests import QtRequestManager
from rare.utils.qt_requests import QtRequests
logger = getLogger("About")
@ -34,7 +34,7 @@ class About(QWidget):
self.ui.open_browser.setVisible(False)
self.ui.open_browser.setEnabled(False)
self.manager = QtRequestManager("json")
self.manager = QtRequests(parent=self)
self.manager.get(
"https://api.github.com/repos/RareDevs/Rare/releases/latest",
self.update_available_finished,

View file

@ -1,3 +1,4 @@
from PyQt5.QtGui import QShowEvent, QHideEvent
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
from legendary.core import LegendaryCore
@ -26,7 +27,7 @@ class Shop(QStackedWidget):
self.shop = ShopWidget(cache_dir(), self.core, self.api_core)
self.wishlist_widget = Wishlist(self.api_core)
self.store_tabs = QTabWidget()
self.store_tabs = QTabWidget(parent=self)
self.store_tabs.addTab(self.shop, self.tr("Games"))
self.store_tabs.addTab(self.wishlist_widget, self.tr("Wishlist"))
@ -50,15 +51,23 @@ class Shop(QStackedWidget):
self.api_core.update_wishlist.connect(self.update_wishlist)
self.wishlist_widget.update_wishlist_signal.connect(self.update_wishlist)
def showEvent(self, a0: QShowEvent) -> None:
if a0.spontaneous() or self.init:
return super().showEvent(a0)
self.shop.load()
self.wishlist_widget.update_wishlist()
self.init = True
return super().showEvent(a0)
def hideEvent(self, a0: QHideEvent) -> None:
if a0.spontaneous():
return super().hideEvent(a0)
# TODO: Implement store unloading
return super().hideEvent(a0)
def update_wishlist(self):
self.shop.update_wishlist()
def load(self):
if not self.init:
self.init = True
self.shop.load()
self.wishlist_widget.update_wishlist()
def show_game_info(self, data):
self.info.update_game(data)
self.setCurrentIndex(2)

View file

@ -13,8 +13,8 @@ from PyQt5.QtWidgets import (
QGridLayout,
)
from rare.components.tabs.store.shop_models import ShopGame
from rare.shared import LegendaryCoreSingleton
from rare.components.tabs.shop.shop_models import ShopGame
from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info
from rare.utils.extra_widgets import ImageLabel
from rare.utils.misc import icon

View file

@ -6,7 +6,7 @@ from PyQt5.QtGui import QFont
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from rare.components.tabs.shop.shop_models import ImageUrlModel
from rare.components.tabs.store.shop_models import ImageUrlModel
from rare.ui.components.tabs.store.wishlist_widget import Ui_WishlistWidget
from rare.utils.extra_widgets import ImageLabel
from rare.utils.misc import icon

View file

@ -3,14 +3,14 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QObject
from rare.components.tabs.shop.constants import (
from rare.components.tabs.store.constants import (
wishlist_query,
search_query,
add_to_wishlist_query,
remove_from_wishlist_query,
)
from rare.components.tabs.shop.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequestManager
from rare.components.tabs.store.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequests
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
@ -25,8 +25,8 @@ class ShopApiCore(QObject):
self.language_code: str = lc
self.country_code: str = cc
self.locale = f"{self.language_code}-{self.country_code}"
self.manager = QtRequestManager()
self.auth_manager = QtRequestManager(authorization_token=auth_token)
self.manager = QtRequests(parent=self)
self.auth_manager = QtRequests(token=auth_token, parent=self)
self.browse_active = False
self.next_browse_request = tuple(())
@ -52,6 +52,7 @@ class ShopApiCore(QObject):
def get_wishlist(self, handle_func):
self.auth_manager.post(
graphql_url,
lambda data: self._handle_wishlist(data, handle_func),
{
"query": wishlist_query,
"variables": {
@ -59,7 +60,6 @@ class ShopApiCore(QObject):
"locale": f"{self.language_code}-{self.country_code}",
},
},
lambda data: self._handle_wishlist(data, handle_func),
)
def _handle_wishlist(self, data, handle_func):
@ -95,7 +95,7 @@ class ShopApiCore(QObject):
}
self.manager.post(
graphql_url, payload, lambda data: self._handle_search(data, handle_func)
graphql_url, lambda data: self._handle_search(data, handle_func), payload,
)
def _handle_search(self, data, handle_func):
@ -189,8 +189,8 @@ class ShopApiCore(QObject):
}
self.auth_manager.post(
graphql_url,
payload,
lambda data: self._handle_add_to_wishlist(data, handle_func),
payload,
)
def _handle_add_to_wishlist(self, data, handle_func):
@ -216,8 +216,8 @@ class ShopApiCore(QObject):
}
self.auth_manager.post(
graphql_url,
payload,
lambda data: self._handle_remove_from_wishlist(data, handle_func),
payload,
)
def _handle_remove_from_wishlist(self, data, handle_func):

View file

@ -68,6 +68,10 @@ class ShopWidget(QScrollArea, Ui_ShopWidget):
self.init_filter()
self.search_bar.setHidden(True)
self.filter_gb.setHidden(True)
self.filter_game_gb.setHidden(True)
# self.search_bar.textChanged.connect(self.load_completer)
def load(self):

View file

@ -1,8 +1,8 @@
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QStackedWidget, QMessageBox
from rare.components.tabs.shop import ShopApiCore
from rare.components.tabs.shop.game_widgets import WishlistWidget
from rare.components.tabs.store import ShopApiCore
from rare.components.tabs.store.game_widgets import WishlistWidget
from rare.ui.components.tabs.store.wishlist import Ui_Wishlist
from rare.utils.extra_widgets import WaitingSpinner
from rare.utils.misc import icon

View file

@ -43,22 +43,23 @@ class LegendaryCLI(LegendaryCLIReal):
def unlock_installed(func):
@functools.wraps(func)
def unlock(self, *args, **kwargs):
self.logger.debug("Using unlock decorator")
if not self.core.lgd.lock_installed():
self.logger.debug("Data is locked, trying to forcufully release it")
self.logger.info("Data is locked, trying to forcufully release it")
# self.core.lgd._installed_lock.release(force=True)
try:
ret = func(self, *args, **kwargs)
except Exception as e:
raise e
finally:
self.core.lgd._installed_lock.release(force=True)
self.core.lgd.unlock_installed()
return ret
return unlock
def resolve_aliases(self, name):
return super(LegendaryCLI, self)._resolve_aliases(name)
@unlock_installed
@unlock_installed.__func__
def install_game(self, args: LgndrInstallGameArgs) -> Optional[Tuple[DLManager, AnalysisResult, InstalledGame, Game, bool, Optional[str], ConditionCheckResult]]:
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(args.indirect_status, self.logger)
@ -229,7 +230,7 @@ class LegendaryCLI(LegendaryCLIReal):
return dlm, analysis, igame, game, args.repair_mode, repair_file, res
# Rare: This is currently handled in DownloadThread, this is a trial
@unlock_installed
@unlock_installed.__func__
def install_game_real(self, args: LgndrInstallGameRealArgs, dlm: DLManager, game: Game, igame: InstalledGame) -> LgndrInstallGameRealRet:
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(args.indirect_status, self.logger)
@ -315,7 +316,7 @@ class LegendaryCLI(LegendaryCLIReal):
return ret
@unlock_installed
@unlock_installed.__func__
def install_game_cleanup(self, game: Game, igame: InstalledGame, repair_mode: bool = False, repair_file: str = '') -> None:
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(LgndrIndirectStatus(), self.logger)
@ -386,7 +387,7 @@ class LegendaryCLI(LegendaryCLIReal):
else:
logger.info('Automatic installation not available on Linux.')
@unlock_installed
@unlock_installed.__func__
def uninstall_game(self, args: LgndrUninstallGameArgs) -> None:
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(args.indirect_status, self.logger, logging.WARNING)
@ -562,7 +563,7 @@ class LegendaryCLI(LegendaryCLIReal):
logger.info(f'Run "legendary repair {args.app_name}" to repair your game installation.')
return len(failed), len(missing)
@unlock_installed
@unlock_installed.__func__
def import_game(self, args: LgndrImportGameArgs) -> None:
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(args.indirect_status, self.logger)
@ -667,11 +668,11 @@ class LegendaryCLI(LegendaryCLIReal):
logger.info(f'{"DLC" if game.is_dlc else "Game"} "{game.app_title}" has been imported.')
return
@unlock_installed
@unlock_installed.__func__
def egs_sync(self, args):
return super(LegendaryCLI, self).egs_sync(args)
@unlock_installed
@unlock_installed.__func__
def move(self, args):
# Override logger for the local context to use message as part of the indirect return value
logger = LgndrIndirectLogger(args.indirect_status, self.logger)

View file

@ -17,16 +17,19 @@ from legendary.models.game import Game, InstalledGame
from legendary.models.manifest import ManifestMeta
from rare.lgndr.downloader.mp.manager import DLManager
from rare.lgndr.lfs.lgndry import LGDLFS
from rare.lgndr.glue.exception import LgndrException, LgndrLogHandler
legendary.core.DLManager = DLManager
legendary.core.LGDLFS = LGDLFS
# fmt: off
class LegendaryCore(LegendaryCoreReal):
def __init__(self, override_config=None, timeout=10.0):
super(LegendaryCore, self).__init__(override_config=override_config, timeout=timeout)
def __init__(self, *args, **kwargs):
super(LegendaryCore, self).__init__(*args, **kwargs)
self.log.info("Using Rare's LegendaryCore monkey")
self.handler = LgndrLogHandler(logging.CRITICAL)
self.log.addHandler(self.handler)
@ -34,15 +37,16 @@ class LegendaryCore(LegendaryCoreReal):
def unlock_installed(func):
@functools.wraps(func)
def unlock(self, *args, **kwargs):
self.log.debug("Using unlock decorator")
if not self.lgd.lock_installed():
self.log.debug("Data is locked, trying to forcufully release it")
self.log.info("Data is locked, trying to forcufully release it")
# self.lgd._installed_lock.release(force=True)
try:
ret = func(self, *args, **kwargs)
except Exception as e:
raise e
finally:
self.lgd._installed_lock.release(force=True)
self.lgd.unlock_installed()
return ret
return unlock
@ -94,7 +98,7 @@ class LegendaryCore(LegendaryCoreReal):
finally:
pass
@unlock_installed
@unlock_installed.__func__
def egl_import(self, app_name):
try:
super(LegendaryCore, self).egl_import(app_name)
@ -140,7 +144,7 @@ class LegendaryCore(LegendaryCoreReal):
if delete_files:
delete_folder(os.path.join(igame.install_path, '.egstore'))
@unlock_installed
@unlock_installed.__func__
def egl_export(self, app_name):
try:
super(LegendaryCore, self).egl_export(app_name)
@ -149,6 +153,15 @@ class LegendaryCore(LegendaryCoreReal):
finally:
pass
@unlock_installed.__func__
def egl_sync(self, app_name=''):
try:
super(LegendaryCore, self).egl_sync(app_name)
except LgndrException as ret:
raise ret
finally:
pass
def prepare_overlay_install(self, path=None):
dlm, analysis_result, igame = super(LegendaryCore, self).prepare_overlay_install(path)
# lk: monkeypatch status_q (the queue for download stats)

View file

@ -17,6 +17,10 @@ from rare.lgndr.models.downloading import UIUpdate
# fmt: off
class DLManager(DLManagerReal):
def __init__(self, *args, **kwargs):
super(DLManager, self).__init__(*args, **kwargs)
self.log.info("Using Rare's DLManager monkey")
# Rare: prototype to avoid undefined variable in type checkers
signals_queue: MPQueue

View file

@ -1,16 +1,19 @@
from dataclasses import dataclass, field
from enum import IntEnum
from typing import Callable, List, Optional, Dict
from typing import List, Optional, Dict
from rare.lgndr.glue.monkeys import (
LgndrIndirectStatus,
GetBooleanChoiceProtocol,
SdlPromptProtocol,
VerifyStdoutProtocol,
UiUpdateProtocol,
get_boolean_choice_factory,
sdl_prompt_factory,
verify_stdout_factory,
ui_update_factory,
DLManagerSignals,
)
from rare.lgndr.models.downloading import UIUpdate
"""
@dataclass(kw_only=True)
@ -34,7 +37,7 @@ class LgndrImportGameArgs:
yes: bool = False
# Rare: Extra arguments
indirect_status: LgndrIndirectStatus = field(default_factory=LgndrIndirectStatus)
get_boolean_choice: Callable[[str, bool], bool] = get_boolean_choice_factory(True)
get_boolean_choice: GetBooleanChoiceProtocol = get_boolean_choice_factory(True)
@dataclass
@ -45,8 +48,8 @@ class LgndrUninstallGameArgs:
yes: bool = False
# Rare: Extra arguments
indirect_status: LgndrIndirectStatus = field(default_factory=LgndrIndirectStatus)
get_boolean_choice_main: Callable[[str, bool], bool] = get_boolean_choice_factory(True)
get_boolean_choice_handler: Callable[[str, bool], bool] = get_boolean_choice_factory(True)
get_boolean_choice_main: GetBooleanChoiceProtocol = get_boolean_choice_factory(True)
get_boolean_choice_handler: GetBooleanChoiceProtocol = get_boolean_choice_factory(True)
@dataclass
@ -54,7 +57,7 @@ class LgndrVerifyGameArgs:
app_name: str
# Rare: Extra arguments
indirect_status: LgndrIndirectStatus = field(default_factory=LgndrIndirectStatus)
verify_stdout: Callable[[int, int, float, float], None] = verify_stdout_factory(None)
verify_stdout: VerifyStdoutProtocol = verify_stdout_factory(None)
@dataclass
@ -95,11 +98,11 @@ class LgndrInstallGameArgs:
yes: bool = True
# Rare: Extra arguments
indirect_status: LgndrIndirectStatus = field(default_factory=LgndrIndirectStatus)
get_boolean_choice: Callable[[str, bool], bool] = get_boolean_choice_factory(True)
verify_stdout: Callable[[int, int, float, float], None] = verify_stdout_factory(None)
get_boolean_choice: GetBooleanChoiceProtocol = get_boolean_choice_factory(True)
verify_stdout: VerifyStdoutProtocol = verify_stdout_factory(None)
def __post_init__(self):
self.sdl_prompt: Callable[[Dict, str], List[str]] = sdl_prompt_factory(self.install_tag)
self.sdl_prompt: SdlPromptProtocol = sdl_prompt_factory(self.install_tag)
@dataclass
@ -117,7 +120,7 @@ class LgndrInstallGameRealArgs:
# Rare: Extra arguments
install_prereqs: bool = False
indirect_status: LgndrIndirectStatus = field(default_factory=LgndrIndirectStatus)
ui_update: Callable[[UIUpdate], None] = ui_update_factory(None)
ui_update: UiUpdateProtocol = ui_update_factory(None)
dlm_signals: DLManagerSignals = field(default_factory=DLManagerSignals)

View file

@ -1,20 +1,30 @@
import logging
from dataclasses import dataclass
from typing import Callable, List, Optional, Dict
from typing import Protocol, List, Optional, Dict
from rare.lgndr.models.downloading import UIUpdate
logger = logging.getLogger("LgndrMonkeys")
def get_boolean_choice_factory(value: bool = True) -> Callable[[str, bool], bool]:
def get_boolean_choice(prompt: str, default: bool) -> bool:
class GetBooleanChoiceProtocol(Protocol):
def __call__(self, prompt: str, default: bool = ...) -> bool:
...
def get_boolean_choice_factory(value: bool = True) -> GetBooleanChoiceProtocol:
def get_boolean_choice(prompt: str, default: bool = value) -> bool:
logger.debug("get_boolean_choice: %s, default: %s, choice: %s", prompt, default, value)
return value
return get_boolean_choice
def sdl_prompt_factory(install_tag: Optional[List[str]] = None) -> Callable[[Dict, str], List[str]]:
class SdlPromptProtocol(Protocol):
def __call__(self, sdl_data: Dict, title: str) -> List[str]:
...
def sdl_prompt_factory(install_tag: Optional[List[str]] = None) -> SdlPromptProtocol:
def sdl_prompt(sdl_data: Dict, title: str) -> List[str]:
logger.debug("sdl_prompt: %s", title)
for key in sdl_data.keys():
@ -25,9 +35,12 @@ def sdl_prompt_factory(install_tag: Optional[List[str]] = None) -> Callable[[Dic
return sdl_prompt
def verify_stdout_factory(
callback: Callable[[int, int, float, float], None] = None
) -> Callable[[int, int, float, float], None]:
class VerifyStdoutProtocol(Protocol):
def __call__(self, a0: int, a1: int, a2: float, a3: float) -> None:
...
def verify_stdout_factory(callback: VerifyStdoutProtocol = None) -> VerifyStdoutProtocol:
def verify_stdout(a0: int, a1: int, a2: float, a3: float) -> None:
if callback is not None and callable(callback):
callback(a0, a1, a2, a3)
@ -36,7 +49,12 @@ def verify_stdout_factory(
return verify_stdout
def ui_update_factory(callback: Callable[[UIUpdate], None] = None) -> Callable[[UIUpdate], None]:
class UiUpdateProtocol(Protocol):
def __call__(self, status: UIUpdate) -> None:
...
def ui_update_factory(callback: UiUpdateProtocol = None) -> UiUpdateProtocol:
def ui_update(status: UIUpdate) -> None:
if callback is not None and callable(callback):
callback(status)

View file

15
rare/lgndr/lfs/lgndry.py Normal file
View file

@ -0,0 +1,15 @@
import os
from filelock import FileLock
from legendary.lfs.lgndry import LGDLFS as LGDLFSReal
class LGDLFS(LGDLFSReal):
def __init__(self, *args, **kwargs):
super(LGDLFS, self).__init__(*args, **kwargs)
self.log.info("Using Rare's LGDLFS monkey")
# Rare: Default FileLock in Python 3.11 is thread-local, so replace it with a non-local verison
self._installed_lock = FileLock(os.path.join(self.path, 'installed.json') + '.lock', thread_local=False)
def unlock_installed(self):
self._installed_lock.release(force=True)

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/install_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_InstallDialog(object):
def setupUi(self, InstallDialog):
InstallDialog.setObjectName("InstallDialog")
InstallDialog.resize(324, 232)
InstallDialog.resize(272, 238)
InstallDialog.setWindowTitle("InstallDialog")
self.install_dialog_layout = QtWidgets.QFormLayout(InstallDialog)
self.install_dialog_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
@ -25,9 +25,6 @@ class Ui_InstallDialog(object):
self.install_dir_label = QtWidgets.QLabel(InstallDialog)
self.install_dir_label.setObjectName("install_dir_label")
self.install_dialog_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.install_dir_label)
self.install_dir_layout = QtWidgets.QHBoxLayout()
self.install_dir_layout.setObjectName("install_dir_layout")
self.install_dialog_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.install_dir_layout)
self.platform_label = QtWidgets.QLabel(InstallDialog)
self.platform_label.setObjectName("platform_label")
self.install_dialog_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.platform_label)

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>232</height>
<width>272</width>
<height>238</height>
</rect>
</property>
<property name="windowTitle">
@ -31,9 +31,6 @@
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="install_dir_layout"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="platform_label">
<property name="text">

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/launch_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -36,7 +36,7 @@ class Ui_LaunchDialog(object):
def retranslateUi(self, LaunchDialog):
_translate = QtCore.QCoreApplication.translate
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching Rare"))
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching - Rare"))
self.title_label.setText(_translate("LaunchDialog", "<h2>Launching Rare</h2>"))

View file

@ -29,7 +29,7 @@
</size>
</property>
<property name="windowTitle">
<string>Launching Rare</string>
<string>Launching - Rare</string>
</property>
<layout class="QVBoxLayout" name="launch_layout">
<item>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/login/browser_login.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,16 +14,35 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
BrowserLogin.resize(182, 210)
BrowserLogin.resize(320, 243)
BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.browser_layout.setObjectName("browser_layout")
self.main_layout = QtWidgets.QVBoxLayout(BrowserLogin)
self.main_layout.setObjectName("main_layout")
self.title_label = QtWidgets.QLabel(BrowserLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.title_label.sizePolicy().hasHeightForWidth())
self.title_label.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.main_layout.addWidget(self.title_label)
self.form_layout = QtWidgets.QFormLayout()
self.form_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.form_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
self.form_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.form_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.form_layout.setObjectName("form_layout")
self.open_button = QtWidgets.QPushButton(BrowserLogin)
self.open_button.setObjectName("open_button")
self.form_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.open_button)
self.link_layout = QtWidgets.QHBoxLayout()
self.link_layout.setObjectName("link_layout")
self.link_text = QtWidgets.QLineEdit(BrowserLogin)
self.link_text.setText("")
self.link_text.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.link_text.setReadOnly(True)
self.link_text.setPlaceholderText("")
self.link_text.setObjectName("link_text")
@ -32,47 +51,40 @@ class Ui_BrowserLogin(object):
self.copy_button.setText("")
self.copy_button.setObjectName("copy_button")
self.link_layout.addWidget(self.copy_button)
self.browser_layout.addLayout(self.link_layout, 2, 0, 1, 2)
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.browser_layout.addItem(spacerItem, 4, 0, 1, 2)
self.open_button = QtWidgets.QPushButton(BrowserLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.open_button.sizePolicy().hasHeightForWidth())
self.open_button.setSizePolicy(sizePolicy)
self.open_button.setObjectName("open_button")
self.browser_layout.addWidget(self.open_button, 1, 0, 1, 1)
self.sid_layout = QtWidgets.QHBoxLayout()
self.sid_layout.setObjectName("sid_layout")
self.browser_layout.addLayout(self.sid_layout, 1, 1, 1, 1)
self.title_label = QtWidgets.QLabel(BrowserLogin)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.browser_layout.addWidget(self.title_label, 0, 0, 1, 2)
self.info_label = QtWidgets.QLabel(BrowserLogin)
self.link_layout.setStretch(0, 1)
self.form_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.link_layout)
self.sid_label = QtWidgets.QLabel(BrowserLogin)
self.sid_label.setText("authorizationCode")
self.sid_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.sid_label.setObjectName("sid_label")
self.form_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.sid_label)
self.status_label = QtWidgets.QLabel(BrowserLogin)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
self.status_label.setFont(font)
self.status_label.setText("")
self.status_label.setObjectName("status_label")
self.form_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.status_label)
self.main_layout.addLayout(self.form_layout)
spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.main_layout.addItem(spacerItem)
self.info_label = QtWidgets.QLabel(BrowserLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.info_label.sizePolicy().hasHeightForWidth())
self.info_label.setSizePolicy(sizePolicy)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
self.browser_layout.addWidget(self.info_label, 5, 0, 1, 2)
self.status_label = QtWidgets.QLabel(BrowserLogin)
self.status_label.setText("")
self.status_label.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
self.status_label.setObjectName("status_label")
self.browser_layout.addWidget(self.status_label, 3, 0, 1, 2)
self.main_layout.addWidget(self.info_label)
self.retranslateUi(BrowserLogin)
def retranslateUi(self, BrowserLogin):
_translate = QtCore.QCoreApplication.translate
self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
self.title_label.setText(_translate("BrowserLogin", "Login through browser"))
self.info_label.setText(_translate("BrowserLogin", "Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the <b><code>authorizationCode</code></b> in the input above."))
self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
self.info_label.setText(_translate("BrowserLogin", "<i>Click the <strong>Open Browser</strong> button to open the login page in your web browser or copy the link and paste it in any web browser. After logging in using the browser, copy the text in the quotes after </i><code><b>authorizationCode</b></code><i> in the same line into the empty input above.<br><br><strong>DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!</strong></i>"))
if __name__ == "__main__":

View file

@ -6,75 +6,22 @@
<rect>
<x>0</x>
<y>0</y>
<width>182</width>
<height>210</height>
<width>320</width>
<height>243</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">BrowserLogin</string>
</property>
<layout class="QGridLayout" name="browser_layout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="link_layout">
<item>
<widget class="QLineEdit" name="link_text">
<property name="text">
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copy_button">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<spacer name="vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="open_button">
<layout class="QVBoxLayout" name="main_layout">
<item>
<widget class="QLabel" name="title_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Open Browser</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="sid_layout"/>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="title_label">
<property name="font">
<font>
<weight>75</weight>
@ -86,31 +33,104 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item>
<layout class="QFormLayout" name="form_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="open_button">
<property name="text">
<string>Open Browser</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="link_layout" stretch="1,0">
<item>
<widget class="QLineEdit" name="link_text">
<property name="text">
<string notr="true"/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copy_button">
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sid_label">
<property name="text">
<string notr="true">authorizationCode</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="status_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="vscaper">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Click the button to open the login page in a browser or copy the link and paste it in a browser. After logging in, copy the &lt;b&gt;&lt;code&gt;authorizationCode&lt;/code&gt;&lt;/b&gt; in the input above.</string>
<string>&lt;i&gt;Click the &lt;strong&gt;Open Browser&lt;/strong&gt; button to open the login page in your web browser or copy the link and paste it in any web browser. After logging in using the browser, copy the text in the quotes after &lt;/i&gt;&lt;code&gt;&lt;b&gt;authorizationCode&lt;/b&gt;&lt;/code&gt;&lt;i&gt; in the same line into the empty input above.&lt;br&gt;&lt;br&gt;&lt;strong&gt;DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!&lt;/strong&gt;&lt;/i&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="status_label">
<property name="text">
<string notr="true"/>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/login/import_login.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,24 +14,35 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ImportLogin(object):
def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin")
ImportLogin.resize(242, 120)
ImportLogin.resize(233, 156)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ImportLogin.sizePolicy().hasHeightForWidth())
ImportLogin.setSizePolicy(sizePolicy)
ImportLogin.setWindowTitle("ImportLogin")
self.import_layout = QtWidgets.QGridLayout(ImportLogin)
self.import_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.import_layout.setObjectName("import_layout")
self.prefix_label = QtWidgets.QLabel(ImportLogin)
self.prefix_label.setObjectName("prefix_label")
self.import_layout.addWidget(self.prefix_label, 1, 0, 1, 1)
self.main_layout = QtWidgets.QVBoxLayout(ImportLogin)
self.main_layout.setObjectName("main_layout")
self.title_label = QtWidgets.QLabel(ImportLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.title_label.sizePolicy().hasHeightForWidth())
self.title_label.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
self.import_layout.addWidget(self.title_label, 0, 0, 1, 3)
self.prefix_tool = QtWidgets.QToolButton(ImportLogin)
self.prefix_tool.setObjectName("prefix_tool")
self.import_layout.addWidget(self.prefix_tool, 1, 2, 1, 1)
self.main_layout.addWidget(self.title_label)
self.form_layout = QtWidgets.QFormLayout()
self.form_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
self.form_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
self.form_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.form_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.form_layout.setObjectName("form_layout")
self.prefix_layout = QtWidgets.QHBoxLayout()
self.prefix_layout.setObjectName("prefix_layout")
self.prefix_combo = QtWidgets.QComboBox(ImportLogin)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@ -40,33 +51,43 @@ class Ui_ImportLogin(object):
self.prefix_combo.setSizePolicy(sizePolicy)
self.prefix_combo.setEditable(True)
self.prefix_combo.setObjectName("prefix_combo")
self.import_layout.addWidget(self.prefix_combo, 1, 1, 1, 1)
self.prefix_layout.addWidget(self.prefix_combo)
self.prefix_tool = QtWidgets.QToolButton(ImportLogin)
self.prefix_tool.setObjectName("prefix_tool")
self.prefix_layout.addWidget(self.prefix_tool)
self.prefix_layout.setStretch(0, 1)
self.form_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.prefix_layout)
self.prefix_label = QtWidgets.QLabel(ImportLogin)
self.prefix_label.setObjectName("prefix_label")
self.form_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.prefix_label)
self.status_label = QtWidgets.QLabel(ImportLogin)
font = QtGui.QFont()
font.setItalic(True)
self.status_label.setFont(font)
self.status_label.setText("")
self.status_label.setWordWrap(True)
self.status_label.setObjectName("status_label")
self.import_layout.addWidget(self.status_label, 2, 1, 1, 2)
self.form_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.status_label)
self.main_layout.addLayout(self.form_layout)
spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.main_layout.addItem(spacerItem)
self.info_label = QtWidgets.QLabel(ImportLogin)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.info_label.sizePolicy().hasHeightForWidth())
self.info_label.setSizePolicy(sizePolicy)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
self.import_layout.addWidget(self.info_label, 4, 0, 1, 3)
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.import_layout.addItem(spacerItem, 3, 0, 1, 3)
self.main_layout.addWidget(self.info_label)
self.retranslateUi(ImportLogin)
def retranslateUi(self, ImportLogin):
_translate = QtCore.QCoreApplication.translate
self.prefix_label.setText(_translate("ImportLogin", "Select prefix"))
self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL"))
self.prefix_tool.setText(_translate("ImportLogin", "Browse"))
self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process."))
self.prefix_label.setText(_translate("ImportLogin", "Select prefix"))
self.info_label.setText(_translate("ImportLogin", "<i>Please select the Wine prefix where Epic Games Launcher is installed. You will get logged out from EGL in the process.</i>"))
if __name__ == "__main__":

View file

@ -6,26 +6,28 @@
<rect>
<x>0</x>
<y>0</y>
<width>242</width>
<height>120</height>
<width>233</width>
<height>156</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">ImportLogin</string>
</property>
<layout class="QGridLayout" name="import_layout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="prefix_label">
<property name="text">
<string>Select prefix</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<layout class="QVBoxLayout" name="main_layout">
<item>
<widget class="QLabel" name="title_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -37,69 +39,94 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="prefix_tool">
<property name="text">
<string>Browse</string>
<item>
<layout class="QFormLayout" name="form_layout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
</widget>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="1">
<layout class="QHBoxLayout" name="prefix_layout" stretch="1,0">
<item>
<widget class="QComboBox" name="prefix_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="prefix_tool">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="prefix_label">
<property name="text">
<string>Select prefix</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="status_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="prefix_combo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QLabel" name="status_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="info_label">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>You will get logged out from EGL in the process.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<item>
<spacer name="vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="info_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;i&gt;Please select the Wine prefix where Epic Games Launcher is installed. You will get logged out from EGL in the process.&lt;/i&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/login/landing_page.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,49 +14,58 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LandingPage(object):
def setupUi(self, LandingPage):
LandingPage.setObjectName("LandingPage")
LandingPage.resize(311, 87)
LandingPage.resize(293, 78)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(LandingPage.sizePolicy().hasHeightForWidth())
LandingPage.setSizePolicy(sizePolicy)
LandingPage.setWindowTitle("LandingPage")
self.landing_layout = QtWidgets.QGridLayout(LandingPage)
self.landing_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.landing_layout.setObjectName("landing_layout")
self.main_layout = QtWidgets.QFormLayout(LandingPage)
self.main_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
self.main_layout.setLabelAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.main_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.main_layout.setObjectName("main_layout")
self.login_label = QtWidgets.QLabel(LandingPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_label.sizePolicy().hasHeightForWidth())
self.login_label.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.login_label.setFont(font)
self.login_label.setObjectName("login_label")
self.landing_layout.addWidget(self.login_label, 0, 0, 1, 3)
self.main_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.login_label)
self.login_browser_radio = QtWidgets.QRadioButton(LandingPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_browser_radio.sizePolicy().hasHeightForWidth())
self.login_browser_radio.setSizePolicy(sizePolicy)
self.login_browser_radio.setObjectName("login_browser_radio")
self.landing_layout.addWidget(self.login_browser_radio, 1, 0, 1, 1)
self.login_import_radio = QtWidgets.QRadioButton(LandingPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_import_radio.sizePolicy().hasHeightForWidth())
self.login_import_radio.setSizePolicy(sizePolicy)
self.login_import_radio.setObjectName("login_import_radio")
self.landing_layout.addWidget(self.login_import_radio, 2, 0, 1, 1)
self.main_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.login_browser_radio)
self.login_browser_label = QtWidgets.QLabel(LandingPage)
font = QtGui.QFont()
font.setItalic(True)
self.login_browser_label.setFont(font)
self.login_browser_label.setObjectName("login_browser_label")
self.landing_layout.addWidget(self.login_browser_label, 1, 1, 1, 2)
self.main_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.login_browser_label)
self.login_import_radio = QtWidgets.QRadioButton(LandingPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.login_import_radio.sizePolicy().hasHeightForWidth())
self.login_import_radio.setSizePolicy(sizePolicy)
self.login_import_radio.setObjectName("login_import_radio")
self.main_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.login_import_radio)
self.login_import_label = QtWidgets.QLabel(LandingPage)
font = QtGui.QFont()
font.setItalic(True)
self.login_import_label.setFont(font)
self.login_import_label.setObjectName("login_import_label")
self.landing_layout.addWidget(self.login_import_label, 2, 1, 1, 2)
self.landing_layout.setColumnStretch(2, 1)
self.landing_layout.setRowStretch(1, 1)
self.landing_layout.setRowStretch(2, 1)
self.main_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.login_import_label)
self.retranslateUi(LandingPage)
@ -64,8 +73,8 @@ class Ui_LandingPage(object):
_translate = QtCore.QCoreApplication.translate
self.login_label.setText(_translate("LandingPage", "Select login method"))
self.login_browser_radio.setText(_translate("LandingPage", "Browser"))
self.login_import_radio.setText(_translate("LandingPage", "Import"))
self.login_browser_label.setText(_translate("LandingPage", "Login using a browser."))
self.login_import_radio.setText(_translate("LandingPage", "Import"))
self.login_import_label.setText(_translate("LandingPage", "Import from Epic Games Launcher"))

View file

@ -6,19 +6,37 @@
<rect>
<x>0</x>
<y>0</y>
<width>311</width>
<height>87</height>
<width>293</width>
<height>78</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">LandingPage</string>
</property>
<layout class="QGridLayout" name="landing_layout" rowstretch="0,1,1" columnstretch="0,0,1">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<layout class="QFormLayout" name="main_layout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0" colspan="3">
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="formAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="login_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
@ -33,7 +51,7 @@
<item row="1" column="0">
<widget class="QRadioButton" name="login_browser_radio">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -43,20 +61,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="login_import_radio">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<item row="1" column="1">
<widget class="QLabel" name="login_browser_label">
<property name="font">
<font>
@ -68,7 +73,20 @@
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<item row="2" column="0">
<widget class="QRadioButton" name="login_import_radio">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="login_import_label">
<property name="font">
<font>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/dialogs/login/login_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LoginDialog(object):
def setupUi(self, LoginDialog):
LoginDialog.setObjectName("LoginDialog")
LoginDialog.resize(324, 132)
LoginDialog.resize(241, 128)
self.login_layout = QtWidgets.QVBoxLayout(LoginDialog)
self.login_layout.setObjectName("login_layout")
spacerItem = QtWidgets.QSpacerItem(0, 17, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
@ -46,7 +46,7 @@ class Ui_LoginDialog(object):
def retranslateUi(self, LoginDialog):
_translate = QtCore.QCoreApplication.translate
LoginDialog.setWindowTitle(_translate("LoginDialog", "Rare Login"))
LoginDialog.setWindowTitle(_translate("LoginDialog", "Login - Rare"))
self.welcome_label.setText(_translate("LoginDialog", "<h1>Welcome to Rare</h1>"))
self.exit_button.setText(_translate("LoginDialog", "Exit"))
self.back_button.setText(_translate("LoginDialog", "Back"))

View file

@ -6,12 +6,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>324</width>
<height>132</height>
<width>241</width>
<height>128</height>
</rect>
</property>
<property name="windowTitle">
<string>Rare Login</string>
<string>Login - Rare</string>
</property>
<layout class="QVBoxLayout" name="login_layout" stretch="0,0,0,0,0">
<item>

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/integrations/egl_sync_group.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
# Created by: PyQt5 UI code generator 5.15.10
#
# 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.
@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_EGLSyncGroup(object):
def setupUi(self, EGLSyncGroup):
EGLSyncGroup.setObjectName("EGLSyncGroup")
EGLSyncGroup.resize(217, 88)
EGLSyncGroup.resize(424, 106)
EGLSyncGroup.setWindowTitle("EGLSyncGroup")
EGLSyncGroup.setCheckable(False)
EGLSyncGroup.setChecked(False)
@ -24,19 +24,21 @@ class Ui_EGLSyncGroup(object):
self.egl_path_edit_label = QtWidgets.QLabel(EGLSyncGroup)
self.egl_path_edit_label.setObjectName("egl_path_edit_label")
self.egl_sync_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.egl_path_edit_label)
self.egl_path_edit_layout = QtWidgets.QHBoxLayout()
self.egl_path_edit_layout.setObjectName("egl_path_edit_layout")
self.egl_sync_layout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.egl_path_edit_layout)
self.egl_sync_check_label = QtWidgets.QLabel(EGLSyncGroup)
self.egl_sync_check_label.setObjectName("egl_sync_check_label")
self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.egl_sync_check_label)
self.egl_sync_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.egl_sync_check_label)
self.egl_sync_check = QtWidgets.QCheckBox(EGLSyncGroup)
self.egl_sync_check.setText("")
font = QtGui.QFont()
font.setItalic(True)
self.egl_sync_check.setFont(font)
self.egl_sync_check.setObjectName("egl_sync_check")
self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.egl_sync_check)
self.egl_sync_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.egl_sync_check)
self.import_export_layout = QtWidgets.QVBoxLayout()
self.import_export_layout.setObjectName("import_export_layout")
self.egl_sync_layout.setLayout(2, QtWidgets.QFormLayout.SpanningRole, self.import_export_layout)
self.egl_sync_layout.setLayout(3, QtWidgets.QFormLayout.SpanningRole, self.import_export_layout)
self.egl_path_info_label = QtWidgets.QLabel(EGLSyncGroup)
self.egl_path_info_label.setObjectName("egl_path_info_label")
self.egl_sync_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.egl_path_info_label)
self.retranslateUi(EGLSyncGroup)
@ -45,6 +47,8 @@ class Ui_EGLSyncGroup(object):
EGLSyncGroup.setTitle(_translate("EGLSyncGroup", "Sync with Epic Games Launcher"))
self.egl_path_edit_label.setText(_translate("EGLSyncGroup", "Prefix/Manifest path"))
self.egl_sync_check_label.setText(_translate("EGLSyncGroup", "Enable automatic sync"))
self.egl_sync_check.setText(_translate("EGLSyncGroup", "This will immediately synchronize with EGL"))
self.egl_path_info_label.setText(_translate("EGLSyncGroup", "Estimated path"))
if __name__ == "__main__":

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>217</width>
<height>88</height>
<width>424</width>
<height>106</height>
</rect>
</property>
<property name="windowTitle">
@ -33,26 +33,35 @@
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="egl_path_edit_layout"/>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="egl_sync_check_label">
<property name="text">
<string>Enable automatic sync</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QCheckBox" name="egl_sync_check">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string notr="true"/>
<string>This will immediately synchronize with EGL</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<layout class="QVBoxLayout" name="import_export_layout"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="egl_path_info_label">
<property name="text">
<string>Estimated path</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View file

@ -1,4 +1,3 @@
import os
from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal
@ -14,8 +13,8 @@ from PyQt5.QtWidgets import (
)
from rare.utils.misc import icon as qta_icon
from rare.utils.paths import tmp_dir
from rare.utils.qt_requests import QtRequestManager
from rare.utils.paths import cache_dir
from rare.utils.qt_requests import QtRequests
logger = getLogger("ExtraWidgets")
@ -81,8 +80,10 @@ class ImageLabel(QLabel):
def __init__(self, parent=None):
super(ImageLabel, self).__init__(parent=parent)
self.path = tmp_dir()
self.manager = QtRequestManager("bytes")
self.manager = QtRequests(
cache=str(cache_dir().joinpath("store")),
parent=self
)
def update_image(self, url, name="", size: tuple = (240, 320)):
self.setFixedSize(*size)
@ -95,11 +96,7 @@ class ImageLabel(QLabel):
else:
name_extension = "tall"
self.name = f"{self.name}_{name_extension}.png"
if not os.path.exists(os.path.join(self.path, self.name)):
self.manager.get(url, self.image_ready)
# self.request.finished.connect(self.image_ready)
else:
self.show_image()
self.manager.get(url, self.image_ready)
def image_ready(self, data):
try:
@ -115,17 +112,9 @@ class ImageLabel(QLabel):
transformMode=Qt.SmoothTransformation,
)
image.save(os.path.join(self.path, self.name))
pixmap = QPixmap().fromImage(image)
self.setPixmap(pixmap)
def show_image(self):
self.image = QPixmap(os.path.join(self.path, self.name)).scaled(
*self.img_size, transformMode=Qt.SmoothTransformation
)
self.setPixmap(self.image)
class ButtonLineEdit(QLineEdit):
buttonClicked = pyqtSignal()

View file

@ -1,109 +1,139 @@
import json
from dataclasses import dataclass
from dataclasses import dataclass, field
from email.message import Message
from logging import getLogger
from typing import Callable
from typing import Callable, Dict, TypeVar, List, Tuple
from typing import Union
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QJsonParseError, QJsonDocument
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
import orjson
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QUrlQuery, pyqtSlot
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QNetworkDiskCache
logger = getLogger("QtRequests")
class QtRequestManager(QObject):
data_ready = pyqtSignal(object)
request = None
request_active = None
def __init__(self, type: str = "json", authorization_token: str = None):
super(QtRequestManager, self).__init__()
self.manager = QNetworkAccessManager()
self.type = type
self.authorization_token = authorization_token
self.request_queue = []
def post(self, url: str, payload: dict, handle_func):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
self.request_active = RequestQueueItem(handle_func=handle_func)
payload = json.dumps(payload).encode("utf-8")
request.setHeader(
QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
)
if self.authorization_token is not None:
request.setRawHeader(
b"Authorization", self.authorization_token.encode()
)
self.request = self.manager.post(request, payload)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(
RequestQueueItem(
method="post", url=url, payload=payload, handle_func=handle_func
)
)
def get(self, url: str, handle_func: Callable[[Union[dict, bytes]], None]):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(
QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
)
self.request_active = RequestQueueItem(handle_func=handle_func)
self.request = self.manager.get(request)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(
RequestQueueItem(method="get", url=url, handle_func=handle_func)
)
def prepare_data(self):
# self.request_active = False
data = {} if self.type == "json" else b""
if self.request:
try:
if self.request.error() == QNetworkReply.NoError:
if self.type == "json":
error = QJsonParseError()
json_data = QJsonDocument.fromJson(
self.request.readAll().data(), error
)
if QJsonParseError.NoError == error.error:
data = json.loads(json_data.toJson().data().decode())
else:
logger.error(error.errorString())
else:
data = self.request.readAll().data()
except RuntimeError as e:
logger.error(str(e))
self.request_active.handle_func(data)
self.request.deleteLater()
self.request_active = None
if self.request_queue:
if self.request_queue[0].method == "post":
self.post(
self.request_queue[0].url,
self.request_queue[0].payload,
self.request_queue[0].handle_func,
)
else:
self.get(self.request_queue[0].url, self.request_queue[0].handle_func)
self.request_queue.pop(0)
REQUEST_LIMIT = 8
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36"
RequestHandler = TypeVar("RequestHandler", bound=Callable[[Union[Dict, bytes]], None])
@dataclass
class RequestQueueItem:
method: str = None
url: str = None
handle_func: Callable[[Union[dict, bytes]], None] = None
payload: dict = None
url: QUrl = None
payload: Dict = field(default_factory=dict)
params: Dict = field(default_factory=dict)
handlers: List[RequestHandler] = field(default_factory=list)
def __eq__(self, other):
return self.method == other.method and self.url == other.url
class QtRequests(QObject):
data_ready = pyqtSignal(object)
def __init__(self, cache: str = None, token: str = None, parent=None):
super(QtRequests, self).__init__(parent=parent)
self.log = getLogger(f"{type(self).__name__}_{type(parent).__name__}")
self.manager = QNetworkAccessManager(self)
self.manager.finished.connect(self.__on_finished)
self.manager.finished.connect(self.__process_next)
self.cache = None
if cache is not None:
self.log.debug("Using cache dir %s", cache)
self.cache = QNetworkDiskCache(self)
self.cache.setCacheDirectory(cache)
self.manager.setCache(self.cache)
if token is not None:
self.log.debug("Manager is authorized")
self.token = token
self.__pending_requests = []
self.__active_requests = {}
@staticmethod
def __prepare_query(url, params) -> QUrl:
url = QUrl(url)
query = QUrlQuery(url)
for k, v in params.items():
query.addQueryItem(str(k), str(v))
url.setQuery(query)
return url
def __prepare_request(self, item: RequestQueueItem) -> QNetworkRequest:
request = QNetworkRequest(item.url)
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json;charset=UTF-8")
request.setHeader(QNetworkRequest.UserAgentHeader, USER_AGENT)
request.setAttribute(QNetworkRequest.RedirectPolicyAttribute, QNetworkRequest.NoLessSafeRedirectPolicy)
if self.cache is not None:
request.setAttribute(QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.PreferCache)
if self.token is not None:
request.setRawHeader(b"Authorization", self.token.encode())
return request
def __post(self, item: RequestQueueItem):
request = self.__prepare_request(item)
payload = orjson.dumps(item.payload) # pylint: disable=maybe-no-member
reply = self.manager.post(request, payload)
reply.errorOccurred.connect(self.__on_error)
self.__active_requests[reply] = item
def post(self, url: str, handler: RequestHandler, payload: dict):
item = RequestQueueItem(method="post", url=QUrl(url), payload=payload, handlers=[handler])
if len(self.__active_requests) < REQUEST_LIMIT:
self.__post(item)
else:
self.__pending_requests.append(item)
def __get(self, item: RequestQueueItem):
request = self.__prepare_request(item)
reply = self.manager.get(request)
reply.errorOccurred.connect(self.__on_error)
self.__active_requests[reply] = item
def get(self, url: str, handler: RequestHandler, payload: Dict = None, params: Dict = None):
url = self.__prepare_query(url, params) if params is not None else QUrl(url)
item = RequestQueueItem(method="get", url=url, payload=payload, handlers=[handler])
if len(self.__active_requests) < REQUEST_LIMIT:
self.__get(item)
else:
self.__pending_requests.append(item)
def __on_error(self, error: QNetworkReply.NetworkError) -> None:
self.log.error(error)
@staticmethod
def __parse_content_type(header) -> Tuple[str, str]:
# lk: this looks weird but `cgi` is deprecated, PEP 594 suggests this way of parsing MIME
m = Message()
m['content-type'] = header
return m.get_content_type(), m.get_content_charset()
def __process_next(self):
if self.__pending_requests:
item = self.__pending_requests.pop(0)
if item.method == "post":
self.__post(item)
elif item.method == "get":
self.__get(item)
else:
raise NotImplementedError
@pyqtSlot(QNetworkReply)
def __on_finished(self, reply: QNetworkReply):
item = self.__active_requests.pop(reply, None)
if item is None:
self.log.error("QNetworkReply: %s without associated item", reply.url().toString())
reply.deleteLater()
return
if reply.error():
self.log.error(reply.errorString())
else:
mimetype, charset = self.__parse_content_type(reply.header(QNetworkRequest.ContentTypeHeader))
maintype, subtype = mimetype.split("/")
bin_data = reply.readAll().data()
if mimetype == "application/json":
data = orjson.loads(bin_data)
elif maintype == "image":
data = bin_data
else:
data = None
for handler in item.handlers:
handler(data)
reply.deleteLater()

View file

@ -137,18 +137,11 @@ class IndicatorLineEdit(QWidget):
self.line_edit.setObjectName(f"{type(self).__name__}Edit")
self.line_edit.setPlaceholderText(placeholder if placeholder else self.tr("Default"))
self.line_edit.setSizePolicy(horiz_policy, QSizePolicy.Fixed)
# Add hint_label to line_edit
self.line_edit.setLayout(QHBoxLayout())
self.hint_label = QLabel()
self.hint_label.setObjectName(f"{type(self).__name__}Label")
self.hint_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.line_edit.layout().setContentsMargins(0, 0, 10, 0)
self.line_edit.layout().addWidget(self.hint_label)
# Add completer
self.setCompleter(completer)
layout.addWidget(self.line_edit)
if edit_func is not None:
self.indicator_label = QLabel()
self.indicator_label = QLabel(self)
self.indicator_label.setPixmap(qta_icon("ei.info-circle", color="gray").pixmap(16, 16))
self.indicator_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
layout.addWidget(self.indicator_label)
@ -188,10 +181,6 @@ class IndicatorLineEdit(QWidget):
def setText(self, text: str):
self.line_edit.setText(text)
def setHintText(self, text: str):
self.hint_label.setFrameRect(self.line_edit.rect())
self.hint_label.setText(text)
def setCompleter(self, completer: Optional[QCompleter]):
if old := self.line_edit.completer():
old.deleteLater()

View file

@ -18,7 +18,7 @@ from PyQt5.QtWidgets import (
QTabBar,
QTabWidget,
QVBoxLayout,
QScrollArea, QLayout,
QScrollArea, QLayout, QSizePolicy,
)
from rare.utils.misc import icon as qta_icon
@ -100,13 +100,14 @@ class SideTabContainer(QWidget):
if not hasattr(widget, "implements_scrollarea") or not widget.implements_scrollarea:
self.scrollarea = QScrollArea(self)
self.scrollarea.setWidgetResizable(True)
self.scrollarea.setSizeAdjustPolicy(QScrollArea.AdjustToContents)
self.scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scrollarea.setFrameStyle(QScrollArea.NoFrame)
self.scrollarea.setMinimumWidth(
widget.sizeHint().width() + self.scrollarea.verticalScrollBar().sizeHint().width()
)
self.scrollarea.setWidgetResizable(True)
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
self.scrollarea.setWidget(widget)
layout.addWidget(self.scrollarea)
else: