diff --git a/.gitignore b/.gitignore index f16df8f..b47393f 100644 --- a/.gitignore +++ b/.gitignore @@ -86,9 +86,6 @@ __pycache__/ # Distribution / packaging .Python -# Artefactos del icono macOS (regenerables desde assets/icon.png con build.sh) -assets/icon.icns -assets/icon.iconset/ build/ develop-eggs/ dist/ diff --git a/README.md b/README.md index d239941..af162c1 100644 --- a/README.md +++ b/README.md @@ -99,9 +99,9 @@ binario suelto sino una **app nativa**: # -> dist/jlauncher-v-macos-.dmg (arrastrar la app a Aplicaciones) ``` -El icono es provisional y se construye a `assets/icon.icns` desde `assets/icon.png` -(regenerable con `QT_QPA_PLATFORM=offscreen .venv/bin/python assets/make_icon.py`). Para -cambiarlo, sustituye `assets/icon.png` por un PNG cuadrado 1024×1024 y recompila. +El icono vive en `icon/`: el build usa `icon/icon.icns` para el bundle y copia +`icon/icon.png` (1024×1024) dentro del `.app` para el diálogo «Quant a». Para cambiarlo, +sustituye esos ficheros (regenerables con `icon/create_icons.py`). A diferencia del onefile, la `.app` **no** escribe junto a sí misma (rompería al moverla a `/Applications`): guarda sus datos en diff --git a/assets/icon.png b/assets/icon.png deleted file mode 100644 index 2d6f380..0000000 Binary files a/assets/icon.png and /dev/null differ diff --git a/assets/make_icon.py b/assets/make_icon.py deleted file mode 100644 index 3aa00fc..0000000 --- a/assets/make_icon.py +++ /dev/null @@ -1,99 +0,0 @@ -"""Genera un icono provisional para jlauncher (PNG 1024x1024). - -Dibuja un «squircle» con degradado y un triángulo de «play» (es un lanzador de juegos). -Se ejecuta sin pantalla con la plataforma offscreen de Qt: - - QT_QPA_PLATFORM=offscreen .venv/bin/python assets/make_icon.py - -Salida: assets/icon.png. El .icns lo construye build.sh con iconutil. -""" - -from __future__ import annotations - -import os -from pathlib import Path - -os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") - -from PySide6.QtCore import QPointF, QRectF, Qt # noqa: E402 -from PySide6.QtGui import ( # noqa: E402 - QBrush, - QColor, - QGuiApplication, - QImage, - QLinearGradient, - QPainter, - QPainterPath, -) - -SIZE = 1024 - - -def render() -> QImage: - img = QImage(SIZE, SIZE, QImage.Format_ARGB32) - img.fill(Qt.transparent) - - p = QPainter(img) - p.setRenderHint(QPainter.Antialiasing, True) - - # «Squircle»: rectángulo con esquinas redondeadas estilo macOS (~22% del lado). - margin = SIZE * 0.06 - rect = QRectF(margin, margin, SIZE - 2 * margin, SIZE - 2 * margin) - radius = rect.width() * 0.2237 - body = QPainterPath() - body.addRoundedRect(rect, radius, radius) - - grad = QLinearGradient(rect.topLeft(), rect.bottomRight()) - grad.setColorAt(0.0, QColor("#4b3bd6")) - grad.setColorAt(1.0, QColor("#7b2ff7")) - p.fillPath(body, QBrush(grad)) - - # Barras verticales tenues: guiño a «jail». - p.save() - p.setClipPath(body) - p.setPen(Qt.NoPen) - p.setBrush(QColor(255, 255, 255, 26)) - bars = 5 - bar_w = rect.width() * 0.052 - gap = (rect.width() - bars * bar_w) / (bars + 1) - x = rect.left() + gap - for _ in range(bars): - p.drawRoundedRect(QRectF(x, rect.top(), bar_w, rect.height()), bar_w / 2, bar_w / 2) - x += bar_w + gap - p.restore() - - # Triángulo de «play» centrado, blanco con esquinas redondeadas. - cx, cy = rect.center().x(), rect.center().y() - r = rect.width() * 0.26 - tri = QPainterPath() - tri.moveTo(QPointF(cx - r * 0.55, cy - r * 0.95)) - tri.lineTo(QPointF(cx - r * 0.55, cy + r * 0.95)) - tri.lineTo(QPointF(cx + r * 1.0, cy)) - tri.closeSubpath() - - pen_brush = QColor("#ffffff") - stroker_pen = p.pen() - stroker_pen.setColor(pen_brush) - stroker_pen.setWidthF(r * 0.28) - stroker_pen.setJoinStyle(Qt.RoundJoin) - stroker_pen.setCapStyle(Qt.RoundCap) - p.strokePath(tri, stroker_pen) - p.fillPath(tri, pen_brush) - - p.end() - return img - - -def main() -> int: - QGuiApplication([]) - out = Path(__file__).resolve().parent / "icon.png" - img = render() - if not img.save(str(out), "PNG"): - print(f"[icon] no se pudo guardar {out}") - return 1 - print(f"[icon] generado {out} ({SIZE}x{SIZE})") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/build.sh b/build.sh index 79b8afb..0dc9b61 100755 --- a/build.sh +++ b/build.sh @@ -40,39 +40,23 @@ echo "[build] versión: v${VERSION}" echo "[build] limpiando artefactos previos…" rm -rf dist build app.build app.dist app.onefile-build -# --------------------------------------------------------------------------- -# Construye assets/icon.icns desde assets/icon.png (regenerable con make_icon.py). -# --------------------------------------------------------------------------- -build_icns() { - local png="assets/icon.png" - local icns="assets/icon.icns" - if [ ! -f "$png" ]; then - echo "[build] generando icono provisional (assets/icon.png)…" - QT_QPA_PLATFORM=offscreen .venv/bin/python assets/make_icon.py - fi - echo "[build] construyendo ${icns}…" - local iconset="assets/icon.iconset" - rm -rf "$iconset"; mkdir -p "$iconset" - local s - for s in 16 32 128 256 512; do - sips -z "$s" "$s" "$png" --out "$iconset/icon_${s}x${s}.png" >/dev/null - sips -z $((s*2)) $((s*2)) "$png" --out "$iconset/icon_${s}x${s}@2x.png" >/dev/null - done - iconutil -c icns "$iconset" -o "$icns" - rm -rf "$iconset" -} +ICON_ICNS="icon/icon.icns" +ICON_PNG="icon/icon.png" if [ "$OS" = "darwin" ]; then # ------------------------------------------------------------------------- # macOS: app bundle + DMG # ------------------------------------------------------------------------- - build_icns + if [ ! -f "$ICON_ICNS" ]; then + echo "[build] falta ${ICON_ICNS}" >&2 + exit 1 + fi echo "[build] compilando jlauncher.app (PySide6; puede tardar varios minutos)…" .venv/bin/python -m nuitka \ --standalone \ --macos-create-app-bundle \ - --macos-app-icon=assets/icon.icns \ + --macos-app-icon="$ICON_ICNS" \ --macos-app-name=jlauncher \ --macos-app-version="$VERSION" \ --macos-signed-app-name=com.jailgames.jlauncher \ @@ -102,7 +86,7 @@ if [ "$OS" = "darwin" ]; then 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» + cp "$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/paths.py b/jlauncher/paths.py index b15dc17..48a94d9 100644 --- a/jlauncher/paths.py +++ b/jlauncher/paths.py @@ -65,7 +65,7 @@ def app_icon_path() -> Path | None: if bundle is not None: candidate = bundle / "Contents" / "Resources" / "icon.png" else: - candidate = base_dir() / "assets" / "icon.png" + candidate = base_dir() / "icon" / "icon.png" return candidate if candidate.exists() else None