cosmic/widget/icon/
handle.rs1use super::Icon;
5use crate::widget::{image, svg};
6use std::borrow::Cow;
7use std::ffi::OsStr;
8use std::fs::File;
9use std::hash::Hash;
10use std::io::Read;
11use std::path::{Path, PathBuf};
12
13#[must_use]
14#[derive(Clone, Debug, Hash, derive_setters::Setters)]
15pub struct Handle {
16 pub symbolic: bool,
17 #[setters(skip)]
18 pub data: Data,
19}
20
21impl Handle {
22 #[inline]
23 pub fn icon(self) -> Icon {
24 super::icon(self)
25 }
26}
27
28#[must_use]
29#[derive(Clone, Debug, Hash)]
30pub enum Data {
31 Image(image::Handle),
33 Svg(svg::Handle),
34}
35
36enum SvgSource {
37 Path,
38 Bytes(Vec<u8>),
39}
40
41fn svg_source(path: &Path) -> Option<SvgSource> {
42 if path
43 .extension()
44 .and_then(OsStr::to_str)
45 .is_some_and(|extension| extension.eq_ignore_ascii_case("svg"))
46 {
47 return Some(SvgSource::Path);
48 }
49
50 let Ok(mut file) = File::open(path) else {
51 return None;
52 };
53
54 let Ok(metadata) = file.metadata() else {
55 return None;
56 };
57 const MAX_SVG_SIZE: u64 = 16 * 1024 * 1024;
58 if !metadata.file_type().is_file() || metadata.len() > MAX_SVG_SIZE {
59 return None;
60 }
61
62 let mut prefix = [0; 32];
63 let Ok(length) = file.read(&mut prefix) else {
64 return None;
65 };
66 let prefix = &prefix[..length];
67
68 if ::image::guess_format(prefix).is_ok() {
69 return None;
70 }
71
72 let mut bytes = Vec::with_capacity(metadata.len() as usize);
73 bytes.extend_from_slice(prefix);
74 if file.read_to_end(&mut bytes).is_err() {
75 return None;
76 }
77
78 let document = roxmltree::Document::parse(std::str::from_utf8(&bytes).ok()?).ok()?;
79 (document.root_element().tag_name().name() == "svg").then_some(SvgSource::Bytes(bytes))
80}
81
82pub fn from_path(path: PathBuf) -> Handle {
84 Handle {
85 symbolic: path
86 .file_stem()
87 .and_then(OsStr::to_str)
88 .is_some_and(|name| name.ends_with("-symbolic")),
89 data: match svg_source(&path) {
90 Some(SvgSource::Path) => Data::Svg(svg::Handle::from_path(path)),
91 Some(SvgSource::Bytes(bytes)) => Data::Svg(svg::Handle::from_memory(bytes)),
92 None => Data::Image(image::Handle::from_path(path)),
93 },
94 }
95}
96
97pub fn from_raster_bytes(
99 bytes: impl Into<Cow<'static, [u8]>>
100 + std::convert::AsRef<[u8]>
101 + std::marker::Send
102 + std::marker::Sync
103 + 'static,
104) -> Handle {
105 fn inner(bytes: Cow<'static, [u8]>) -> Handle {
106 Handle {
107 symbolic: false,
108 data: match bytes {
109 Cow::Owned(b) => Data::Image(image::Handle::from_bytes(b)),
110 Cow::Borrowed(b) => Data::Image(image::Handle::from_bytes(b)),
111 },
112 }
113 }
114
115 inner(bytes.into())
116}
117
118pub fn from_raster_pixels(
120 width: u32,
121 height: u32,
122 pixels: impl Into<Cow<'static, [u8]>>
123 + std::convert::AsRef<[u8]>
124 + std::marker::Send
125 + std::marker::Sync,
126) -> Handle {
127 fn inner(width: u32, height: u32, pixels: Cow<'static, [u8]>) -> Handle {
128 Handle {
129 symbolic: false,
130 data: match pixels {
131 Cow::Owned(pixels) => Data::Image(image::Handle::from_rgba(width, height, pixels)),
132 Cow::Borrowed(pixels) => {
133 Data::Image(image::Handle::from_rgba(width, height, pixels))
134 }
135 },
136 }
137 }
138
139 inner(width, height, pixels.into())
140}
141
142pub fn from_svg_bytes(bytes: impl Into<Cow<'static, [u8]>>) -> Handle {
144 Handle {
145 symbolic: false,
146 data: Data::Svg(svg::Handle::from_memory(bytes)),
147 }
148}