From 0d62965edb0c716512fd9a551b8f8e27f461940b Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 13:20:00 +0200
Subject: [PATCH 01/17] Dialogs: Make titles uniform
---
rare/ui/components/dialogs/launch_dialog.py | 4 ++--
rare/ui/components/dialogs/launch_dialog.ui | 2 +-
rare/ui/components/dialogs/login/login_dialog.py | 4 ++--
rare/ui/components/dialogs/login/login_dialog.ui | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/rare/ui/components/dialogs/launch_dialog.py b/rare/ui/components/dialogs/launch_dialog.py
index 791bd1e0b..9c30344ed 100644
--- a/rare/ui/components/dialogs/launch_dialog.py
+++ b/rare/ui/components/dialogs/launch_dialog.py
@@ -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 @@ def setupUi(self, LaunchDialog):
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", "
Launching Rare
"))
diff --git a/rare/ui/components/dialogs/launch_dialog.ui b/rare/ui/components/dialogs/launch_dialog.ui
index bb3b9e3c5..b8a5838ec 100644
--- a/rare/ui/components/dialogs/launch_dialog.ui
+++ b/rare/ui/components/dialogs/launch_dialog.ui
@@ -29,7 +29,7 @@
- Launching Rare
+ Launching - Rare
-
diff --git a/rare/ui/components/dialogs/login/login_dialog.py b/rare/ui/components/dialogs/login/login_dialog.py
index 614308c55..47b0d1380 100644
--- a/rare/ui/components/dialogs/login/login_dialog.py
+++ b/rare/ui/components/dialogs/login/login_dialog.py
@@ -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.
@@ -46,7 +46,7 @@ def setupUi(self, LoginDialog):
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", "
Welcome to Rare
"))
self.exit_button.setText(_translate("LoginDialog", "Exit"))
self.back_button.setText(_translate("LoginDialog", "Back"))
diff --git a/rare/ui/components/dialogs/login/login_dialog.ui b/rare/ui/components/dialogs/login/login_dialog.ui
index 5b75dde8c..a7f5229fb 100644
--- a/rare/ui/components/dialogs/login/login_dialog.ui
+++ b/rare/ui/components/dialogs/login/login_dialog.ui
@@ -11,7 +11,7 @@
- Rare Login
+ Login - Rare
-
From 8c83f8aa23c1cffabab8d66cabd0954769edaf9a Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 13:21:19 +0200
Subject: [PATCH 02/17] InstallDialog: Add install_dir_edit widget without
using an extra layout
---
rare/components/dialogs/install_dialog.py | 7 +++++--
rare/ui/components/dialogs/install_dialog.py | 7 ++-----
rare/ui/components/dialogs/install_dialog.ui | 7 ++-----
3 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py
index 4b1e405d4..e379ab7c7 100644
--- a/rare/components/dialogs/install_dialog.py
+++ b/rare/components/dialogs/install_dialog.py
@@ -6,7 +6,7 @@
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 @@ def __init__(self, rgame: RareGame, options: InstallOptionsModel, parent=None):
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.indexOf(self.ui.install_dir_label),
+ QFormLayout.FieldRole, self.install_dir_edit
+ )
if self.options.update:
self.ui.install_dir_label.setEnabled(False)
diff --git a/rare/ui/components/dialogs/install_dialog.py b/rare/ui/components/dialogs/install_dialog.py
index b74ca1818..de10c4add 100644
--- a/rare/ui/components/dialogs/install_dialog.py
+++ b/rare/ui/components/dialogs/install_dialog.py
@@ -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 @@
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 @@ def setupUi(self, InstallDialog):
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)
diff --git a/rare/ui/components/dialogs/install_dialog.ui b/rare/ui/components/dialogs/install_dialog.ui
index 684809e07..6db58a645 100644
--- a/rare/ui/components/dialogs/install_dialog.ui
+++ b/rare/ui/components/dialogs/install_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 324
- 232
+ 272
+ 238
@@ -31,9 +31,6 @@
- -
-
-
-
From 64e9e312c745cc74b8c63ebec5e812cd69c738de Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 13:52:45 +0200
Subject: [PATCH 03/17] BrowserLogin: Improve instructions
---
rare/ui/components/dialogs/login/browser_login.py | 6 +++---
rare/ui/components/dialogs/login/browser_login.ui | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/rare/ui/components/dialogs/login/browser_login.py b/rare/ui/components/dialogs/login/browser_login.py
index d35da12f3..0bf8d4eac 100644
--- a/rare/ui/components/dialogs/login/browser_login.py
+++ b/rare/ui/components/dialogs/login/browser_login.py
@@ -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,7 +14,7 @@
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
- BrowserLogin.resize(182, 210)
+ BrowserLogin.resize(198, 204)
BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@@ -72,7 +72,7 @@ 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
authorizationCode
in the input above."))
+ self.info_label.setText(_translate("BrowserLogin", "Click the Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the authorizationCode
contents into the empty input above."))
if __name__ == "__main__":
diff --git a/rare/ui/components/dialogs/login/browser_login.ui b/rare/ui/components/dialogs/login/browser_login.ui
index 98577f992..341006289 100644
--- a/rare/ui/components/dialogs/login/browser_login.ui
+++ b/rare/ui/components/dialogs/login/browser_login.ui
@@ -6,8 +6,8 @@
0
0
- 182
- 210
+ 198
+ 204
@@ -94,7 +94,7 @@
- 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.
+ Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above.
true
From e188cee4e56df1f971baa567b9bc72ef66abc401 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 14:10:58 +0200
Subject: [PATCH 04/17] BrowserLogin: Add privacy note
---
rare/ui/components/dialogs/login/browser_login.py | 4 ++--
rare/ui/components/dialogs/login/browser_login.ui | 6 +++---
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/rare/ui/components/dialogs/login/browser_login.py b/rare/ui/components/dialogs/login/browser_login.py
index 0bf8d4eac..32012c568 100644
--- a/rare/ui/components/dialogs/login/browser_login.py
+++ b/rare/ui/components/dialogs/login/browser_login.py
@@ -14,7 +14,7 @@
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
- BrowserLogin.resize(198, 204)
+ BrowserLogin.resize(237, 243)
BrowserLogin.setWindowTitle("BrowserLogin")
self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
@@ -72,7 +72,7 @@ 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 Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the authorizationCode
contents into the empty input above."))
+ self.info_label.setText(_translate("BrowserLogin", "Click the Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the authorizationCode
contents into the empty input above. DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE AT ANY FORM (TEXT OR SCREENSHOT)!"))
if __name__ == "__main__":
diff --git a/rare/ui/components/dialogs/login/browser_login.ui b/rare/ui/components/dialogs/login/browser_login.ui
index 341006289..3635f85d1 100644
--- a/rare/ui/components/dialogs/login/browser_login.ui
+++ b/rare/ui/components/dialogs/login/browser_login.ui
@@ -6,8 +6,8 @@
0
0
- 198
- 204
+ 237
+ 243
@@ -94,7 +94,7 @@
- Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above.
+ Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above. <b>DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE AT ANY FORM (TEXT OR SCREENSHOT)!</b>
true
From 0ec80e5678cbe1ee135dd36a83d0fadc535fb27e Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 16:23:41 +0200
Subject: [PATCH 05/17] LoginDialog: Set minimum size in a way that avoids
resizing on changing widgets
The forms were updated to be more informative and easier to read.
NOTE: The reason the form is bigger than the contents is because the
QLabels have word-wrapping enabled and as such they substitute width
for height (`hasHeightforWidth`)
---
rare/components/dialogs/login/__init__.py | 9 +-
.../components/dialogs/login/browser_login.py | 7 +-
rare/components/dialogs/login/import_login.py | 2 +
.../components/dialogs/login/browser_login.py | 76 ++++++-----
.../components/dialogs/login/browser_login.ui | 127 +++++++++++-------
.../components/dialogs/login/import_login.py | 59 +++++---
.../components/dialogs/login/import_login.ui | 97 +++++++++----
.../components/dialogs/login/landing_page.py | 50 ++++---
.../components/dialogs/login/landing_page.ui | 66 ++++++---
.../components/dialogs/login/login_dialog.py | 2 +-
.../components/dialogs/login/login_dialog.ui | 4 +-
11 files changed, 329 insertions(+), 170 deletions(-)
diff --git a/rare/components/dialogs/login/__init__.py b/rare/components/dialogs/login/__init__.py
index f9ca2b599..be75ef2d3 100644
--- a/rare/components/dialogs/login/__init__.py
+++ b/rare/components/dialogs/login/__init__.py
@@ -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,6 @@ def __init__(self, core: LegendaryCore, parent=None):
self.args = ArgumentsSingleton()
self.login_stack = SlidingStackedWidget(parent=self)
- self.login_stack.setMinimumSize(480, 180)
self.ui.login_stack_layout.addWidget(self.login_stack)
self.landing_page = LandingPage(self.login_stack)
@@ -65,6 +64,12 @@ def __init__(self, core: LegendaryCore, parent=None):
self.import_page.success.connect(self.login_successful)
self.import_page.changed.connect(lambda: self.ui.next_button.setEnabled(self.import_page.is_valid()))
+ self.login_stack.setMinimumSize(
+ self.landing_page.sizeHint().expandedTo(
+ self.browser_page.sizeHint().expandedTo(self.import_page.sizeHint())
+ )
+ )
+
self.ui.next_button.setEnabled(False)
self.ui.back_button.setEnabled(False)
diff --git a/rare/components/dialogs/login/browser_login.py b/rare/components/dialogs/login/browser_login.py
index 0da3e55d7..b354544e7 100644
--- a/rare/components/dialogs/login/browser_login.py
+++ b/rare/components/dialogs/login/browser_login.py
@@ -4,7 +4,7 @@
from PyQt5.QtCore import pyqtSignal, QUrl
from PyQt5.QtGui import QDesktopServices
-from PyQt5.QtWidgets import QFrame, qApp
+from PyQt5.QtWidgets import QFrame, qApp, QFormLayout
from legendary.core import LegendaryCore
from legendary.utils import webview_login
@@ -34,8 +34,9 @@ def __init__(self, core: LegendaryCore, parent=None):
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.browser_layout.setWidget(
+ self.ui.browser_layout.indexOf(self.ui.sid_label), QFormLayout.FieldRole, self.sid_edit
+ )
self.ui.open_button.clicked.connect(self.open_browser)
self.sid_edit.textChanged.connect(self.changed.emit)
diff --git a/rare/components/dialogs/login/import_login.py b/rare/components/dialogs/login/import_login.py
index 5a3bcc178..c817591fe 100644
--- a/rare/components/dialogs/login/import_login.py
+++ b/rare/components/dialogs/login/import_login.py
@@ -15,6 +15,8 @@
class ImportLogin(QFrame):
success = pyqtSignal()
changed = pyqtSignal()
+
+ # FIXME: Use pathspec instead of duplicated code
if os.name == "nt":
localappdata = os.path.expandvars("%LOCALAPPDATA%")
else:
diff --git a/rare/ui/components/dialogs/login/browser_login.py b/rare/ui/components/dialogs/login/browser_login.py
index 32012c568..99afc8494 100644
--- a/rare/ui/components/dialogs/login/browser_login.py
+++ b/rare/ui/components/dialogs/login/browser_login.py
@@ -14,16 +14,37 @@
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
- BrowserLogin.resize(237, 243)
+ BrowserLogin.resize(480, 184)
+ BrowserLogin.setMinimumSize(QtCore.QSize(480, 180))
BrowserLogin.setWindowTitle("BrowserLogin")
- self.browser_layout = QtWidgets.QGridLayout(BrowserLogin)
- self.browser_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+ self.browser_layout = QtWidgets.QFormLayout(BrowserLogin)
+ self.browser_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
+ self.browser_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.browser_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.browser_layout.setObjectName("browser_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.browser_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label)
+ self.open_button = QtWidgets.QPushButton(BrowserLogin)
+ self.open_button.setObjectName("open_button")
+ self.browser_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.open_button)
+ self.sid_label = QtWidgets.QLabel(BrowserLogin)
+ self.sid_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.sid_label.setObjectName("sid_label")
+ self.browser_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.sid_label)
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 +53,42 @@ def setupUi(self, BrowserLogin):
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)
+ self.browser_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.link_layout)
+ self.status_label = QtWidgets.QLabel(BrowserLogin)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
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)
+ sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
+ self.status_label.setSizePolicy(sizePolicy)
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.status_label.setFont(font)
+ self.status_label.setText("")
+ self.status_label.setWordWrap(True)
+ self.status_label.setObjectName("status_label")
+ self.browser_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.status_label)
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)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
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.browser_layout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.info_label)
+ spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.browser_layout.setItem(4, QtWidgets.QFormLayout.SpanningRole, spacerItem)
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 Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the authorizationCode
contents into the empty input above. DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE AT ANY FORM (TEXT OR SCREENSHOT)!"))
+ self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
+ self.sid_label.setText(_translate("BrowserLogin", "Code"))
+ self.info_label.setText(_translate("BrowserLogin", "Click the Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the authorizationCode
contents into the empty input above. DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!"))
if __name__ == "__main__":
diff --git a/rare/ui/components/dialogs/login/browser_login.ui b/rare/ui/components/dialogs/login/browser_login.ui
index 3635f85d1..7d67856d1 100644
--- a/rare/ui/components/dialogs/login/browser_login.ui
+++ b/rare/ui/components/dialogs/login/browser_login.ui
@@ -6,27 +6,72 @@
0
0
- 237
- 243
+ 480
+ 184
+
+
+ 480
+ 180
+
+
BrowserLogin
-
-
- QLayout::SetDefaultConstraint
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
- -
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 75
+ true
+
+
+
+ Login through browser
+
+
+
+ -
+
+
+ Open Browser
+
+
+
+ -
+
+
+ Code
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
-
true
@@ -44,72 +89,58 @@
- -
-
-
- Qt::Vertical
-
-
-
- 0
- 0
-
-
-
-
- -
-
+
-
+
-
+
0
0
-
- Open Browser
-
-
-
- -
-
-
- -
-
-
- 75
- true
-
+
- Login through browser
+
+
+
+ true
-
+
+
+ 0
+ 0
+
+
true
- Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above. <b>DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE AT ANY FORM (TEXT OR SCREENSHOT)!</b>
+ Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above. <b>DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!</b>
true
- -
-
-
-
+
-
+
+
+ Qt::Vertical
-
- Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
+
+
+ 0
+ 0
+
-
+
diff --git a/rare/ui/components/dialogs/login/import_login.py b/rare/ui/components/dialogs/login/import_login.py
index 834fc13f5..ab695011b 100644
--- a/rare/ui/components/dialogs/login/import_login.py
+++ b/rare/ui/components/dialogs/login/import_login.py
@@ -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,37 @@
class Ui_ImportLogin(object):
def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin")
- ImportLogin.resize(242, 120)
+ ImportLogin.resize(480, 180)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(ImportLogin.sizePolicy().hasHeightForWidth())
+ ImportLogin.setSizePolicy(sizePolicy)
+ ImportLogin.setMinimumSize(QtCore.QSize(480, 180))
ImportLogin.setWindowTitle("ImportLogin")
- self.import_layout = QtWidgets.QGridLayout(ImportLogin)
- self.import_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+ self.import_layout = QtWidgets.QFormLayout(ImportLogin)
+ self.import_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
+ self.import_layout.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
+ self.import_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.import_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
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.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.import_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label)
+ self.prefix_label = QtWidgets.QLabel(ImportLogin)
+ self.prefix_label.setObjectName("prefix_label")
+ self.import_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.prefix_label)
+ 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,31 +53,45 @@ def setupUi(self, ImportLogin):
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.import_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.prefix_layout)
self.status_label = QtWidgets.QLabel(ImportLogin)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
+ self.status_label.setSizePolicy(sizePolicy)
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.import_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.status_label)
self.info_label = QtWidgets.QLabel(ImportLogin)
+ 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)
font = QtGui.QFont()
font.setItalic(True)
self.info_label.setFont(font)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
- self.import_layout.addWidget(self.info_label, 4, 0, 1, 3)
+ self.import_layout.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.info_label)
spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.import_layout.addItem(spacerItem, 3, 0, 1, 3)
+ self.import_layout.setItem(3, QtWidgets.QFormLayout.SpanningRole, spacerItem)
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_label.setText(_translate("ImportLogin", "Select prefix"))
self.prefix_tool.setText(_translate("ImportLogin", "Browse"))
self.info_label.setText(_translate("ImportLogin", "You will get logged out from EGL in the process."))
diff --git a/rare/ui/components/dialogs/login/import_login.ui b/rare/ui/components/dialogs/login/import_login.ui
index d49ca6359..8c47d9d5e 100644
--- a/rare/ui/components/dialogs/login/import_login.ui
+++ b/rare/ui/components/dialogs/login/import_login.ui
@@ -6,26 +6,46 @@
0
0
- 242
- 120
+ 480
+ 180
+
+
+ 0
+ 0
+
+
+
+
+ 480
+ 180
+
+
ImportLogin
-
-
- QLayout::SetDefaultConstraint
+
+
+ QFormLayout::AllNonFixedFieldsGrow
- -
-
-
- Select prefix
-
-
-
- -
+
+ QFormLayout::WrapLongRows
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
-
+
+
+ 0
+ 0
+
+
75
@@ -37,28 +57,45 @@
- -
-
+
-
+
- Browse
+ Select prefix
-
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+ -
+
+
+ Browse
+
+
+
+
+
+ -
+
-
+
0
0
-
- true
-
-
-
- -
-
true
@@ -72,8 +109,14 @@
- -
+
-
+
+
+ 0
+ 0
+
+
true
@@ -87,7 +130,7 @@
- -
+
-
Qt::Vertical
diff --git a/rare/ui/components/dialogs/login/landing_page.py b/rare/ui/components/dialogs/login/landing_page.py
index 760acf1b6..e8ef4e0b2 100644
--- a/rare/ui/components/dialogs/login/landing_page.py
+++ b/rare/ui/components/dialogs/login/landing_page.py
@@ -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,59 @@
class Ui_LandingPage(object):
def setupUi(self, LandingPage):
LandingPage.setObjectName("LandingPage")
- LandingPage.resize(311, 87)
+ LandingPage.resize(480, 180)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(LandingPage.sizePolicy().hasHeightForWidth())
+ LandingPage.setSizePolicy(sizePolicy)
+ LandingPage.setMinimumSize(QtCore.QSize(480, 180))
LandingPage.setWindowTitle("LandingPage")
- self.landing_layout = QtWidgets.QGridLayout(LandingPage)
- self.landing_layout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+ self.landing_layout = QtWidgets.QFormLayout(LandingPage)
+ self.landing_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
+ self.landing_layout.setLabelAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
+ self.landing_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.landing_layout.setObjectName("landing_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.landing_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.landing_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.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.login_browser_label)
self.login_import_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_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.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.landing_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.landing_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.login_import_label)
self.retranslateUi(LandingPage)
@@ -64,8 +74,8 @@ def retranslateUi(self, LandingPage):
_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"))
diff --git a/rare/ui/components/dialogs/login/landing_page.ui b/rare/ui/components/dialogs/login/landing_page.ui
index 55ac7c3bd..466cb9df3 100644
--- a/rare/ui/components/dialogs/login/landing_page.ui
+++ b/rare/ui/components/dialogs/login/landing_page.ui
@@ -6,19 +6,43 @@
0
0
- 311
- 87
+ 480
+ 180
+
+
+ 0
+ 0
+
+
+
+
+ 480
+ 180
+
+
LandingPage
-
-
- QLayout::SetDefaultConstraint
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
-
+
-
+
+
+ 0
+ 0
+
+
75
@@ -33,7 +57,7 @@
-
-
+
0
0
@@ -43,10 +67,22 @@
+ -
+
+
+
+ true
+
+
+
+ Login using a browser.
+
+
+
-
-
+
0
0
@@ -56,19 +92,7 @@
- -
-
-
-
- true
-
-
-
- Login using a browser.
-
-
-
- -
+
-
diff --git a/rare/ui/components/dialogs/login/login_dialog.py b/rare/ui/components/dialogs/login/login_dialog.py
index 47b0d1380..76eb33108 100644
--- a/rare/ui/components/dialogs/login/login_dialog.py
+++ b/rare/ui/components/dialogs/login/login_dialog.py
@@ -14,7 +14,7 @@
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)
diff --git a/rare/ui/components/dialogs/login/login_dialog.ui b/rare/ui/components/dialogs/login/login_dialog.ui
index a7f5229fb..5419629b4 100644
--- a/rare/ui/components/dialogs/login/login_dialog.ui
+++ b/rare/ui/components/dialogs/login/login_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 324
- 132
+ 241
+ 128
From 60383d28445b8c453cd990296e377e65eb00daac Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 11 Dec 2023 23:13:57 +0200
Subject: [PATCH 06/17] IndicatorLineEdit: Remove hint label
It looks like it is not used by anything any more.
---
rare/widgets/indicator_edit.py | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/rare/widgets/indicator_edit.py b/rare/widgets/indicator_edit.py
index 1197407f6..c9a76bb66 100644
--- a/rare/widgets/indicator_edit.py
+++ b/rare/widgets/indicator_edit.py
@@ -137,18 +137,11 @@ def __init__(
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 @@ def text(self) -> str:
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()
From 8e1a3c8c733c6738e1e75d624571fb2a2c288410 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 00:42:25 +0200
Subject: [PATCH 07/17] LoginDialog: Calculate a fixed height for the login
stack based on width
* Properly find row to insert sid_edit into
* Use lazy evaluation for logging
* Clean the login forms by using separate layouts for the interactive fields
---
rare/components/dialogs/login/__init__.py | 21 ++-
.../components/dialogs/login/browser_login.py | 12 +-
rare/components/dialogs/login/import_login.py | 10 +-
.../components/dialogs/login/browser_login.py | 54 +++----
.../components/dialogs/login/browser_login.ui | 147 ++++++++----------
.../components/dialogs/login/import_login.py | 50 +++---
.../components/dialogs/login/import_login.ui | 146 ++++++++---------
.../components/dialogs/login/landing_page.py | 23 ++-
.../components/dialogs/login/landing_page.ui | 12 +-
9 files changed, 222 insertions(+), 253 deletions(-)
diff --git a/rare/components/dialogs/login/__init__.py b/rare/components/dialogs/login/__init__.py
index be75ef2d3..42ba6452a 100644
--- a/rare/components/dialogs/login/__init__.py
+++ b/rare/components/dialogs/login/__init__.py
@@ -48,6 +48,7 @@ def __init__(self, core: LegendaryCore, parent=None):
self.args = ArgumentsSingleton()
self.login_stack = SlidingStackedWidget(parent=self)
+ self.login_stack.setMinimumWidth(480)
self.ui.login_stack_layout.addWidget(self.login_stack)
self.landing_page = LandingPage(self.login_stack)
@@ -64,9 +65,23 @@ def __init__(self, core: LegendaryCore, parent=None):
self.import_page.success.connect(self.login_successful)
self.import_page.changed.connect(lambda: self.ui.next_button.setEnabled(self.import_page.is_valid()))
- self.login_stack.setMinimumSize(
- self.landing_page.sizeHint().expandedTo(
- self.browser_page.sizeHint().expandedTo(self.import_page.sizeHint())
+ # # 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()),
)
)
diff --git a/rare/components/dialogs/login/browser_login.py b/rare/components/dialogs/login/browser_login.py
index b354544e7..8b10296fa 100644
--- a/rare/components/dialogs/login/browser_login.py
+++ b/rare/components/dialogs/login/browser_login.py
@@ -4,7 +4,7 @@
from PyQt5.QtCore import pyqtSignal, QUrl
from PyQt5.QtGui import QDesktopServices
-from PyQt5.QtWidgets import QFrame, qApp, QFormLayout
+from PyQt5.QtWidgets import QFrame, qApp, QFormLayout, QLineEdit
from legendary.core import LegendaryCore
from legendary.utils import webview_login
@@ -31,11 +31,13 @@ def __init__(self, core: LegendaryCore, parent=None):
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.browser_layout.setWidget(
- self.ui.browser_layout.indexOf(self.ui.sid_label), QFormLayout.FieldRole, 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)
@@ -69,7 +71,7 @@ def do_login(self):
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."))
@@ -83,7 +85,7 @@ def open_browser(self):
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.")
diff --git a/rare/components/dialogs/login/import_login.py b/rare/components/dialogs/login/import_login.py
index c817591fe..7cbe0a9e6 100644
--- a/rare/components/dialogs/login/import_login.py
+++ b/rare/components/dialogs/login/import_login.py
@@ -47,10 +47,6 @@ def __init__(self, core: LegendaryCore, parent=None):
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)
@@ -99,15 +95,15 @@ def do_login(self):
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)
diff --git a/rare/ui/components/dialogs/login/browser_login.py b/rare/ui/components/dialogs/login/browser_login.py
index 99afc8494..0b67bd8f0 100644
--- a/rare/ui/components/dialogs/login/browser_login.py
+++ b/rare/ui/components/dialogs/login/browser_login.py
@@ -14,14 +14,10 @@
class Ui_BrowserLogin(object):
def setupUi(self, BrowserLogin):
BrowserLogin.setObjectName("BrowserLogin")
- BrowserLogin.resize(480, 184)
- BrowserLogin.setMinimumSize(QtCore.QSize(480, 180))
+ BrowserLogin.resize(320, 243)
BrowserLogin.setWindowTitle("BrowserLogin")
- self.browser_layout = QtWidgets.QFormLayout(BrowserLogin)
- self.browser_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
- self.browser_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
- self.browser_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
- 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)
@@ -33,14 +29,16 @@ def setupUi(self, BrowserLogin):
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
- self.browser_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.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.browser_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.open_button)
- self.sid_label = QtWidgets.QLabel(BrowserLogin)
- self.sid_label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
- self.sid_label.setObjectName("sid_label")
- self.browser_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.sid_label)
+ 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)
@@ -53,33 +51,32 @@ def setupUi(self, BrowserLogin):
self.copy_button.setText("")
self.copy_button.setObjectName("copy_button")
self.link_layout.addWidget(self.copy_button)
- self.browser_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.link_layout)
+ 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)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
- self.status_label.setSizePolicy(sizePolicy)
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.browser_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.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)
- font = QtGui.QFont()
- font.setItalic(True)
- self.info_label.setFont(font)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
- self.browser_layout.setWidget(5, QtWidgets.QFormLayout.SpanningRole, self.info_label)
- spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.browser_layout.setItem(4, QtWidgets.QFormLayout.SpanningRole, spacerItem)
+ self.main_layout.addWidget(self.info_label)
self.retranslateUi(BrowserLogin)
@@ -87,8 +84,7 @@ def retranslateUi(self, BrowserLogin):
_translate = QtCore.QCoreApplication.translate
self.title_label.setText(_translate("BrowserLogin", "Login through browser"))
self.open_button.setText(_translate("BrowserLogin", "Open Browser"))
- self.sid_label.setText(_translate("BrowserLogin", "Code"))
- self.info_label.setText(_translate("BrowserLogin", "Click the Open Browser button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the
authorizationCode
contents into the empty input above. DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!"))
+ self.info_label.setText(_translate("BrowserLogin", "Click the Open Browser 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 authorizationCode
in the same line into the empty input above.
DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!"))
if __name__ == "__main__":
diff --git a/rare/ui/components/dialogs/login/browser_login.ui b/rare/ui/components/dialogs/login/browser_login.ui
index 7d67856d1..8331b84cb 100644
--- a/rare/ui/components/dialogs/login/browser_login.ui
+++ b/rare/ui/components/dialogs/login/browser_login.ui
@@ -6,30 +6,15 @@
0
0
- 480
- 184
+ 320
+ 243
-
-
- 480
- 180
-
-
BrowserLogin
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
- -
+
+
-
@@ -48,40 +33,68 @@
- -
-
-
- Open Browser
+
-
+
+
+ QLayout::SetFixedSize
-
-
- -
-
-
- Code
+
+ QFormLayout::AllNonFixedFieldsGrow
-
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- -
-
-
-
-
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
-
+
-
+ Open Browser
-
- true
+
+
+ -
+
+
-
+
+
+
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ authorizationCode
-
-
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
+
+
+ true
+
+
@@ -89,26 +102,20 @@
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
-
+
-
+
+
+ Qt::Vertical
-
- true
+
+
+ 20
+ 10
+
-
+
- -
+
-
@@ -116,32 +123,14 @@
0
-
-
- true
-
-
- Click the <b>Open Browser</b> button to open the login page in a browser or copy the link and paste it in a browser. After logging in using the browser, copy the <b><code>authorizationCode</code></b> contents into the empty input above. <b>DO NOT SHARE THE INFORMATION IN THE BROWSER PAGE WITH ANYONE IN ANY FORM (TEXT OR SCREENSHOT)!</b>
+ <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>
true
- -
-
-
- Qt::Vertical
-
-
-
- 0
- 0
-
-
-
-
diff --git a/rare/ui/components/dialogs/login/import_login.py b/rare/ui/components/dialogs/login/import_login.py
index ab695011b..e61059821 100644
--- a/rare/ui/components/dialogs/login/import_login.py
+++ b/rare/ui/components/dialogs/login/import_login.py
@@ -14,20 +14,15 @@
class Ui_ImportLogin(object):
def setupUi(self, ImportLogin):
ImportLogin.setObjectName("ImportLogin")
- ImportLogin.resize(480, 180)
+ 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.setMinimumSize(QtCore.QSize(480, 180))
ImportLogin.setWindowTitle("ImportLogin")
- self.import_layout = QtWidgets.QFormLayout(ImportLogin)
- self.import_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
- self.import_layout.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
- self.import_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
- self.import_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
- self.import_layout.setObjectName("import_layout")
+ 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)
@@ -39,10 +34,13 @@ def setupUi(self, ImportLogin):
font.setWeight(75)
self.title_label.setFont(font)
self.title_label.setObjectName("title_label")
- self.import_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.title_label)
- self.prefix_label = QtWidgets.QLabel(ImportLogin)
- self.prefix_label.setObjectName("prefix_label")
- self.import_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.prefix_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.prefix_layout = QtWidgets.QHBoxLayout()
self.prefix_layout.setObjectName("prefix_layout")
self.prefix_combo = QtWidgets.QComboBox(ImportLogin)
@@ -57,43 +55,39 @@ def setupUi(self, ImportLogin):
self.prefix_tool = QtWidgets.QToolButton(ImportLogin)
self.prefix_tool.setObjectName("prefix_tool")
self.prefix_layout.addWidget(self.prefix_tool)
- self.import_layout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.prefix_layout)
+ 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)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth())
- self.status_label.setSizePolicy(sizePolicy)
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.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.status_label)
+ 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)
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)
- font = QtGui.QFont()
- font.setItalic(True)
- self.info_label.setFont(font)
self.info_label.setWordWrap(True)
self.info_label.setObjectName("info_label")
- self.import_layout.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.info_label)
- spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.import_layout.setItem(3, QtWidgets.QFormLayout.SpanningRole, spacerItem)
+ self.main_layout.addWidget(self.info_label)
self.retranslateUi(ImportLogin)
def retranslateUi(self, ImportLogin):
_translate = QtCore.QCoreApplication.translate
self.title_label.setText(_translate("ImportLogin", "Import existing session from EGL"))
- self.prefix_label.setText(_translate("ImportLogin", "Select prefix"))
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", "Please select the Wine prefix where Epic Games Launcher is installed. You will get logged out from EGL in the process."))
if __name__ == "__main__":
diff --git a/rare/ui/components/dialogs/login/import_login.ui b/rare/ui/components/dialogs/login/import_login.ui
index 8c47d9d5e..c396215a8 100644
--- a/rare/ui/components/dialogs/login/import_login.ui
+++ b/rare/ui/components/dialogs/login/import_login.ui
@@ -6,8 +6,8 @@
0
0
- 480
- 180
+ 233
+ 156
@@ -16,29 +16,11 @@
0
-
-
- 480
- 180
-
-
ImportLogin
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
-
- QFormLayout::WrapLongRows
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
- -
+
+
-
@@ -57,59 +39,79 @@
- -
-
-
- Select prefix
+
-
+
+
+ QLayout::SetFixedSize
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- true
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ true
+
+
+
+ -
+
+
+ Browse
+
+
+
+
+
+ -
+
+
+ Select prefix
- -
-
+
-
+
+
+
+ true
+
+
- Browse
+
- -
-
-
-
- 0
- 0
-
-
-
-
- true
-
-
-
-
+
-
+
+
+ Qt::Vertical
-
- true
+
+
+ 20
+ 10
+
-
+
- -
+
-
@@ -117,32 +119,14 @@
0
-
-
- true
-
-
- You will get logged out from EGL in the process.
+ <i>Please select the Wine prefix where Epic Games Launcher is installed. You will get logged out from EGL in the process.</i>
true
- -
-
-
- Qt::Vertical
-
-
-
- 0
- 0
-
-
-
-
diff --git a/rare/ui/components/dialogs/login/landing_page.py b/rare/ui/components/dialogs/login/landing_page.py
index e8ef4e0b2..e13b27be7 100644
--- a/rare/ui/components/dialogs/login/landing_page.py
+++ b/rare/ui/components/dialogs/login/landing_page.py
@@ -14,19 +14,18 @@
class Ui_LandingPage(object):
def setupUi(self, LandingPage):
LandingPage.setObjectName("LandingPage")
- LandingPage.resize(480, 180)
+ 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.setMinimumSize(QtCore.QSize(480, 180))
LandingPage.setWindowTitle("LandingPage")
- self.landing_layout = QtWidgets.QFormLayout(LandingPage)
- self.landing_layout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
- self.landing_layout.setLabelAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
- self.landing_layout.setFormAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
- 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)
@@ -38,7 +37,7 @@ def setupUi(self, LandingPage):
font.setWeight(75)
self.login_label.setFont(font)
self.login_label.setObjectName("login_label")
- self.landing_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.login_label)
+ self.main_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.login_label)
self.login_browser_radio = QtWidgets.QRadioButton(LandingPage)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
@@ -46,13 +45,13 @@ def setupUi(self, LandingPage):
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.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.login_browser_radio)
+ 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.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.login_browser_label)
+ 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)
@@ -60,13 +59,13 @@ def setupUi(self, LandingPage):
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.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.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.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.login_import_label)
+ self.main_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.login_import_label)
self.retranslateUi(LandingPage)
diff --git a/rare/ui/components/dialogs/login/landing_page.ui b/rare/ui/components/dialogs/login/landing_page.ui
index 466cb9df3..43e4dc018 100644
--- a/rare/ui/components/dialogs/login/landing_page.ui
+++ b/rare/ui/components/dialogs/login/landing_page.ui
@@ -6,8 +6,8 @@
0
0
- 480
- 180
+ 293
+ 78
@@ -16,16 +16,10 @@
0
-
-
- 480
- 180
-
-
LandingPage
-
+
QFormLayout::AllNonFixedFieldsGrow
From 3ce62facd21c3291f072639f3d4139873a8120a3 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 00:45:29 +0200
Subject: [PATCH 08/17] InstallDialog: Properly find the the row when
`inserting install_dir_edit`
---
rare/components/dialogs/install_dialog.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rare/components/dialogs/install_dialog.py b/rare/components/dialogs/install_dialog.py
index e379ab7c7..52e0aab2e 100644
--- a/rare/components/dialogs/install_dialog.py
+++ b/rare/components/dialogs/install_dialog.py
@@ -86,7 +86,7 @@ def __init__(self, rgame: RareGame, options: InstallOptionsModel, parent=None):
parent=self,
)
self.ui.install_dialog_layout.setWidget(
- self.ui.install_dialog_layout.indexOf(self.ui.install_dir_label),
+ self.ui.install_dialog_layout.getWidgetPosition(self.ui.install_dir_label)[0],
QFormLayout.FieldRole, self.install_dir_edit
)
From b5a18f2858f8a8098a2c4f419747fe6d575c5f0b Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 00:46:22 +0200
Subject: [PATCH 09/17] ImportGroup: Properly find the row when inserting
`path_edit` and `app_name_edit`
---
rare/components/tabs/games/integrations/import_group.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/rare/components/tabs/games/integrations/import_group.py b/rare/components/tabs/games/integrations/import_group.py
index 55ba9e812..24e05dff2 100644
--- a/rare/components/tabs/games/integrations/import_group.py
+++ b/rare/components/tabs/games/integrations/import_group.py
@@ -199,7 +199,8 @@ def __init__(self, parent=None):
)
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 @@ def __init__(self, parent=None):
)
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)
From d5634213915b0bd16ad4b1dbb32fdd84014fcb8d Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 01:00:07 +0200
Subject: [PATCH 10/17] EGLSyncGroup: Properly find the correct rows to insert
widgets
---
.../tabs/games/integrations/egl_sync_group.py | 18 +++++++------
.../tabs/games/integrations/egl_sync_group.py | 22 ++++++++-------
.../tabs/games/integrations/egl_sync_group.ui | 27 ++++++++++++-------
3 files changed, 41 insertions(+), 26 deletions(-)
diff --git a/rare/components/tabs/games/integrations/egl_sync_group.py b/rare/components/tabs/games/integrations/egl_sync_group.py
index d03032035..fcacaf2a0 100644
--- a/rare/components/tabs/games/integrations/egl_sync_group.py
+++ b/rare/components/tabs/games/integrations/egl_sync_group.py
@@ -6,7 +6,7 @@
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 @@ def __init__(self, parent=None):
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)
diff --git a/rare/ui/components/tabs/games/integrations/egl_sync_group.py b/rare/ui/components/tabs/games/integrations/egl_sync_group.py
index e6a12f74a..d59c2b2a8 100644
--- a/rare/ui/components/tabs/games/integrations/egl_sync_group.py
+++ b/rare/ui/components/tabs/games/integrations/egl_sync_group.py
@@ -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 @@
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 @@ def setupUi(self, EGLSyncGroup):
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 @@ def retranslateUi(self, EGLSyncGroup):
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__":
diff --git a/rare/ui/components/tabs/games/integrations/egl_sync_group.ui b/rare/ui/components/tabs/games/integrations/egl_sync_group.ui
index 150669f03..10b71aab0 100644
--- a/rare/ui/components/tabs/games/integrations/egl_sync_group.ui
+++ b/rare/ui/components/tabs/games/integrations/egl_sync_group.ui
@@ -6,8 +6,8 @@
0
0
- 217
- 88
+ 424
+ 106
@@ -33,26 +33,35 @@
- -
-
-
- -
+
-
Enable automatic sync
- -
+
-
+
+
+ true
+
+
-
+ This will immediately synchronize with EGL
- -
+
-
+ -
+
+
+ Estimated path
+
+
+
From 6c0a92966efec354a354ae04616884b5c5c53fd0 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 01:05:58 +0200
Subject: [PATCH 11/17] GameSettings: Properly find row to insert widgets.
---
rare/components/tabs/games/game_info/game_settings.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/rare/components/tabs/games/game_info/game_settings.py b/rare/components/tabs/games/game_info/game_settings.py
index 9ed73aac3..702ba6665 100644
--- a/rare/components/tabs/games/game_info/game_settings.py
+++ b/rare/components/tabs/games/game_info/game_settings.py
@@ -44,9 +44,8 @@ def __init__(self, parent=None):
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)
From 0ef2497afbe63e874623b3bd580b642b50f3fd79 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 12:41:18 +0200
Subject: [PATCH 12/17] QtRequests: Handle multiple requests concurrently and
use caching
It is still junky but less so. It allows us to use QNetworkRequest's
caching mechanism instead of doing so on our own per-case.
---
rare/components/tabs/__init__.py | 3 -
rare/components/tabs/settings/about.py | 4 +-
rare/components/tabs/shop/__init__.py | 23 ++-
rare/components/tabs/shop/shop_api_core.py | 15 +-
rare/utils/extra_widgets.py | 25 +--
rare/utils/qt_requests.py | 226 ++++++++++++---------
6 files changed, 161 insertions(+), 135 deletions(-)
diff --git a/rare/components/tabs/__init__.py b/rare/components/tabs/__init__.py
index c0c195ccf..7de07f4d5 100644
--- a/rare/components/tabs/__init__.py
+++ b/rare/components/tabs/__init__.py
@@ -86,9 +86,6 @@ def mouse_clicked(self, index):
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)
diff --git a/rare/components/tabs/settings/about.py b/rare/components/tabs/settings/about.py
index 35c520efc..97032a39e 100644
--- a/rare/components/tabs/settings/about.py
+++ b/rare/components/tabs/settings/about.py
@@ -7,7 +7,7 @@
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 @@ def __init__(self, parent=None):
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,
diff --git a/rare/components/tabs/shop/__init__.py b/rare/components/tabs/shop/__init__.py
index db5b71101..1c9493fe9 100644
--- a/rare/components/tabs/shop/__init__.py
+++ b/rare/components/tabs/shop/__init__.py
@@ -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 @@ def __init__(self, core: LegendaryCore):
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 @@ def __init__(self, core: LegendaryCore):
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)
diff --git a/rare/components/tabs/shop/shop_api_core.py b/rare/components/tabs/shop/shop_api_core.py
index 068896282..bb1422b9f 100644
--- a/rare/components/tabs/shop/shop_api_core.py
+++ b/rare/components/tabs/shop/shop_api_core.py
@@ -10,7 +10,8 @@
remove_from_wishlist_query,
)
from rare.components.tabs.shop.shop_models import BrowseModel
-from rare.utils.qt_requests import QtRequestManager
+from rare.utils.qt_requests import QtRequests
+from rare.utils.paths import cache_dir
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
@@ -25,8 +26,8 @@ def __init__(self, auth_token, lc: str, cc: str):
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 +53,7 @@ def _handle_free_games(self, data, handle_func):
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 +61,6 @@ def get_wishlist(self, handle_func):
"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 +96,7 @@ def search_game(self, name, handle_func):
}
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 +190,8 @@ def add_to_wishlist(self, namespace, offer_id, handle_func: callable):
}
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 +217,8 @@ def remove_from_wishlist(self, namespace, offer_id, handle_func: callable):
}
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):
diff --git a/rare/utils/extra_widgets.py b/rare/utils/extra_widgets.py
index 2a653d6f3..bfdddfa29 100644
--- a/rare/utils/extra_widgets.py
+++ b/rare/utils/extra_widgets.py
@@ -1,4 +1,3 @@
-import os
from logging import getLogger
from PyQt5.QtCore import Qt, pyqtSignal
@@ -14,8 +13,8 @@
)
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 @@ def update_image(self, url, name="", size: tuple = (240, 320)):
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 @@ def image_ready(self, data):
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()
diff --git a/rare/utils/qt_requests.py b/rare/utils/qt_requests.py
index b5eaa1889..23f4dfc93 100644
--- a/rare/utils/qt_requests.py
+++ b/rare/utils/qt_requests.py
@@ -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")
+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])
-class QtRequestManager(QObject):
+@dataclass
+class RequestQueueItem:
+ method: str = 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)
- 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)
+ 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.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)
+ 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.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,
- )
+ 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:
- self.get(self.request_queue[0].url, self.request_queue[0].handle_func)
- self.request_queue.pop(0)
-
-
-@dataclass
-class RequestQueueItem:
- method: str = None
- url: str = None
- handle_func: Callable[[Union[dict, bytes]], None] = None
- payload: dict = None
+ 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()
From 83dd118784d157ca5a460107a4e8b9b1cec2a0a3 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 12:48:43 +0200
Subject: [PATCH 13/17] ShopWidget: The shop is broken, set it only show the
free games and the wishlist
---
rare/components/tabs/shop/shop_widget.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/rare/components/tabs/shop/shop_widget.py b/rare/components/tabs/shop/shop_widget.py
index 36785a486..af5a02ce1 100644
--- a/rare/components/tabs/shop/shop_widget.py
+++ b/rare/components/tabs/shop/shop_widget.py
@@ -68,6 +68,10 @@ def __init__(self, path, core: LegendaryCore, shop_api: ShopApiCore):
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):
From 490f0dc69fe89300602e865bdb2e5802ab4216a5 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Tue, 12 Dec 2023 13:02:07 +0200
Subject: [PATCH 14/17] Shop: Rename module to `store`
---
rare/components/tabs/__init__.py | 2 +-
rare/components/tabs/{shop => store}/__init__.py | 0
rare/components/tabs/{shop => store}/constants.py | 0
rare/components/tabs/{shop => store}/game_info.py | 2 +-
rare/components/tabs/{shop => store}/game_widgets.py | 2 +-
rare/components/tabs/{shop => store}/search_results.py | 0
rare/components/tabs/{shop => store}/shop_api_core.py | 5 ++---
rare/components/tabs/{shop => store}/shop_models.py | 0
rare/components/tabs/{shop => store}/shop_widget.py | 0
rare/components/tabs/{shop => store}/wishlist.py | 4 ++--
10 files changed, 7 insertions(+), 8 deletions(-)
rename rare/components/tabs/{shop => store}/__init__.py (100%)
rename rare/components/tabs/{shop => store}/constants.py (100%)
rename rare/components/tabs/{shop => store}/game_info.py (99%)
rename rare/components/tabs/{shop => store}/game_widgets.py (98%)
rename rare/components/tabs/{shop => store}/search_results.py (100%)
rename rare/components/tabs/{shop => store}/shop_api_core.py (98%)
rename rare/components/tabs/{shop => store}/shop_models.py (100%)
rename rare/components/tabs/{shop => store}/shop_widget.py (100%)
rename rare/components/tabs/{shop => store}/wishlist.py (96%)
diff --git a/rare/components/tabs/__init__.py b/rare/components/tabs/__init__.py
index 7de07f4d5..de1f4e847 100644
--- a/rare/components/tabs/__init__.py
+++ b/rare/components/tabs/__init__.py
@@ -8,7 +8,7 @@
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
diff --git a/rare/components/tabs/shop/__init__.py b/rare/components/tabs/store/__init__.py
similarity index 100%
rename from rare/components/tabs/shop/__init__.py
rename to rare/components/tabs/store/__init__.py
diff --git a/rare/components/tabs/shop/constants.py b/rare/components/tabs/store/constants.py
similarity index 100%
rename from rare/components/tabs/shop/constants.py
rename to rare/components/tabs/store/constants.py
diff --git a/rare/components/tabs/shop/game_info.py b/rare/components/tabs/store/game_info.py
similarity index 99%
rename from rare/components/tabs/shop/game_info.py
rename to rare/components/tabs/store/game_info.py
index ccc9a68b2..650450408 100644
--- a/rare/components/tabs/shop/game_info.py
+++ b/rare/components/tabs/store/game_info.py
@@ -13,8 +13,8 @@
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
diff --git a/rare/components/tabs/shop/game_widgets.py b/rare/components/tabs/store/game_widgets.py
similarity index 98%
rename from rare/components/tabs/shop/game_widgets.py
rename to rare/components/tabs/store/game_widgets.py
index 0da342d2d..7cc2c1f8b 100644
--- a/rare/components/tabs/shop/game_widgets.py
+++ b/rare/components/tabs/store/game_widgets.py
@@ -6,7 +6,7 @@
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
diff --git a/rare/components/tabs/shop/search_results.py b/rare/components/tabs/store/search_results.py
similarity index 100%
rename from rare/components/tabs/shop/search_results.py
rename to rare/components/tabs/store/search_results.py
diff --git a/rare/components/tabs/shop/shop_api_core.py b/rare/components/tabs/store/shop_api_core.py
similarity index 98%
rename from rare/components/tabs/shop/shop_api_core.py
rename to rare/components/tabs/store/shop_api_core.py
index bb1422b9f..f1368b43e 100644
--- a/rare/components/tabs/shop/shop_api_core.py
+++ b/rare/components/tabs/store/shop_api_core.py
@@ -3,15 +3,14 @@
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.components.tabs.store.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequests
-from rare.utils.paths import cache_dir
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
diff --git a/rare/components/tabs/shop/shop_models.py b/rare/components/tabs/store/shop_models.py
similarity index 100%
rename from rare/components/tabs/shop/shop_models.py
rename to rare/components/tabs/store/shop_models.py
diff --git a/rare/components/tabs/shop/shop_widget.py b/rare/components/tabs/store/shop_widget.py
similarity index 100%
rename from rare/components/tabs/shop/shop_widget.py
rename to rare/components/tabs/store/shop_widget.py
diff --git a/rare/components/tabs/shop/wishlist.py b/rare/components/tabs/store/wishlist.py
similarity index 96%
rename from rare/components/tabs/shop/wishlist.py
rename to rare/components/tabs/store/wishlist.py
index df1cae19a..140ad8ad7 100644
--- a/rare/components/tabs/shop/wishlist.py
+++ b/rare/components/tabs/store/wishlist.py
@@ -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
From 9de5627be3560deee5385468a897dfbea5c69a65 Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Mon, 10 Apr 2023 13:52:32 +0300
Subject: [PATCH 15/17] SideTabContainer: Set size policy for the container
scrollarea container widget
---
rare/components/tabs/games/game_info/__init__.py | 1 +
rare/widgets/side_tab.py | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/rare/components/tabs/games/game_info/__init__.py b/rare/components/tabs/games/game_info/__init__.py
index d22a0e002..027085c1e 100644
--- a/rare/components/tabs/games/game_info/__init__.py
+++ b/rare/components/tabs/games/game_info/__init__.py
@@ -72,6 +72,7 @@ def keyPressEvent(self, e: QKeyEvent):
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()
diff --git a/rare/widgets/side_tab.py b/rare/widgets/side_tab.py
index c22589ba8..639495b2c 100644
--- a/rare/widgets/side_tab.py
+++ b/rare/widgets/side_tab.py
@@ -18,7 +18,7 @@
QTabBar,
QTabWidget,
QVBoxLayout,
- QScrollArea, QLayout,
+ QScrollArea, QLayout, QSizePolicy,
)
from rare.utils.misc import icon as qta_icon
@@ -100,13 +100,14 @@ def __init__(self, widget: Union[QWidget, SideTabContentsProtocol], title: str =
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:
From f361828f374da4a7f49ebe5b261d4f6d81c1b51c Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Wed, 13 Dec 2023 15:00:36 +0200
Subject: [PATCH 16/17] Lgndr: Replace `_installed_lock` with a
non-thread-local instance.
Since Python 3.11, `FileLock` is thread-local by default, which causes
numerous issues with Rare because of numerous operations running in
`QThreads` and `QRunnables`. To work around it, add a monkey LGDLFS class
that uses a non-thread-local instance of `FileLock`. Since the monkey class
exists, implement a `unlock_installed` method for code clarity
* Add decorate `LegendaryCore.egl_sync` with `unlock_installed`
* Log that Rare's monkeys are in use
* Add function signature protocols based on `typing.Protocol`
---
rare/lgndr/cli.py | 5 +++--
rare/lgndr/core.py | 21 ++++++++++++++----
rare/lgndr/downloader/mp/manager.py | 4 ++++
rare/lgndr/glue/arguments.py | 23 ++++++++++---------
rare/lgndr/glue/monkeys.py | 34 ++++++++++++++++++++++-------
rare/lgndr/lfs/__init__.py | 0
rare/lgndr/lfs/lgndry.py | 15 +++++++++++++
7 files changed, 78 insertions(+), 24 deletions(-)
create mode 100644 rare/lgndr/lfs/__init__.py
create mode 100644 rare/lgndr/lfs/lgndry.py
diff --git a/rare/lgndr/cli.py b/rare/lgndr/cli.py
index 74ca2a8a8..f831cc7c2 100644
--- a/rare/lgndr/cli.py
+++ b/rare/lgndr/cli.py
@@ -43,15 +43,16 @@ def __del__(self):
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
diff --git a/rare/lgndr/core.py b/rare/lgndr/core.py
index 1b86207ef..1dcc197b0 100644
--- a/rare/lgndr/core.py
+++ b/rare/lgndr/core.py
@@ -17,16 +17,19 @@
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 @@ def __init__(self, override_config=None, timeout=10.0):
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
@@ -149,6 +153,15 @@ def egl_export(self, app_name):
finally:
pass
+ @unlock_installed
+ 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)
diff --git a/rare/lgndr/downloader/mp/manager.py b/rare/lgndr/downloader/mp/manager.py
index b667a5d91..ea9358487 100644
--- a/rare/lgndr/downloader/mp/manager.py
+++ b/rare/lgndr/downloader/mp/manager.py
@@ -17,6 +17,10 @@
# 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
diff --git a/rare/lgndr/glue/arguments.py b/rare/lgndr/glue/arguments.py
index 35f32cad5..915132fbc 100644
--- a/rare/lgndr/glue/arguments.py
+++ b/rare/lgndr/glue/arguments.py
@@ -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)
diff --git a/rare/lgndr/glue/monkeys.py b/rare/lgndr/glue/monkeys.py
index bf784631f..62be07853 100644
--- a/rare/lgndr/glue/monkeys.py
+++ b/rare/lgndr/glue/monkeys.py
@@ -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(sdl_data: Dict, title: str) -> List[str]:
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(a0: int, a1: int, a2: float, a3: float) -> None:
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)
diff --git a/rare/lgndr/lfs/__init__.py b/rare/lgndr/lfs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/rare/lgndr/lfs/lgndry.py b/rare/lgndr/lfs/lgndry.py
new file mode 100644
index 000000000..4d3aae688
--- /dev/null
+++ b/rare/lgndr/lfs/lgndry.py
@@ -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)
From 90acf4b9cea7962dd0d0bf0030a47d7af994cf9d Mon Sep 17 00:00:00 2001
From: loathingKernel <142770+loathingKernel@users.noreply.github.com>
Date: Wed, 13 Dec 2023 15:04:22 +0200
Subject: [PATCH 17/17] Lgndr: Make the decorator compatible with python 3.9
Revert this once we move to python `>=3.10`
---
rare/lgndr/cli.py | 14 +++++++-------
rare/lgndr/core.py | 6 +++---
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/rare/lgndr/cli.py b/rare/lgndr/cli.py
index f831cc7c2..9261bcb8a 100644
--- a/rare/lgndr/cli.py
+++ b/rare/lgndr/cli.py
@@ -59,7 +59,7 @@ def unlock(self, *args, **kwargs):
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)
@@ -230,7 +230,7 @@ def install_game(self, args: LgndrInstallGameArgs) -> Optional[Tuple[DLManager,
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)
@@ -316,7 +316,7 @@ def install_game_real(self, args: LgndrInstallGameRealArgs, dlm: DLManager, game
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)
@@ -387,7 +387,7 @@ def input(x): return 'y' if choice else 'n'
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)
@@ -563,7 +563,7 @@ def verify_game(self, args: Union[LgndrVerifyGameArgs, LgndrInstallGameArgs], pr
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)
@@ -668,11 +668,11 @@ def import_game(self, args: LgndrImportGameArgs) -> None:
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)
diff --git a/rare/lgndr/core.py b/rare/lgndr/core.py
index 1dcc197b0..aee89a78d 100644
--- a/rare/lgndr/core.py
+++ b/rare/lgndr/core.py
@@ -98,7 +98,7 @@ def uninstall_game(self, installed_game: InstalledGame, delete_files=True, delet
finally:
pass
- @unlock_installed
+ @unlock_installed.__func__
def egl_import(self, app_name):
try:
super(LegendaryCore, self).egl_import(app_name)
@@ -144,7 +144,7 @@ def egstore_delete(self, igame: InstalledGame, delete_files=True):
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)
@@ -153,7 +153,7 @@ def egl_export(self, app_name):
finally:
pass
- @unlock_installed
+ @unlock_installed.__func__
def egl_sync(self, app_name=''):
try:
super(LegendaryCore, self).egl_sync(app_name)