macOS: Put the application icons in a frame

Bloody waste of time. In a few years somebody at Apple will decide
they like triangular icons or 3D icons or God knows what and I will have
to waste a few more hours of my life on their idiocy.
This commit is contained in:
Kovid Goyal 2025-03-11 15:08:29 +05:30
parent df44a5843e
commit 83602bbfab
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 138 additions and 33 deletions

68
icons/icns/make_iconsets.py Normal file → Executable file
View File

@ -8,6 +8,7 @@ import os
import shutil
import subprocess
import sys
import tempfile
d, j, a = (getattr(os.path, x) for x in ('dirname', 'join', 'abspath'))
base = d(a(__file__))
@ -18,30 +19,45 @@ sources = {'calibre':j(imgsrc, 'calibre.svg'), 'ebook-edit':j(imgsrc, 'tweak.svg
if sys.argv[-1] == 'only-logo':
sources = {'calibre':sources['calibre']}
for name, src in sources.items():
iconset = name + '.iconset'
if os.path.exists(iconset):
shutil.rmtree(iconset)
os.mkdir(iconset)
os.chdir(iconset)
try:
for sz in (16, 32, 128, 256, 512, 1024):
iname = f'icon_{sz}x{sz}.png'
iname2x = f'icon_{sz // 2}x{sz // 2}@2x.png'
if src.endswith('.svg'):
subprocess.check_call(['rsvg-convert', src, '-w', str(sz), '-h', str(sz), '-o', iname])
else:
# We have a 512x512 png image
if sz == 512:
shutil.copy2(src, iname)
def render_svg(src, sz, dest):
subprocess.check_call(['rsvg-convert', src, '-w', str(sz), '-h', str(sz), '-o', dest])
with tempfile.TemporaryDirectory() as tdir:
def render_frame(sz: int):
f = os.path.join(tdir, f'frame-{sz}.png')
if not os.path.exists(f):
render_svg(j(imgsrc, 'frame.svg'), sz, f)
return f
def render_framed(sz: int, iname: str, shrink_factor: float = 0.76):
frame = render_frame(sz)
icon = os.path.join(tdir, f'icon-{sz}.png')
render_svg(src, int(shrink_factor * sz), icon)
subprocess.check_call(f'convert {frame} {icon} -gravity center -compose over -composite {iname}'.split())
for name, src in sources.items():
iconset = name + '.iconset'
if os.path.exists(iconset):
shutil.rmtree(iconset)
os.mkdir(iconset)
os.chdir(iconset)
try:
for sz in (16, 32, 128, 256, 512, 1024):
iname = f'icon_{sz}x{sz}.png'
iname2x = f'icon_{sz // 2}x{sz // 2}@2x.png'
if sz < 128:
render_svg(src, sz, iname)
else:
subprocess.check_call(['convert', src, '-resize', f'{sz}x{sz}', iname])
if sz > 16:
shutil.copy2(iname, iname2x)
if sz > 512:
os.remove(iname)
for name in (iname, iname2x):
if os.path.exists(name):
subprocess.check_call(['optipng', '-o7', '-strip', 'all', name])
finally:
os.chdir('..')
render_framed(sz, iname)
if sz > 16:
shutil.copy2(iname, iname2x)
if sz > 512:
os.remove(iname)
for name in (iname, iname2x):
if os.path.exists(name):
subprocess.check_call(['optipng', '-o7', '-strip', 'all', name])
finally:
os.chdir('..')

83
imgsrc/frame.svg Normal file
View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="1024"
height="1024"
fill="none"
version="1.1"
id="svg13"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<g
filter="url(#a)"
id="g2">
<rect
width="824"
height="824"
x="100"
y="100"
fill="#fff"
rx="184"
id="rect1" />
<rect
width="824"
height="824"
x="100"
y="100"
fill="url(#b)"
rx="184"
id="rect2" />
</g>
<defs
id="defs13">
<linearGradient
id="b"
x1="512"
x2="512"
y1="100"
y2="924"
gradientUnits="userSpaceOnUse">
<stop
stop-opacity="0"
id="stop10" />
<stop
offset="1"
stop-opacity=".2"
id="stop11" />
</linearGradient>
<filter
id="a"
width="868"
height="868"
x="78"
y="89"
color-interpolation-filters="sRGB"
filterUnits="userSpaceOnUse">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood11" />
<feColorMatrix
in="SourceAlpha"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
id="feColorMatrix11" />
<feOffset
dy="11"
id="feOffset11" />
<feGaussianBlur
stdDeviation="11"
id="feGaussianBlur11" />
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.28 0"
id="feColorMatrix12" />
<feBlend
in2="BackgroundImageFix"
result="effect1_dropShadow"
id="feBlend12" />
<feBlend
in="SourceGraphic"
in2="effect1_dropShadow"
result="shape"
id="feBlend13" />
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -127,7 +127,8 @@ def init_qt(args):
# Ancient broken VNC servers cannot handle icons of size greater than 256
# https://www.mobileread.com/forums/showthread.php?t=278447
ic = 'lt.png' if is_x11 else 'library.png'
app.setWindowIcon(QIcon(I(ic, allow_user_override=False)))
if not ismacos:
app.setWindowIcon(QIcon(I(ic, allow_user_override=False)))
return app, opts, args

View File

@ -8,7 +8,7 @@ import time
from qt.core import QIcon
from calibre.constants import EDITOR_APP_UID, islinux
from calibre.constants import EDITOR_APP_UID, islinux, ismacos
from calibre.ebooks.oeb.polish.check.css import shutdown as shutdown_css_check_pool
from calibre.gui2 import Application, decouple, set_gui_prefs, setup_gui_option_parser
from calibre.ptempfile import reset_base_dir
@ -60,7 +60,8 @@ def _run(args, notify=None):
from calibre.utils.webengine import setup_default_profile
setup_default_profile()
app.load_builtin_fonts()
app.setWindowIcon(QIcon.ic('tweak.png'))
if not ismacos:
app.setWindowIcon(QIcon.ic('tweak.png'))
main = Main(opts, notify=notify)
main.set_exception_handler()
main.show()

View File

@ -10,6 +10,7 @@ from itertools import product
from qt.core import (
QAction,
QApplication,
QDockWidget,
QEvent,
QHBoxLayout,
@ -285,7 +286,8 @@ class Main(MainWindow):
traceback.print_exc()
self.setWindowTitle(self.APP_NAME)
self.boss = Boss(self, notify=notify)
self.setWindowIcon(QIcon.ic('tweak.png'))
if not ismacos:
self.setWindowIcon(QApplication.instance().windowIcon())
self.opts = opts
self.path_to_ebook = None
self.container = None

View File

@ -125,7 +125,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
def __init__(self, opts, parent=None, gui_debug=None):
MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True)
self.setVisible(False)
self.setWindowIcon(QApplication.instance().windowIcon())
if not ismacos:
self.setWindowIcon(QApplication.instance().windowIcon())
self.extra_files_watcher = ExtraFilesWatcher(self)
self.jobs_pointer = Pointer(self)
self.proceed_requested.connect(self.do_proceed,

View File

@ -9,7 +9,7 @@ from contextlib import closing
from qt.core import QIcon, QObject, Qt, QTimer, pyqtSignal
from calibre.constants import VIEWER_APP_UID, islinux
from calibre.constants import VIEWER_APP_UID, islinux, ismacos
from calibre.gui2 import Application, error_dialog, setup_gui_option_parser
from calibre.gui2.listener import send_message_in_process
from calibre.gui2.viewer.config import get_session_pref, vprefs
@ -149,7 +149,8 @@ def run_gui(app, opts, args, internal_book_data, listener=None):
acc = EventAccumulator(app)
app.file_event_hook = acc
app.load_builtin_fonts()
app.setWindowIcon(QIcon.ic('viewer.png'))
if not ismacos:
app.setWindowIcon(QIcon.ic('viewer.png'))
migrate_previous_viewer_prefs()
main = EbookViewer(
open_at=opts.open_at, continue_reading=opts.continue_reading, force_reload=opts.force_reload,