diff --git a/build.sh b/build.sh
index 4100244..79b8afb 100755
--- a/build.sh
+++ b/build.sh
@@ -100,8 +100,9 @@ if [ "$OS" = "darwin" ]; then
mv "$PRODUCED" "$APP"
fi
- echo "[build] sembrando games.toml en Contents/Resources…"
+ echo "[build] sembrando games.toml y icon.png en Contents/Resources…"
cp games.toml "$APP/Contents/Resources/games.toml"
+ cp assets/icon.png "$APP/Contents/Resources/icon.png" # usado por el diálogo «Quant a»
# Bundle ad-hoc (sin Developer ID): quitamos quarantine para abrir sin fricción local.
xattr -dr com.apple.quarantine "$APP" 2>/dev/null || true
diff --git a/jlauncher/__main__.py b/jlauncher/__main__.py
index b27ffe9..7821a79 100644
--- a/jlauncher/__main__.py
+++ b/jlauncher/__main__.py
@@ -4,10 +4,11 @@ from __future__ import annotations
import sys
+from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QMessageBox
from .config import load_config
-from .paths import config_file, data_root
+from .paths import app_icon_path, config_file, data_root
from .ui.main_window import MainWindow
from .ui.theme import apply_theme
@@ -15,6 +16,9 @@ from .ui.theme import apply_theme
def main() -> int:
app = QApplication(sys.argv)
app.setApplicationName("jlauncher")
+ icon_path = app_icon_path()
+ if icon_path is not None:
+ app.setWindowIcon(QIcon(str(icon_path)))
# Tema del sistema para el posible diálogo de error previo a la ventana;
# MainWindow re-aplica el modo guardado (system/light/dark) y vigila los cambios.
apply_theme(app)
diff --git a/jlauncher/paths.py b/jlauncher/paths.py
index d50f58e..b15dc17 100644
--- a/jlauncher/paths.py
+++ b/jlauncher/paths.py
@@ -55,6 +55,20 @@ def macos_app_bundle() -> Path | None:
return None
+def app_icon_path() -> Path | None:
+ """Ruta al PNG del icono para la UI (icono de ventana/Dock y diálogo «Quant a»).
+
+ En .app vive en ``Contents/Resources/icon.png``; desde fuente, en ``assets/icon.png``.
+ Devuelve None si no se encuentra.
+ """
+ bundle = macos_app_bundle()
+ if bundle is not None:
+ candidate = bundle / "Contents" / "Resources" / "icon.png"
+ else:
+ candidate = base_dir() / "assets" / "icon.png"
+ return candidate if candidate.exists() else None
+
+
def support_dir() -> Path:
"""Carpeta de soporte escribible en macOS (.app); se crea si no existe."""
root = (
diff --git a/jlauncher/ui/main_window.py b/jlauncher/ui/main_window.py
index c17372d..768f750 100644
--- a/jlauncher/ui/main_window.py
+++ b/jlauncher/ui/main_window.py
@@ -5,10 +5,14 @@ from __future__ import annotations
from pathlib import Path
from PySide6.QtCore import QEasingCurve, QPropertyAnimation, QThreadPool, Qt, QTimer
-from PySide6.QtGui import QAction, QActionGroup
+from PySide6.QtGui import QAction, QActionGroup, QPixmap
from PySide6.QtWidgets import (
QApplication,
+ QDialog,
+ QDialogButtonBox,
+ QHBoxLayout,
QInputDialog,
+ QLabel,
QLineEdit,
QMainWindow,
QMessageBox,
@@ -21,6 +25,7 @@ from PySide6.QtWidgets import (
from .. import __version__, gitops
from ..config import Config, Game
+from ..paths import app_icon_path
from ..settings import load_settings, save_settings
from ..workers import CheckUpdatesWorker, DownloadWorker, RunWorker
from . import theme
@@ -257,13 +262,73 @@ class MainWindow(QMainWindow):
help_menu.addAction(self.action_about)
def _show_about(self) -> None:
- QMessageBox.about(
- self,
- f"Quant a {APP_NAME}",
- f"{APP_NAME}
"
- f"Versió {__version__}
"
- "© 2026 JailDesigner",
- )
+ """Diàleg «Quant a» personalitzat: icona, nom gran i tot centrat."""
+ dlg = QDialog(self)
+ dlg.setWindowTitle(f"Quant a {APP_NAME}")
+ dlg.setModal(True)
+
+ lay = QVBoxLayout(dlg)
+ lay.setContentsMargins(40, 30, 40, 24)
+ lay.setSpacing(0)
+
+ # Logo (si el trobem); s'escala suau des del PNG gran.
+ icon_path = app_icon_path()
+ if icon_path is not None:
+ pix = QPixmap(str(icon_path))
+ if not pix.isNull():
+ logo = QLabel(alignment=Qt.AlignCenter)
+ logo.setPixmap(
+ pix.scaled(
+ 96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation
+ )
+ )
+ lay.addWidget(logo)
+ lay.addSpacing(16)
+
+ # Nom de l'app: gran, en negreta i amb el morat de la marca.
+ name = QLabel(APP_NAME, alignment=Qt.AlignCenter)
+ nf = name.font()
+ nf.setPointSize(nf.pointSize() + 13)
+ nf.setBold(True)
+ name.setFont(nf)
+ name.setStyleSheet("color: #7c4dff;")
+ lay.addWidget(name)
+ lay.addSpacing(4)
+
+ # Versió en cursiva i atenuada.
+ ver = QLabel(f"Versió {__version__}", alignment=Qt.AlignCenter)
+ vf = ver.font()
+ vf.setItalic(True)
+ vf.setPointSize(vf.pointSize() + 1)
+ ver.setFont(vf)
+ ver.setStyleSheet("color: #8a8a8a;")
+ lay.addWidget(ver)
+ lay.addSpacing(18)
+
+ # Lema discret.
+ tag = QLabel("Clona, compila i juga · jailgames", alignment=Qt.AlignCenter)
+ tag.setStyleSheet("color: #8a8a8a;")
+ lay.addWidget(tag)
+ lay.addSpacing(14)
+
+ copy = QLabel("© 2026 JailDesigner", alignment=Qt.AlignCenter)
+ cf = copy.font()
+ cf.setPointSize(cf.pointSize() - 1)
+ copy.setFont(cf)
+ lay.addWidget(copy)
+ lay.addSpacing(24)
+
+ # Botó OK centrat.
+ buttons = QDialogButtonBox(QDialogButtonBox.Ok)
+ buttons.button(QDialogButtonBox.Ok).setText("D'acord")
+ buttons.accepted.connect(dlg.accept)
+ row = QHBoxLayout()
+ row.addStretch(1)
+ row.addWidget(buttons)
+ row.addStretch(1)
+ lay.addLayout(row)
+
+ dlg.exec()
def _build_theme_menu(self, parent_menu) -> None:
"""Submenú Tema amb tres opcions exclusives: Sistema / Clar / Fosc."""