Merge pull request #352 from loathingKernel/next
Preparation for future features
This commit is contained in:
commit
826d38ca55
|
@ -76,14 +76,15 @@ class Rare(RareApp):
|
|||
@pyqtSlot()
|
||||
def launch_app(self):
|
||||
self.launch_dialog = LaunchDialog(parent=None)
|
||||
self.launch_dialog.exit_app.connect(self.launch_dialog.close)
|
||||
self.launch_dialog.exit_app.connect(self.__on_exit_app)
|
||||
self.launch_dialog.start_app.connect(self.start_app)
|
||||
self.launch_dialog.start_app.connect(self.launch_dialog.close)
|
||||
self.launch_dialog.rejected.connect(self.__on_exit_app)
|
||||
# lk: the reason we use the `start_app` signal here instead of accepted, is to keep the dialog
|
||||
# until the main window has been created, then we call `accept()` to close the dialog
|
||||
self.launch_dialog.start_app.connect(self.__on_start_app)
|
||||
self.launch_dialog.start_app.connect(self.launch_dialog.accept)
|
||||
self.launch_dialog.login()
|
||||
|
||||
@pyqtSlot()
|
||||
def start_app(self):
|
||||
def __on_start_app(self):
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.re_login)
|
||||
self.poke_timer()
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import platform
|
||||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import QDialog, QApplication
|
||||
from requests.exceptions import ConnectionError, HTTPError
|
||||
|
||||
from rare.components.dialogs.login import LoginDialog
|
||||
from rare.shared import RareCore
|
||||
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
|
||||
from rare.widgets.dialogs import BaseDialog
|
||||
from rare.widgets.elide_label import ElideLabel
|
||||
|
||||
logger = getLogger("LaunchDialog")
|
||||
|
||||
|
||||
class LaunchDialog(QDialog):
|
||||
exit_app = pyqtSignal(int)
|
||||
class LaunchDialog(BaseDialog):
|
||||
# lk: the reason we use the `start_app` signal here instead of accepted, is to keep the dialog
|
||||
# until the main window has been created, then we call `accept()` to close the dialog
|
||||
start_app = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(LaunchDialog, self).__init__(parent=parent)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self.setWindowFlags(
|
||||
Qt.Window
|
||||
| Qt.Dialog
|
||||
|
@ -29,12 +28,10 @@ class LaunchDialog(QDialog):
|
|||
| Qt.WindowMinimizeButtonHint
|
||||
| Qt.MSWindowsFixedSizeDialogHint
|
||||
)
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
|
||||
self.ui = Ui_LaunchDialog()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
self.reject_close = True
|
||||
|
||||
self.progress_info = ElideLabel(parent=self)
|
||||
self.progress_info.setFixedHeight(False)
|
||||
self.ui.launch_layout.addWidget(self.progress_info)
|
||||
|
@ -46,9 +43,11 @@ class LaunchDialog(QDialog):
|
|||
self.args = self.rcore.args()
|
||||
|
||||
self.login_dialog = LoginDialog(core=self.core, parent=parent)
|
||||
self.login_dialog.rejected.connect(self.reject)
|
||||
self.login_dialog.accepted.connect(self.do_launch)
|
||||
|
||||
def login(self):
|
||||
do_launch = True
|
||||
can_launch = True
|
||||
try:
|
||||
if self.args.offline:
|
||||
pass
|
||||
|
@ -56,25 +55,29 @@ class LaunchDialog(QDialog):
|
|||
# Force an update check and notice in case there are API changes
|
||||
# self.core.check_for_updates(force=True)
|
||||
# self.core.force_show_update = True
|
||||
if self.core.login():
|
||||
if self.core.login(force_refresh=True):
|
||||
logger.info("You are logged in")
|
||||
self.login_dialog.close()
|
||||
else:
|
||||
raise ValueError("You are not logged in. Open Login Window")
|
||||
raise ValueError("You are not logged in. Opening login window.")
|
||||
except ValueError as e:
|
||||
logger.info(str(e))
|
||||
# Do not set parent, because it won't show a task bar icon
|
||||
# Update: Inherit the same parent as LaunchDialog
|
||||
do_launch = self.login_dialog.login()
|
||||
can_launch = False
|
||||
self.login_dialog.open()
|
||||
except (HTTPError, ConnectionError) as e:
|
||||
logger.warning(e)
|
||||
self.args.offline = True
|
||||
finally:
|
||||
if do_launch:
|
||||
if not self.args.silent:
|
||||
self.show()
|
||||
self.launch()
|
||||
else:
|
||||
self.exit_app.emit(0)
|
||||
if can_launch:
|
||||
self.do_launch()
|
||||
|
||||
@pyqtSlot()
|
||||
def do_launch(self):
|
||||
if not self.args.silent:
|
||||
self.open()
|
||||
self.launch()
|
||||
|
||||
def launch(self):
|
||||
self.progress_info.setText(self.tr("Preparing Rare"))
|
||||
|
@ -87,9 +90,4 @@ class LaunchDialog(QDialog):
|
|||
|
||||
def __on_completed(self):
|
||||
logger.info("App starting")
|
||||
self.reject_close = False
|
||||
self.start_app.emit()
|
||||
|
||||
def reject(self) -> None:
|
||||
if not self.reject_close:
|
||||
super(LaunchDialog, self).reject()
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
from logging import getLogger
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtWidgets import QLayout, QDialog, QMessageBox, QFrame
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QLayout, QMessageBox, QFrame
|
||||
from legendary.core import LegendaryCore
|
||||
|
||||
from rare.shared import ArgumentsSingleton
|
||||
from rare.ui.components.dialogs.login.landing_page import Ui_LandingPage
|
||||
from rare.ui.components.dialogs.login.login_dialog import Ui_LoginDialog
|
||||
from rare.utils.misc import icon
|
||||
from rare.widgets.dialogs import BaseDialog
|
||||
from rare.widgets.sliding_stack import SlidingStackedWidget
|
||||
from .browser_login import BrowserLogin
|
||||
from .import_login import ImportLogin
|
||||
|
@ -22,12 +24,10 @@ class LandingPage(QFrame):
|
|||
self.ui.setupUi(self)
|
||||
|
||||
|
||||
class LoginDialog(QDialog):
|
||||
exit_app: pyqtSignal = pyqtSignal(int)
|
||||
class LoginDialog(BaseDialog):
|
||||
|
||||
def __init__(self, core: LegendaryCore, parent=None):
|
||||
super(LoginDialog, self).__init__(parent=parent)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self.setWindowFlags(
|
||||
Qt.Window
|
||||
| Qt.Dialog
|
||||
|
@ -38,7 +38,7 @@ class LoginDialog(QDialog):
|
|||
| Qt.WindowCloseButtonHint
|
||||
| Qt.MSWindowsFixedSizeDialogHint
|
||||
)
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
|
||||
self.ui = Ui_LoginDialog()
|
||||
self.ui.setupUi(self)
|
||||
|
||||
|
@ -93,13 +93,22 @@ class LoginDialog(QDialog):
|
|||
self.landing_page.ui.login_import_radio.clicked.connect(lambda: self.ui.next_button.setEnabled(True))
|
||||
self.landing_page.ui.login_import_radio.clicked.connect(self.import_radio_clicked)
|
||||
|
||||
self.ui.exit_button.clicked.connect(self.close)
|
||||
self.ui.exit_button.clicked.connect(self.reject)
|
||||
self.ui.back_button.clicked.connect(self.back_clicked)
|
||||
self.ui.next_button.clicked.connect(self.next_clicked)
|
||||
|
||||
self.login_stack.setCurrentWidget(self.landing_page)
|
||||
|
||||
self.layout().setSizeConstraint(QLayout.SetFixedSize)
|
||||
self.ui.exit_button.setIcon(icon("fa.remove"))
|
||||
self.ui.back_button.setIcon(icon("fa.chevron-left"))
|
||||
self.ui.next_button.setIcon(icon("fa.chevron-right"))
|
||||
|
||||
# lk: Set next as the default button only to stop closing the dialog when pressing enter
|
||||
self.ui.exit_button.setAutoDefault(False)
|
||||
self.ui.back_button.setAutoDefault(False)
|
||||
self.ui.next_button.setAutoDefault(True)
|
||||
|
||||
self.ui.main_layout.setSizeConstraint(QLayout.SetFixedSize)
|
||||
|
||||
def back_clicked(self):
|
||||
self.ui.back_button.setEnabled(False)
|
||||
|
@ -129,15 +138,14 @@ class LoginDialog(QDialog):
|
|||
|
||||
def login(self):
|
||||
if self.args.test_start:
|
||||
return False
|
||||
self.exec_()
|
||||
return self.logged_in
|
||||
self.reject()
|
||||
self.open()
|
||||
|
||||
def login_successful(self):
|
||||
try:
|
||||
if self.core.login():
|
||||
self.logged_in = True
|
||||
self.close()
|
||||
self.accept()
|
||||
else:
|
||||
raise ValueError("Login failed.")
|
||||
except Exception as e:
|
||||
|
@ -146,3 +154,4 @@ class LoginDialog(QDialog):
|
|||
self.ui.next_button.setEnabled(False)
|
||||
self.logged_in = False
|
||||
QMessageBox.warning(None, self.tr("Login error"), str(e))
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class ListWidget(object):
|
|||
|
||||
self.install_btn = QPushButton(parent=widget)
|
||||
self.install_btn.setObjectName(f"{type(self).__name__}Button")
|
||||
self.install_btn.setIcon(icon("ri.install-fill"))
|
||||
self.install_btn.setIcon(icon("ri.install-line"))
|
||||
self.install_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.install_btn.setFixedWidth(120)
|
||||
|
||||
|
|
|
@ -10,8 +10,9 @@ from legendary.lfs import eos
|
|||
from rare.models.install import InstallOptionsModel
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
|
||||
from rare.ui.components.tabs.games.integrations.eos_widget import Ui_EosWidget
|
||||
from rare.utils.misc import icon
|
||||
|
||||
logger = getLogger("EOS")
|
||||
logger = getLogger("EpicOverlay")
|
||||
|
||||
|
||||
def get_wine_prefixes() -> List[str]:
|
||||
|
@ -42,83 +43,86 @@ class CheckForUpdateWorker(QRunnable):
|
|||
self.signals.update_available.emit(self.core.overlay_update_available)
|
||||
|
||||
|
||||
class EOSGroup(QGroupBox, Ui_EosWidget):
|
||||
class EOSGroup(QGroupBox):
|
||||
def __init__(self, parent=None):
|
||||
super(EOSGroup, self).__init__(parent=parent)
|
||||
self.setupUi(self)
|
||||
self.ui = Ui_EosWidget()
|
||||
self.ui.setupUi(self)
|
||||
# lk: set object names for CSS properties
|
||||
self.install_button.setObjectName("InstallButton")
|
||||
self.uninstall_button.setObjectName("UninstallButton")
|
||||
self.ui.install_button.setObjectName("InstallButton")
|
||||
self.ui.install_button.setIcon(icon("ri.install-line"))
|
||||
self.ui.uninstall_button.setObjectName("UninstallButton")
|
||||
self.ui.uninstall_button.setIcon(icon("ri.uninstall-line"))
|
||||
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.signals = GlobalSignalsSingleton()
|
||||
|
||||
self.prefix_enabled = False
|
||||
|
||||
self.enabled_cb.stateChanged.connect(self.change_enable)
|
||||
self.uninstall_button.clicked.connect(self.uninstall_overlay)
|
||||
self.ui.enabled_cb.stateChanged.connect(self.change_enable)
|
||||
self.ui.uninstall_button.clicked.connect(self.uninstall_overlay)
|
||||
|
||||
self.update_button.setVisible(False)
|
||||
self.ui.update_button.setVisible(False)
|
||||
self.overlay = self.core.lgd.get_overlay_install_info()
|
||||
|
||||
self.signals.application.overlay_installed.connect(self.overlay_installation_finished)
|
||||
self.signals.application.prefix_updated.connect(self.update_prefixes)
|
||||
|
||||
self.update_check_button.clicked.connect(self.check_for_update)
|
||||
self.install_button.clicked.connect(self.install_overlay)
|
||||
self.update_button.clicked.connect(lambda: self.install_overlay(True))
|
||||
self.ui.update_check_button.clicked.connect(self.check_for_update)
|
||||
self.ui.install_button.clicked.connect(self.install_overlay)
|
||||
self.ui.update_button.clicked.connect(lambda: self.install_overlay(True))
|
||||
|
||||
if self.overlay: # installed
|
||||
self.installed_version_lbl.setText(f"<b>{self.overlay.version}</b>")
|
||||
self.installed_path_lbl.setText(f"<b>{self.overlay.install_path}</b>")
|
||||
self.overlay_stack.setCurrentIndex(0)
|
||||
self.ui.installed_version_lbl.setText(f"<b>{self.overlay.version}</b>")
|
||||
self.ui.installed_path_lbl.setText(f"<b>{self.overlay.install_path}</b>")
|
||||
self.ui.overlay_stack.setCurrentIndex(0)
|
||||
else:
|
||||
self.overlay_stack.setCurrentIndex(1)
|
||||
self.enable_frame.setDisabled(True)
|
||||
self.ui.overlay_stack.setCurrentIndex(1)
|
||||
self.ui.enable_frame.setDisabled(True)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
self.current_prefix = None
|
||||
self.select_pfx_combo.setVisible(False)
|
||||
self.ui.select_pfx_combo.setVisible(False)
|
||||
else:
|
||||
self.current_prefix = os.path.expanduser("~/.wine") \
|
||||
if os.path.exists(os.path.expanduser("~/.wine")) \
|
||||
else None
|
||||
pfxs = get_wine_prefixes()
|
||||
for pfx in pfxs:
|
||||
self.select_pfx_combo.addItem(pfx.replace(os.path.expanduser("~/"), "~/"))
|
||||
self.ui.select_pfx_combo.addItem(pfx.replace(os.path.expanduser("~/"), "~/"))
|
||||
if not pfxs:
|
||||
self.enable_frame.setDisabled(True)
|
||||
self.ui.enable_frame.setDisabled(True)
|
||||
else:
|
||||
self.select_pfx_combo.setCurrentIndex(0)
|
||||
self.ui.select_pfx_combo.setCurrentIndex(0)
|
||||
|
||||
self.select_pfx_combo.currentIndexChanged.connect(self.update_select_combo)
|
||||
self.ui.select_pfx_combo.currentIndexChanged.connect(self.update_select_combo)
|
||||
if pfxs:
|
||||
self.update_select_combo(None)
|
||||
|
||||
self.enabled_info_label.setText("")
|
||||
self.ui.enabled_info_label.setText("")
|
||||
|
||||
self.threadpool = QThreadPool.globalInstance()
|
||||
|
||||
def update_prefixes(self):
|
||||
logger.debug("Updated prefixes")
|
||||
pfxs = get_wine_prefixes() # returns /home/whatever
|
||||
self.select_pfx_combo.clear()
|
||||
self.ui.select_pfx_combo.clear()
|
||||
|
||||
for pfx in pfxs:
|
||||
self.select_pfx_combo.addItem(pfx.replace(os.path.expanduser("~/"), "~/"))
|
||||
self.ui.select_pfx_combo.addItem(pfx.replace(os.path.expanduser("~/"), "~/"))
|
||||
|
||||
if self.current_prefix in pfxs:
|
||||
self.select_pfx_combo.setCurrentIndex(
|
||||
self.select_pfx_combo.findText(self.current_prefix.replace(os.path.expanduser("~/"), "~/")))
|
||||
self.ui.select_pfx_combo.setCurrentIndex(
|
||||
self.ui.select_pfx_combo.findText(self.current_prefix.replace(os.path.expanduser("~/"), "~/")))
|
||||
|
||||
def check_for_update(self):
|
||||
def worker_finished(update_available):
|
||||
self.update_button.setVisible(update_available)
|
||||
self.update_check_button.setDisabled(False)
|
||||
self.ui.update_button.setVisible(update_available)
|
||||
self.ui.update_check_button.setDisabled(False)
|
||||
if not update_available:
|
||||
self.update_check_button.setText(self.tr("No update available"))
|
||||
self.ui.update_check_button.setText(self.tr("No update available"))
|
||||
|
||||
self.update_check_button.setDisabled(True)
|
||||
self.ui.update_check_button.setDisabled(True)
|
||||
worker = CheckForUpdateWorker()
|
||||
worker.signals.update_available.connect(worker_finished)
|
||||
QThreadPool.globalInstance().start(worker)
|
||||
|
@ -131,18 +135,18 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
QMessageBox.warning(self, "Error", self.tr("Something went wrong, when installing overlay"))
|
||||
return
|
||||
|
||||
self.overlay_stack.setCurrentIndex(0)
|
||||
self.installed_version_lbl.setText(f"<b>{self.overlay.version}</b>")
|
||||
self.installed_path_lbl.setText(f"<b>{self.overlay.install_path}</b>")
|
||||
self.ui.overlay_stack.setCurrentIndex(0)
|
||||
self.ui.installed_version_lbl.setText(f"<b>{self.overlay.version}</b>")
|
||||
self.ui.installed_path_lbl.setText(f"<b>{self.overlay.install_path}</b>")
|
||||
|
||||
self.update_button.setVisible(False)
|
||||
self.ui.update_button.setVisible(False)
|
||||
|
||||
self.enable_frame.setEnabled(True)
|
||||
self.ui.enable_frame.setEnabled(True)
|
||||
|
||||
def update_select_combo(self, i: None):
|
||||
if i is None:
|
||||
i = self.select_pfx_combo.currentIndex()
|
||||
prefix = os.path.expanduser(self.select_pfx_combo.itemText(i))
|
||||
i = self.ui.select_pfx_combo.currentIndex()
|
||||
prefix = os.path.expanduser(self.ui.select_pfx_combo.itemText(i))
|
||||
if platform.system() != "Windows" and not os.path.isfile(os.path.join(prefix, "user.reg")):
|
||||
return
|
||||
self.current_prefix = prefix
|
||||
|
@ -151,10 +155,10 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
overlay_enabled = False
|
||||
if reg_paths['overlay_path'] and self.core.is_overlay_install(reg_paths['overlay_path']):
|
||||
overlay_enabled = True
|
||||
self.enabled_cb.setChecked(overlay_enabled)
|
||||
self.ui.enabled_cb.setChecked(overlay_enabled)
|
||||
|
||||
def change_enable(self):
|
||||
enabled = self.enabled_cb.isChecked()
|
||||
enabled = self.ui.enabled_cb.isChecked()
|
||||
if not enabled:
|
||||
try:
|
||||
eos.remove_registry_entries(self.current_prefix)
|
||||
|
@ -164,7 +168,7 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
"Failed to disable Overlay. Probably it is installed by Epic Games Launcher"))
|
||||
return
|
||||
logger.info("Disabled Epic Overlay")
|
||||
self.enabled_info_label.setText(self.tr("Disabled"))
|
||||
self.ui.enabled_info_label.setText(self.tr("Disabled"))
|
||||
else:
|
||||
if not self.overlay:
|
||||
available_installs = self.core.search_overlay_installs(self.current_prefix)
|
||||
|
@ -177,7 +181,7 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
|
||||
if not self.core.is_overlay_install(path):
|
||||
logger.error(f'Not a valid Overlay installation: {path}')
|
||||
self.select_pfx_combo.removeItem(self.select_pfx_combo.currentIndex())
|
||||
self.ui.select_pfx_combo.removeItem(self.ui.select_pfx_combo.currentIndex())
|
||||
return
|
||||
|
||||
path = os.path.normpath(path)
|
||||
|
@ -202,7 +206,7 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
QMessageBox.warning(self, "Error", self.tr(
|
||||
"Failed to enable EOS overlay. Maybe it is already installed by Epic Games Launcher"))
|
||||
return
|
||||
self.enabled_info_label.setText(self.tr("Enabled"))
|
||||
self.ui.enabled_info_label.setText(self.tr("Enabled"))
|
||||
logger.info(f'Enabled overlay at: {path}')
|
||||
|
||||
def update_checkbox(self):
|
||||
|
@ -210,14 +214,14 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
enabled = False
|
||||
if reg_paths['overlay_path'] and self.core.is_overlay_install(reg_paths['overlay_path']):
|
||||
enabled = True
|
||||
self.enabled_cb.setChecked(enabled)
|
||||
self.ui.enabled_cb.setChecked(enabled)
|
||||
|
||||
def install_overlay(self, update=False):
|
||||
base_path = os.path.join(self.core.get_default_install_dir(), ".overlay")
|
||||
if update:
|
||||
if not self.overlay:
|
||||
self.overlay_stack.setCurrentIndex(1)
|
||||
self.enable_frame.setDisabled(True)
|
||||
self.ui.overlay_stack.setCurrentIndex(1)
|
||||
self.ui.enable_frame.setDisabled(True)
|
||||
QMessageBox.warning(self, "Warning", self.tr("Overlay is not installed. Could not update"))
|
||||
return
|
||||
base_path = self.overlay.install_path
|
||||
|
@ -231,7 +235,7 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
def uninstall_overlay(self):
|
||||
if not self.core.is_overlay_installed():
|
||||
logger.error('No legendary-managed overlay installation found.')
|
||||
self.overlay_stack.setCurrentIndex(1)
|
||||
self.ui.overlay_stack.setCurrentIndex(1)
|
||||
return
|
||||
|
||||
if QMessageBox.No == QMessageBox.question(
|
||||
|
@ -242,7 +246,7 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
if platform.system() == "Windows":
|
||||
eos.remove_registry_entries(None)
|
||||
else:
|
||||
for prefix in [self.select_pfx_combo.itemText(i) for i in range(self.select_pfx_combo.count())]:
|
||||
for prefix in [self.ui.select_pfx_combo.itemText(i) for i in range(self.ui.select_pfx_combo.count())]:
|
||||
logger.info(f"Removing registry entries from {prefix}")
|
||||
try:
|
||||
eos.remove_registry_entries(os.path.expanduser(prefix))
|
||||
|
@ -250,6 +254,6 @@ class EOSGroup(QGroupBox, Ui_EosWidget):
|
|||
logger.warning(f"{prefix}: {e}")
|
||||
|
||||
self.core.remove_overlay_install()
|
||||
self.overlay_stack.setCurrentIndex(1)
|
||||
self.ui.overlay_stack.setCurrentIndex(1)
|
||||
|
||||
self.enable_frame.setDisabled(True)
|
||||
self.ui.enable_frame.setDisabled(True)
|
||||
|
|
|
@ -8,12 +8,15 @@ from PyQt5.QtWidgets import (
|
|||
)
|
||||
|
||||
from rare.components.tabs.settings.widgets.env_vars import EnvVars
|
||||
from rare.components.tabs.settings.widgets.linux import LinuxSettings
|
||||
from rare.components.tabs.settings.widgets.proton import ProtonSettings
|
||||
from rare.components.tabs.settings.widgets.wrapper import WrapperSettings
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.ui.components.tabs.settings.game_settings import Ui_GameSettings
|
||||
|
||||
if platform.system() != "Windows":
|
||||
from rare.components.tabs.settings.widgets.linux import LinuxSettings
|
||||
if platform.system() != "Darwin":
|
||||
from rare.components.tabs.settings.widgets.proton import ProtonSettings
|
||||
|
||||
logger = getLogger("GameSettings")
|
||||
|
||||
|
||||
|
@ -88,15 +91,16 @@ class DefaultGameSettings(QWidget):
|
|||
self.env_vars.update_game(app_name)
|
||||
|
||||
|
||||
class LinuxAppSettings(LinuxSettings):
|
||||
def __init__(self):
|
||||
super(LinuxAppSettings, self).__init__()
|
||||
if platform.system() != "Windows":
|
||||
class LinuxAppSettings(LinuxSettings):
|
||||
def __init__(self):
|
||||
super(LinuxAppSettings, self).__init__()
|
||||
|
||||
def update_game(self, app_name):
|
||||
self.name = app_name
|
||||
self.wine_prefix.setText(self.load_prefix())
|
||||
self.wine_exec.setText(self.load_setting(self.name, "wine_executable"))
|
||||
def update_game(self, app_name):
|
||||
self.name = app_name
|
||||
self.wine_prefix.setText(self.load_prefix())
|
||||
self.wine_exec.setText(self.load_setting(self.name, "wine_executable"))
|
||||
|
||||
self.dxvk.load_settings(self.name)
|
||||
self.dxvk.load_settings(self.name)
|
||||
|
||||
self.mangohud.load_settings(self.name)
|
||||
self.mangohud.load_settings(self.name)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import platform
|
||||
import re
|
||||
import sys
|
||||
from collections import ChainMap
|
||||
|
@ -9,6 +10,10 @@ from PyQt5.QtGui import QFont
|
|||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.utils.misc import icon
|
||||
|
||||
if platform.system() != "Windows":
|
||||
if platform.system() != "Darwin":
|
||||
from rare.utils import proton
|
||||
|
||||
|
||||
class EnvVarsTableModel(QAbstractTableModel):
|
||||
def __init__(self, core: LegendaryCore, parent = None):
|
||||
|
@ -23,11 +28,13 @@ class EnvVarsTableModel(QAbstractTableModel):
|
|||
|
||||
self.__readonly = [
|
||||
"STEAM_COMPAT_DATA_PATH",
|
||||
"STEAM_COMPAT_CLIENT_INSTALL_PATH",
|
||||
"WINEPREFIX",
|
||||
"DXVK_HUD",
|
||||
"MANGOHUD_CONFIG",
|
||||
]
|
||||
if platform.system() != "Windows":
|
||||
if platform.system() != "Darwin":
|
||||
self.__readonly.extend(proton.get_steam_environment(None).keys())
|
||||
|
||||
self.__default: str = "default"
|
||||
self.__appname: str = None
|
||||
|
|
|
@ -9,37 +9,13 @@ from PyQt5.QtWidgets import QGroupBox, QFileDialog
|
|||
from rare.components.tabs.settings import LinuxSettings
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.ui.components.tabs.settings.proton import Ui_ProtonSettings
|
||||
from rare.utils import config_helper
|
||||
from rare.utils import config_helper, proton
|
||||
from rare.widgets.indicator_edit import PathEdit, IndicatorReasonsCommon
|
||||
from .wrapper import WrapperSettings
|
||||
|
||||
logger = getLogger("Proton")
|
||||
|
||||
|
||||
def find_proton_combos():
|
||||
possible_proton_combos = []
|
||||
compatibilitytools_dirs = [
|
||||
os.path.expanduser("~/.steam/steam/steamapps/common"),
|
||||
"/usr/share/steam/compatibilitytools.d",
|
||||
os.path.expanduser("~/.steam/compatibilitytools.d"),
|
||||
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
|
||||
]
|
||||
for c in compatibilitytools_dirs:
|
||||
if os.path.exists(c):
|
||||
for i in os.listdir(c):
|
||||
proton = os.path.join(c, i, "proton")
|
||||
compatibilitytool = os.path.join(c, i, "compatibilitytool.vdf")
|
||||
toolmanifest = os.path.join(c, i, "toolmanifest.vdf")
|
||||
if os.path.exists(proton) and (
|
||||
os.path.exists(compatibilitytool) or os.path.exists(toolmanifest)
|
||||
):
|
||||
wrapper = f'"{proton}" run'
|
||||
possible_proton_combos.append(wrapper)
|
||||
if not possible_proton_combos:
|
||||
logger.warning("Unable to find any Proton version")
|
||||
return possible_proton_combos
|
||||
|
||||
|
||||
class ProtonSettings(QGroupBox):
|
||||
# str: option key
|
||||
environ_changed = pyqtSignal(str)
|
||||
|
@ -53,7 +29,7 @@ class ProtonSettings(QGroupBox):
|
|||
self._linux_settings = linux_settings
|
||||
self._wrapper_settings = wrapper_settings
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.possible_proton_combos = find_proton_combos()
|
||||
self.possible_proton_combos = proton.find_proton_combos()
|
||||
|
||||
self.ui.proton_combo.addItems(self.possible_proton_combos)
|
||||
self.ui.proton_combo.currentIndexChanged.connect(self.change_proton)
|
||||
|
|
|
@ -36,7 +36,7 @@ class Ui_LaunchDialog(object):
|
|||
|
||||
def retranslateUi(self, LaunchDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching - Rare"))
|
||||
LaunchDialog.setWindowTitle(_translate("LaunchDialog", "Launching"))
|
||||
self.title_label.setText(_translate("LaunchDialog", "<h2>Launching Rare</h2>"))
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Launching - Rare</string>
|
||||
<string>Launching</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="launch_layout">
|
||||
<item>
|
||||
|
|
|
@ -15,38 +15,38 @@ class Ui_LoginDialog(object):
|
|||
def setupUi(self, LoginDialog):
|
||||
LoginDialog.setObjectName("LoginDialog")
|
||||
LoginDialog.resize(241, 128)
|
||||
self.login_layout = QtWidgets.QVBoxLayout(LoginDialog)
|
||||
self.login_layout.setObjectName("login_layout")
|
||||
self.main_layout = QtWidgets.QVBoxLayout(LoginDialog)
|
||||
self.main_layout.setObjectName("main_layout")
|
||||
spacerItem = QtWidgets.QSpacerItem(0, 17, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
self.login_layout.addItem(spacerItem)
|
||||
self.main_layout.addItem(spacerItem)
|
||||
self.welcome_label = QtWidgets.QLabel(LoginDialog)
|
||||
self.welcome_label.setObjectName("welcome_label")
|
||||
self.login_layout.addWidget(self.welcome_label)
|
||||
self.main_layout.addWidget(self.welcome_label)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(0, 17, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
self.login_layout.addItem(spacerItem1)
|
||||
self.main_layout.addItem(spacerItem1)
|
||||
self.login_stack_layout = QtWidgets.QVBoxLayout()
|
||||
self.login_stack_layout.setObjectName("login_stack_layout")
|
||||
self.login_layout.addLayout(self.login_stack_layout)
|
||||
self.main_layout.addLayout(self.login_stack_layout)
|
||||
self.button_layout = QtWidgets.QHBoxLayout()
|
||||
self.button_layout.setObjectName("button_layout")
|
||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.button_layout.addItem(spacerItem2)
|
||||
self.exit_button = QtWidgets.QPushButton(LoginDialog)
|
||||
self.exit_button.setObjectName("exit_button")
|
||||
self.button_layout.addWidget(self.exit_button)
|
||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.button_layout.addItem(spacerItem2)
|
||||
self.back_button = QtWidgets.QPushButton(LoginDialog)
|
||||
self.back_button.setObjectName("back_button")
|
||||
self.button_layout.addWidget(self.back_button)
|
||||
self.next_button = QtWidgets.QPushButton(LoginDialog)
|
||||
self.next_button.setObjectName("next_button")
|
||||
self.button_layout.addWidget(self.next_button)
|
||||
self.login_layout.addLayout(self.button_layout)
|
||||
self.main_layout.addLayout(self.button_layout)
|
||||
|
||||
self.retranslateUi(LoginDialog)
|
||||
|
||||
def retranslateUi(self, LoginDialog):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
LoginDialog.setWindowTitle(_translate("LoginDialog", "Login - Rare"))
|
||||
LoginDialog.setWindowTitle(_translate("LoginDialog", "Login"))
|
||||
self.welcome_label.setText(_translate("LoginDialog", "<h1>Welcome to Rare</h1>"))
|
||||
self.exit_button.setText(_translate("LoginDialog", "Exit"))
|
||||
self.back_button.setText(_translate("LoginDialog", "Back"))
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Login - Rare</string>
|
||||
<string>Login</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="login_layout" stretch="0,0,0,0,0">
|
||||
<layout class="QVBoxLayout" name="main_layout" stretch="0,0,0,0,0">
|
||||
<item>
|
||||
<spacer name="login_vspacer_top">
|
||||
<property name="orientation">
|
||||
|
@ -58,6 +58,13 @@
|
|||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="button_layout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="exit_button">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="button_hspacer">
|
||||
<property name="orientation">
|
||||
|
@ -71,13 +78,6 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exit_button">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="back_button">
|
||||
<property name="text">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/import_sync/eos_widget.ui'
|
||||
# Form implementation generated from reading ui file 'rare/ui/components/tabs/games/integrations/eos_widget.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.7
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
#
|
||||
# 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.
|
||||
|
@ -30,99 +30,85 @@ class Ui_EosWidget(object):
|
|||
self.overlay_stack.setObjectName("overlay_stack")
|
||||
self.overlay_info_page = QtWidgets.QWidget()
|
||||
self.overlay_info_page.setObjectName("overlay_info_page")
|
||||
self.formLayout_3 = QtWidgets.QFormLayout(self.overlay_info_page)
|
||||
self.formLayout_3.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout_3.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
|
||||
self.formLayout_3.setObjectName("formLayout_3")
|
||||
self.overlay_info_layout = QtWidgets.QFormLayout(self.overlay_info_page)
|
||||
self.overlay_info_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.overlay_info_layout.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
|
||||
self.overlay_info_layout.setObjectName("overlay_info_layout")
|
||||
self.installed_version_info_lbl = QtWidgets.QLabel(self.overlay_info_page)
|
||||
self.installed_version_info_lbl.setObjectName("installed_version_info_lbl")
|
||||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.installed_version_info_lbl)
|
||||
self.overlay_info_layout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.installed_version_info_lbl)
|
||||
self.installed_version_lbl = QtWidgets.QLabel(self.overlay_info_page)
|
||||
self.installed_version_lbl.setText("error")
|
||||
self.installed_version_lbl.setObjectName("installed_version_lbl")
|
||||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.installed_version_lbl)
|
||||
self.overlay_info_layout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.installed_version_lbl)
|
||||
self.installed_path_info_lbl = QtWidgets.QLabel(self.overlay_info_page)
|
||||
self.installed_path_info_lbl.setObjectName("installed_path_info_lbl")
|
||||
self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.installed_path_info_lbl)
|
||||
self.overlay_info_layout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.installed_path_info_lbl)
|
||||
self.installed_path_lbl = QtWidgets.QLabel(self.overlay_info_page)
|
||||
self.installed_path_lbl.setText("error")
|
||||
self.installed_path_lbl.setObjectName("installed_path_lbl")
|
||||
self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.installed_path_lbl)
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.overlay_info_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.installed_path_lbl)
|
||||
self.info_buttons_layout = QtWidgets.QHBoxLayout()
|
||||
self.info_buttons_layout.setObjectName("info_buttons_layout")
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.info_buttons_layout.addItem(spacerItem)
|
||||
self.uninstall_button = QtWidgets.QPushButton(self.overlay_info_page)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.uninstall_button.sizePolicy().hasHeightForWidth())
|
||||
self.uninstall_button.setSizePolicy(sizePolicy)
|
||||
self.uninstall_button.setMaximumSize(QtCore.QSize(150, 16777215))
|
||||
self.uninstall_button.setMinimumSize(QtCore.QSize(120, 0))
|
||||
self.uninstall_button.setObjectName("uninstall_button")
|
||||
self.horizontalLayout.addWidget(self.uninstall_button)
|
||||
self.info_buttons_layout.addWidget(self.uninstall_button)
|
||||
self.update_check_button = QtWidgets.QPushButton(self.overlay_info_page)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.update_check_button.sizePolicy().hasHeightForWidth())
|
||||
self.update_check_button.setSizePolicy(sizePolicy)
|
||||
self.update_check_button.setMaximumSize(QtCore.QSize(150, 16777215))
|
||||
self.update_check_button.setMinimumSize(QtCore.QSize(120, 0))
|
||||
self.update_check_button.setObjectName("update_check_button")
|
||||
self.horizontalLayout.addWidget(self.update_check_button)
|
||||
self.info_buttons_layout.addWidget(self.update_check_button)
|
||||
self.update_button = QtWidgets.QPushButton(self.overlay_info_page)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.update_button.sizePolicy().hasHeightForWidth())
|
||||
self.update_button.setSizePolicy(sizePolicy)
|
||||
self.update_button.setMaximumSize(QtCore.QSize(150, 16777215))
|
||||
self.update_button.setMinimumSize(QtCore.QSize(120, 0))
|
||||
self.update_button.setObjectName("update_button")
|
||||
self.horizontalLayout.addWidget(self.update_button)
|
||||
self.formLayout_3.setLayout(3, QtWidgets.QFormLayout.SpanningRole, self.horizontalLayout)
|
||||
self.info_buttons_layout.addWidget(self.update_button)
|
||||
self.overlay_info_layout.setLayout(3, QtWidgets.QFormLayout.SpanningRole, self.info_buttons_layout)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(6, 6, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.formLayout_3.setItem(2, QtWidgets.QFormLayout.SpanningRole, spacerItem1)
|
||||
self.overlay_info_layout.setItem(2, QtWidgets.QFormLayout.SpanningRole, spacerItem1)
|
||||
self.overlay_stack.addWidget(self.overlay_info_page)
|
||||
self.overlay_install_page = QtWidgets.QWidget()
|
||||
self.overlay_install_page.setObjectName("overlay_install_page")
|
||||
self.formLayout = QtWidgets.QFormLayout(self.overlay_install_page)
|
||||
self.formLayout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.formLayout.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.overlay_install_layout = QtWidgets.QFormLayout(self.overlay_install_page)
|
||||
self.overlay_install_layout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.overlay_install_layout.setFormAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
|
||||
self.overlay_install_layout.setObjectName("overlay_install_layout")
|
||||
self.label = QtWidgets.QLabel(self.overlay_install_page)
|
||||
self.label.setObjectName("label")
|
||||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.label)
|
||||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||
self.overlay_install_layout.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.label)
|
||||
self.install_buttons_layout = QtWidgets.QHBoxLayout()
|
||||
self.install_buttons_layout.setObjectName("install_buttons_layout")
|
||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_3.addItem(spacerItem2)
|
||||
self.install_buttons_layout.addItem(spacerItem2)
|
||||
self.install_button = QtWidgets.QPushButton(self.overlay_install_page)
|
||||
self.install_button.setMinimumSize(QtCore.QSize(120, 0))
|
||||
self.install_button.setObjectName("install_button")
|
||||
self.horizontalLayout_3.addWidget(self.install_button)
|
||||
self.formLayout.setLayout(2, QtWidgets.QFormLayout.SpanningRole, self.horizontalLayout_3)
|
||||
self.install_buttons_layout.addWidget(self.install_button)
|
||||
self.overlay_install_layout.setLayout(2, QtWidgets.QFormLayout.SpanningRole, self.install_buttons_layout)
|
||||
spacerItem3 = QtWidgets.QSpacerItem(6, 6, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.formLayout.setItem(1, QtWidgets.QFormLayout.SpanningRole, spacerItem3)
|
||||
self.overlay_install_layout.setItem(1, QtWidgets.QFormLayout.SpanningRole, spacerItem3)
|
||||
self.overlay_stack.addWidget(self.overlay_install_page)
|
||||
self.eos_layout.addWidget(self.overlay_stack)
|
||||
self.enable_frame = QtWidgets.QFrame(EosWidget)
|
||||
self.enable_frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
|
||||
self.enable_frame.setFrameShadow(QtWidgets.QFrame.Raised)
|
||||
self.enable_frame.setObjectName("enable_frame")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.enable_frame)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.enable_layout = QtWidgets.QVBoxLayout(self.enable_frame)
|
||||
self.enable_layout.setObjectName("enable_layout")
|
||||
self.select_pfx_combo = QtWidgets.QComboBox(self.enable_frame)
|
||||
self.select_pfx_combo.setObjectName("select_pfx_combo")
|
||||
self.verticalLayout.addWidget(self.select_pfx_combo)
|
||||
self.enable_layout.addWidget(self.select_pfx_combo)
|
||||
self.enabled_cb = QtWidgets.QCheckBox(self.enable_frame)
|
||||
self.enabled_cb.setObjectName("enabled_cb")
|
||||
self.verticalLayout.addWidget(self.enabled_cb)
|
||||
self.enable_layout.addWidget(self.enabled_cb)
|
||||
self.enabled_info_label = QtWidgets.QLabel(self.enable_frame)
|
||||
font = QtGui.QFont()
|
||||
font.setItalic(True)
|
||||
self.enabled_info_label.setFont(font)
|
||||
self.enabled_info_label.setText("")
|
||||
self.enabled_info_label.setObjectName("enabled_info_label")
|
||||
self.verticalLayout.addWidget(self.enabled_info_label)
|
||||
self.enable_layout.addWidget(self.enabled_info_label)
|
||||
self.eos_layout.addWidget(self.enable_frame)
|
||||
|
||||
self.retranslateUi(EosWidget)
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="overlay_info_page">
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<layout class="QFormLayout" name="overlay_info_layout">
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
|
@ -74,9 +74,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="info_buttons_layout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<spacer name="info_buttons_hspacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -90,16 +90,10 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="uninstall_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -109,16 +103,10 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="update_check_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -128,16 +116,10 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="update_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>16777215</height>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
|
@ -148,7 +130,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<spacer name="info_page_vspacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
@ -163,7 +145,7 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="overlay_install_page">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<layout class="QFormLayout" name="overlay_install_layout">
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
|
@ -178,9 +160,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<layout class="QHBoxLayout" name="install_buttons_layout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<spacer name="install_buttons_hspacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -194,6 +176,12 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="install_button">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Install</string>
|
||||
</property>
|
||||
|
@ -202,7 +190,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<spacer name="install_page_vspacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
@ -226,7 +214,7 @@
|
|||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="enable_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="select_pfx_combo"/>
|
||||
</item>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from typing import Callable, Optional
|
||||
import os
|
||||
from typing import Callable, Optional, Set, Any
|
||||
|
||||
from legendary.core import LegendaryCore
|
||||
from legendary.models.config import LGDConf
|
||||
|
@ -40,6 +41,48 @@ def remove_option(app_name, option):
|
|||
|
||||
def remove_section(app_name):
|
||||
return
|
||||
# Disabled due to env variables implementation
|
||||
if _config.has_section(app_name):
|
||||
_config.remove_section(app_name)
|
||||
save_config()
|
||||
|
||||
|
||||
def get_game_option(option: str, app_name: Optional[str] = None, fallback: Any = None) -> str:
|
||||
_option = _config.get("default", option, fallback=fallback)
|
||||
if app_name is not None:
|
||||
_option = _config.get(app_name, option, fallback=_option)
|
||||
return _option
|
||||
|
||||
|
||||
def get_game_envvar(option: str, app_name: Optional[str] = None, fallback: Any = None) -> str:
|
||||
_option = _config.get("default.env", option, fallback=fallback)
|
||||
if app_name is not None:
|
||||
_option = _config.get(f'{app_name}.env', option, fallback=_option)
|
||||
return _option
|
||||
|
||||
|
||||
def get_wine_prefix(app_name: Optional[str] = None, fallback: Any = None) -> str:
|
||||
_prefix = os.path.join(
|
||||
_config.get("default.env", "STEAM_COMPAT_DATA_PATH", fallback=fallback), "pfx")
|
||||
if app_name is not None:
|
||||
_prefix = os.path.join(
|
||||
_config.get(f'{app_name}.env', "STEAM_COMPAT_DATA_PATH", fallback=_prefix), "pfx")
|
||||
_prefix = _config.get("default.env", "WINEPREFIX", fallback=_prefix)
|
||||
_prefix = _config.get("default", "wine_prefix", fallback=_prefix)
|
||||
if app_name is not None:
|
||||
_prefix = _config.get(f'{app_name}.env', 'WINEPREFIX', fallback=_prefix)
|
||||
_prefix = _config.get(app_name, 'wine_prefix', fallback=_prefix)
|
||||
return _prefix
|
||||
|
||||
|
||||
def get_wine_prefixes() -> Set[str]:
|
||||
_prefixes = []
|
||||
for name, section in _config.items():
|
||||
pfx = section.get("WINEPREFIX") or section.get("wine_prefix")
|
||||
if not pfx:
|
||||
pfx = os.path.join(compatdata, "pfx") if (compatdata := section.get("STEAM_COMPAT_DATA_PATH")) else ""
|
||||
if pfx:
|
||||
_prefixes.append(pfx)
|
||||
_prefixes = [os.path.expanduser(prefix) for prefix in _prefixes]
|
||||
return {p for p in _prefixes if os.path.isdir(p)}
|
||||
|
||||
|
|
271
rare/utils/proton.py
Normal file
271
rare/utils/proton.py
Normal file
|
@ -0,0 +1,271 @@
|
|||
import os
|
||||
from dataclasses import dataclass
|
||||
from logging import getLogger
|
||||
from typing import Optional, Union, List, Dict
|
||||
|
||||
import vdf
|
||||
|
||||
logger = getLogger("Proton")
|
||||
|
||||
steam_compat_client_install_paths = [os.path.expanduser("~/.local/share/Steam")]
|
||||
|
||||
|
||||
def find_steam() -> str:
|
||||
# return the first valid path
|
||||
for path in steam_compat_client_install_paths:
|
||||
if os.path.isdir(path) and os.path.isfile(os.path.join(path, "steam.sh")):
|
||||
return path
|
||||
|
||||
|
||||
def find_libraries(steam_path: str) -> List[str]:
|
||||
vdf_path = os.path.join(steam_path, "steamapps", "libraryfolders.vdf")
|
||||
with open(vdf_path, "r") as f:
|
||||
libraryfolders = vdf.load(f)["libraryfolders"]
|
||||
libraries = [os.path.join(folder["path"], "steamapps") for key, folder in libraryfolders.items()]
|
||||
return libraries
|
||||
|
||||
|
||||
@dataclass
|
||||
class SteamBase:
|
||||
steam_path: str
|
||||
tool_path: str
|
||||
toolmanifest: dict
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.tool_path == other.tool_path
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.tool_path)
|
||||
|
||||
def commandline(self):
|
||||
cmd = "".join([f"'{self.tool_path}'", self.toolmanifest["manifest"]["commandline"]])
|
||||
cmd = os.path.normpath(cmd)
|
||||
# NOTE: "waitforexitandrun" seems to be the verb used in by steam to execute stuff
|
||||
cmd = cmd.replace("%verb%", "waitforexitandrun")
|
||||
return cmd
|
||||
|
||||
|
||||
@dataclass
|
||||
class SteamRuntime(SteamBase):
|
||||
steam_library: str
|
||||
appmanifest: dict
|
||||
|
||||
def name(self):
|
||||
return self.appmanifest["AppState"]["name"]
|
||||
|
||||
def appid(self):
|
||||
return self.appmanifest["AppState"]["appid"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProtonTool(SteamRuntime):
|
||||
runtime: SteamRuntime = None
|
||||
|
||||
def __bool__(self):
|
||||
if appid := self.toolmanifest.get("require_tool_appid", False):
|
||||
return self.runtime is not None and self.runtime.appid() == appid
|
||||
|
||||
def commandline(self):
|
||||
runtime_cmd = self.runtime.commandline()
|
||||
cmd = super().commandline()
|
||||
return " ".join([runtime_cmd, cmd])
|
||||
|
||||
|
||||
@dataclass
|
||||
class CompatibilityTool(SteamBase):
|
||||
compatibilitytool: dict
|
||||
runtime: SteamRuntime = None
|
||||
|
||||
def __bool__(self):
|
||||
if appid := self.toolmanifest.get("require_tool_appid", False):
|
||||
return self.runtime is not None and self.runtime.appid() == appid
|
||||
|
||||
def name(self):
|
||||
name, data = list(self.compatibilitytool["compatibilitytools"]["compat_tools"].items())[0]
|
||||
return data["display_name"]
|
||||
|
||||
def commandline(self):
|
||||
runtime_cmd = self.runtime.commandline() if self.runtime is not None else ""
|
||||
cmd = super().commandline()
|
||||
return " ".join([runtime_cmd, cmd])
|
||||
|
||||
|
||||
def find_appmanifests(library: str) -> List[dict]:
|
||||
appmanifests = []
|
||||
for entry in os.scandir(library):
|
||||
if entry.is_file() and entry.name.endswith(".acf"):
|
||||
with open(os.path.join(library, entry.name), "r") as f:
|
||||
appmanifest = vdf.load(f)
|
||||
appmanifests.append(appmanifest)
|
||||
return appmanifests
|
||||
|
||||
|
||||
def find_protons(steam_path: str, library: str) -> List[ProtonTool]:
|
||||
protons = []
|
||||
appmanifests = find_appmanifests(library)
|
||||
common = os.path.join(library, "common")
|
||||
for appmanifest in appmanifests:
|
||||
folder = appmanifest["AppState"]["installdir"]
|
||||
tool_path = os.path.join(common, folder)
|
||||
if os.path.isfile(vdf_file := os.path.join(tool_path, "toolmanifest.vdf")):
|
||||
with open(vdf_file, "r") as f:
|
||||
toolmanifest = vdf.load(f)
|
||||
if toolmanifest["manifest"]["compatmanager_layer_name"] == "proton":
|
||||
protons.append(
|
||||
ProtonTool(
|
||||
steam_path=steam_path,
|
||||
steam_library=library,
|
||||
appmanifest=appmanifest,
|
||||
tool_path=tool_path,
|
||||
toolmanifest=toolmanifest,
|
||||
)
|
||||
)
|
||||
return protons
|
||||
|
||||
|
||||
def find_compatibility_tools(steam_path: str) -> List[CompatibilityTool]:
|
||||
compatibilitytools_paths = {
|
||||
"/usr/share/steam/compatibilitytools.d",
|
||||
os.path.expanduser(os.path.join(steam_path, "compatibilitytools.d")),
|
||||
os.path.expanduser("~/.steam/compatibilitytools.d"),
|
||||
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
|
||||
}
|
||||
compatibilitytools_paths = {
|
||||
os.path.realpath(path) for path in compatibilitytools_paths if os.path.isdir(path)
|
||||
}
|
||||
tools = []
|
||||
for path in compatibilitytools_paths:
|
||||
for entry in os.scandir(path):
|
||||
if entry.is_dir():
|
||||
tool_path = os.path.join(path, entry.name)
|
||||
tool_vdf = os.path.join(tool_path, "compatibilitytool.vdf")
|
||||
manifest_vdf = os.path.join(tool_path, "toolmanifest.vdf")
|
||||
if os.path.isfile(tool_vdf) and os.path.isfile(manifest_vdf):
|
||||
with open(tool_vdf, "r") as f:
|
||||
compatibilitytool = vdf.load(f)
|
||||
with open(manifest_vdf, "r") as f:
|
||||
manifest = vdf.load(f)
|
||||
tools.append(
|
||||
CompatibilityTool(
|
||||
steam_path=steam_path,
|
||||
tool_path=tool_path,
|
||||
toolmanifest=manifest,
|
||||
compatibilitytool=compatibilitytool,
|
||||
)
|
||||
)
|
||||
return tools
|
||||
|
||||
|
||||
def find_runtimes(steam_path: str, library: str) -> Dict[str, SteamRuntime]:
|
||||
runtimes = {}
|
||||
appmanifests = find_appmanifests(library)
|
||||
common = os.path.join(library, "common")
|
||||
for appmanifest in appmanifests:
|
||||
folder = appmanifest["AppState"]["installdir"]
|
||||
tool_path = os.path.join(common, folder)
|
||||
if os.path.isfile(vdf_file := os.path.join(tool_path, "toolmanifest.vdf")):
|
||||
with open(vdf_file, "r") as f:
|
||||
toolmanifest = vdf.load(f)
|
||||
if toolmanifest["manifest"]["compatmanager_layer_name"] == "container-runtime":
|
||||
runtimes.update(
|
||||
{
|
||||
appmanifest["AppState"]["appid"]: SteamRuntime(
|
||||
steam_path=steam_path,
|
||||
steam_library=library,
|
||||
appmanifest=appmanifest,
|
||||
tool_path=tool_path,
|
||||
toolmanifest=toolmanifest,
|
||||
)
|
||||
}
|
||||
)
|
||||
return runtimes
|
||||
|
||||
|
||||
def find_runtime(
|
||||
tool: Union[ProtonTool, CompatibilityTool], runtimes: Dict[str, SteamRuntime]
|
||||
) -> Optional[SteamRuntime]:
|
||||
required_tool = tool.toolmanifest["manifest"].get("require_tool_appid")
|
||||
if required_tool is None:
|
||||
return None
|
||||
return runtimes[required_tool]
|
||||
|
||||
|
||||
def get_steam_environment(tool: Optional[Union[ProtonTool, CompatibilityTool]], app_name: str = None) -> Dict:
|
||||
environ = {}
|
||||
# If the tool is unset, return all affected env variable names
|
||||
# IMPORTANT: keep this in sync with the code below
|
||||
if tool is None:
|
||||
environ["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = ""
|
||||
environ["STEAM_COMPAT_LIBRARY_PATHS"] = ""
|
||||
environ["STEAM_COMPAT_MOUNTS"] = ""
|
||||
environ["STEAM_COMPAT_TOOL_PATHS"] = ""
|
||||
return environ
|
||||
|
||||
environ["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = tool.steam_path
|
||||
if isinstance(tool, ProtonTool):
|
||||
environ["STEAM_COMPAT_LIBRARY_PATHS"] = tool.steam_library
|
||||
if tool.runtime is not None:
|
||||
compat_mounts = [tool.tool_path, tool.runtime.tool_path]
|
||||
environ["STEAM_COMPAT_MOUNTS"] = ":".join(compat_mounts)
|
||||
tool_paths = [tool.tool_path]
|
||||
if tool.runtime is not None:
|
||||
tool_paths.append(tool.runtime.tool_path)
|
||||
environ["STEAM_COMPAT_TOOL_PATHS"] = ":".join(tool_paths)
|
||||
return environ
|
||||
|
||||
|
||||
def find_tools() -> List[Union[ProtonTool, CompatibilityTool]]:
|
||||
steam_path = find_steam()
|
||||
logger.debug("Using Steam install in %s", steam_path)
|
||||
steam_libraries = find_libraries(steam_path)
|
||||
logger.debug("Searching for tools in libraries %s", steam_libraries)
|
||||
|
||||
runtimes = {}
|
||||
for library in steam_libraries:
|
||||
runtimes.update(find_runtimes(steam_path, library))
|
||||
|
||||
tools = []
|
||||
for library in steam_libraries:
|
||||
tools.extend(find_protons(steam_path, library))
|
||||
tools.extend(find_compatibility_tools(steam_path))
|
||||
|
||||
for tool in tools:
|
||||
runtime = find_runtime(tool, runtimes)
|
||||
tool.runtime = runtime
|
||||
|
||||
return tools
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from pprint import pprint
|
||||
|
||||
_tools = find_tools()
|
||||
pprint(_tools)
|
||||
|
||||
for tool in _tools:
|
||||
print(get_steam_environment(tool))
|
||||
print(tool.name(), tool.commandline())
|
||||
|
||||
|
||||
def find_proton_combos():
|
||||
possible_proton_combos = []
|
||||
compatibilitytools_dirs = [
|
||||
os.path.expanduser("~/.steam/steam/steamapps/common"),
|
||||
"/usr/share/steam/compatibilitytools.d",
|
||||
os.path.expanduser("~/.steam/compatibilitytools.d"),
|
||||
os.path.expanduser("~/.steam/root/compatibilitytools.d"),
|
||||
]
|
||||
for c in compatibilitytools_dirs:
|
||||
if os.path.exists(c):
|
||||
for i in os.listdir(c):
|
||||
proton = os.path.join(c, i, "proton")
|
||||
compatibilitytool = os.path.join(c, i, "compatibilitytool.vdf")
|
||||
toolmanifest = os.path.join(c, i, "toolmanifest.vdf")
|
||||
if os.path.exists(proton) and (
|
||||
os.path.exists(compatibilitytool) or os.path.exists(toolmanifest)
|
||||
):
|
||||
wrapper = f'"{proton}" run'
|
||||
possible_proton_combos.append(wrapper)
|
||||
if not possible_proton_combos:
|
||||
logger.warning("Unable to find any Proton version")
|
||||
return possible_proton_combos
|
283
rare/widgets/dialogs.py
Normal file
283
rare/widgets/dialogs.py
Normal file
|
@ -0,0 +1,283 @@
|
|||
import sys
|
||||
from abc import abstractmethod
|
||||
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, QCoreApplication, QSize
|
||||
from PyQt5.QtGui import QCloseEvent, QKeyEvent, QKeySequence
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QApplication,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QWidget,
|
||||
QLayout, QSpacerItem, QSizePolicy,
|
||||
)
|
||||
|
||||
from rare.utils.misc import icon
|
||||
|
||||
|
||||
def dialog_title_game(text: str, app_title: str) -> str:
|
||||
return f"{text} '{app_title}'"
|
||||
|
||||
|
||||
def dialog_title(text: str) -> str:
|
||||
return f"{text} - {QCoreApplication.instance().applicationName()}"
|
||||
|
||||
|
||||
class BaseDialog(QDialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(BaseDialog, self).__init__(parent=parent)
|
||||
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint)
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
|
||||
def setWindowTitle(self, a0):
|
||||
super().setWindowTitle(dialog_title(a0))
|
||||
|
||||
def exec(self):
|
||||
raise RuntimeError(f"Don't use `exec()` with {type(self).__name__}")
|
||||
|
||||
def exec_(self):
|
||||
raise RuntimeError(f"Don't use `exec_()` with {type(self).__name__}")
|
||||
|
||||
# lk: because you will eventually find yourself back here.
|
||||
# on QDialogs the Esc key closes the dialog through keyPressEvent(),
|
||||
# which ultimately call `reject()`. Pressing the Enter/Return button
|
||||
# is a shortcut for pressing the default button and thus calling `accept()`
|
||||
# In turn both `accept()` and `reject()` evetually call `done()`.
|
||||
|
||||
# In the base dialog ignore both. In the subclasses, call the method
|
||||
# from QDialog if required, not this one.
|
||||
# `super(BaseDialog, self).keyPressEvent(a0)`
|
||||
def keyPressEvent(self, a0: QKeyEvent) -> None:
|
||||
if a0.matches(QKeySequence.Cancel):
|
||||
a0.ignore()
|
||||
return
|
||||
if a0.key() == Qt.Key_Enter or a0.key() == Qt.Key_Return:
|
||||
a0.ignore()
|
||||
return
|
||||
super().keyPressEvent(a0)
|
||||
|
||||
# Using the 'X' button on the window manager comes directly here.
|
||||
# It is a spontaneous event so simply ignore it.
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
if a0.spontaneous():
|
||||
a0.ignore()
|
||||
return
|
||||
super().closeEvent(a0)
|
||||
|
||||
|
||||
class ButtonDialog(BaseDialog):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(ButtonDialog, self).__init__(parent=parent)
|
||||
|
||||
self.reject_button = QPushButton(self)
|
||||
self.reject_button.setText(self.tr("Cancel"))
|
||||
self.reject_button.setIcon(icon("fa.remove"))
|
||||
self.reject_button.setAutoDefault(False)
|
||||
self.reject_button.clicked.connect(self.reject)
|
||||
|
||||
self.accept_button = QPushButton(self)
|
||||
self.accept_button.setAutoDefault(False)
|
||||
self.accept_button.clicked.connect(self.accept)
|
||||
|
||||
self.button_layout = QHBoxLayout()
|
||||
self.button_layout.addWidget(self.reject_button)
|
||||
self.button_layout.addStretch(20)
|
||||
self.button_layout.addStretch(1)
|
||||
self.button_layout.addWidget(self.accept_button)
|
||||
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
# lk: dirty way to set a minimum width with fixed size constraint
|
||||
spacer = QSpacerItem(
|
||||
480, self.main_layout.spacing(),
|
||||
QSizePolicy.Expanding, QSizePolicy.Fixed
|
||||
)
|
||||
self.main_layout.addItem(spacer)
|
||||
self.main_layout.addLayout(self.button_layout)
|
||||
self.main_layout.setSizeConstraint(QLayout.SetFixedSize)
|
||||
|
||||
def close(self):
|
||||
raise RuntimeError(f"Don't use `close()` with {type(self).__name__}")
|
||||
|
||||
def setCentralWidget(self, widget: QWidget):
|
||||
widget.layout().setContentsMargins(0, 0, 0, 0)
|
||||
self.main_layout.insertWidget(0, widget)
|
||||
|
||||
def setCentralLayout(self, layout: QLayout):
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.main_layout.insertLayout(0, layout)
|
||||
|
||||
@abstractmethod
|
||||
def accept_handler(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def reject_handler(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def done_handler(self):
|
||||
raise NotImplementedError
|
||||
|
||||
# These only apply to QDialog. If we move to QWidget for embedded dialogs
|
||||
# we have to use close() and custom handling.
|
||||
|
||||
# lk: Override accept to run our abstract handling method
|
||||
def accept(self):
|
||||
self.accept_handler()
|
||||
super().accept()
|
||||
|
||||
# lk: Override reject to run our abstract handling method
|
||||
def reject(self):
|
||||
self.reject_handler()
|
||||
super().reject()
|
||||
|
||||
# lk: Override `done()` to to run our abstract handling method
|
||||
def done(self, a0):
|
||||
self.done_handler()
|
||||
super().done(a0)
|
||||
|
||||
# lk: Ignore BaseDialog::keyPressEvent and call QDialog::keyPressEvent
|
||||
# because we handle accept and reject here.
|
||||
def keyPressEvent(self, a0: QKeyEvent) -> None:
|
||||
super(BaseDialog, self).keyPressEvent(a0)
|
||||
|
||||
# lk: Ignore BaseDialog::closeEvent and call QDialog::closeEvent
|
||||
# because we handle accept and reject here.
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
super(BaseDialog, self).closeEvent(a0)
|
||||
|
||||
|
||||
class ActionDialog(ButtonDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(ActionDialog, self).__init__(parent=parent)
|
||||
self.__reject_close = False
|
||||
|
||||
self.action_button = QPushButton(self)
|
||||
self.action_button.setAutoDefault(True)
|
||||
self.action_button.clicked.connect(self.action)
|
||||
|
||||
self.button_layout.insertWidget(2, self.action_button)
|
||||
|
||||
def active(self) -> bool:
|
||||
return self.__reject_close
|
||||
|
||||
def setActive(self, active: bool):
|
||||
self.reject_button.setDisabled(active)
|
||||
self.action_button.setDisabled(active)
|
||||
self.accept_button.setDisabled(active)
|
||||
self.__reject_close = active
|
||||
|
||||
@abstractmethod
|
||||
def action_handler(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@pyqtSlot()
|
||||
def action(self):
|
||||
self.setActive(True)
|
||||
self.action_handler()
|
||||
|
||||
# lk: Ignore all key presses if there is an ongoing action
|
||||
def keyPressEvent(self, a0: QKeyEvent) -> None:
|
||||
if self.__reject_close:
|
||||
a0.ignore()
|
||||
return
|
||||
super(BaseDialog, self).keyPressEvent(a0)
|
||||
|
||||
# lk: Ignore all closeEvents if there is an ongoing action
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
if self.__reject_close:
|
||||
a0.ignore()
|
||||
return
|
||||
super(BaseDialog, self).closeEvent(a0)
|
||||
|
||||
|
||||
__all__ = ["dialog_title", "dialog_title_game", "BaseDialog", "ButtonDialog", "ActionDialog"]
|
||||
|
||||
|
||||
class TestDialog(BaseDialog):
|
||||
def __init__(self, parent=None):
|
||||
super(TestDialog, self).__init__(parent=parent)
|
||||
|
||||
self.accept_button = QPushButton("accept", self)
|
||||
self.reject_button = QPushButton("reject", self)
|
||||
self.action_button = QPushButton("action", self)
|
||||
self.button_box = QDialogButtonBox(Qt.Horizontal, self)
|
||||
self.button_box.addButton(self.accept_button, QDialogButtonBox.AcceptRole)
|
||||
self.button_box.addButton(self.reject_button, QDialogButtonBox.RejectRole)
|
||||
self.button_box.addButton(self.action_button, QDialogButtonBox.ActionRole)
|
||||
|
||||
self.button_box.accepted.connect(self.accept)
|
||||
self.button_box.rejected.connect(self.reject)
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.addWidget(self.button_box)
|
||||
|
||||
self.setMinimumWidth(480)
|
||||
|
||||
def setWindowTitle(self, a0):
|
||||
super().setWindowTitle(dialog_title(a0))
|
||||
|
||||
def close(self):
|
||||
print("in close")
|
||||
super().close()
|
||||
|
||||
def closeEvent(self, a0: QCloseEvent) -> None:
|
||||
print("in closeEvent")
|
||||
if a0.spontaneous():
|
||||
print("is spontaneous")
|
||||
a0.ignore()
|
||||
return
|
||||
if self.reject_close:
|
||||
a0.ignore()
|
||||
else:
|
||||
self._on_close()
|
||||
super().closeEvent(a0)
|
||||
# super().closeEvent(a0)
|
||||
|
||||
def done(self, a0):
|
||||
print(f"in done {a0}")
|
||||
return
|
||||
super().done(a0)
|
||||
|
||||
def accept(self):
|
||||
print("in accept")
|
||||
self._on_accept()
|
||||
# return
|
||||
# super().accept()
|
||||
|
||||
def reject(self):
|
||||
print("in reject")
|
||||
self._on_reject()
|
||||
# return
|
||||
# super().reject()
|
||||
|
||||
def _on_close(self):
|
||||
print("in _on_close")
|
||||
|
||||
def _on_accept(self):
|
||||
print("in _on_accepted")
|
||||
# self.close()
|
||||
|
||||
def _on_reject(self):
|
||||
print("in _on_rejected")
|
||||
self.close()
|
||||
|
||||
def keyPressEvent(self, a0: QKeyEvent) -> None:
|
||||
super(BaseDialog, self).keyPressEvent(a0)
|
||||
|
||||
|
||||
def test_dialog():
|
||||
app = QApplication(sys.argv)
|
||||
dlg = TestDialog(None)
|
||||
dlg.show()
|
||||
ret = app.exec()
|
||||
sys.exit(ret)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_dialog()
|
|
@ -4,4 +4,5 @@ QtAwesome
|
|||
setuptools
|
||||
legendary-gl>=0.20.34
|
||||
orjson
|
||||
vdf; platform_system != "Windows"
|
||||
pywin32; platform_system == "Windows"
|
||||
|
|
Loading…
Reference in a new issue