Skip to main content

euv_engine/asset/
impl.rs

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