Skip to main content

egui_charts/icons/
icon.rs

1//! Icon struct for compile-time embedded SVG icons
2
3use egui::Vec2;
4
5/// A compile-time embedded SVG icon.
6///
7/// Icons are embedded at compile time using `include_bytes!`, which means
8/// they are part of the binary and don't require filesystem access at runtime.
9/// Create your own with [`icon_from_path!`](crate::icon_from_path).
10///
11/// # Example
12///
13/// ```
14/// use egui::Vec2;
15/// use egui_charts::icons::icons;
16///
17/// # egui::__run_test_ui(|ui| {
18/// ui.add(icons::SETTINGS.as_image(Vec2::splat(20.0)));
19/// # });
20/// ```
21#[derive(Clone, Copy)]
22pub struct Icon {
23    /// Name/path of the icon (for debugging and cache keys)
24    pub name: &'static str,
25    /// Raw SVG bytes embedded at compile time
26    pub svg_bytes: &'static [u8],
27}
28
29impl Icon {
30    /// Create a new icon from embedded bytes.
31    ///
32    /// This is typically called via the `icon_from_path!` macro.
33    #[inline]
34    pub const fn new(name: &'static str, svg_bytes: &'static [u8]) -> Self {
35        Self { name, svg_bytes }
36    }
37
38    /// Convert to an egui Image widget with the specified size.
39    ///
40    /// # Arguments
41    ///
42    /// * `size` - The size to render the icon at (width and height)
43    #[inline]
44    pub fn as_image(&self, size: Vec2) -> egui::Image<'static> {
45        // The logical width in the URI lets the icon loader size strokes for the display scale.
46        let uri = format!("{}{}/{}", super::loader::URI_PREFIX, size.x, self.name);
47        egui::Image::from_bytes(uri, self.svg_bytes).fit_to_exact_size(size)
48    }
49
50    /// Convert to an egui Image widget with the specified size and tint color.
51    ///
52    /// # Arguments
53    ///
54    /// * `size` - The size to render the icon at
55    /// * `tint` - The color to tint the icon with
56    #[inline]
57    pub fn as_image_tinted(&self, size: Vec2, tint: egui::Color32) -> egui::Image<'static> {
58        self.as_image(size).tint(tint)
59    }
60
61    /// Convert to an egui Image widget with square dimensions.
62    ///
63    /// # Arguments
64    ///
65    /// * `size` - The size (both width and height) to render the icon at
66    #[inline]
67    pub fn as_image_square(&self, size: f32) -> egui::Image<'static> {
68        self.as_image(Vec2::splat(size))
69    }
70
71    /// Get the name/path of this icon (useful for debugging).
72    #[inline]
73    pub const fn name(&self) -> &'static str {
74        self.name
75    }
76
77    /// Get the raw SVG bytes.
78    #[inline]
79    pub const fn svg_bytes(&self) -> &'static [u8] {
80        self.svg_bytes
81    }
82
83    /// Get the size of the SVG data in bytes.
84    #[inline]
85    pub const fn byte_size(&self) -> usize {
86        self.svg_bytes.len()
87    }
88
89    /// Convert to an egui Button widget with the icon.
90    ///
91    /// The button will automatically tint the icon to match text color,
92    /// providing proper visual feedback for hover/active states.
93    ///
94    /// # Arguments
95    ///
96    /// * `size` - The size to render the icon at
97    #[inline]
98    pub fn as_button(&self, size: Vec2) -> egui::Button<'static> {
99        egui::Button::image(self.as_image(size)).image_tint_follows_text_color(true)
100    }
101
102    /// Convert to an egui Button widget with the icon and a text label.
103    ///
104    /// # Arguments
105    ///
106    /// * `size` - The size to render the icon at
107    /// * `label` - The text label to display next to the icon
108    #[inline]
109    pub fn as_button_with_label(
110        &self,
111        size: Vec2,
112        label: impl Into<egui::WidgetText>,
113    ) -> egui::Button<'static> {
114        egui::Button::image_and_text(self.as_image(size), label).image_tint_follows_text_color(true)
115    }
116
117    /// Convert to an egui Button widget with square icon dimensions.
118    ///
119    /// # Arguments
120    ///
121    /// * `size` - The size (both width and height) to render the icon at
122    #[inline]
123    pub fn as_button_square(&self, size: f32) -> egui::Button<'static> {
124        self.as_button(Vec2::splat(size))
125    }
126}
127
128impl std::fmt::Debug for Icon {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        f.debug_struct("Icon")
131            .field("name", &self.name)
132            .field("byte_size", &self.svg_bytes.len())
133            .finish()
134    }
135}
136
137impl PartialEq for Icon {
138    fn eq(&self, other: &Self) -> bool {
139        // Compare by name since bytes comparison would be expensive
140        self.name == other.name
141    }
142}
143
144impl Eq for Icon {}
145
146impl std::hash::Hash for Icon {
147    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
148        self.name.hash(state);
149    }
150}