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 {
31 ImageCacheElement {
32 image_cache_provider: Box::new(image_cache_provider),
33 style: StyleRefinement::default(),
34 children: SmallVec::default(),
35 }
36}
37
38#[derive(Clone)]
42pub struct AnyImageCache {
43 image_cache: AnyEntity,
44 load_fn: fn(
45 image_cache: &AnyEntity,
46 resource: &Resource,
47 window: &mut Window,
48 cx: &mut App,
49 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>,
50}
51
52impl<I: ImageCache> From<Entity<I>> for AnyImageCache {
53 fn from(image_cache: Entity<I>) -> Self {
54 Self {
55 image_cache: image_cache.into_any(),
56 load_fn: any_image_cache::load::<I>,
57 }
58 }
59}
60
61impl AnyImageCache {
62 pub fn load(
65 &self,
66 resource: &Resource,
67 window: &mut Window,
68 cx: &mut App,
69 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
70 (self.load_fn)(&self.image_cache, resource, window, cx)
71 }
72}
73
74mod any_image_cache {
75 use super::*;
76
77 pub(crate) fn load<I: 'static + ImageCache>(
78 image_cache: &AnyEntity,
79 resource: &Resource,
80 window: &mut Window,
81 cx: &mut App,
82 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
83 let image_cache = image_cache.clone().downcast::<I>().unwrap();
84 image_cache.update(cx, |image_cache, cx| image_cache.load(resource, window, cx))
85 }
86}
87
88pub struct ImageCacheElement {
91 image_cache_provider: Box<dyn ImageCacheProvider>,
92 style: StyleRefinement,
93 children: SmallVec<[AnyElement; 2]>,
94}
95
96impl ParentElement for ImageCacheElement {
97 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
98 self.children.extend(elements)
99 }
100}
101
102impl Styled for ImageCacheElement {
103 fn style(&mut self) -> &mut StyleRefinement {
104 &mut self.style
105 }
106}
107
108impl IntoElement for ImageCacheElement {
109 type Element = Self;
110
111 fn into_element(self) -> Self::Element {
112 self
113 }
114}
115
116impl Element for ImageCacheElement {
117 type RequestLayoutState = SmallVec<[LayoutId; 4]>;
118 type PrepaintState = ();
119
120 fn id(&self) -> Option<ElementId> {
121 None
122 }
123
124 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
125 None
126 }
127
128 fn request_layout(
129 &mut self,
130 _id: Option<&GlobalElementId>,
131 _inspector_id: Option<&InspectorElementId>,
132 window: &mut Window,
133 cx: &mut App,
134 ) -> (LayoutId, Self::RequestLayoutState) {
135 let image_cache = self.image_cache_provider.provide(window, cx);
136 window.with_image_cache(Some(image_cache), |window| {
137 let child_layout_ids = self
138 .children
139 .iter_mut()
140 .map(|child| child.request_layout(window, cx))
141 .collect::<SmallVec<_>>();
142 let mut style = Style::default();
143 style.refine(&self.style);
144 let layout_id = window.request_layout(style, child_layout_ids.iter().copied(), cx);
145 (layout_id, child_layout_ids)
146 })
147 }
148
149 fn prepaint(
150 &mut self,
151 _id: Option<&GlobalElementId>,
152 _inspector_id: Option<&InspectorElementId>,
153 _bounds: Bounds<Pixels>,
154 _request_layout: &mut Self::RequestLayoutState,
155 window: &mut Window,
156 cx: &mut App,
157 ) -> Self::PrepaintState {
158 for child in &mut self.children {
159 child.prepaint(window, cx);
160 }
161 }
162
163 fn paint(
164 &mut self,
165 _id: Option<&GlobalElementId>,
166 _inspector_id: Option<&InspectorElementId>,
167 _bounds: Bounds<Pixels>,
168 _request_layout: &mut Self::RequestLayoutState,
169 _prepaint: &mut Self::PrepaintState,
170 window: &mut Window,
171 cx: &mut App,
172 ) {
173 let image_cache = self.image_cache_provider.provide(window, cx);
174 window.with_image_cache(Some(image_cache), |window| {
175 for child in &mut self.children {
176 child.paint(window, cx);
177 }
178 })
179 }
180}
181
182pub struct ImageCacheItem(CachedLoad<Result<Arc<RenderImage>, ImageCacheError>>);
187
188impl std::fmt::Debug for ImageCacheItem {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 f.debug_struct("ImageCacheItem")
191 .field("result", &self.get())
192 .finish()
193 }
194}
195
196impl ImageCacheItem {
197 pub fn new(source: &Resource, cx: &mut App) -> Self {
199 let future = AssetLogger::<ImageAssetLoader>::load(source.clone(), cx);
200 Self(CachedLoad::new(future, cx))
201 }
202
203 pub fn get(&self) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
205 self.0.get()
206 }
207
208 pub fn use_image(&self, window: &Window) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
210 self.0.use_by(window.current_view())
211 }
212}
213
214pub trait ImageCache: 'static {
220 fn load(
226 &mut self,
227 resource: &Resource,
228 window: &mut Window,
229 cx: &mut App,
230 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>>;
231}
232
233pub trait ImageCacheProvider: 'static {
237 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache;
241}
242
243impl<T: ImageCache> ImageCacheProvider for Entity<T> {
244 fn provide(&mut self, _window: &mut Window, _cx: &mut App) -> AnyImageCache {
245 self.clone().into()
246 }
247}
248
249pub struct RetainAllImageCache(HashMap<u64, ImageCacheItem>);
256
257impl fmt::Debug for RetainAllImageCache {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 f.debug_struct("HashMapImageCache")
260 .field("num_images", &self.0.len())
261 .finish()
262 }
263}
264
265impl RetainAllImageCache {
266 #[inline]
271 pub fn new(cx: &mut App) -> Entity<Self> {
272 let e = cx.new(|_cx| RetainAllImageCache(HashMap::new()));
273 cx.observe_release(&e, |image_cache, cx| {
274 for (_, item) in std::mem::replace(&mut image_cache.0, HashMap::new()) {
275 if let Some(Ok(image)) = item.get() {
276 cx.drop_image(image, None);
277 }
278 }
279 })
280 .detach();
281 e
282 }
283
284 pub fn load(
289 &mut self,
290 source: &Resource,
291 window: &mut Window,
292 cx: &mut App,
293 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
294 let hash = hash(source);
295
296 self.0
297 .entry(hash)
298 .or_insert_with(|| ImageCacheItem::new(source, cx))
299 .use_image(window)
300 }
301
302 pub fn clear(&mut self, window: &mut Window, cx: &mut App) {
306 for (_, item) in std::mem::replace(&mut self.0, HashMap::new()) {
307 if let Some(Ok(image)) = item.get() {
308 cx.drop_image(image, Some(window));
309 }
310 }
311 }
312
313 pub fn remove(&mut self, source: &Resource, window: &mut Window, cx: &mut App) {
316 let hash = hash(source);
317 if let Some(item) = self.0.remove(&hash)
318 && let Some(Ok(image)) = item.get()
319 {
320 cx.drop_image(image, Some(window));
321 }
322 }
323
324 pub fn len(&self) -> usize {
326 self.0.len()
327 }
328
329 pub fn is_empty(&self) -> bool {
331 self.0.is_empty()
332 }
333}
334
335impl ImageCache for RetainAllImageCache {
336 fn load(
337 &mut self,
338 resource: &Resource,
339 window: &mut Window,
340 cx: &mut App,
341 ) -> Option<Result<Arc<RenderImage>, ImageCacheError>> {
342 RetainAllImageCache::load(self, resource, window, cx)
343 }
344}
345
346pub fn retain_all(id: impl Into<ElementId>) -> RetainAllImageCacheProvider {
362 RetainAllImageCacheProvider { id: id.into() }
363}
364
365pub struct RetainAllImageCacheProvider {
367 id: ElementId,
368}
369
370impl ImageCacheProvider for RetainAllImageCacheProvider {
371 fn provide(&mut self, window: &mut Window, cx: &mut App) -> AnyImageCache {
372 window
373 .with_global_id(self.id.clone(), |global_id, window| {
374 window.with_element_state::<Entity<RetainAllImageCache>, _>(
375 global_id,
376 |cache, _window| {
377 let mut cache = cache.unwrap_or_else(|| RetainAllImageCache::new(cx));
378 (cache.clone(), cache)
379 },
380 )
381 })
382 .into()
383 }
384}