mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-08 00:07:45 +02:00
Release v0.6.0
This commit is contained in:
@@ -22,6 +22,7 @@ from PySide6.QtGui import QFont, QClipboard
|
||||
from ....backend.services.update_service import UpdateService
|
||||
from ....backend.models.configuration import SystemInfo
|
||||
from .... import __version__
|
||||
from jackify.frontends.gui.mixins.thread_lifecycle_mixin import ThreadLifecycleMixin
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -45,7 +46,7 @@ class UpdateCheckThread(QThread):
|
||||
self.update_check_finished.emit(None)
|
||||
|
||||
|
||||
class AboutDialog(QDialog):
|
||||
class AboutDialog(ThreadLifecycleMixin, QDialog):
|
||||
"""About dialog showing system info and app details."""
|
||||
|
||||
def __init__(self, system_info: SystemInfo, parent=None):
|
||||
@@ -420,8 +421,7 @@ Python: {platform.python_version()}"""
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Handle dialog close event."""
|
||||
if self.update_check_thread and self.update_check_thread.isRunning():
|
||||
self.update_check_thread.terminate()
|
||||
self.update_check_thread.wait()
|
||||
|
||||
self.update_check_thread = self._park_thread(
|
||||
self.update_check_thread, ["update_available", "no_update", "check_failed"]
|
||||
)
|
||||
event.accept()
|
||||
@@ -12,7 +12,7 @@ from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QWidget,
|
||||
QSpacerItem, QSizePolicy, QFrame, QApplication
|
||||
)
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QIcon, QFont
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -145,7 +145,8 @@ class ENBProtonDialog(QDialog):
|
||||
# OK button
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch()
|
||||
self.ok_btn = QPushButton("I Understand")
|
||||
self.ok_btn = QPushButton("I Understand (3s)")
|
||||
self.ok_btn.setEnabled(False)
|
||||
self.ok_btn.setStyleSheet(
|
||||
"QPushButton { "
|
||||
" background: #3fb7d6; "
|
||||
@@ -162,9 +163,19 @@ class ENBProtonDialog(QDialog):
|
||||
"QPushButton:pressed { "
|
||||
" background: #2d8fa8; "
|
||||
"}"
|
||||
"QPushButton:disabled { "
|
||||
" background: #555; "
|
||||
" color: #aaa; "
|
||||
"}"
|
||||
)
|
||||
self.ok_btn.clicked.connect(self.accept)
|
||||
btn_row.addWidget(self.ok_btn)
|
||||
|
||||
self._protect_countdown = 3
|
||||
self._protect_timer = QTimer(self)
|
||||
self._protect_timer.setInterval(1000)
|
||||
self._protect_timer.timeout.connect(self._on_protect_tick)
|
||||
self._protect_timer.start()
|
||||
btn_row.addStretch()
|
||||
layout.addLayout(btn_row)
|
||||
|
||||
@@ -173,6 +184,15 @@ class ENBProtonDialog(QDialog):
|
||||
|
||||
logger.info(f"ENBProtonDialog created for modlist: {modlist_name}")
|
||||
|
||||
def _on_protect_tick(self):
|
||||
self._protect_countdown -= 1
|
||||
if self._protect_countdown > 0:
|
||||
self.ok_btn.setText(f"I Understand ({self._protect_countdown}s)")
|
||||
else:
|
||||
self._protect_timer.stop()
|
||||
self.ok_btn.setText("I Understand")
|
||||
self.ok_btn.setEnabled(True)
|
||||
|
||||
def _set_dialog_icon(self):
|
||||
"""Set the dialog icon to Wabbajack icon if available"""
|
||||
try:
|
||||
|
||||
@@ -361,7 +361,8 @@ class ManualDownloadDialog(QDialog):
|
||||
logger.debug("Could not persist manual_download_concurrent_limit", exc_info=True)
|
||||
|
||||
def _on_pick_folder(self) -> None:
|
||||
chosen = QFileDialog.getExistingDirectory(self, "Select watch folder", str(self._watch_dir))
|
||||
from jackify.frontends.gui.utils import browse_directory
|
||||
chosen = browse_directory(self, "Select watch folder", str(self._watch_dir))
|
||||
if chosen:
|
||||
from jackify.backend.services.download_watcher_service import WatcherConfig
|
||||
self._watch_dir = Path(chosen)
|
||||
@@ -441,7 +442,7 @@ class ManualDownloadDialog(QDialog):
|
||||
def _on_all_done_slot(self, completed: int, skipped: int) -> None:
|
||||
from PySide6.QtCore import QTimer
|
||||
self._progress_label.setText(
|
||||
f"All downloads complete ({completed} accepted, {skipped} deferred) — closing..."
|
||||
f"All downloads complete ({completed} accepted, {skipped} deferred) - closing..."
|
||||
)
|
||||
# Raise now while the dialog is still visible so the user sees the completion state
|
||||
self._raise_main_window()
|
||||
|
||||
@@ -11,6 +11,7 @@ from PySide6.QtWidgets import (
|
||||
from PySide6.QtCore import Qt, QThread, Signal
|
||||
from PySide6.QtGui import QPixmap, QIcon, QFont
|
||||
from .. import shared_theme
|
||||
from jackify.frontends.gui.mixins.thread_lifecycle_mixin import ThreadLifecycleMixin
|
||||
|
||||
|
||||
class FlatpakInstallThread(QThread):
|
||||
@@ -26,7 +27,7 @@ class FlatpakInstallThread(QThread):
|
||||
self.finished.emit(success, message)
|
||||
|
||||
|
||||
class ProtontricksErrorDialog(QDialog):
|
||||
class ProtontricksErrorDialog(ThreadLifecycleMixin, QDialog):
|
||||
"""
|
||||
Dialog shown when protontricks is not found
|
||||
Provides options to install via Flatpak or get native installation guidance
|
||||
@@ -322,7 +323,7 @@ class ProtontricksErrorDialog(QDialog):
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Handle dialog close event"""
|
||||
if self.install_thread and self.install_thread.isRunning():
|
||||
self.install_thread.terminate()
|
||||
self.install_thread.wait()
|
||||
self.install_thread = self._park_thread(
|
||||
self.install_thread, ["install_complete", "install_failed", "progress_update"]
|
||||
)
|
||||
event.accept()
|
||||
@@ -92,9 +92,10 @@ class SettingsDialog(SettingsDialogTabsMixin, SettingsDialogProtonMixin, QDialog
|
||||
self.api_show_btn.setStyleSheet("")
|
||||
|
||||
def _pick_directory(self, line_edit):
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "Select Directory", line_edit.text() or os.path.expanduser("~"))
|
||||
from jackify.frontends.gui.utils import browse_directory
|
||||
dir_path = browse_directory(self, "Select Directory", line_edit.text())
|
||||
if dir_path:
|
||||
line_edit.setText(os.path.realpath(dir_path))
|
||||
line_edit.setText(dir_path)
|
||||
|
||||
def _show_help(self):
|
||||
MessageService.information(self, "Help", "Help/documentation coming soon!", safety_level="low")
|
||||
@@ -125,6 +126,7 @@ class SettingsDialog(SettingsDialogTabsMixin, SettingsDialogProtonMixin, QDialog
|
||||
api_key = text.strip()
|
||||
self.config_handler.save_api_key(api_key)
|
||||
|
||||
|
||||
def _update_oauth_status(self):
|
||||
from jackify.backend.services.nexus_auth_service import NexusAuthService
|
||||
auth_service = NexusAuthService()
|
||||
@@ -309,6 +311,10 @@ class SettingsDialog(SettingsDialogTabsMixin, SettingsDialogProtonMixin, QDialog
|
||||
self.config_handler.set("game_proton_path", resolved_game_path)
|
||||
self.config_handler.set("game_proton_version", resolved_game_version)
|
||||
|
||||
# Save auto tool compat preference
|
||||
self.config_handler.set('auto_tool_compat', self.auto_tool_compat_checkbox.isChecked())
|
||||
self.config_handler.set('force_github_updates', self.force_github_updates_checkbox.isChecked())
|
||||
|
||||
# Save component installation method preference
|
||||
if self.winetricks_radio.isChecked():
|
||||
method = 'winetricks'
|
||||
|
||||
@@ -170,6 +170,7 @@ class SettingsDialogTabsMixin:
|
||||
advanced_layout.addWidget(auth_group)
|
||||
advanced_layout.addSpacing(12)
|
||||
|
||||
|
||||
self.resource_settings_path = os.path.expanduser("~/.config/jackify/resource_settings.json")
|
||||
self.resource_settings = self._load_json(self.resource_settings_path)
|
||||
self.resource_edits = {}
|
||||
@@ -275,6 +276,27 @@ class SettingsDialogTabsMixin:
|
||||
self.component_method_group.addButton(self.protontricks_radio, 1)
|
||||
component_method_layout.addWidget(self.protontricks_radio)
|
||||
component_layout.addLayout(component_method_layout)
|
||||
|
||||
self.auto_tool_compat_checkbox = QCheckBox("Apply tool compatibility settings during install/configure")
|
||||
self.auto_tool_compat_checkbox.setChecked(self.config_handler.get('auto_tool_compat', True))
|
||||
self.auto_tool_compat_checkbox.setToolTip(
|
||||
"Automatically apply Wine registry fixes for xEdit, Pandora, and DLL overrides "
|
||||
"at the end of every install or configure workflow. Disable if you find it adds "
|
||||
"noticeable delay."
|
||||
)
|
||||
self.auto_tool_compat_checkbox.setStyleSheet("color: #fff;")
|
||||
component_layout.addWidget(self.auto_tool_compat_checkbox)
|
||||
|
||||
self.force_github_updates_checkbox = QCheckBox("Use GitHub as update source (bypass Nexus CDN)")
|
||||
self.force_github_updates_checkbox.setChecked(self.config_handler.get('force_github_updates', False))
|
||||
self.force_github_updates_checkbox.setToolTip(
|
||||
"Always download Jackify updates directly from GitHub Releases instead of Nexus CDN. "
|
||||
"Enable this if self-updates fail or stall. GitHub delivers the AppImage directly; "
|
||||
"Nexus delivers a .7z archive that Jackify must extract."
|
||||
)
|
||||
self.force_github_updates_checkbox.setStyleSheet("color: #fff;")
|
||||
component_layout.addWidget(self.force_github_updates_checkbox)
|
||||
|
||||
advanced_layout.addWidget(component_group)
|
||||
advanced_layout.addStretch()
|
||||
self.tab_widget.addTab(advanced_tab, "Advanced")
|
||||
|
||||
@@ -92,6 +92,8 @@ class SuccessDialog(QDialog):
|
||||
suffix_text = "configured successfully!"
|
||||
elif self.workflow_type == "configure_existing":
|
||||
suffix_text = "configuration updated successfully!"
|
||||
elif self.workflow_type == "tool_config":
|
||||
suffix_text = "tool compatibility configured successfully!"
|
||||
else:
|
||||
# Fallback for other workflow types
|
||||
message_text = self._build_success_message()
|
||||
@@ -118,18 +120,19 @@ class SuccessDialog(QDialog):
|
||||
# Ensure the label uses full width of the card before wrapping
|
||||
card_layout.addWidget(message_label)
|
||||
|
||||
# Time taken
|
||||
time_label = QLabel(f"Completed in {self.time_taken}")
|
||||
time_label.setAlignment(Qt.AlignCenter)
|
||||
time_label.setStyleSheet(
|
||||
"QLabel { "
|
||||
" font-size: 12px; "
|
||||
" color: #b0b0b0; "
|
||||
" font-style: italic; "
|
||||
" margin-bottom: 10px; "
|
||||
"}"
|
||||
)
|
||||
card_layout.addWidget(time_label)
|
||||
# Time taken (omit label if time is not available)
|
||||
if self.time_taken:
|
||||
time_label = QLabel(f"Completed in {self.time_taken}")
|
||||
time_label.setAlignment(Qt.AlignCenter)
|
||||
time_label.setStyleSheet(
|
||||
"QLabel { "
|
||||
" font-size: 12px; "
|
||||
" color: #b0b0b0; "
|
||||
" font-style: italic; "
|
||||
" margin-bottom: 10px; "
|
||||
"}"
|
||||
)
|
||||
card_layout.addWidget(time_label)
|
||||
|
||||
# Next steps guidance
|
||||
next_steps_text = self._build_next_steps()
|
||||
@@ -240,15 +243,37 @@ class SuccessDialog(QDialog):
|
||||
game_display = self.game_name or self.modlist_name
|
||||
|
||||
base_message = ""
|
||||
if self.workflow_type == "tuxborn":
|
||||
if self.workflow_type == "tool_config":
|
||||
base_message = (
|
||||
f"Modding tools for {self.modlist_name} are now configured. "
|
||||
"xEdit, Synthesis, Pandora, and DLL overrides are ready to use from within Mod Organizer 2."
|
||||
)
|
||||
elif self.workflow_type == "tuxborn":
|
||||
base_message = f"You can now launch Tuxborn from Steam and enjoy your modded {game_display} experience!"
|
||||
elif self.workflow_type == "install" and self.modlist_name == "Wabbajack":
|
||||
base_message = "You can now launch Wabbajack from Steam and install modlists. Once the modlist install is complete, you can run \"Configure New Modlist\" in Jackify to complete the configuration for running the modlist on Linux."
|
||||
else:
|
||||
base_message = f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!"
|
||||
try:
|
||||
from jackify.backend.handlers.config_handler import ConfigHandler
|
||||
auto_tool_compat = ConfigHandler().get('auto_tool_compat', True)
|
||||
except Exception:
|
||||
auto_tool_compat = True
|
||||
|
||||
tool_hint = (
|
||||
"<br><br>"
|
||||
"<span style=\"color:#b0b0b0; font-size:12px;\">"
|
||||
"If you use modding tools such as xEdit, Synthesis, or Pandora, "
|
||||
"run <b>Configure Tool Compatibility</b> from the Additional Tasks menu."
|
||||
"</span>"
|
||||
) if not auto_tool_compat else ""
|
||||
|
||||
base_message = (
|
||||
f"You can now launch {self.modlist_name} from Steam and enjoy your modded {game_display} experience!"
|
||||
f"{tool_hint}"
|
||||
)
|
||||
|
||||
# ENB Proton warning shown in separate dialog
|
||||
return base_message
|
||||
return base_message
|
||||
|
||||
def _update_countdown(self):
|
||||
if self._countdown > 0:
|
||||
|
||||
@@ -17,6 +17,7 @@ from PySide6.QtCore import Qt, QThread, Signal
|
||||
from PySide6.QtGui import QPixmap, QFont
|
||||
|
||||
from ....backend.services.update_service import UpdateService, UpdateInfo
|
||||
from jackify.frontends.gui.mixins.thread_lifecycle_mixin import ThreadLifecycleMixin
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -51,7 +52,7 @@ class UpdateDownloadThread(QThread):
|
||||
self.download_finished.emit(None)
|
||||
|
||||
|
||||
class UpdateDialog(QDialog):
|
||||
class UpdateDialog(ThreadLifecycleMixin, QDialog):
|
||||
"""Dialog for notifying users about updates and handling downloads."""
|
||||
|
||||
def __init__(self, update_info: UpdateInfo, update_service: UpdateService, parent=None):
|
||||
@@ -335,9 +336,7 @@ class UpdateDialog(QDialog):
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Handle dialog close event."""
|
||||
if self.download_thread and self.download_thread.isRunning():
|
||||
# Cancel download if in progress
|
||||
self.download_thread.terminate()
|
||||
self.download_thread.wait()
|
||||
|
||||
self.download_thread = self._park_thread(
|
||||
self.download_thread, ["progress_updated", "download_finished"]
|
||||
)
|
||||
event.accept()
|
||||
Reference in New Issue
Block a user