mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-01-17 19:47:00 +01:00
Sync from development - prepare for v0.2.1.1
This commit is contained in:
@@ -45,19 +45,20 @@ class SuccessDialog(QDialog):
|
||||
self.setStyleSheet("QDialog { background: #181818; color: #fff; border-radius: 12px; }" )
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(0)
|
||||
layout.setContentsMargins(30, 30, 30, 30)
|
||||
layout.setContentsMargins(30, 20, 30, 20) # Reduced top/bottom margins to prevent truncation
|
||||
|
||||
# --- Card background for content ---
|
||||
card = QFrame(self)
|
||||
card.setObjectName("successCard")
|
||||
card.setFrameShape(QFrame.StyledPanel)
|
||||
card.setFrameShadow(QFrame.Raised)
|
||||
card.setFixedWidth(440)
|
||||
card.setMinimumHeight(380)
|
||||
# Increase card width and reduce margins to maximize text width for 800p screens
|
||||
card.setFixedWidth(460)
|
||||
# Remove fixed minimum height to allow natural sizing based on content
|
||||
card.setMaximumHeight(16777215) # Remove max height constraint to allow expansion
|
||||
card_layout = QVBoxLayout(card)
|
||||
card_layout.setSpacing(12)
|
||||
card_layout.setContentsMargins(28, 28, 28, 28)
|
||||
card_layout.setContentsMargins(20, 28, 20, 28) # Reduced left/right margins to give more text width
|
||||
card.setStyleSheet(
|
||||
"QFrame#successCard { "
|
||||
" background: #23272e; "
|
||||
@@ -81,29 +82,38 @@ class SuccessDialog(QDialog):
|
||||
card_layout.addWidget(title_label)
|
||||
|
||||
# Personalized success message (modlist name in Jackify Blue, but less bold)
|
||||
message_text = self._build_success_message()
|
||||
modlist_name_html = f'<span style="color:#3fb7d6; font-size:17px; font-weight:500;">{self.modlist_name}</span>'
|
||||
if self.workflow_type == "install":
|
||||
message_html = f"<span style='font-size:15px;'>{modlist_name_html} installed successfully!</span>"
|
||||
suffix_text = "installed successfully!"
|
||||
elif self.workflow_type == "configure_new":
|
||||
suffix_text = "configured successfully!"
|
||||
elif self.workflow_type == "configure_existing":
|
||||
suffix_text = "configuration updated successfully!"
|
||||
else:
|
||||
message_html = message_text
|
||||
# Fallback for other workflow types
|
||||
message_text = self._build_success_message()
|
||||
suffix_text = message_text.replace(self.modlist_name, "").strip()
|
||||
|
||||
# Build complete message with proper HTML formatting - ensure both parts are visible
|
||||
message_html = f'{modlist_name_html} <span style="font-size:15px; color:#e0e0e0;">{suffix_text}</span>'
|
||||
message_label = QLabel(message_html)
|
||||
# Center the success message within the wider card for all screen sizes
|
||||
message_label.setAlignment(Qt.AlignCenter)
|
||||
message_label.setWordWrap(True)
|
||||
message_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding)
|
||||
message_label.setMinimumHeight(30) # Ensure label has minimum height to be visible
|
||||
message_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
message_label.setStyleSheet(
|
||||
"QLabel { "
|
||||
" font-size: 15px; "
|
||||
" color: #e0e0e0; "
|
||||
" line-height: 1.3; "
|
||||
" margin-bottom: 6px; "
|
||||
" word-wrap: break-word; "
|
||||
" padding: 0px; "
|
||||
"}"
|
||||
)
|
||||
message_label.setTextFormat(Qt.RichText)
|
||||
# Ensure the label itself is centered in the card layout and uses full width
|
||||
card_layout.addWidget(message_label, alignment=Qt.AlignCenter)
|
||||
# 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}")
|
||||
@@ -123,7 +133,7 @@ class SuccessDialog(QDialog):
|
||||
next_steps_label = QLabel(next_steps_text)
|
||||
next_steps_label.setAlignment(Qt.AlignCenter)
|
||||
next_steps_label.setWordWrap(True)
|
||||
next_steps_label.setMinimumHeight(100)
|
||||
# Remove fixed minimum height to allow natural sizing
|
||||
next_steps_label.setStyleSheet(
|
||||
"QLabel { "
|
||||
" font-size: 13px; "
|
||||
|
||||
@@ -1495,7 +1495,8 @@ class JackifyMainWindow(QMainWindow):
|
||||
4: "Install Modlist Screen",
|
||||
5: "Install TTW Screen",
|
||||
6: "Configure New Modlist",
|
||||
7: "Configure Existing Modlist",
|
||||
7: "Wabbajack Installer",
|
||||
8: "Configure Existing Modlist",
|
||||
}
|
||||
screen_name = screen_names.get(index, f"Unknown Screen (Index {index})")
|
||||
widget = self.stacked_widget.widget(index)
|
||||
@@ -1919,7 +1920,6 @@ def main():
|
||||
debug_mode = True
|
||||
# Temporarily save CLI debug flag to config so engine can see it
|
||||
config_handler.set('debug_mode', True)
|
||||
print("[DEBUG] CLI --debug flag detected, saved debug_mode=True to config")
|
||||
import logging
|
||||
|
||||
# Initialize file logging on root logger so all modules inherit it
|
||||
@@ -1928,13 +1928,22 @@ def main():
|
||||
# Only rotate log file when debug mode is enabled
|
||||
if debug_mode:
|
||||
logging_handler.rotate_log_for_logger('jackify_gui', 'jackify-gui.log')
|
||||
root_logger = logging_handler.setup_logger('', 'jackify-gui.log', is_general=True) # Empty name = root logger
|
||||
|
||||
root_logger = logging_handler.setup_logger('', 'jackify-gui.log', is_general=True, debug_mode=debug_mode) # Empty name = root logger
|
||||
|
||||
# CRITICAL: Set root logger level BEFORE any child loggers are used
|
||||
# This ensures DEBUG messages from child loggers propagate correctly
|
||||
if debug_mode:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
print("[Jackify] Debug mode enabled (from config or CLI)")
|
||||
root_logger.setLevel(logging.DEBUG)
|
||||
logging.getLogger().setLevel(logging.DEBUG) # Also set on root via getLogger() for compatibility
|
||||
root_logger.debug("CLI --debug flag detected, saved debug_mode=True to config")
|
||||
root_logger.info("Debug mode enabled (from config or CLI)")
|
||||
else:
|
||||
root_logger.setLevel(logging.WARNING)
|
||||
logging.getLogger().setLevel(logging.WARNING)
|
||||
|
||||
# Root logger should not propagate (it's the top level)
|
||||
# Child loggers will propagate to root logger by default (unless they explicitly set propagate=False)
|
||||
root_logger.propagate = False
|
||||
|
||||
dev_mode = '--dev' in sys.argv
|
||||
|
||||
@@ -1990,7 +1999,7 @@ def main():
|
||||
icon = QIcon(path)
|
||||
if not icon.isNull():
|
||||
if debug_mode:
|
||||
print(f"[DEBUG] Using AppImage icon: {path}")
|
||||
logging.getLogger().debug(f"Using AppImage icon: {path}")
|
||||
break
|
||||
|
||||
# Priority 3: Fallback to any PNG in assets directory
|
||||
@@ -2001,8 +2010,8 @@ def main():
|
||||
icon = QIcon(try_path)
|
||||
|
||||
if debug_mode:
|
||||
print(f"[DEBUG] Final icon path: {icon_path}")
|
||||
print(f"[DEBUG] Icon is null: {icon.isNull()}")
|
||||
logging.getLogger().debug(f"Final icon path: {icon_path}")
|
||||
logging.getLogger().debug(f"Icon is null: {icon.isNull()}")
|
||||
|
||||
app.setWindowIcon(icon)
|
||||
window = JackifyMainWindow(dev_mode=dev_mode)
|
||||
|
||||
@@ -648,7 +648,7 @@ class ConfigureExistingModlistScreen(QWidget):
|
||||
|
||||
class ConfigurationThread(QThread):
|
||||
progress_update = Signal(str)
|
||||
configuration_complete = Signal(bool, str, str)
|
||||
configuration_complete = Signal(bool, str, str, bool)
|
||||
error_occurred = Signal(str)
|
||||
|
||||
def __init__(self, modlist_name, install_dir, resolution):
|
||||
@@ -691,8 +691,8 @@ class ConfigureExistingModlistScreen(QWidget):
|
||||
def progress_callback(message):
|
||||
self.progress_update.emit(message)
|
||||
|
||||
def completion_callback(success, message, modlist_name):
|
||||
self.configuration_complete.emit(success, message, modlist_name)
|
||||
def completion_callback(success, message, modlist_name, enb_detected=False):
|
||||
self.configuration_complete.emit(success, message, modlist_name, enb_detected)
|
||||
|
||||
def manual_steps_callback(modlist_name, retry_count):
|
||||
# Existing modlists shouldn't need manual steps, but handle gracefully
|
||||
@@ -729,7 +729,7 @@ class ConfigureExistingModlistScreen(QWidget):
|
||||
self._safe_append_text(f"[ERROR] Failed to start configuration: {e}")
|
||||
MessageService.critical(self, "Configuration Error", f"Failed to start configuration: {e}", safety_level="medium")
|
||||
|
||||
def on_configuration_complete(self, success, message, modlist_name):
|
||||
def on_configuration_complete(self, success, message, modlist_name, enb_detected=False):
|
||||
"""Handle configuration completion"""
|
||||
# Re-enable all controls when workflow completes
|
||||
self._enable_controls_after_operation()
|
||||
|
||||
@@ -30,7 +30,7 @@ from jackify.backend.utils.nexus_premium_detector import is_non_premium_indicato
|
||||
from jackify.backend.handlers.progress_parser import ProgressStateManager
|
||||
from jackify.frontends.gui.widgets.progress_indicator import OverallProgressIndicator
|
||||
from jackify.frontends.gui.widgets.file_progress_list import FileProgressList
|
||||
from jackify.shared.progress_models import InstallationPhase, InstallationProgress, OperationType
|
||||
from jackify.shared.progress_models import InstallationPhase, InstallationProgress, OperationType, FileProgress
|
||||
# Modlist gallery (imported at module level to avoid import delay when opening dialog)
|
||||
from jackify.frontends.gui.screens.modlist_gallery import ModlistGalleryDialog
|
||||
|
||||
@@ -2718,7 +2718,6 @@ class InstallModlistScreen(QWidget):
|
||||
# Render loop handles smooth updates - just set target state
|
||||
|
||||
current_step = progress_state.phase_step
|
||||
from jackify.shared.progress_models import FileProgress, OperationType
|
||||
|
||||
display_items = []
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ class InstallTTWScreen(QWidget):
|
||||
instruction_text = QLabel(
|
||||
"Tale of Two Wastelands installation requires a .mpi file you can get from: "
|
||||
'<a href="https://mod.pub/ttw/133/files">https://mod.pub/ttw/133/files</a> '
|
||||
"(requires a user account for mod.db)"
|
||||
"(requires a user account for ModPub)"
|
||||
)
|
||||
instruction_text.setWordWrap(True)
|
||||
instruction_text.setStyleSheet("color: #ccc; font-size: 12px; margin: 0px; padding: 0px; line-height: 1.2;")
|
||||
|
||||
@@ -209,7 +209,9 @@ class ModlistTasksScreen(QWidget):
|
||||
elif action_id == "configure_new_modlist":
|
||||
self.stacked_widget.setCurrentIndex(6) # Configure New Modlist Screen
|
||||
elif action_id == "configure_existing_modlist":
|
||||
self.stacked_widget.setCurrentIndex(7) # Configure Existing Modlist Screen
|
||||
self.stacked_widget.setCurrentIndex(8) # Configure Existing Modlist Screen
|
||||
elif action_id == "install_wabbajack":
|
||||
self.stacked_widget.setCurrentIndex(7) # Wabbajack Installer Screen
|
||||
|
||||
def go_back(self):
|
||||
"""Return to main menu"""
|
||||
|
||||
@@ -106,6 +106,77 @@ class OverallProgressIndicator(QWidget):
|
||||
if not display_text or display_text == "Processing...":
|
||||
display_text = progress.phase_name or progress.phase.value.title() or "Processing..."
|
||||
|
||||
# Add total download size, remaining size (MB/GB), and ETA for download phase
|
||||
from jackify.shared.progress_models import InstallationPhase, FileProgress
|
||||
if progress.phase == InstallationPhase.DOWNLOAD:
|
||||
# Try to get overall download totals - either from data_total or aggregate from active_files
|
||||
total_bytes = progress.data_total
|
||||
processed_bytes = progress.data_processed
|
||||
using_aggregated = False
|
||||
|
||||
# If data_total is 0, try to aggregate from active_files
|
||||
if total_bytes == 0 and progress.active_files:
|
||||
total_bytes = sum(f.total_size for f in progress.active_files if f.total_size > 0)
|
||||
processed_bytes = sum(f.current_size for f in progress.active_files if f.current_size > 0)
|
||||
using_aggregated = True
|
||||
|
||||
# Add remaining download size (MB or GB) if available
|
||||
if total_bytes > 0:
|
||||
remaining_bytes = total_bytes - processed_bytes
|
||||
if remaining_bytes > 0:
|
||||
# Format as MB if less than 1GB, otherwise GB
|
||||
if remaining_bytes < (1024.0 ** 3):
|
||||
remaining_mb = remaining_bytes / (1024.0 ** 2)
|
||||
display_text += f" | {remaining_mb:.1f}MB remaining"
|
||||
else:
|
||||
remaining_gb = remaining_bytes / (1024.0 ** 3)
|
||||
display_text += f" | {remaining_gb:.1f}GB remaining"
|
||||
|
||||
# Calculate ETA - prefer aggregated calculation for concurrent downloads
|
||||
eta_seconds = -1.0
|
||||
if using_aggregated:
|
||||
# For concurrent downloads: sum all active download speeds (not average)
|
||||
# This gives us the combined throughput
|
||||
active_speeds = [f.speed for f in progress.active_files if f.speed > 0]
|
||||
if active_speeds:
|
||||
combined_speed = sum(active_speeds) # Sum speeds for concurrent downloads
|
||||
if combined_speed > 0:
|
||||
eta_seconds = remaining_bytes / combined_speed
|
||||
else:
|
||||
# Use the standard ETA calculation from progress model
|
||||
eta_seconds = progress.get_eta_seconds(use_smoothing=True)
|
||||
|
||||
# Format and display ETA
|
||||
if eta_seconds > 0:
|
||||
if eta_seconds < 60:
|
||||
display_text += f" | ETA: {int(eta_seconds)}s"
|
||||
elif eta_seconds < 3600:
|
||||
mins = int(eta_seconds // 60)
|
||||
secs = int(eta_seconds % 60)
|
||||
if secs > 0:
|
||||
display_text += f" | ETA: {mins}m {secs}s"
|
||||
else:
|
||||
display_text += f" | ETA: {mins}m"
|
||||
else:
|
||||
hours = int(eta_seconds // 3600)
|
||||
mins = int((eta_seconds % 3600) // 60)
|
||||
if mins > 0:
|
||||
display_text += f" | ETA: {hours}h {mins}m"
|
||||
else:
|
||||
display_text += f" | ETA: {hours}h"
|
||||
else:
|
||||
# No total size available - try to show ETA if we have speed info from active files
|
||||
if progress.active_files:
|
||||
active_speeds = [f.speed for f in progress.active_files if f.speed > 0]
|
||||
if active_speeds:
|
||||
# Can't calculate accurate ETA without total size, but could show speed
|
||||
pass
|
||||
# Fallback to standard ETA if available
|
||||
if not using_aggregated:
|
||||
eta_display = progress.eta_display
|
||||
if eta_display:
|
||||
display_text += f" | ETA: {eta_display}"
|
||||
|
||||
self.status_label.setText(display_text)
|
||||
|
||||
# Update progress bar if enabled
|
||||
@@ -150,6 +221,23 @@ class OverallProgressIndicator(QWidget):
|
||||
tooltip_parts.append(f"Step: {progress.phase_progress_text}")
|
||||
if progress.data_progress_text:
|
||||
tooltip_parts.append(f"Data: {progress.data_progress_text}")
|
||||
|
||||
# Add total download size in GB for download phase
|
||||
from jackify.shared.progress_models import InstallationPhase
|
||||
if progress.phase == InstallationPhase.DOWNLOAD and progress.data_total > 0:
|
||||
total_gb = progress.total_download_size_gb
|
||||
remaining_gb = progress.remaining_download_size_gb
|
||||
if total_gb > 0:
|
||||
tooltip_parts.append(f"Total Download: {total_gb:.2f}GB")
|
||||
if remaining_gb > 0:
|
||||
tooltip_parts.append(f"Remaining: {remaining_gb:.2f}GB")
|
||||
|
||||
# Add ETA for download phase
|
||||
if progress.phase == InstallationPhase.DOWNLOAD:
|
||||
eta_display = progress.eta_display
|
||||
if eta_display:
|
||||
tooltip_parts.append(f"Estimated Time Remaining: {eta_display}")
|
||||
|
||||
if progress.overall_percent > 0:
|
||||
tooltip_parts.append(f"Overall: {progress.overall_percent:.1f}%")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user