1use iced::widget::{Svg, svg};
7use iced::{Color, Length, Theme};
8
9pub use lucide_static_svg::Icon;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct Glyph {
14 name: &'static str,
15 svg: &'static str,
16}
17
18impl Glyph {
19 pub const fn new(icon: Icon) -> Self {
21 Self {
22 name: icon.name(),
23 svg: icon.svg_str(),
24 }
25 }
26
27 pub const fn name(self) -> &'static str {
29 self.name
30 }
31
32 pub const fn svg(self) -> &'static str {
34 self.svg
35 }
36}
37
38#[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
52pub const DISABLED_OPACITY: f32 = 0.5;
54
55pub const fn opacity(enabled: bool) -> f32 {
57 if enabled { 1.0 } else { DISABLED_OPACITY }
58}
59
60pub fn icon<'a>(glyph: Glyph, size: f32) -> Svg<'a, Theme> {
62 themed(glyph, size, 1.0, |theme| {
63 theme.extended_palette().background.base.text
64 })
65}
66
67pub fn tinted<'a>(glyph: Glyph, size: f32, color: Option<Color>) -> Svg<'a, Theme> {
71 match color {
72 Some(color) => themed(glyph, size, color.a, move |_| color),
73 None => icon(glyph, size),
74 }
75}
76
77pub fn themed<'a>(
84 glyph: Glyph,
85 size: f32,
86 opacity: f32,
87 color: impl Fn(&Theme) -> Color + 'a,
88) -> Svg<'a, Theme> {
89 svg(svg::Handle::from_memory(glyph.svg().as_bytes()))
90 .width(Length::Fixed(size))
91 .height(Length::Fixed(size))
92 .opacity(opacity.clamp(0.0, 1.0))
93 .style(move |theme: &Theme, _status| svg::Style {
94 color: Some(opaque(color(theme))),
95 })
96}
97
98pub const fn opaque(color: Color) -> Color {
100 Color { a: 1.0, ..color }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn disabled_icons_are_drawn_at_half_opacity() {
109 assert_eq!(opacity(true), 1.0);
110 assert_eq!(opacity(false), DISABLED_OPACITY);
111 }
112
113 #[test]
114 fn opaque_keeps_the_channels_and_drops_the_alpha() {
115 let colour = opaque(Color::from_rgba(0.1, 0.2, 0.3, 0.4));
116 assert_eq!(colour, Color::from_rgb(0.1, 0.2, 0.3));
117 }
118
119 #[test]
120 fn macro_resolves_name_and_markup() {
121 let glyph = crate::lucide!(ChevronRight);
122 assert_eq!(glyph.name(), "chevron-right");
123 assert!(glyph.svg().starts_with("<svg"));
124 }
125
126 #[test]
127 fn lucide_svgs_use_current_colour_so_tinting_works() {
128 assert!(crate::lucide!(Save).svg().contains("currentColor"));
129 }
130}