zng_wgt_material_icons/
lib.rs

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