1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Li/Mikewolfli/Wei Li(mikewolfli@163.com)
// SPDX-License-Identifier: MIT
//! Host-registered icons: a name and its outline, added to the built-in set at runtime.
//!
//! # The gap this closes
//!
//! [`IconName`](crate::widget::IconName) is a closed enum generated from
//! `tools/icon_tokens.txt` — a *fixed* set. That is the right shape for the crate's own icons
//! (a compile-time table, no lookup cost, no missing-icon surprise), but it leaves a host with no
//! way to draw **its own** icon: an application-specific glyph, a brand mark, a product icon set
//! the crate does not ship.
//!
//! This module is that way in. [`register_icon`] binds a token to SVG path data, and
//! [`Icon::set_icon`](crate::widget::Icon::set_icon) — which already accepts any string — draws the
//! registered outline for a name no `IconName` variant covers.
//!
//! # Why this is separate from `IconName`
//!
//! The two answer different questions and must not be conflated:
//!
//! * **`IconName`** is the crate's *published vocabulary*. A variant existing is a promise that the
//! icon ships and is tested (the census covers every variant). It must stay a closed set, or that
//! promise has no meaning.
//! * **The registry** is the *host's extension point*. It makes no promise about what is
//! registered, only about how it is looked up and drawn.
//!
//! Folding host icons into `IconName` would need a runtime-sized enum, which is what the crate's
//! zero-cost-abstraction rule (principle #28) exists to avoid — and would make `ALL` unanswerable.
//!
//! # Why this is worth having when the icon set is already 68 deep
//!
//! `docs/plans/research-icons.md` §2 rejects an *icon font* partly because a glyph cannot be cached
//! the way static geometry can. That reasoning applies to the crate's **own** set (which is why it
//! is bundled path data) and not to this: a registered icon carries the same `&'static str` path
//! data as a bundled one, so it goes through the identical draw path. What the registry adds is
//! *reach* — a host's own icons — for the cost of one small table.
use crate;
/// How many host icons may be registered at once.
///
/// A fixed capacity, like the runtime font registry: the lookup runs per draw, from threads that
/// hold no lock, and a growable table behind a lock would put that lock on the draw path. Sixteen
/// is a UI's worth of application-specific glyphs (a title bar and a toolbar) without making the
/// per-lookup copy large.
pub const MAX_REGISTERED_ICONS: usize = 16;
/// Register `paths` as the outline for `name`, replacing any earlier registration of that name.
///
/// Returns `true` when the icon was accepted, `false` when the registry is full or an argument is
/// empty. A `false` is a refusal, not a silent drop: a host that registered an icon and got `false`
/// must not believe its icon is drawable.
///
/// # The path data
///
/// `paths` is SVG path data (`d`) on the same 960-unit design grid as the built-in icons, with
/// **negative `y` upward** — see [`IconData`](crate::widget::IconData). A host can take that
/// straight from a Material Symbols file, or from any source that uses that grid; a different grid
/// will draw at the wrong scale, because the grid is a property of the data and this signature
/// does not carry it. `register_icon_on_grid` exists for a source on another grid.
///
/// # Why the name is `&'static str`
///
/// The same reason the font registry takes `&'static [u8]`: the lookup runs per draw and must not
/// allocate or lock. A `String` would force one or the other. A host that has a runtime name can
/// `Box::leak` it — an explicit, one-time cost.
///
/// # Example
///
/// ```no_run
/// use rust_widgets::widget::register_icon;
///
/// // A 960-grid outline with negative y upward, as Material Symbols files use.
/// let triangle = ["M480-200 240-440l56-56 184 184 184-184 56 56-240 240Z"];
/// assert!(register_icon("disclosure", &triangle));
/// ```
/// [`register_icon`], for a source on a grid other than the built-in 960 units.
///
/// A host that draws from a 24-unit viewBox (Lucide, Tabler, Feather) passes `24` here rather than
/// rescaling the path, so the data stays the source's own numbers.
/// Forget every registered icon. The built-in set is unaffected.
///
/// Returns how many were removed, so the effect is observable rather than something a test has to
/// infer.
/// How many host icons are currently registered.
/// Whether `name` is a registered host icon (as opposed to a built-in [`IconName`] token).
///
/// A caller uses this to tell "my icon is registered" from "the crate happens to ship this name",
/// which matters when deciding whether a missing icon is a registration bug.
///
/// [`IconName`]: crate::widget::IconName
/// A registered icon's outline, or `None` when the name is not registered.
///
/// Exposed so a test can assert what was stored without rendering, and so a host can check its own
/// registration without a draw.
/// A host-registered icon, as [`registered_icon`] returns it.