Usa l'icona real de icon/ al bundle macOS i al «Quant a»

This commit is contained in:
2026-05-30 15:56:51 +02:00
parent a71a1be88d
commit fd8eedab76
6 changed files with 12 additions and 130 deletions
-3
View File
@@ -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/
+3 -3
View File
@@ -99,9 +99,9 @@ binario suelto sino una **app nativa**:
# -> dist/jlauncher-v<versión>-macos-<arch>.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
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

-99
View File
@@ -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())
+8 -24
View File
@@ -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
+1 -1
View File
@@ -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