icon_loader/icon.rs
1mod icon_dir;
2mod icon_file;
3mod icon_theme;
4
5pub use icon_dir::{IconDir, IconSizeType};
6pub use icon_file::{IconFile, IconFileType};
7
8pub(crate) use icon_theme::IconThemes;
9
10/// Struct containing information about a themed icon.
11#[derive(Clone, Debug, Hash, PartialEq, Eq)]
12pub struct Icon {
13 icon_name: String,
14 theme_name: String,
15 files: Vec<IconFile>,
16}
17
18impl Icon {
19 /// Returns the associated icon's name.
20 pub fn icon_name(&self) -> &str {
21 &self.icon_name
22 }
23
24 /// Returns the associated icon's theme name.
25 pub fn theme_name(&self) -> &str {
26 &self.theme_name
27 }
28
29 /// Returns the icon files found for the associated icon.
30 pub fn files(&self) -> &[IconFile] {
31 &self.files
32 }
33
34 /// Returns the file of the associated icon that fits the given size best and has a scale of 1.
35 /// If there is no exact fit available, the next bigger one is chosen.
36 /// If there is no bigger one, the next smaller one is returned.
37 /// If that cannot be found, the scale restriction is ignored.
38 ///
39 /// # Arguments
40 ///
41 /// * `size` - The ideal size of the returned icon file.
42 ///
43 ///# Example
44 ///
45 /// ```
46 /// use std::ops::Deref;
47 /// use icon_loader::IconLoader;
48 ///
49 /// let loader = IconLoader::new();
50 /// if let Some(icon) = loader.load_icon("minimum") {
51 /// let icon_file = icon.file_for_size(32);
52 /// };
53 /// ```
54 pub fn file_for_size(&self, size: u16) -> &IconFile {
55 self.file_for_size_scaled(size, 1)
56 }
57
58 /// Returns the file of the associated icon that fits the given size and scale best.
59 /// If there is no exact fit available, the next bigger size is chosen.
60 /// If there is no bigger fit with the given scale, the next smaller one is returned.
61 /// If no file with the preferred scale can be found, one with the size `size * scale` and scale 1 is looked for.
62 /// If that cannot be found, the scale restriction is ignored.
63 ///
64 /// # Arguments
65 ///
66 /// * `size` - The ideal size of the returned icon file.
67 /// * `scale` - The preferred scale of the returned icon file.
68 ///
69 ///# Example
70 ///
71 /// ```
72 /// use std::ops::Deref;
73 /// use icon_loader::IconLoader;
74 ///
75 /// let loader = IconLoader::new();
76 /// if let Some(icon) = loader.load_icon("minimum") {
77 /// let icon_file = icon.file_for_size_scaled(32, 2);
78 /// };
79 /// ```
80 pub fn file_for_size_scaled(&self, size: u16, scale: u16) -> &IconFile {
81 if let Some(file) = self.file_for_size_filtered(size, |file| file.scale() == scale) {
82 return file;
83 }
84
85 if let Some(file) = self.file_for_size_filtered(size * scale, |file| file.scale() == 1) {
86 return file;
87 }
88
89 // If we don't filter, there is always at least one file on disk.
90 self.file_for_size_filtered(size, |_| true).unwrap()
91 }
92
93 /// Returns the file of the associated icon that fits the given size best and matches the provided filter.
94 /// If there is no exact fit available, the next bigger one is chosen.
95 /// If there is no bigger one, the next smaller one is returned.
96 /// Use this, if you want only files of type PNG or anything like that.
97 ///
98 /// # Arguments
99 ///
100 /// * `size` - The ideal size of the returned icon file.
101 /// * `filter` - A function that takes a reference to an [`IconFile`](icon::IconFile) and returns true, if it passes the test and false otherwise.
102 ///
103 /// # Example
104 ///
105 /// ```
106 /// use std::ops::Deref;
107 /// use icon_loader::{IconLoader, IconFileType};
108 ///
109 /// let loader = IconLoader::new();
110 /// if let Some(icon) = loader.load_icon("minimum") {
111 /// let icon_file = icon.file_for_size_filtered(32, |file| file.icon_type() == IconFileType::PNG);
112 /// };
113 /// ```
114 pub fn file_for_size_filtered(
115 &self,
116 size: u16,
117 filter: impl Fn(&IconFile) -> bool,
118 ) -> Option<&IconFile> {
119 let files = self.files.iter().filter(|&file| filter(file));
120
121 // Try to return an exact fit.
122 if let Some(icon_file) = files.clone().find(|file| file.dir_info().size() == size) {
123 return Some(icon_file);
124 }
125
126 // Try to return a threshold fit.
127 if let Some(icon_file) = files
128 .clone()
129 .filter(|file| file.dir_info().size_type() == IconSizeType::Threshold)
130 .find(|file| {
131 size >= file.dir_info().size() - file.dir_info().threshold()
132 && size <= file.dir_info().size() + file.dir_info().threshold()
133 })
134 {
135 return Some(icon_file);
136 }
137
138 // Try to return a slightly bigger fit.
139 if let Some(icon_file) = files
140 .clone()
141 .filter(|file| file.dir_info().size() > size)
142 .min_by_key(|file| file.dir_info().size())
143 {
144 return Some(icon_file);
145 }
146
147 // Return the biggest available.
148 files.max_by_key(|file| file.dir_info().size())
149 }
150
151 pub(crate) fn new(icon_name: String, theme_name: String, files: Vec<IconFile>) -> Option<Self> {
152 if icon_name.is_empty() || theme_name.is_empty() || files.is_empty() {
153 None
154 } else {
155 Some(Self {
156 files,
157 icon_name,
158 theme_name,
159 })
160 }
161 }
162}