Skip to main content

iced_cube/
icon.rs

1//! Lucide icons rendered through iced's `svg` widget.
2//!
3//! Build a [`Glyph`] with the [`lucide!`](crate::lucide) macro so it is
4//! resolved at compile time and only the icons you use end up in the binary.
5
6use iced::widget::{Svg, svg};
7use iced::{Color, Length, Theme};
8
9pub use lucide_static_svg::Icon;
10
11/// Embedded SVG source for one icon.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct Glyph {
14    name: &'static str,
15    svg: &'static str,
16}
17
18impl Glyph {
19    /// Resolves an icon. Call this in a `const` so unused icons are not linked.
20    pub const fn new(icon: Icon) -> Self {
21        Self {
22            name: icon.name(),
23            svg: icon.svg_str(),
24        }
25    }
26
27    /// Kebab-case Lucide name, such as `chevron-right`.
28    pub const fn name(self) -> &'static str {
29        self.name
30    }
31
32    /// Raw SVG markup.
33    pub const fn svg(self) -> &'static str {
34        self.svg
35    }
36}
37
38/// Resolves a Lucide icon to a [`Glyph`] at compile time.
39///
40/// ```
41/// let save = iced_cube::lucide!(Save);
42/// assert_eq!(save.name(), "save");
43/// ```
44#[macro_export]
45macro_rules! lucide {
46    ($name:ident) => {{
47        const GLYPH: $crate::icon::Glyph = $crate::icon::Glyph::new($crate::icon::Icon::$name);
48        GLYPH
49    }};
50}
51
52/// Renders a glyph at `size` pixels, tinted with the theme foreground.
53pub fn icon<'a>(glyph: Glyph, size: f32) -> Svg<'a, Theme> {
54    tinted(glyph, size, None)
55}
56
57/// Renders a glyph at `size` pixels in a fixed colour, or the theme
58/// foreground when `color` is `None`.
59pub fn tinted<'a>(glyph: Glyph, size: f32, color: Option<Color>) -> Svg<'a, Theme> {
60    svg(svg::Handle::from_memory(glyph.svg().as_bytes()))
61        .width(Length::Fixed(size))
62        .height(Length::Fixed(size))
63        .style(move |theme: &Theme, _status| svg::Style {
64            color: Some(color.unwrap_or(theme.extended_palette().background.base.text)),
65        })
66}
67
68#[cfg(test)]
69mod tests {
70    #[test]
71    fn macro_resolves_name_and_markup() {
72        let glyph = crate::lucide!(ChevronRight);
73        assert_eq!(glyph.name(), "chevron-right");
74        assert!(glyph.svg().starts_with("<svg"));
75    }
76
77    #[test]
78    fn lucide_svgs_use_current_colour_so_tinting_works() {
79        assert!(crate::lucide!(Save).svg().contains("currentColor"));
80    }
81}