Skip to main content

brep_app/
icons.rs

1//! The inline-icon catalog: every `assets/glyphs/*.svg` as SVG source, keyed by
2//! the character it draws.
3//!
4//! This is the lookup half of the SVG-icon feature; [`crate::icon_text`] is the
5//! drawing half. The table itself is generated by `build.rs` at compile time
6//! from the glyph SVGs, so an icon is catalogued by existing, with no list here
7//! to fall out of step. See `build.rs` for the two transforms it applies (the
8//! `#111` ink sentinel, and the viewBox-derived aspect ratio).
9//!
10//! Why both a font and an SVG catalog: the font is what egui lays out inside
11//! ordinary text, and it stays the fallback for every glyph that is drawn as
12//! text (toolbar buttons, `ui.label`s not yet converted). The catalog is what
13//! lets the same character be drawn as a real image — which is what makes
14//! genuine multi-colour icons possible, since a TrueType glyph can only be one
15//! colour. The stacked private-use layers in
16//! [`crate::panels::toolbar_button`] exist only to work around that limit.
17//!
18//! Three icons already take that route — the bug (U+1F41E), the book
19//! (U+1F4DA) and the pencil (U+270D). Each was composed from the very layers
20//! `toolbar_button` stacks by hand, in the same palette, so they are the same
21//! artwork expressed once instead of assembled at paint time. Their glyph SVGs
22//! mark the ink layer `brep:font-outline="1"`, so the TTF is byte-for-byte what
23//! it was and a text use of those characters still renders exactly as before —
24//! the colour is additive, visible only through this catalog.
25
26// The generated `pub static ICONS: &[Icon]`, sorted by codepoint.
27include!(concat!(env!("OUT_DIR"), "/icon_catalog.rs"));
28
29/// One catalogued icon: the SVG source for a character, plus what the renderer
30/// needs to place and colour it.
31#[derive(Debug, Clone, Copy, PartialEq)]
32pub struct Icon {
33    /// The character this icon draws — what a string must contain to select it.
34    pub ch: char,
35    /// PostScript glyph name (e.g. `icon_2699`), for debugging and docs.
36    pub name: &'static str,
37    /// A stable, per-icon `bytes://…svg` URI. egui caches the decoded texture
38    /// under it, so it must be constant across frames — and it must end in
39    /// `.svg`, which is how `egui_extras`' loader recognises the format.
40    pub uri: &'static str,
41    /// The SVG source, with `mono` artwork already rewritten to white.
42    pub svg: &'static str,
43    /// width / height of the viewBox — the proportion the font draws it at.
44    pub aspect: f32,
45    /// SVG cropped to painted bounds, for standalone artwork in icon tiles.
46    pub artwork_svg: &'static str,
47    /// Aspect ratio of the painted artwork, excluding canvas padding.
48    pub artwork_aspect: f32,
49    /// Monochrome artwork that should take the colour of the surrounding text.
50    /// `false` means the SVG carries its own colours and must not be tinted.
51    pub mono: bool,
52}
53
54/// The icon for `ch`, or `None` if the catalog has none — in which case the
55/// character stays ordinary text and the font draws it.
56pub fn lookup(ch: char) -> Option<&'static Icon> {
57    let i = ICONS.binary_search_by_key(&ch, |icon| icon.ch).ok()?;
58    Some(&ICONS[i])
59}
60
61/// Whether `ch` has an icon. Cheaper to read at a call site than `lookup().is_some()`.
62pub fn has(ch: char) -> bool {
63    lookup(ch).is_some()
64}
65
66/// The catalogued artwork for a label that is exactly one character.
67///
68/// This is the test every icon-drawing site makes — toolbar buttons, tree rows,
69/// palette rows — so they all agree on what is an icon. `None` for a
70/// multi-character label (a composite, not an icon) and for an uncatalogued
71/// character; both are drawn as ordinary text.
72///
73/// MONOCHROME artwork is included. It used to be excluded, on the grounds that
74/// the icon font already drew it in the live text colour for free — but that
75/// exclusion was the last thing keeping a font in the binary. Callers tint a
76/// `mono` icon to their text colour (it is white in the catalog, so a multiply
77/// lands it exactly) and leave a colour one alone.
78pub fn artwork(glyph: &str) -> Option<&'static Icon> {
79    let mut chars = glyph.chars();
80    let icon = lookup(chars.next()?)?;
81    chars.next().is_none().then_some(icon)
82}
83
84// BREP private tests: 4d18c45d7a290280