euv_engine/asset/impl.rs
1use super::*;
2
3/// Implements cache query and management for `AssetCache`.
4impl AssetCache {
5 /// Returns the state of the asset with the given URL, or `None` if not cached.
6 ///
7 /// # Arguments
8 ///
9 /// - `U: AsRef<str>` - The asset URL.
10 ///
11 /// # Returns
12 ///
13 /// - `Option<AssetState>` - The asset state, or `None`.
14 pub fn get_state<U>(&self, url: U) -> Option<AssetState>
15 where
16 U: AsRef<str>,
17 {
18 self.get_entries()
19 .get(url.as_ref())
20 .map(|entry: &AssetEntry| entry.get_state())
21 }
22
23 /// Returns the loaded image for the given URL, or `None` if not loaded.
24 ///
25 /// # Arguments
26 ///
27 /// - `U: AsRef<str>` - The asset URL.
28 ///
29 /// # Returns
30 ///
31 /// - `Option<HtmlImageElement>` - The loaded image, or `None`.
32 pub fn get_image<U>(&self, url: U) -> Option<HtmlImageElement>
33 where
34 U: AsRef<str>,
35 {
36 let entry: &AssetEntry = self.get_entries().get(url.as_ref())?;
37 if entry.get_state() != AssetState::Loaded {
38 return None;
39 }
40 entry.get_image()
41 }
42
43 /// Returns `true` if all assets in the cache have finished loading.
44 ///
45 /// # Returns
46 ///
47 /// - `bool` - True if no assets are in the `Loading` state.
48 pub fn is_all_loaded(&self) -> bool {
49 self.get_entries()
50 .values()
51 .all(|entry: &AssetEntry| entry.get_state() != AssetState::Loading)
52 }
53
54 /// Returns the number of assets that have been successfully loaded.
55 ///
56 /// # Returns
57 ///
58 /// - `usize` - The count of loaded assets.
59 pub fn loaded_count(&self) -> usize {
60 self.get_entries()
61 .values()
62 .filter(|entry: &&AssetEntry| entry.get_state() == AssetState::Loaded)
63 .count()
64 }
65
66 /// Removes all entries from the cache.
67 pub fn clear(&mut self) {
68 self.get_mut_entries().clear();
69 }
70}
71
72/// Implements `Default` for `AssetCache` as a new empty cache.
73impl Default for AssetCache {
74 /// Constructs a default [`AssetCache`] value.
75 ///
76 /// # Returns
77 ///
78 /// - `AssetCache` - A default-constructed instance with the documented initial state.
79 fn default() -> AssetCache {
80 AssetCache::new()
81 }
82}
83
84/// Implements asynchronous asset loading for `AssetLoader`.
85impl AssetLoader {
86 /// Begins loading an image asset from the given URL.
87 ///
88 /// Creates an `HtmlImageElement`, sets its `src`, and registers `onload`/`onerror`
89 /// callbacks to update the shared cache state. The image loads asynchronously.
90 ///
91 /// # Arguments
92 ///
93 /// - `String` - The URL of the image to load.
94 pub fn load_image(&mut self, url: String) {
95 let Ok(image) = HtmlImageElement::new() else {
96 return;
97 };
98 let entry: AssetEntry = AssetEntry::new(
99 AssetType::Image,
100 AssetState::Loading,
101 Some(image.clone()),
102 url.clone(),
103 );
104 self.get_cache()
105 .get_mut()
106 .get_mut_entries()
107 .insert(url.clone(), entry);
108 *self.get_mut_pending_count() += 1;
109 let cache_clone: Rc<EngineCell<AssetCache>> = self.get_cache().clone();
110 let url_for_onload: String = url.clone();
111 let url_for_onerror: String = url.clone();
112 let onload_closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
113 let cache_ref: &mut AssetCache = cache_clone.get_mut();
114 if let Some(mut entry) = cache_ref.get_entries().get(&url_for_onload).cloned() {
115 entry.set_state(AssetState::Loaded);
116 cache_ref
117 .get_mut_entries()
118 .insert(url_for_onload.clone(), entry);
119 }
120 }));
121 let cache_clone_err: Rc<EngineCell<AssetCache>> = self.get_cache().clone();
122 let onerror_closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
123 let cache_ref: &mut AssetCache = cache_clone_err.get_mut();
124 if let Some(mut entry) = cache_ref.get_entries().get(&url_for_onerror).cloned() {
125 entry.set_state(AssetState::Error);
126 cache_ref
127 .get_mut_entries()
128 .insert(url_for_onerror.clone(), entry);
129 }
130 }));
131 image.set_onload(Some(onload_closure.as_ref().unchecked_ref()));
132 image.set_onerror(Some(onerror_closure.as_ref().unchecked_ref()));
133 image.set_src(&url);
134 self.get_closures().get_mut().push(onload_closure);
135 self.get_closures().get_mut().push(onerror_closure);
136 }
137
138 /// Returns whether all requested assets have finished loading.
139 ///
140 /// # Returns
141 ///
142 /// - `bool` - True if no assets are pending.
143 pub fn is_all_loaded(&self) -> bool {
144 self.get_cache().get().is_all_loaded()
145 }
146
147 /// Returns the loaded image for the given URL.
148 ///
149 /// # Arguments
150 ///
151 /// - `U: AsRef<str>` - The asset URL.
152 ///
153 /// # Returns
154 ///
155 /// - `Option<HtmlImageElement>` - The loaded image, or `None`.
156 pub fn get_image<U>(&self, url: U) -> Option<HtmlImageElement>
157 where
158 U: AsRef<str>,
159 {
160 self.get_cache().get().get_image(url.as_ref())
161 }
162
163 /// Returns the progress ratio of loaded assets.
164 ///
165 /// # Returns
166 ///
167 /// - `f64` - The ratio in the range 0.0 to 1.0.
168 pub fn progress(&self) -> f64 {
169 let cache_ref: &AssetCache = self.get_cache().get();
170 let total: usize = cache_ref.get_entries().len();
171 if total == 0 {
172 return 1.0;
173 }
174 cache_ref.loaded_count() as f64 / total as f64
175 }
176}
177
178/// Implements `Default` for `AssetLoader` as a new empty loader.
179impl Default for AssetLoader {
180 /// Constructs a default [`AssetLoader`] value.
181 ///
182 /// # Returns
183 ///
184 /// - `AssetLoader` - A default-constructed instance with the documented initial state.
185 fn default() -> AssetLoader {
186 AssetLoader::new()
187 }
188}
189
190/// Implements static asset creation utilities for `AssetLoader`.
191impl AssetLoader {
192 /// Creates an `HtmlImageElement` from the given URL without caching.
193 ///
194 /// The image loads asynchronously. Returns immediately with the image element
195 /// whose `src` is set but may not have finished loading yet.
196 ///
197 /// # Arguments
198 ///
199 /// - `U: AsRef<str>` - The image URL.
200 ///
201 /// # Returns
202 ///
203 /// - `Option<HtmlImageElement>` - The image element, or `None` if creation failed.
204 pub fn create_image_element<U>(url: U) -> Option<HtmlImageElement>
205 where
206 U: AsRef<str>,
207 {
208 let image: HtmlImageElement = HtmlImageElement::new().ok()?;
209 image.set_src(url.as_ref());
210 Some(image)
211 }
212}