1use crate::{
2 AnyElement, AnyEntity, App, AppContext, Asset, AssetLogger, Bounds, Element, ElementId, Entity,
3 GlobalElementId, ImageAssetLoader, ImageCacheError, InspectorElementId, IntoElement, LayoutId,
4 ParentElement, Pixels, RenderImage, Resource, Style, StyleRefinement, Styled, Window, hash,
5};
6
7use crate::asset_cache::CachedLoad;
8use refineable::Refineable;
9use smallvec::SmallVec;
10use std::{collections::HashMap, fmt, sync::Arc};
11
12pub fn image_cache(image_cache_provider: impl ImageCacheProvider) -> ImageCacheElement {
15 ImageCacheElement {
16 image_cache_provider: Box::new(image_cache_provider),
17 style: StyleRefinement::default(),
18 children: SmallVec::default(),
19 }
20}
21
22#[derive(Clone)]
24pub struct AnyImageCache {
25 image_cache: AnyEntity,
26 load_fn: fn(
27 image_cache: &AnyEntity,
28 resource: &Resource,
29 window: &mut Window,
30 cx: &mut App,
31 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>,
32}
33
34impl<I: ImageCache> From<Entity<I>> for AnyImageCache {
35 fn from(image_cache: Entity<I>) -> Self {
36 Self {
37 image_cache: image_cache.into_any(),
38 load_fn: any_image_cache::load::<I>,
39 }
40 }
41}
42
43impl AnyImageCache {
44 pub fn load(
47 &self,
48 resource: &Resource,
49 window: &mut Window,
50 cx: &mut App,
51 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
52 (self.load_fn)(&self.image_cache, resource, window, cx)
53 }
54}
55
56mod any_image_cache {
57 use super::*;
58
59 pub(crate) fn load<I: 'static + ImageCache>(
60 image_cache: &AnyEntity,
61 resource: &Resource,
62 window: &mut Window,
63 cx: &mut App,
64 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
65 let image_cache = image_cache.clone().downcast::<I>().unwrap();
66 image_cache.update(cx, |image_cache, cx| image_cache.load(resource, window, cx))
67 }
68}
69
70pub struct ImageCacheElement {
72 image_cache_provider: Box<dyn ImageCacheProvider>,
73 style: StyleRefinement,
74 children: SmallVec<[AnyElement; 2]>,
75}
76
77impl ParentElement for ImageCacheElement {
78 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
79 self.children.extend(elements)
80 }
81}
82
83impl Styled for ImageCacheElement {
84 fn style(&mut self) -> &mut StyleRefinement {
85 &mut self.style
86 }
87}
88
89impl IntoElement for ImageCacheElement {
90 type Element = Self;
91
92 fn into_element(self) -> Self::Element {
93 self
94 }
95}
96
97impl Element for ImageCacheElement {
98 type RequestLayoutState = SmallVec<[LayoutId; 4]>;
99 type PrepaintState = ();
100
101 fn id(&self) -> Option<ElementId> {
102 None
103 }
104
105 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
106 None
107 }
108
109 fn request_layout(
110 &mut self,
111 _id: Option<&GlobalElementId>,
112 _inspector_id: Option<&InspectorElementId>,
113 window: &mut Window,
114 cx: &mut App,
115 ) -> (LayoutId, Self::RequestLayoutState) {
116 let image_cache = self.image_cache_provider.provide(window, cx);
117 window.with_image_cache(Some(image_cache), |window| {
118 let child_layout_ids = self
119 .children
120 .iter_mut()
121 .map(|child| child.request_layout(window, cx))
122 .collect::<SmallVec<_>>();
123 let mut style = Style::default();
124 style.refine(&self.style);
125 let layout_id = window.request_layout(style, child_layout_ids.iter().copied(), cx);
126 (layout_id, child_layout_ids)
127 })
128 }
129
130 fn prepaint(
131 &mut self,
132 _id: Option<&GlobalElementId>,
133 _inspector_id: Option<&InspectorElementId>,
134 _bounds: Bounds<Pixels>,
135 _request_layout: &mut Self::RequestLayoutState,
136 window: &mut Window,
137 cx: &mut App,
138 ) -> Self::PrepaintState {
139 for child in &mut self.children {
140 child.prepaint(window, cx);
141 }
142 }
143
144 fn paint(
145 &mut self,
146 _id: Option<&GlobalElementId>,
147 _inspector_id: Option<&InspectorElementId>,
148 _bounds: Bounds<Pixels>,
149 _request_layout: &mut Self::RequestLayoutState,
150 _prepaint: &mut Self::PrepaintState,
151 window: &mut Window,
152 cx: &mut App,
153 ) {
154 let image_cache = self.image_cache_provider.provide(window, cx);
155 window.with_image_cache(Some(image_cache), |window| {
156 for child in &mut self.children {
157 child.paint(window, cx);
158 }
159 })
160 }
161}
162
163pub struct ImageCacheItem(CachedLoad<Result<Arc<RenderImage>, ImageCacheError>>);
168
169impl std::fmt::Debug for ImageCacheItem {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 f.debug_struct("ImageCacheItem")
172 .field("result", &self.get())
173 .finish()
174 }
175}
176
177impl ImageCacheItem {
178 pub fn new(source: &Resource, cx: &mut App) -> Self {
180 let future = AssetLogger::<ImageAssetLoader>::load(source.clone(), cx);
181 Self(CachedLoad::new(future, cx))
182 }
183
184 pub fn get(&self) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
186 self.0.get()
187 }
188
189 pub fn use_image(&self, window: &Window) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
191 self.0.use_by(window.current_view())
192 }
193}
194
195pub trait ImageCache: 'static {
198 fn load(
201 &mut self,
202 resource: &Resource,
203 window: &mut Window,
204 cx: &mut App,
205 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>;
206}
207
208pub trait ImageCacheProvider: 'static {
211 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache;
213}
214
215impl<T: ImageCache> ImageCacheProvider for Entity<T> {
216 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache {
217 self.clone().into()
218 }
219}
220
221pub struct RetainAllImageCache(HashMap<u64, ImageCacheItem>);
223
224impl fmt::Debug for RetainAllImageCache {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 f.debug_struct("HashMapImageCache")
227 .field("num_images", &self.0.len())
228 .finish()
229 }
230}
231
232impl RetainAllImageCache {
233 #[inline]
235 pub fn new(cx: &mut App) -> Entity<Self> {
236 let e = cx.new(|_cx| RetainAllImageCache(HashMap::new()));
237 cx.observe_release(&e, |image_cache, cx| {
238 for (_, item) in std::mem::replace(&mut image_cache.0, HashMap::new()) {
239 if let Some(Ok(image)) = item.get() {
240 cx.drop_image(image, None);
241 }
242 }
243 })
244 .detach();
245 e
246 }
247
248 pub fn load(
252 &mut self,
253 source: &Resource,
254 window: &mut Window,
255 cx: &mut App,
256 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
257 let hash = hash(source);
258
259 self.0
260 .entry(hash)
261 .or_insert_with(|| ImageCacheItem::new(source, cx))
262 .use_image(window)
263 }
264
265 pub fn clear(&mut self, window: &mut Window, cx: &mut App) {
267 for (_, item) in std::mem::replace(&mut self.0, HashMap::new()) {
268 if let Some(Ok(image)) = item.get() {
269 cx.drop_image(image, Some(window));
270 }
271 }
272 }
273
274 pub fn remove(&mut self, source: &Resource, window: &mut Window, cx: &mut App) {
276 let hash = hash(source);
277 if let Some(item) = self.0.remove(&hash)
278 && let Some(Ok(image)) = item.get()
279 {
280 cx.drop_image(image, Some(window));
281 }
282 }
283
284 pub fn len(&self) -> usize {
286 self.0.len()
287 }
288
289 pub fn is_empty(&self) -> bool {
291 self.0.is_empty()
292 }
293}
294
295impl ImageCache for RetainAllImageCache {
296 fn load(
297 &mut self,
298 resource: &Resource,
299 window: &mut Window,
300 cx: &mut App,
301 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
302 RetainAllImageCache::load(self, resource, window, cx)
303 }
304}
305
306pub fn retain_all(id: impl Into<ElementId>) -> RetainAllImageCacheProvider {
308 RetainAllImageCacheProvider { id: id.into() }
309}
310
311pub struct RetainAllImageCacheProvider {
313 id: ElementId,
314}
315
316impl ImageCacheProvider for RetainAllImageCacheProvider {
317 fn provide(&mut self, window: &mut Window, cx: &mut App) -> AnyImageCache {
318 window
319 .with_global_id(self.id.clone(), |global_id, window| {
320 window.with_element_state::<Entity<RetainAllImageCache>, _>(
321 global_id,
322 |cache, _window| {
323 let mut cache = cache.unwrap_or_else(|| RetainAllImageCache::new(cx));
324 (cache.clone(), cache)
325 },
326 )
327 })
328 .into()
329 }
330}