1use super::resource_key::ResourceKeyBuildHasher;
2use super::{
3 resource_provider_ptr_eq, IDeferredContent, IResourceDictionary, IResourceNode, IResourceProvider,
4 IThemeVariantProvider, ResourceHostRef, ResourceKey, ResourceProvider, ResourceProviderImpl, ResourceValue,
5 ResourcesChangedEventArgs,
6};
7use crate::collections::{FerroDictionary, FerroList, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs, ResetBehavior};
8use crate::metadata::IServiceProvider;
9use crate::reactive::IDisposable;
10use crate::styling::ThemeVariant;
11use crate::{ferro_class, instantiate, BoxedValue, FerroObject, FerroObjectImpl, ObjectType, Ref, Upcast};
12use std::cell::{OnceCell, RefCell};
13use std::collections::HashMap;
14use std::rc::Rc;
15
16#[derive(Clone)]
17enum ResourceItem {
18 Value(ResourceValue),
19 Deferred(Rc<dyn IDeferredContent>),
20 NotSharedDeferred(Rc<dyn IDeferredContent>),
21}
22
23struct DeferredItem<F: Fn(Option<&Rc<dyn IServiceProvider>>) -> Option<BoxedValue>>(F);
24
25impl<F: Fn(Option<&Rc<dyn IServiceProvider>>) -> Option<BoxedValue>> IDeferredContent for DeferredItem<F> {
26 fn build(&self, service_provider: Option<&Rc<dyn IServiceProvider>>) -> Option<BoxedValue> {
27 (self.0)(service_provider)
28 }
29}
30
31#[repr(C)]
33pub struct ResourceDictionary {
34 base: ResourceProvider,
35 last_deferred_item_key: RefCell<Option<ResourceKey>>,
36 inner: RefCell<HashMap<ResourceKey, ResourceItem, ResourceKeyBuildHasher>>,
37 merged_dictionaries: OnceCell<FerroList<Rc<dyn IResourceProvider>>>,
38 theme_dictionaries: OnceCell<FerroDictionary<ThemeVariant, Rc<dyn IThemeVariantProvider>>>,
39 key: RefCell<Option<ThemeVariant>>,
40}
41
42ferro_class!(ResourceDictionary: ResourceProvider);
43crate::ferro_class_info!(ResourceDictionary {
44 new: ResourceDictionary::new,
45 interfaces: [Rc<dyn IResourceProvider>, Rc<dyn IResourceDictionary>, Rc<dyn IThemeVariantProvider>],
46});
47
48impl FerroObjectImpl for ResourceDictionary {}
49
50impl ResourceProviderImpl for ResourceDictionary {
51 fn has_resources(this: &Self) -> bool {
52 if !this.inner.borrow().is_empty() {
53 return true;
54 }
55 if let Some(merged) = this.merged_dictionaries.get() {
56 if !merged.is_empty() {
57 return merged.snapshot().iter().any(|i| i.has_resources());
58 }
59 }
60 false
61 }
62
63 fn try_get_resource(this: &Self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
64 if let Some(value) = this.try_get_value(key) {
65 return Some(value);
66 }
67
68 if this.theme_dictionaries.get().is_some() {
69 if let Some(theme) = theme {
70 if *theme != ThemeVariant::default() {
71 if let Some(provider) = this.theme_dictionary(theme) {
72 if let Some(value) = provider.try_get_resource(key, Some(theme)) {
73 return Some(value);
74 }
75 }
76
77 let mut theme_inherit = theme.inherit_variant();
78 while let Some(inherit) = theme_inherit {
79 if let Some(provider) = this.theme_dictionary(&inherit) {
80 if let Some(value) = provider.try_get_resource(key, Some(theme)) {
81 return Some(value);
82 }
83 }
84 theme_inherit = inherit.inherit_variant();
85 }
86 }
87 }
88
89 if let Some(provider) = this.theme_dictionary(&ThemeVariant::default()) {
90 if let Some(value) = provider.try_get_resource(key, theme) {
91 return Some(value);
92 }
93 }
94 }
95
96 if let Some(merged) = this.merged_dictionaries.get() {
97 if !merged.is_empty() {
98 for provider in merged.snapshot().iter().rev() {
99 if let Some(value) = provider.try_get_resource(key, theme) {
100 return Some(value);
101 }
102 }
103 }
104 }
105
106 None
107 }
108
109 fn on_add_owner(this: &Self, owner: &ResourceHostRef) {
110 let mut has_resources = !this.inner.borrow().is_empty();
111
112 if let Some(merged) = this.merged_dictionaries.get() {
113 for i in merged.snapshot().iter() {
114 i.add_owner(owner);
115 has_resources |= i.has_resources();
116 }
117 }
118
119 for (_, i) in this.theme_dictionaries_snapshot() {
120 i.add_owner(owner);
121 has_resources |= i.has_resources();
122 }
123
124 if has_resources {
125 owner.notify_hosted_resources_changed(ResourcesChangedEventArgs::create());
126 }
127 }
128
129 fn on_remove_owner(this: &Self, owner: &ResourceHostRef) {
130 let mut has_resources = !this.inner.borrow().is_empty();
131
132 if let Some(merged) = this.merged_dictionaries.get() {
133 for i in merged.snapshot().iter() {
134 i.remove_owner(owner);
135 has_resources |= i.has_resources();
136 }
137 }
138
139 for (_, i) in this.theme_dictionaries_snapshot() {
140 i.remove_owner(owner);
141 has_resources |= i.has_resources();
142 }
143
144 if has_resources {
145 owner.notify_hosted_resources_changed(ResourcesChangedEventArgs::create());
146 }
147 }
148}
149
150impl ResourceDictionary {
151 fn construct_from(base: ResourceProvider) -> Self {
152 Self {
153 base,
154 last_deferred_item_key: RefCell::new(None),
155 inner: RefCell::new(HashMap::default()),
156 merged_dictionaries: OnceCell::new(),
157 theme_dictionaries: OnceCell::new(),
158 key: RefCell::new(None),
159 }
160 }
161
162 pub fn construct() -> Self {
164 Self::construct_from(ResourceProvider::construct())
165 }
166
167 pub fn construct_with_owner(owner: &ResourceHostRef) -> Self {
169 Self::construct_from(ResourceProvider::construct_with_owner(owner))
170 }
171
172 pub fn new() -> Ref<Self> {
174 instantiate(Self::construct())
175 }
176
177 pub fn with_owner(owner: impl Into<ResourceHostRef>) -> Ref<Self> {
179 instantiate(Self::construct_with_owner(&owner.into()))
180 }
181
182 pub fn count(&self) -> usize {
184 self.inner.borrow().len()
185 }
186
187 pub fn get(&self, key: &ResourceKey) -> ResourceValue {
190 self.try_get_value(key).flatten()
191 }
192
193 pub fn set(&self, key: impl Into<ResourceKey>, value: ResourceValue) {
195 self.inner.borrow_mut().insert(key.into(), ResourceItem::Value(value));
196 self.raise_resources_changed();
197 }
198
199 pub fn keys(&self) -> Vec<ResourceKey> {
201 self.inner.borrow().keys().cloned().collect()
202 }
203
204 pub fn merged_dictionaries(&self) -> FerroList<Rc<dyn IResourceProvider>> {
208 self.merged_dictionaries_ref().clone()
209 }
210
211 fn merged_dictionaries_ref(&self) -> &FerroList<Rc<dyn IResourceProvider>> {
212 self.merged_dictionaries.get_or_init(|| {
213 let list = FerroList::new();
214 list.set_reset_behavior(ResetBehavior::Remove);
215 let weak = self.to_ref().downgrade();
216 list.add_collection_changed(Rc::new(
217 move |e: &NotifyCollectionChangedEventArgs<'_, Rc<dyn IResourceProvider>>| {
218 let Some(this) = weak.upgrade() else { return };
219 let added = |items: &[Rc<dyn IResourceProvider>]| {
220 for x in items {
221 if let Some(owner) = this.owner() {
222 x.add_owner(&owner);
223 }
224 }
225 };
226 let removed = |items: &[Rc<dyn IResourceProvider>]| {
227 for x in items {
228 if let Some(owner) = this.owner() {
229 x.remove_owner(&owner);
230 }
231 }
232 };
233 match e.action {
234 NotifyCollectionChangedAction::Add => added(e.new_items),
235 NotifyCollectionChangedAction::Remove => removed(e.old_items),
236 NotifyCollectionChangedAction::Replace => {
237 removed(e.old_items);
238 added(e.new_items);
239 }
240 NotifyCollectionChangedAction::Reset => panic!("Dictionary reset not supported"),
241 NotifyCollectionChangedAction::Move => {}
242 }
243 },
244 ));
245 list
246 })
247 }
248
249 pub fn add_merged_dictionary(&self, provider: impl Into<Rc<dyn IResourceProvider>>) {
251 self.merged_dictionaries().add(provider.into());
252 }
253
254 pub fn remove_merged_dictionary(&self, provider: &Rc<dyn IResourceProvider>) -> bool {
256 let list = self.merged_dictionaries();
257 let index = list.snapshot().iter().position(|p| resource_provider_ptr_eq(p, provider));
258 match index {
259 Some(index) => {
260 list.remove_at(index);
261 true
262 }
263 None => false,
264 }
265 }
266
267 pub fn theme_dictionaries(&self) -> FerroDictionary<ThemeVariant, Rc<dyn IThemeVariantProvider>> {
273 self.theme_dictionaries
274 .get_or_init(|| {
275 let dictionary = FerroDictionary::with_capacity(2);
276 let weak = self.to_ref().downgrade();
277 dictionary.add_collection_changed(Rc::new(
278 move |e: &NotifyCollectionChangedEventArgs<'_, (ThemeVariant, Rc<dyn IThemeVariantProvider>)>| {
279 let Some(this) = weak.upgrade() else { return };
280 let added = |items: &[(ThemeVariant, Rc<dyn IThemeVariantProvider>)]| {
281 for (_, x) in items {
282 if let Some(owner) = this.owner() {
283 x.add_owner(&owner);
284 }
285 }
286 };
287 let removed = |items: &[(ThemeVariant, Rc<dyn IThemeVariantProvider>)]| {
288 for (_, x) in items {
289 if let Some(owner) = this.owner() {
290 x.remove_owner(&owner);
291 }
292 }
293 };
294 match e.action {
295 NotifyCollectionChangedAction::Add => added(e.new_items),
296 NotifyCollectionChangedAction::Remove => removed(e.old_items),
297 NotifyCollectionChangedAction::Replace | NotifyCollectionChangedAction::Move => {
298 removed(e.old_items);
299 added(e.new_items);
300 }
301 NotifyCollectionChangedAction::Reset => panic!("Dictionary reset not supported"),
302 }
303 },
304 ));
305 dictionary
306 })
307 .clone()
308 }
309
310 pub fn theme_dictionaries_snapshot(&self) -> Vec<(ThemeVariant, Rc<dyn IThemeVariantProvider>)> {
312 match self.theme_dictionaries.get() {
313 Some(dictionaries) if !dictionaries.is_empty() => dictionaries.to_vec(),
314 _ => Vec::new(),
315 }
316 }
317
318 pub fn theme_dictionary(&self, variant: &ThemeVariant) -> Option<Rc<dyn IThemeVariantProvider>> {
320 self.theme_dictionaries.get().and_then(|d| d.try_get_value(variant))
321 }
322
323 pub fn add_theme_dictionary(&self, variant: ThemeVariant, provider: impl Into<Rc<dyn IThemeVariantProvider>>) {
326 self.theme_dictionaries().add(variant, provider.into());
327 }
328
329 pub fn set_theme_dictionary(&self, variant: ThemeVariant, provider: impl Into<Rc<dyn IThemeVariantProvider>>) {
331 self.theme_dictionaries().set(variant, provider.into());
332 }
333
334 pub fn remove_theme_dictionary(&self, variant: &ThemeVariant) -> bool {
337 self.theme_dictionaries.get().is_some_and(|d| d.remove(variant))
338 }
339
340 pub fn key(&self) -> Option<ThemeVariant> {
343 self.key.borrow().clone()
344 }
345
346 pub fn set_key(&self, value: Option<ThemeVariant>) {
347 *self.key.borrow_mut() = value;
348 }
349
350 pub fn add(&self, key: impl Into<ResourceKey>, value: ResourceValue) {
352 self.add_item(key.into(), ResourceItem::Value(value));
353 }
354
355 pub fn add_value<T: PartialEq + 'static>(&self, key: impl Into<ResourceKey>, value: T) {
357 self.add(key, Some(Rc::new(value)));
358 }
359
360 fn add_item(&self, key: ResourceKey, item: ResourceItem) {
361 {
362 let mut inner = self.inner.borrow_mut();
363 if inner.contains_key(&key) {
364 panic!("An item with the same key has already been added. Key: {key}");
365 }
366 inner.insert(key, item);
367 }
368 self.raise_resources_changed();
369 }
370
371 pub fn add_deferred_fn(
374 &self,
375 key: impl Into<ResourceKey>,
376 factory: impl Fn(Option<&Rc<dyn IServiceProvider>>) -> Option<BoxedValue> + 'static,
377 ) {
378 self.add_item(key.into(), ResourceItem::Deferred(Rc::new(DeferredItem(factory))));
379 }
380
381 pub fn add_deferred(&self, key: impl Into<ResourceKey>, deferred_content: Rc<dyn IDeferredContent>) {
384 self.add_item(key.into(), ResourceItem::Deferred(deferred_content));
385 }
386
387 pub fn add_not_shared_deferred(&self, key: impl Into<ResourceKey>, deferred_content: Rc<dyn IDeferredContent>) {
390 self.add_item(key.into(), ResourceItem::NotSharedDeferred(deferred_content));
391 }
392
393 pub fn set_items(&self, values: impl IntoIterator<Item = (ResourceKey, ResourceValue)>) {
395 {
396 let mut inner = self.inner.borrow_mut();
397 for (key, value) in values {
398 inner.insert(key, ResourceItem::Value(value));
399 }
400 }
401 self.raise_resources_changed();
402 }
403
404 pub fn clear(&self) {
406 let cleared = {
407 let mut inner = self.inner.borrow_mut();
408 if inner.is_empty() {
409 false
410 } else {
411 inner.clear();
412 true
413 }
414 };
415 if cleared {
416 self.raise_resources_changed();
417 }
418 }
419
420 pub fn contains_key(&self, key: &ResourceKey) -> bool {
422 self.inner.borrow().contains_key(key)
423 }
424
425 pub fn remove(&self, key: &ResourceKey) -> bool {
427 let removed = self.inner.borrow_mut().remove(key).is_some();
428 if removed {
429 self.raise_resources_changed();
430 }
431 removed
432 }
433
434 pub fn try_get_value(&self, key: &ResourceKey) -> Option<ResourceValue> {
437 let item = self.inner.borrow().get(key).cloned()?;
438 let (deferred, shared) = match item {
439 ResourceItem::Value(value) => return Some(value),
440 ResourceItem::Deferred(deferred) => (deferred, true),
441 ResourceItem::NotSharedDeferred(deferred) => (deferred, false),
442 };
443
444 if self.last_deferred_item_key.borrow().as_ref() == Some(key) {
447 return None;
448 }
449
450 struct Reset<'a>(&'a RefCell<Option<ResourceKey>>);
451 impl Drop for Reset<'_> {
452 fn drop(&mut self) {
453 *self.0.borrow_mut() = None;
454 }
455 }
456
457 *self.last_deferred_item_key.borrow_mut() = Some(key.clone());
458 let _reset = Reset(&self.last_deferred_item_key);
459 let value = deferred.build(None);
460 if shared {
461 self.inner.borrow_mut().insert(key.clone(), ResourceItem::Value(value.clone()));
462 }
463 Some(value)
464 }
465
466 pub fn ensure_capacity(&self, capacity: usize) {
469 let mut inner = self.inner.borrow_mut();
470 let additional = capacity.saturating_sub(inner.len());
471 inner.reserve(additional);
472 }
473
474 pub fn contains_deferred_key(&self, key: &ResourceKey) -> bool {
476 matches!(
477 self.inner.borrow().get(key),
478 Some(ResourceItem::Deferred(_) | ResourceItem::NotSharedDeferred(_))
479 )
480 }
481}
482
483struct ResourceDictionaryHandle(Ref<ResourceDictionary>);
485
486impl IResourceNode for ResourceDictionaryHandle {
487 fn reference_id(&self) -> *const () {
488 let object: &FerroObject = (*self.0).upcast();
489 object as *const FerroObject as *const ()
490 }
491
492 fn has_resources(&self) -> bool {
493 self.0.has_resources()
494 }
495
496 fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
497 self.0.try_get_resource(key, theme)
498 }
499}
500
501impl IResourceProvider for ResourceDictionaryHandle {
502 fn owner(&self) -> Option<ResourceHostRef> {
503 self.0.owner()
504 }
505
506 fn owner_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
507 self.0.subscribe_owner_changed(handler)
508 }
509
510 fn add_owner(&self, owner: &ResourceHostRef) {
511 self.0.add_owner(owner)
512 }
513
514 fn remove_owner(&self, owner: &ResourceHostRef) {
515 self.0.remove_owner(owner)
516 }
517
518 fn as_object(&self) -> Option<&FerroObject> {
519 Some((*self.0).upcast())
520 }
521}
522
523impl IThemeVariantProvider for ResourceDictionaryHandle {
524 fn key(&self) -> Option<ThemeVariant> {
525 self.0.key()
526 }
527
528 fn set_key(&self, value: Option<ThemeVariant>) {
529 self.0.set_key(value)
530 }
531}
532
533impl IResourceDictionary for ResourceDictionaryHandle {
534 fn merged_dictionaries_snapshot(&self) -> Rc<Vec<Rc<dyn IResourceProvider>>> {
535 self.0.merged_dictionaries().snapshot()
536 }
537
538 fn theme_dictionaries_snapshot(&self) -> Vec<(ThemeVariant, Rc<dyn IThemeVariantProvider>)> {
539 self.0.theme_dictionaries_snapshot()
540 }
541
542 fn count(&self) -> usize {
543 self.0.count()
544 }
545
546 fn add(&self, key: ResourceKey, value: ResourceValue) {
547 self.0.add(key, value)
548 }
549
550 fn set(&self, key: ResourceKey, value: ResourceValue) {
551 self.0.set(key, value)
552 }
553
554 fn contains_key(&self, key: &ResourceKey) -> bool {
555 self.0.contains_key(key)
556 }
557
558 fn remove(&self, key: &ResourceKey) -> bool {
559 self.0.remove(key)
560 }
561
562 fn try_get_value(&self, key: &ResourceKey) -> Option<ResourceValue> {
563 self.0.try_get_value(key)
564 }
565
566 fn clear(&self) {
567 self.0.clear()
568 }
569}
570
571impl<T: ObjectType + Upcast<ResourceDictionary>> From<Ref<T>> for Rc<dyn IThemeVariantProvider> {
572 fn from(value: Ref<T>) -> Self {
573 Rc::new(ResourceDictionaryHandle(value.upcast()))
574 }
575}
576
577impl<T: ObjectType + Upcast<ResourceDictionary>> From<&Ref<T>> for Rc<dyn IThemeVariantProvider> {
578 fn from(value: &Ref<T>) -> Self {
579 Rc::new(ResourceDictionaryHandle(value.clone().upcast()))
580 }
581}
582
583impl<T: ObjectType + Upcast<ResourceDictionary>> From<Ref<T>> for Rc<dyn IResourceDictionary> {
584 fn from(value: Ref<T>) -> Self {
585 Rc::new(ResourceDictionaryHandle(value.upcast()))
586 }
587}