Add multiple languages (i18n) support.

Now you can add a version in profile manage page.

Add install java button and dialog.
This commit is contained in:
wei
2026-08-04 17:39:38 +08:00
parent 6862f3f4b3
commit f6bf8f5c8b
16 changed files with 2166 additions and 272 deletions
+150
View File
@@ -0,0 +1,150 @@
import threading
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import (
QComboBox,
QDialog,
QDialogButtonBox,
QLabel,
QListWidget,
QPushButton,
QVBoxLayout,
)
from manager.game import InstallableJavaRuntime, JavaManager, JavaRuntime
class InstallJavaDialog(QDialog):
"""Install and register launcher-managed Mojang Java runtimes."""
class WorkerSignals(QObject):
components_loaded = Signal(object, object)
installed = Signal(object, object)
runtime_installed = Signal(object)
def __init__(self, app, manifest: dict | None = None, parent=None):
super().__init__(parent)
self.app = app
self.java_manager: JavaManager = app.game_manager.java
self.manifest = manifest or {}
self.worker_signals = self.WorkerSignals(self)
self.worker_signals.components_loaded.connect(self._components_loaded)
self.worker_signals.installed.connect(self._installed)
self.setWindowTitle(self.app.tr_text("Install Java"))
self.setMinimumWidth(480)
layout = QVBoxLayout(self)
self.requirement_label = QLabel(self._requirement_text())
self.runtime_list = QListWidget()
self.component_dropdown = QComboBox()
self.component_dropdown.setEditable(False)
self.status_label = QLabel(self.app.tr_text("Loading available Mojang runtimes..."))
self.install_button = QPushButton(self.app.tr_text("Install"))
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
layout.addWidget(self.requirement_label)
layout.addWidget(QLabel(self.app.tr_text("Detected Java runtimes")))
layout.addWidget(self.runtime_list)
layout.addWidget(QLabel(self.app.tr_text("Mojang runtime component")))
layout.addWidget(self.component_dropdown)
layout.addWidget(self.status_label)
layout.addWidget(self.install_button)
layout.addWidget(buttons)
buttons.rejected.connect(self.reject)
self.install_button.clicked.connect(self.install_selected)
self._refresh_runtime_list()
threading.Thread(target=self._load_components, daemon=True).start()
def _requirement_text(self) -> str:
requirement = self.manifest.get("javaVersion") or {}
major = requirement.get("majorVersion")
component = requirement.get("component")
if major:
return self.app.tr_text(
"This version requires Java {version}.", version=major
) + f" ({component or 'component unknown'})"
return self.app.tr_text("Choose a Mojang Java runtime component to install.")
def _refresh_runtime_list(self) -> None:
self.runtime_list.clear()
for runtime in self.java_manager.list_runtimes():
state = "available" if runtime.valid else "missing"
self.runtime_list.addItem(
f"Java {runtime.major_version} | {runtime.source} | {state}\n{runtime.executable}"
)
def _load_components(self) -> None:
try:
runtimes = self.java_manager.list_installable_runtimes()
self.worker_signals.components_loaded.emit(runtimes, None)
except Exception as exc:
self.worker_signals.components_loaded.emit([], exc)
def _components_loaded(self, runtimes: list[InstallableJavaRuntime], error) -> None:
requirement = self.manifest.get("javaVersion") or {}
required = requirement.get("component")
required_major = requirement.get("majorVersion")
self.component_dropdown.clear()
for runtime in runtimes:
major = runtime.major_version
if runtime.component == required and required_major is not None:
# The Minecraft version manifest is authoritative for its own
# runtime requirement if Mojang's version name is unusual.
major = int(required_major)
label = f"Java {major} | {runtime.component}" if major is not None else runtime.component
if runtime.raw_version:
label += f" ({runtime.raw_version})"
self.component_dropdown.addItem(label, runtime.component)
if required:
index = self.component_dropdown.findData(required)
if index < 0:
label = f"Java {required_major} | {required}" if required_major else required
self.component_dropdown.addItem(label, required)
index = self.component_dropdown.count() - 1
self.component_dropdown.setCurrentIndex(index)
self.install_button.setEnabled(self.component_dropdown.count() > 0)
self.status_label.setText(
self.app.tr_text("Unable to load component list: {error}", error=error)
if error else self.app.tr_text("Ready to install.")
)
def install_selected(self) -> None:
component = self.component_dropdown.currentData()
if not component:
self.status_label.setText(self.app.tr_text("Select a runtime component."))
return
self.install_button.setEnabled(False)
self.component_dropdown.setEnabled(False)
self.status_label.setText(self.app.tr_text("Installing {component}...", component=component))
callback, task_id = self.app.game_manager.manifest.create_download_progress_callback(
self.app.tr_text("Installing Java ({component})", component=component)
)
def worker():
try:
runtime = self.java_manager.install_runtime(
component,
progress_callback=callback,
redownload_callback=self.app.game_manager.game_file.redownload_files,
)
self.app.widget_manager.download_signal.finished.emit(task_id)
self.worker_signals.installed.emit(runtime, None)
except Exception as exc:
self.app.widget_manager.download_signal.failed.emit(task_id, str(exc))
self.worker_signals.installed.emit(None, exc)
threading.Thread(target=worker, daemon=True).start()
def _installed(self, runtime: JavaRuntime | None, error) -> None:
self.install_button.setEnabled(True)
self.component_dropdown.setEnabled(True)
if error:
self.status_label.setText(self.app.tr_text("Installation failed: {error}", error=error))
return
self._refresh_runtime_list()
self.status_label.setText(self.app.tr_text(
"Java {version} installed successfully.", version=runtime.major_version
))
self.runtime_installed.emit(runtime)