cosmic/widget/icon/
mod.rs1mod named;
7use std::ffi::OsStr;
8use std::sync::Arc;
9
10pub use named::{IconFallback, Named};
11
12mod handle;
13pub use handle::{Data, Handle, from_path, from_raster_bytes, from_raster_pixels, from_svg_bytes};
14
15use crate::Element;
16use derive_setters::Setters;
17use iced::widget::{Image, Svg};
18use iced::{ContentFit, Length, Rectangle};
19use iced_core::Rotation;
20
21pub fn icon(handle: Handle) -> Icon {
23 Icon {
24 content_fit: ContentFit::Fill,
25 handle,
26 height: None,
27 size: 16,
28 class: crate::theme::Svg::default(),
29 rotation: None,
30 width: None,
31 }
32}
33
34pub fn from_name(name: impl Into<Arc<str>>) -> Named {
36 Named::new(name)
37}
38
39#[must_use]
41#[derive(Clone, Setters)]
42pub struct Icon {
43 #[setters(skip)]
44 handle: Handle,
45 class: crate::theme::Svg,
46 #[setters(skip)]
47 pub(super) size: u16,
48 content_fit: ContentFit,
49 #[setters(strip_option)]
50 width: Option<Length>,
51 #[setters(strip_option)]
52 height: Option<Length>,
53 #[setters(strip_option)]
54 rotation: Option<Rotation>,
55}
56
57impl Icon {
58 #[must_use]
59 pub fn into_svg_handle(self) -> Option<crate::widget::svg::Handle> {
60 match self.handle.data {
61 Data::Name(named) => {
62 if let Some(path) = named.path() {
63 if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
64 return Some(iced_core::svg::Handle::from_path(path));
65 }
66 }
67 }
68
69 Data::Image(_) => (),
70 Data::Svg(handle) => return Some(handle),
71 }
72
73 None
74 }
75
76 #[must_use]
77 pub fn size(mut self, size: u16) -> Self {
78 self.size = size;
79 if let Data::Name(named) = &self.handle.data {
81 let mut new_named = named.clone();
82 new_named.size = Some(size);
83 self.handle = new_named.handle();
84 }
85 self
86 }
87
88 #[must_use]
89 fn view<'a, Message: 'a>(self) -> Element<'a, Message> {
90 let from_image = |handle| {
91 Image::new(handle)
92 .width(
93 self.width
94 .unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
95 )
96 .height(
97 self.height
98 .unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
99 )
100 .rotation(self.rotation.unwrap_or_default())
101 .content_fit(self.content_fit)
102 .into()
103 };
104
105 let from_svg = |handle| {
106 Svg::<crate::Theme>::new(handle)
107 .class(self.class.clone())
108 .width(
109 self.width
110 .unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
111 )
112 .height(
113 self.height
114 .unwrap_or_else(|| Length::Fixed(f32::from(self.size))),
115 )
116 .rotation(self.rotation.unwrap_or_default())
117 .content_fit(self.content_fit)
118 .symbolic(self.handle.symbolic)
119 .into()
120 };
121
122 match self.handle.data {
123 Data::Name(named) => {
124 if let Some(path) = named.path() {
125 if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
126 from_svg(iced_core::svg::Handle::from_path(path))
127 } else {
128 from_image(iced_core::image::Handle::from_path(path))
129 }
130 } else {
131 let bytes: &'static [u8] = &[];
132 from_svg(iced_core::svg::Handle::from_memory(bytes))
133 }
134 }
135
136 Data::Image(handle) => from_image(handle),
137 Data::Svg(handle) => from_svg(handle),
138 }
139 }
140}
141
142impl<'a, Message: 'a> From<Icon> for Element<'a, Message> {
143 fn from(icon: Icon) -> Self {
144 icon.view::<Message>()
145 }
146}
147
148pub fn draw(renderer: &mut crate::Renderer, handle: &Handle, icon_bounds: Rectangle) {
150 enum IcedHandle {
151 Svg(iced_core::svg::Handle),
152 Image(iced_core::image::Handle),
153 }
154
155 let iced_handle = match handle.clone().data {
156 Data::Name(named) => named.path().map(|path| {
157 if path.extension().is_some_and(|ext| ext == OsStr::new("svg")) {
158 IcedHandle::Svg(iced_core::svg::Handle::from_path(path))
159 } else {
160 IcedHandle::Image(iced_core::image::Handle::from_path(path))
161 }
162 }),
163
164 Data::Image(handle) => Some(IcedHandle::Image(handle)),
165 Data::Svg(handle) => Some(IcedHandle::Svg(handle)),
166 };
167
168 match iced_handle {
169 Some(IcedHandle::Svg(handle)) => iced_core::svg::Renderer::draw_svg(
170 renderer,
171 iced_core::svg::Svg::new(handle),
172 icon_bounds,
173 ),
174
175 Some(IcedHandle::Image(handle)) => {
176 iced_core::image::Renderer::draw_image(
177 renderer,
178 handle,
179 iced_core::image::FilterMethod::Linear,
180 icon_bounds,
181 iced_core::Radians::from(0),
182 1.0,
183 [0.0; 4],
184 );
185 }
186
187 None => {}
188 }
189}