mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:27:02 +01:00
Every Flutter app still shipped the stock Flutter "beach" launcher icon, and none had an adaptive-icon descriptor at all. Rather than hand-drawing five one-off icons, define the family once and let each app contribute only an accent colour and a glyph. The family: a charcoal #1B1D21 field carrying one glyph in that app's own theme seed colour, at a single stroke weight. Glyphs are optically centred automatically - render.centre_offset rasterises a glyph alone, measures its ink box, and translates (never scales) the fragment, so a row of icons lines up while every icon keeps the exact family stroke weight. safe_box_overflow warns when a glyph exceeds the 560px adaptive-icon safe box; it caught three real overflows that a launcher mask would have clipped. The registry points dufs_client at ~/dufs-cloud/app: ~/dufs_client was a stale, remote-less checkout with an identical package name, since deleted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
"""Per-app glyph artwork for the shared icon family.
|
|
|
|
A glyph is a raw SVG fragment drawn in the 1024x1024 master canvas. It does not
|
|
need to be perfectly positioned: :mod:`python_pkg.app_icons.render` measures the
|
|
rendered ink and recentres the fragment, so every icon in the family sits at the
|
|
same optical height.
|
|
|
|
Fragments inherit ``fill="none"``, ``stroke``, ``stroke-width`` and round caps
|
|
from the wrapping group. A fragment that needs a filled silhouette instead
|
|
overrides those locally and uses :data:`~python_pkg.app_icons.style.ACCENT_MARKER`
|
|
for its colour.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Final
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Glyph:
|
|
"""One app's artwork.
|
|
|
|
Parameters:
|
|
name (str): Stable identifier, used as the ``--glyph`` CLI value.
|
|
description (str): What the glyph depicts, for ``--list`` output.
|
|
body (str): SVG fragment drawn in the 1024x1024 master canvas.
|
|
"""
|
|
|
|
name: str
|
|
description: str
|
|
body: str
|
|
|
|
|
|
# A cloud silhouette outline with a download arrow underneath: a client that
|
|
# pulls files off a remote dufs server.
|
|
_CLOUD_DOWN = """\
|
|
<g transform="translate(296,196) scale(18)" stroke-width="4">
|
|
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 \
|
|
2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 \
|
|
0-2.64-2.05-4.78-4.65-4.96z"/>
|
|
</g>
|
|
<path d="M 512 628 L 512 744"/>
|
|
<path d="M 444 676 L 512 744 L 580 676"/>"""
|
|
|
|
# Bar, two plates and two collars. Plate spacing is well above the minimum
|
|
# negative space so the bar does not fill in at launcher size.
|
|
_BARBELL = """\
|
|
<path d="M 268 512 L 756 512"/>
|
|
<path d="M 364 356 L 364 668"/>
|
|
<path d="M 660 356 L 660 668"/>
|
|
<path d="M 268 432 L 268 592"/>
|
|
<path d="M 756 432 L 756 592"/>"""
|
|
|
|
# Plain clock face. Deliberately no bell "ears": at launcher size they read as
|
|
# noise rather than as an alarm clock.
|
|
_CLOCK = """\
|
|
<circle cx="512" cy="512" r="244"/>
|
|
<path d="M 512 512 L 512 350"/>
|
|
<path d="M 512 512 L 636 512"/>"""
|
|
|
|
# Shield outline guarding filled cutlery. The fork is paired with a knife on
|
|
# purpose: a lone three-tine fork reads as a trident at small sizes.
|
|
_SHIELD_CUTLERY = """\
|
|
<path d="M 512 275 L 752 366 L 752 544 C 752 655 646 723 512 753 \
|
|
C 378 723 272 655 272 544 L 272 366 Z"/>
|
|
<g transform="translate(356,362) scale(13)" fill="{{ACCENT}}" stroke="none">
|
|
<path d="M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03\
|
|
C11.34 12.84 13 11.12 13 9V2h-2v7z"/>
|
|
<path d="M16 12h2.5v10H21V2c-2.76 0-5 2.24-5 5v5z"/>
|
|
</g>"""
|
|
|
|
# Two ticked-off list rows. More specific to a notes/todo app than a lone
|
|
# checkmark, and still legible at 48dp.
|
|
_CHECKLIST = """\
|
|
<path d="M 272 372 L 346 446 L 478 314"/>
|
|
<path d="M 594 400 L 752 400"/>
|
|
<path d="M 272 620 L 346 694 L 478 562"/>
|
|
<path d="M 594 648 L 752 648"/>"""
|
|
|
|
|
|
GLYPHS: Final[dict[str, Glyph]] = {
|
|
glyph.name: glyph
|
|
for glyph in (
|
|
Glyph("cloud-down", "Cloud with a download arrow", _CLOUD_DOWN),
|
|
Glyph("barbell", "Barbell with plates and collars", _BARBELL),
|
|
Glyph("clock", "Clock face with hands", _CLOCK),
|
|
Glyph("shield-cutlery", "Shield guarding a fork and knife", _SHIELD_CUTLERY),
|
|
Glyph("checklist", "Two ticked-off list rows", _CHECKLIST),
|
|
)
|
|
}
|
|
|
|
|
|
def get_glyph(name: str) -> Glyph:
|
|
"""Look up a glyph by name.
|
|
|
|
Parameters:
|
|
name (str): Glyph identifier, e.g. ``"barbell"``.
|
|
|
|
Returns:
|
|
Glyph: The matching glyph.
|
|
|
|
Raises:
|
|
KeyError: If no glyph with that name exists. The message lists the
|
|
available names, since this is usually a CLI typo.
|
|
"""
|
|
try:
|
|
return GLYPHS[name]
|
|
except KeyError:
|
|
available = ", ".join(sorted(GLYPHS))
|
|
msg = f"unknown glyph {name!r}; available: {available}"
|
|
raise KeyError(msg) from None
|