Skip to main content

zng_wgt_material_icons/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Material icons for the [`Icon!`] widget.
5//!
6//! A map from name to icon codepoint is defined in a module for each font. The extension
7//! also registers [`ICONS`] handlers that provide the icons.
8//!
9//! The icons are from the [Material Design Icons] project.
10//!
11//! [`Icon!`]: struct@zng_wgt_text::icon::Icon
12//! [`ICONS`]: struct@zng_wgt::ICONS
13//! [Material Design Icons]: https://github.com/google/material-design-icons
14//!
15//! # Crate
16//!
17#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
18#![warn(unused_extern_crates)]
19#![warn(missing_docs)]
20
21zng_wgt::enable_widget_macros!();
22
23#[cfg(all(
24    feature = "usage_recorder",
25    any(feature = "outlined", feature = "filled", feature = "rounded", feature = "sharp")
26))]
27mod usage_recorder;
28
29#[cfg(all(
30    feature = "embedded",
31    any(feature = "outlined", feature = "filled", feature = "rounded", feature = "sharp")
32))]
33zng_env::on_process_start!(|_| {
34    zng_app::APP.on_init(zng_app::hn!(|args| {
35        if args.is_minimal {
36            return;
37        }
38
39        use zng_wgt::{ICONS, IconRequestArgs, prelude::UiNode, wgt_fn};
40        use zng_wgt_text::icon::{GlyphIcon, Icon};
41
42        #[cfg(feature = "usage_recorder")]
43        usage_recorder::on_init();
44
45        // register fonts
46        let sets = [
47            #[cfg(feature = "outlined")]
48            (outlined::FONT_NAME, outlined::FONT_BYTES),
49            #[cfg(feature = "filled")]
50            (filled::FONT_NAME, filled::FONT_BYTES),
51            #[cfg(feature = "rounded")]
52            (rounded::FONT_NAME, rounded::FONT_BYTES),
53            #[cfg(feature = "sharp")]
54            (sharp::FONT_NAME, sharp::FONT_BYTES),
55        ];
56        for (name, bytes) in sets {
57            let font = zng_ext_font::CustomFont::from_bytes(name, zng_ext_font::FontBytes::from_static(bytes), 0);
58            zng_ext_font::FONTS.register(font);
59        }
60
61        // register icons
62        ICONS.register(wgt_fn!(|args: IconRequestArgs| {
63            if let Some(strong_key) = args.name().strip_prefix("material/") {
64                #[expect(clippy::type_complexity)]
65                let sets: &[(&str, fn(&str) -> Option<GlyphIcon>)] = &[
66                    #[cfg(feature = "outlined")]
67                    ("outlined/", outlined::get),
68                    #[cfg(feature = "filled")]
69                    ("filled/", filled::get),
70                    #[cfg(feature = "rounded")]
71                    ("rounded/", rounded::get),
72                    #[cfg(feature = "sharp")]
73                    ("sharp/", sharp::get),
74                ];
75                for (name, get) in sets {
76                    if let Some(key) = strong_key.strip_prefix(name)
77                        && let Some(ico) = get(key)
78                    {
79                        return Icon!(ico);
80                    }
81                }
82            }
83
84            UiNode::nil()
85        }));
86
87        ICONS.register_fallback(wgt_fn!(|args: IconRequestArgs| {
88            let sets = [
89                #[cfg(feature = "outlined")]
90                outlined::get,
91                #[cfg(feature = "filled")]
92                filled::get,
93                #[cfg(feature = "rounded")]
94                rounded::get,
95                #[cfg(feature = "sharp")]
96                sharp::get,
97            ];
98            for get in sets {
99                if let Some(ico) = get(args.name()) {
100                    return Icon!(ico);
101                }
102            }
103            UiNode::nil()
104        }));
105    }));
106});
107
108#[cfg(any(feature = "outlined", feature = "filled", feature = "rounded", feature = "sharp"))]
109macro_rules! getters {
110    ($FONT_NAME:ident, $MAP:ident) => {
111        /// Gets the [`GlyphIcon`].
112        pub fn get(key: &str) -> Option<GlyphIcon> {
113            let icon = GlyphIcon::new($FONT_NAME.clone(), *$MAP.get(key)?);
114            #[cfg(feature = "usage_recorder")]
115            USAGE.insert(key);
116            Some(icon)
117        }
118
119        #[cfg(feature = "usage_recorder")]
120        pub(crate) static USAGE: std::sync::LazyLock<crate::usage_recorder::UsageRecorder> = std::sync::LazyLock::new(Default::default);
121
122        /// Require the [`GlyphIcon`], logs an error if not found.
123        ///
124        /// # Panics
125        ///
126        /// Panics if the `key` is not found.
127        ///
128        /// [`GlyphIcon`]: struct@zng_wgt_text::icon::GlyphIcon
129        pub fn req(key: &str) -> GlyphIcon {
130            match get(key) {
131                Some(g) => g,
132                None => {
133                    tracing::error!("icon {key:?} not found in `outlined`");
134                    GlyphIcon::new("", '\0')
135                }
136            }
137        }
138
139        /// All icons.
140        pub fn all() -> impl ExactSizeIterator<Item = (&'static str, GlyphIcon)> {
141            $MAP.entries()
142                .map(|(key, val)| (*key, GlyphIcon::new($FONT_NAME.clone(), *val)))
143        }
144    };
145}
146
147/// Outline icons.
148///  
149/// This is the "Material Icons Outlined" font.
150///
151/// # Icons
152///
153/// Use the [`ICONS`] service with key `"material/outlined/{name}"` or `"{name}"` to get an widget that renders the icon.
154///
155/// Use [`outlined::req`] to get a [`GlyphIcon`] directly for use in the [`Icon!`] widget.
156///
157/// [`Icon!`]: struct@zng_wgt_text::icon::Icon
158/// [`GlyphIcon`]: struct@zng_wgt_text::icon::GlyphIcon
159/// [`ICONS`]: struct@zng_wgt::ICONS
160///
161/// | Name | Icon |
162/// |------|------|
163#[doc = include_str!(concat!(env!("OUT_DIR"), "/generated.outlined.docs.txt"))]
164#[cfg(feature = "outlined")]
165pub mod outlined {
166    use zng_ext_font::FontName;
167    use zng_wgt_text::icon::GlyphIcon;
168
169    /// "Material Icons Outlined".
170    pub const FONT_NAME: FontName = FontName::from_static("Material Icons Outlined");
171
172    /// Embedded font bytes.
173    #[cfg(feature = "embedded")]
174    pub const FONT_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/generated.outlined.ttf"));
175
176    include!(concat!(env!("OUT_DIR"), "/generated.outlined.map.rs"));
177    getters!(FONT_NAME, MAP);
178}
179
180/// Filled icons.
181///
182/// This is the "Material Icons" font.
183///
184/// # Icons
185///
186/// Use the [`ICONS`] service with key `"material/filled/{name}"` or `"{name}"` to get an widget that renders the icon.
187///
188/// Use [`filled::req`] to get a [`GlyphIcon`] directly for use in the [`Icon!`] widget.
189///
190/// [`Icon!`]: struct@zng_wgt_text::icon::Icon
191/// [`GlyphIcon`]: struct@zng_wgt_text::icon::GlyphIcon
192/// [`ICONS`]: struct@zng_wgt::ICONS
193///
194/// | Name | Icon |
195/// |------|------|
196#[doc = include_str!(concat!(env!("OUT_DIR"), "/generated.filled.docs.txt"))]
197#[cfg(feature = "filled")]
198pub mod filled {
199    use zng_ext_font::FontName;
200    use zng_wgt_text::icon::GlyphIcon;
201
202    /// "Material Icons".
203    pub const FONT_NAME: FontName = FontName::from_static("Material Icons");
204
205    /// Embedded font bytes.
206    #[cfg(feature = "embedded")]
207    pub const FONT_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/generated.filled.ttf"));
208
209    include!(concat!(env!("OUT_DIR"), "/generated.filled.map.rs"));
210    getters!(FONT_NAME, MAP);
211}
212
213/// Rounded icons.
214///  
215/// This is the "Material Icons Rounded" font.
216///
217/// # Icons
218///
219/// Use the [`ICONS`] service with key `"material/rounded/{name}"` or `"{name}"` to get an widget that renders the icon.
220///
221/// Use [`rounded::req`] to get a [`GlyphIcon`] directly for use in the [`Icon!`] widget.
222///
223/// [`Icon!`]: struct@zng_wgt_text::icon::Icon
224/// [`GlyphIcon`]: struct@zng_wgt_text::icon::GlyphIcon
225/// [`ICONS`]: struct@zng_wgt::ICONS
226///
227/// | Name | Icon |
228/// |------|------|
229#[doc = include_str!(concat!(env!("OUT_DIR"), "/generated.rounded.docs.txt"))]
230#[cfg(feature = "rounded")]
231pub mod rounded {
232    use zng_ext_font::FontName;
233    use zng_wgt_text::icon::GlyphIcon;
234
235    /// "Material Icons Rounded".
236    pub const FONT_NAME: FontName = FontName::from_static("Material Icons Rounded");
237
238    /// Embedded font bytes.
239    #[cfg(feature = "embedded")]
240    pub const FONT_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/generated.rounded.ttf"));
241
242    include!(concat!(env!("OUT_DIR"), "/generated.rounded.map.rs"));
243    getters!(FONT_NAME, MAP);
244}
245
246/// Sharp icons.
247///  
248/// This is the "Material Icons Sharp" font.
249///
250/// # Icons
251///
252/// Use the [`ICONS`] service with key `"material/sharp/{name}"` or `"{name}"` to get an widget that renders the icon.
253///
254/// Use [`sharp::req`] to get a [`GlyphIcon`] directly for use in the [`Icon!`] widget.
255///
256/// [`Icon!`]: struct@zng_wgt_text::icon::Icon
257/// [`GlyphIcon`]: struct@zng_wgt_text::icon::GlyphIcon
258/// [`ICONS`]: struct@zng_wgt::ICONS
259///
260/// | Name | Icon |
261/// |------|------|
262#[doc = include_str!(concat!(env!("OUT_DIR"), "/generated.sharp.docs.txt"))]
263#[cfg(feature = "sharp")]
264pub mod sharp {
265    use zng_ext_font::FontName;
266    use zng_wgt_text::icon::GlyphIcon;
267
268    /// "Material Icons Sharp".
269    pub const FONT_NAME: FontName = FontName::from_static("Material Icons Sharp");
270
271    /// Embedded font bytes.
272    #[cfg(feature = "embedded")]
273    pub const FONT_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/generated.sharp.ttf"));
274
275    include!(concat!(env!("OUT_DIR"), "/generated.sharp.map.rs"));
276    getters!(FONT_NAME, MAP);
277}