1use hashbrown::{HashSet, hash_map::Entry};
2use unic_langid::LanguageIdentifier;
3
4use vizia_storage::Tree;
5
6use crate::{
7 entity::Entity,
8 resource::{
9 ImageId, ImageOrSvg, ImageRetentionPolicy, LoadingStatus, ResourceManager, ResourceRequest,
10 StoredImage,
11 },
12 style::Style,
13 text::TextContext,
14};
15
16use super::{Context, ContextProxy, EventProxy};
17
18#[cfg(feature = "tokio")]
19use std::sync::Arc;
20
21pub struct ResourceContext<'a> {
23 pub(crate) current: Entity,
24 pub(crate) event_proxy: &'a Option<Box<dyn EventProxy>>,
25 pub(crate) resource_manager: &'a mut ResourceManager,
26 pub(crate) style: &'a mut Style,
27 pub(crate) text_context: &'a mut TextContext,
28 pub(crate) tree: &'a Tree<Entity>,
29 #[cfg(feature = "tokio")]
30 pub(crate) task_runtime: Arc<tokio::runtime::Runtime>,
31 #[cfg(feature = "tokio")]
32 pub(crate) named_tasks: crate::context::NamedTaskMap,
33}
34
35impl<'a> ResourceContext<'a> {
36 pub(crate) fn new(cx: &'a mut Context) -> Self {
38 Self {
39 current: cx.current,
40 event_proxy: &cx.event_proxy,
41 resource_manager: &mut cx.resource_manager,
42 style: &mut cx.style,
43 text_context: &mut cx.text_context,
44 tree: &cx.tree,
45 #[cfg(feature = "tokio")]
46 task_runtime: cx.task_runtime.clone(),
47 #[cfg(feature = "tokio")]
48 named_tasks: cx.named_tasks.clone(),
49 }
50 }
51
52 pub fn spawn<F>(&self, target: F)
54 where
55 F: 'static + Send + FnOnce(&mut ContextProxy),
56 {
57 let mut cxp = ContextProxy {
58 current: self.current,
59 event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
60 };
61
62 std::thread::spawn(move || target(&mut cxp));
63 }
64
65 #[cfg(feature = "tokio")]
67 pub fn spawn_task<T, E>(
68 &self,
69 task: crate::context::TaskBuilder<T, E>,
70 ) -> crate::context::TaskHandle
71 where
72 T: Send + 'static,
73 E: Send + 'static,
74 {
75 task.add_to_resource_context(self)
76 }
77
78 pub fn request_resource(
82 &mut self,
83 request: ResourceRequest,
84 options: crate::resource::ResourceLoadOptions,
85 ) -> bool {
86 let mut loaders = std::mem::take(&mut self.resource_manager.resource_loaders);
88
89 for loader in &loaders {
90 if loader.load(request.clone(), options, self) {
91 loaders.append(&mut self.resource_manager.resource_loaders);
93 self.resource_manager.resource_loaders = loaders;
94 return true;
95 }
96 }
97
98 loaders.append(&mut self.resource_manager.resource_loaders);
100 self.resource_manager.resource_loaders = loaders;
101
102 false
103 }
104
105 pub fn load_image(
107 &mut self,
108 path: String,
109 image: skia_safe::Image,
110 policy: ImageRetentionPolicy,
111 ) {
112 let id = if let Some(image_id) = self.resource_manager.image_ids.get(&path) {
113 *image_id
114 } else {
115 let id = self.resource_manager.image_id_manager.create();
116 self.resource_manager.image_ids.insert(path.clone(), id);
117 id
118 };
119
120 if let Some(source_path) = self.resource_manager.image_sources.get(&path).cloned() {
125 let aliases: Vec<String> = self
126 .resource_manager
127 .image_sources
128 .iter()
129 .filter(|(alias, alias_path)| {
130 *alias_path == &source_path
131 && !self.resource_manager.image_ids.contains_key(*alias)
132 })
133 .map(|(alias, _)| alias.clone())
134 .collect();
135 for alias in aliases {
136 self.resource_manager.image_ids.insert(alias, id);
137 }
138 }
139
140 match self.resource_manager.images.entry(id) {
141 Entry::Occupied(mut occ) => {
142 occ.get_mut().image = ImageOrSvg::Image(image);
143 occ.get_mut().dirty = true;
144 occ.get_mut().retention_policy = policy;
145 }
146 Entry::Vacant(vac) => {
147 vac.insert(StoredImage {
148 image: ImageOrSvg::Image(image),
149 retention_policy: policy,
150 used: true,
151 dirty: false,
152 observers: HashSet::new(),
153 });
154 }
155 }
156 let observers: Vec<Entity> = self
159 .resource_manager
160 .images
161 .get(&id)
162 .map(|img| img.observers.iter().copied().collect())
163 .unwrap_or_default();
164 for observer in observers {
165 self.style.needs_relayout(observer);
166 }
167 self.style.needs_relayout(self.current);
168
169 if let Some(proxy) = self.event_proxy.as_ref() {
172 let mut cxp =
173 ContextProxy { current: self.current, event_proxy: Some(proxy.make_clone()) };
174 let _ = cxp.redraw();
175 }
176 }
177
178 pub fn load_svg(
180 &mut self,
181 path: String,
182 data: &[u8],
183 policy: ImageRetentionPolicy,
184 ) -> Option<ImageId> {
185 let id = if let Some(image_id) = self.resource_manager.image_ids.get(&path) {
186 *image_id
187 } else {
188 let id = self.resource_manager.image_id_manager.create();
189 self.resource_manager.image_ids.insert(path.clone(), id);
190 id
191 };
192
193 if let Some(source_path) = self.resource_manager.image_sources.get(&path).cloned() {
196 let aliases: Vec<String> = self
197 .resource_manager
198 .image_sources
199 .iter()
200 .filter(|(alias, alias_path)| {
201 *alias_path == &source_path
202 && !self.resource_manager.image_ids.contains_key(*alias)
203 })
204 .map(|(alias, _)| alias.clone())
205 .collect();
206 for alias in aliases {
207 self.resource_manager.image_ids.insert(alias, id);
208 }
209 }
210
211 if let Ok(svg) =
212 skia_safe::svg::Dom::from_bytes(data, self.text_context.default_font_manager.clone())
213 {
214 match self.resource_manager.images.entry(id) {
215 Entry::Occupied(mut occ) => {
216 occ.get_mut().image = ImageOrSvg::Svg(svg);
217 occ.get_mut().dirty = true;
218 occ.get_mut().retention_policy = policy;
219 }
220 Entry::Vacant(vac) => {
221 vac.insert(StoredImage {
222 image: ImageOrSvg::Svg(svg),
223 retention_policy: policy,
224 used: true,
225 dirty: false,
226 observers: HashSet::new(),
227 });
228 }
229 }
230 let observers: Vec<Entity> = self
231 .resource_manager
232 .images
233 .get(&id)
234 .map(|img| img.observers.iter().copied().collect())
235 .unwrap_or_default();
236 for observer in observers {
237 self.style.needs_relayout(observer);
238 }
239 self.style.needs_relayout(self.current);
240
241 if let Some(proxy) = self.event_proxy.as_ref() {
242 let mut cxp =
243 ContextProxy { current: self.current, event_proxy: Some(proxy.make_clone()) };
244 let _ = cxp.redraw();
245 }
246
247 Some(id)
248 } else {
249 None
250 }
251 }
252
253 pub fn load_font(&mut self, path: String, data: &[u8]) -> bool {
255 if let Some(typeface) = self.text_context.default_font_manager.new_from_data(data, None) {
256 self.text_context.asset_provider.register_typeface(typeface, None);
257
258 let entities: Vec<Entity> = self.tree.into_iter().collect();
260 for entity in entities {
261 self.style.needs_text_update(entity);
262 }
263
264 self.resource_manager.set_resource_status(path, LoadingStatus::Loaded);
265
266 if let Some(proxy) = self.event_proxy.as_ref() {
267 let mut cxp =
268 ContextProxy { current: self.current, event_proxy: Some(proxy.make_clone()) };
269 let _ = cxp.redraw();
270 }
271
272 true
273 } else {
274 self.resource_manager.set_resource_status(path, LoadingStatus::Error);
275 false
276 }
277 }
278
279 pub fn load_translation(&mut self, lang: LanguageIdentifier, path: String, ftl: &str) -> bool {
281 if self.resource_manager.add_translation(lang, ftl.to_string()).is_ok() {
282 let entities: Vec<Entity> = self.tree.into_iter().collect();
283 for entity in entities {
284 self.style.needs_text_update(entity);
285 }
286
287 self.resource_manager.set_resource_status(path, LoadingStatus::Loaded);
288
289 if let Some(proxy) = self.event_proxy.as_ref() {
290 let mut cxp =
291 ContextProxy { current: self.current, event_proxy: Some(proxy.make_clone()) };
292 let _ = cxp.redraw();
293 }
294
295 true
296 } else {
297 self.resource_manager.set_resource_status(path, LoadingStatus::Error);
298 false
299 }
300 }
301}