1use super::activators::IStyleActivator;
2use super::{
3 empty_styles, DuplicateSetterError, IStyle, Setter, SetterBase, StyleChildren, StyleInstance, ThemeVariant,
4};
5use crate::metadata::{from_markup_value, IAddChild};
6use crate::controls::{
7 IResourceNode, IResourceProvider, ResourceDictionary, ResourceHostRef, ResourceKey, ResourceValue,
8 ResourcesChangedEventArgs, WeakResourceHost,
9};
10use crate::property_store::{FrameType, ValueFrame};
11use crate::reactive::{Disposable, IDisposable};
12use crate::animation::IAnimation;
13use crate::collections::FerroList;
14use crate::utilities::HandlerList;
15use crate::{ferro_class, BoxedValue, FerroObject, FerroObjectImpl, ObjectType, Ref, StyledElement, Upcast, WeakRef};
16use std::cell::{Cell, OnceCell, RefCell};
17use std::rc::Rc;
18
19#[repr(C)]
22pub struct StyleBase {
23 base: FerroObject,
24 owner: RefCell<Option<WeakResourceHost>>,
25 children: OnceCell<StyleChildren>,
26 resources: RefCell<Option<Ref<ResourceDictionary>>>,
27 setters: OnceCell<FerroList<Rc<dyn SetterBase>>>,
28 animations: OnceCell<FerroList<Rc<dyn IAnimation>>>,
29 shared_instance: RefCell<Option<Rc<StyleInstance>>>,
32 shared_instance_type: Cell<FrameType>,
33 shared_instances: RefCell<Option<Box<[Option<Rc<StyleInstance>>; 3]>>>,
34 parent: RefCell<Option<WeakRef<StyleBase>>>,
35 owner_changed: HandlerList<dyn Fn()>,
36}
37
38ferro_class! {
39 StyleBase: FerroObject, virtuals StyleBaseImpl: FerroObjectImpl {
40 fn set_parent(this, parent: Option<&Ref<StyleBase>>);
44 fn to_display_string(this) -> String;
47 }
48}
49
50crate::ferro_class_info!(StyleBase {
51 interfaces: [Rc<dyn IStyle>, Rc<dyn IResourceProvider>, Rc<dyn IAddChild<BoxedValue>>],
52});
53
54struct StyleBaseAddChild(Ref<StyleBase>);
57
58impl IAddChild<BoxedValue> for StyleBaseAddChild {
59 fn add_child(&self, child: BoxedValue) {
60 self.0.add_child(&child)
61 }
62
63 fn reference_id(&self) -> usize {
64 &*self.0 as *const StyleBase as usize
65 }
66}
67
68impl From<Ref<StyleBase>> for Rc<dyn IAddChild<BoxedValue>> {
69 fn from(value: Ref<StyleBase>) -> Self {
70 Rc::new(StyleBaseAddChild(value))
71 }
72}
73
74impl FerroObjectImpl for StyleBase {}
75
76impl StyleBaseImpl for StyleBase {
77 fn set_parent(this: &Self, parent: Option<&Ref<StyleBase>>) {
78 *this.parent.borrow_mut() = parent.map(Ref::downgrade);
79 }
80
81 fn to_display_string(this: &Self) -> String {
82 this.get_type().name().to_string()
83 }
84}
85
86impl StyleBase {
87 pub fn construct() -> Self {
89 Self {
90 base: FerroObject::construct(),
91 owner: RefCell::new(None),
92 children: OnceCell::new(),
93 resources: RefCell::new(None),
94 setters: OnceCell::new(),
95 animations: OnceCell::new(),
96 shared_instance: RefCell::new(None),
97 shared_instance_type: Cell::new(FrameType::Style),
98 shared_instances: RefCell::new(None),
99 parent: RefCell::new(None),
100 owner_changed: HandlerList::new(),
101 }
102 }
103
104 pub fn children(&self) -> StyleChildren {
106 self.children.get_or_init(|| StyleChildren::new(self.to_ref().downgrade())).clone()
107 }
108
109 pub fn owner(&self) -> Option<ResourceHostRef> {
112 let owner = self.owner.borrow().clone();
113 owner.and_then(|o| o.upgrade())
114 }
115
116 fn set_owner(&self, value: Option<&ResourceHostRef>) {
117 let old = self.owner();
118 if old.as_ref() != value {
119 *self.owner.borrow_mut() = value.map(ResourceHostRef::downgrade);
120 if !self.owner_changed.is_empty() {
121 for (_, handler) in self.owner_changed.snapshot().iter() {
122 handler();
123 }
124 }
125 }
126 }
127
128 pub fn owner_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
130 self.subscribe_owner_changed(Rc::new(handler))
131 }
132
133 fn subscribe_owner_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
134 let token = self.owner_changed.add(handler);
135 let weak = self.to_ref().downgrade();
136 Disposable::create(move || {
137 if let Some(this) = weak.upgrade() {
138 this.owner_changed.remove(token);
139 }
140 })
141 }
142
143 pub fn parent(&self) -> Option<Ref<StyleBase>> {
145 let parent = self.parent.borrow();
146 parent.as_ref().and_then(WeakRef::upgrade)
147 }
148
149 pub fn resources(&self) -> Ref<ResourceDictionary> {
151 if let Some(resources) = &*self.resources.borrow() {
152 return resources.clone();
153 }
154 let resources = ResourceDictionary::new();
155 self.set_resources(resources.clone());
156 resources
157 }
158
159 pub fn set_resources(&self, value: Ref<ResourceDictionary>) {
160 let had_resources = self.resources.borrow().as_ref().is_some_and(|r| r.has_resources());
161
162 *self.resources.borrow_mut() = Some(value.clone());
163
164 if let Some(owner) = self.owner() {
165 value.add_owner(&owner);
166
167 if had_resources || value.has_resources() {
168 owner.notify_hosted_resources_changed(ResourcesChangedEventArgs::create());
169 }
170 }
171 }
172
173 pub fn setters(&self) -> FerroList<Rc<dyn SetterBase>> {
176 self.setters.get_or_init(FerroList::new).clone()
177 }
178
179 pub fn add_setter(&self, setter: Rc<dyn SetterBase>) {
181 self.setters().add(setter);
182 }
183
184 pub fn clear_setters(&self) {
186 if let Some(setters) = self.setters.get() {
187 setters.clear();
188 }
189 }
190
191 pub fn add(&self, setter: Rc<dyn SetterBase>) {
193 self.add_setter(setter)
194 }
195
196 pub fn animations(&self) -> FerroList<Rc<dyn IAnimation>> {
199 self.animations.get_or_init(FerroList::new).clone()
200 }
201
202 pub fn add_animation(&self, animation: impl Into<Rc<dyn IAnimation>>) {
205 self.animations().add(animation.into());
206 }
207
208 pub fn clear_animations(&self) {
210 if let Some(animations) = self.animations.get() {
211 animations.clear();
212 }
213 }
214
215 pub fn add_child(&self, child: &BoxedValue) {
218 if let Some(setter) = child.downcast_ref::<Rc<Setter>>() {
219 let setter: Rc<dyn SetterBase> = setter.clone();
220 self.setters().add(setter);
221 return;
222 }
223 let untyped = Some(child.clone());
224 if let Some(setter) = from_markup_value::<Rc<dyn SetterBase>>(&untyped) {
225 self.setters().add(setter);
226 } else if let Some(style) = from_markup_value::<Rc<dyn IStyle>>(&untyped) {
227 self.children().add(style);
228 } else {
229 panic!("Cannot add {} to a style.", child.type_name());
230 }
231 }
232
233 pub fn add_style(&self, style: impl Into<Rc<dyn IStyle>>) {
235 self.children().add(style)
236 }
237
238 #[inline]
239 pub(crate) fn has_children(&self) -> bool {
240 self.children.get().is_some_and(|c| !c.is_empty())
241 }
242
243 #[inline]
244 pub(crate) fn has_setters_or_animations(&self) -> bool {
245 self.setters.get().is_some_and(|s| !s.is_empty()) || self.animations.get().is_some_and(|a| !a.is_empty())
246 }
247
248 pub fn try_get_resource(&self, key: &ResourceKey, theme_variant: Option<&ThemeVariant>) -> Option<ResourceValue> {
251 let resources = self.resources.borrow().clone();
252 if let Some(resources) = resources {
253 if let Some(result) = resources.try_get_resource(key, theme_variant) {
254 return Some(result);
255 }
256 }
257
258 if let Some(children) = self.children.get() {
259 if !children.is_empty() {
260 for child in children.snapshot().iter() {
261 if let Some(result) = child.try_get_resource(key, theme_variant) {
262 return Some(result);
263 }
264 }
265 }
266 }
267
268 None
269 }
270
271 #[allow(dead_code)]
274 pub(crate) fn attach(
275 &self,
276 target: &StyledElement,
277 activator: Option<Rc<dyn IStyleActivator>>,
278 type_: FrameType,
279 can_share_instance: bool,
280 ) -> Rc<dyn ValueFrame> {
281 match self.try_attach_instance(target, activator, type_, can_share_instance) {
282 Ok(frame) => frame,
283 Err(error) => panic!("{error}"),
284 }
285 }
286
287 pub(crate) fn try_attach_instance(
291 &self,
292 target: &StyledElement,
293 activator: Option<Rc<dyn IStyleActivator>>,
294 type_: FrameType,
295 mut can_share_instance: bool,
296 ) -> Result<Rc<dyn ValueFrame>, DuplicateSetterError> {
297 let shared = if can_share_instance { self.get_shared_instance(type_) } else { None };
298
299 let instance = match shared {
300 Some(instance) => instance,
301 None => {
302 crate::perf_count!(StyleInstancesCreated);
303 can_share_instance &= activator.is_none();
304
305 let instance = StyleInstance::new(self.to_ref().downgrade(), activator, type_);
306
307 let setters = self.setters.get().map(FerroList::snapshot).unwrap_or_default();
308 for setter in setters.iter() {
309 let setter_instance = setter.clone().instance(&instance, target);
310 can_share_instance &= setter_instance.is_setter;
311 instance.try_add(setter_instance)?;
312 }
313
314 let animations = self.animations.get().map(FerroList::snapshot).unwrap_or_default();
315 if !animations.is_empty() {
316 instance.add_animations(&animations);
317 }
318
319 if can_share_instance {
320 instance.make_shared();
321 self.set_shared_instance(type_, instance.clone());
322 }
323
324 instance
325 }
326 };
327
328 crate::perf_count!(StyleInstancesAttached);
329 target.values().add_frame(target, instance.clone());
330 instance.apply_animations(target);
331 Ok(instance)
332 }
333
334 fn get_shared_instance(&self, type_: FrameType) -> Option<Rc<StyleInstance>> {
335 if let Some(instances) = &*self.shared_instances.borrow() {
336 return instances[type_ as usize].clone();
337 }
338
339 if self.shared_instance_type.get() == type_ {
340 self.shared_instance.borrow().clone()
341 } else {
342 None
343 }
344 }
345
346 fn set_shared_instance(&self, type_: FrameType, instance: Rc<StyleInstance>) {
347 if let Some(instances) = &mut *self.shared_instances.borrow_mut() {
348 instances[type_ as usize] = Some(instance);
349 return;
350 }
351
352 let mut shared = self.shared_instance.borrow_mut();
353 match shared.take() {
354 None => {
355 *shared = Some(instance);
356 self.shared_instance_type.set(type_);
357 }
358 Some(existing) => {
359 let mut instances: Box<[Option<Rc<StyleInstance>>; 3]> = Box::new([None, None, None]);
360 instances[self.shared_instance_type.get() as usize] = Some(existing);
361 instances[type_ as usize] = Some(instance);
362 *self.shared_instances.borrow_mut() = Some(instances);
363 }
364 }
365 }
366
367 pub fn add_owner(&self, owner: &ResourceHostRef) {
369 if self.owner().is_some() {
370 panic!("The Style already has a parent.");
371 }
372
373 self.set_owner(Some(owner));
374 let resources = self.resources.borrow().clone();
375 if let Some(resources) = resources {
376 resources.add_owner(owner);
377 }
378 }
379
380 pub fn remove_owner(&self, owner: &ResourceHostRef) {
382 if self.owner().as_ref() == Some(owner) {
383 self.set_owner(None);
384 let resources = self.resources.borrow().clone();
385 if let Some(resources) = resources {
386 resources.remove_owner(owner);
387 }
388 }
389 }
390}
391
392struct StyleBaseHandle(Ref<StyleBase>);
394
395impl IResourceNode for StyleBaseHandle {
396 fn reference_id(&self) -> *const () {
397 let object: &FerroObject = (*self.0).upcast();
398 object as *const FerroObject as *const ()
399 }
400
401 fn has_resources(&self) -> bool {
402 self.0.resources.borrow().as_ref().is_some_and(|r| r.count() > 0)
403 }
404
405 fn try_get_resource(&self, key: &ResourceKey, theme: Option<&ThemeVariant>) -> Option<ResourceValue> {
406 self.0.try_get_resource(key, theme)
407 }
408}
409
410impl IResourceProvider for StyleBaseHandle {
411 fn owner(&self) -> Option<ResourceHostRef> {
412 self.0.owner()
413 }
414
415 fn owner_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
416 self.0.subscribe_owner_changed(handler)
417 }
418
419 fn add_owner(&self, owner: &ResourceHostRef) {
420 self.0.add_owner(owner)
421 }
422
423 fn remove_owner(&self, owner: &ResourceHostRef) {
424 self.0.remove_owner(owner)
425 }
426
427 fn as_object(&self) -> Option<&FerroObject> {
428 Some((*self.0).upcast())
429 }
430}
431
432impl IStyle for StyleBaseHandle {
433 fn children(&self) -> Rc<Vec<Rc<dyn IStyle>>> {
434 match self.0.children.get() {
435 Some(children) => children.snapshot(),
436 None => empty_styles(),
437 }
438 }
439
440 fn as_object(&self) -> Option<&FerroObject> {
441 Some((*self.0).upcast())
442 }
443
444 fn as_resource_provider(&self) -> Option<&dyn IResourceProvider> {
445 Some(self)
446 }
447}
448
449impl From<Ref<StyleBase>> for Rc<dyn IResourceProvider> {
450 fn from(value: Ref<StyleBase>) -> Self {
451 Rc::new(StyleBaseHandle(value))
452 }
453}
454
455impl From<&Ref<StyleBase>> for Rc<dyn IResourceProvider> {
456 fn from(value: &Ref<StyleBase>) -> Self {
457 Rc::new(StyleBaseHandle(value.clone()))
458 }
459}
460
461impl<T: ObjectType + Upcast<StyleBase>> From<Ref<T>> for Rc<dyn IStyle> {
462 fn from(value: Ref<T>) -> Self {
463 Rc::new(StyleBaseHandle(value.upcast()))
464 }
465}
466
467impl<T: ObjectType + Upcast<StyleBase>> From<&Ref<T>> for Rc<dyn IStyle> {
468 fn from(value: &Ref<T>) -> Self {
469 Rc::new(StyleBaseHandle(value.clone().upcast()))
470 }
471}