ferroui_base/
class_binding_manager.rs1use crate::data::{BindingBase, BindingExpressionBase};
2use crate::{FerroObject, FerroProperty, Ref, StyledElement};
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::rc::Rc;
6
7const CLASS_PROPERTY_PREFIX: &str = "__FerroReserved::Classes::";
8
9crate::ferro_thread_local! {
10 static REGISTERED_PROPERTIES: RefCell<HashMap<String, &'static FerroProperty>> = RefCell::new(HashMap::new());
11}
12
13pub struct ClassBindingManager;
17
18impl ClassBindingManager {
19 pub fn bind(
21 target: &StyledElement,
22 class_name: &str,
23 source: &dyn BindingBase,
24 anchor: Option<&Ref<FerroObject>>,
25 ) -> Rc<dyn BindingExpressionBase> {
26 let property = Self::get_class_property(class_name);
27 target.bind_binding_with_anchor(property, source, anchor)
28 }
29
30 fn register_class_proxy_property(class_name: &str) -> &'static FerroProperty {
31 let property =
32 FerroProperty::register::<StyledElement, bool>(&format!("{CLASS_PROPERTY_PREFIX}{class_name}"), false);
33 let class_name = class_name.to_string();
34 property.changed().subscribe(move |args| {
35 if let Some(element) = args.sender().downcast_ref::<StyledElement>() {
36 let value = args.get_new_value::<bool>();
37 element.classes().set(&class_name, value);
38 }
39 });
40 property
41 }
42
43 pub fn get_class_property(class_name: &str) -> &'static FerroProperty {
45 let prefixed = format!("{CLASS_PROPERTY_PREFIX}{class_name}");
46 let existing = REGISTERED_PROPERTIES.with(|p| p.borrow().get(&prefixed).copied());
47 match existing {
48 Some(property) => property,
49 None => {
50 let property = Self::register_class_proxy_property(class_name);
51 REGISTERED_PROPERTIES.with(|p| p.borrow_mut().insert(prefixed, property));
52 property
53 }
54 }
55 }
56
57 pub fn is_classes_binding_property(property: &FerroProperty) -> Option<&str> {
60 let name = property.name();
61 let prefix_len = CLASS_PROPERTY_PREFIX.len();
62 if name.len() >= prefix_len && name[..prefix_len].eq_ignore_ascii_case(CLASS_PROPERTY_PREFIX) {
63 Some(name.get(prefix_len + 1..).unwrap_or(""))
66 } else {
67 None
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn get_class_property_should_return_same_instance_for_same_class() {
78 let property1 = ClassBindingManager::get_class_property("Foo");
79 let property2 = ClassBindingManager::get_class_property("Foo");
80 assert!(std::ptr::eq(property1, property2));
81 }
82
83 #[test]
84 fn get_class_property_should_return_different_instances_for_different_classes() {
85 let property1 = ClassBindingManager::get_class_property("Foo");
86 let property2 = ClassBindingManager::get_class_property("Bar");
87 assert!(!std::ptr::eq(property1, property2));
88 }
89}