ferroui_diagnostics/diagnostics/view_models/
clr_property_view_model.rs1use super::property_view_model::runtime_type_of;
9use super::{PropertyViewModel, PropertyViewModelKey, PropertyViewModelRef};
10use ferroui_base::data::core::{ValueType, ValueTypes};
11use ferroui_base::data::model::{Event, INotifyPropertyChanged};
12use ferroui_base::metadata::{MarkupProperty, MarkupType, MarkupTypeKind};
13use ferroui_base::{ferro_markup_type, BoxedValue};
14use std::cell::RefCell;
15use std::ops::Deref;
16use std::rc::Rc;
17
18pub struct ClrPropertyViewModel {
21 base: PropertyViewModel,
22 target: BoxedValue,
23 assigned_type: RefCell<ValueType>,
24 value: RefCell<Option<BoxedValue>>,
25 property_type: ValueType,
26 property: &'static MarkupProperty,
27 declaring: &'static MarkupType,
28 name: String,
29 declaring_type: Option<ValueType>,
30}
31
32impl PartialEq for ClrPropertyViewModel {
33 fn eq(&self, other: &Self) -> bool {
34 std::ptr::eq(self, other)
35 }
36}
37
38impl Deref for ClrPropertyViewModel {
39 type Target = PropertyViewModel;
40
41 fn deref(&self) -> &PropertyViewModel {
42 &self.base
43 }
44}
45
46impl INotifyPropertyChanged for ClrPropertyViewModel {
47 fn property_changed(&self) -> &Event<str> {
48 self.base.property_changed()
49 }
50}
51
52impl ClrPropertyViewModel {
53 pub fn new(o: BoxedValue, property: &'static MarkupProperty, declaring_type: &'static MarkupType) -> Rc<Self> {
56 let name = if declaring_type.kind != MarkupTypeKind::Interface {
57 property.name.to_string()
58 } else {
59 format!("{}.{}", declaring_type.name, property.name)
60 };
61 let property_type = (property.type_)();
62
63 let this = Rc::new(Self {
64 base: PropertyViewModel::construct(),
65 target: o,
66 assigned_type: RefCell::new(property_type),
67 value: RefCell::new(None),
68 property_type,
69 property,
70 declaring: declaring_type,
71 name,
72 declaring_type: declaring_type.handle(),
73 });
74
75 this.update();
76
77 this
78 }
79
80 pub fn to_property_view_model(self: &Rc<Self>) -> PropertyViewModelRef {
82 PropertyViewModelRef::Clr(self.clone())
83 }
84
85 pub fn property(&self) -> &'static MarkupProperty {
87 self.property
88 }
89
90 pub fn declaring_markup_type(&self) -> &'static MarkupType {
92 self.declaring
93 }
94
95 pub fn key(&self) -> PropertyViewModelKey {
96 PropertyViewModelKey::Name(self.name.clone())
97 }
98
99 pub fn name(&self) -> String {
100 self.name.clone()
101 }
102
103 pub fn group(&self) -> String {
104 if self.is_pinned() {
105 "Pinned".to_string()
106 } else {
107 "CLR Properties".to_string()
108 }
109 }
110
111 pub fn assigned_type(&self) -> ValueType {
112 *self.assigned_type.borrow()
113 }
114
115 pub fn property_type(&self) -> ValueType {
116 self.property_type
117 }
118
119 pub fn is_readonly(&self) -> bool {
121 self.property.set.is_none()
122 }
123
124 pub fn value(&self) -> Option<BoxedValue> {
125 self.value.borrow().clone()
126 }
127
128 pub fn set_value(&self, value: Option<BoxedValue>) {
131 let Some(set) = self.property.set else {
132 return;
133 };
134 if set(&[Some(self.target.clone()), value]).is_ok() {
135 self.update();
136 }
137 }
138
139 pub fn priority(&self) -> String {
140 String::new()
141 }
142
143 pub fn is_attached(&self) -> Option<bool> {
144 None
145 }
146
147 pub fn declaring_type(&self) -> Option<ValueType> {
148 self.declaring_type
149 }
150
151 pub fn update(&self) {
152 let base = self.base.view_model_base();
153
154 let value: Option<BoxedValue> = match self.property.get {
157 Some(get) => match get(&[Some(self.target.clone())]) {
158 Ok(value) => value.and_then(ValueTypes::normalize),
159 Err(error) => Some(Rc::new(error.to_string()) as BoxedValue),
160 },
161 None => None,
162 };
163 let value_type = runtime_type_of(value.as_ref());
164
165 base.raise_and_set_if_changed(&self.value, value, "Value");
166 base.raise_and_set_if_changed(&self.assigned_type, value_type.unwrap_or(self.property_type), "AssignedType");
167 base.raise_property_changed("Type");
168 }
169
170 pub(crate) fn on_property_changed(&self, property_name: &str) {
172 if property_name == "IsPinned" {
173 self.base.raise_property_changed("Group");
174 }
175 }
176}
177
178ferro_markup_type!(class ClrPropertyViewModel {
179 this: Rc<ClrPropertyViewModel>,
180 handles: [ClrPropertyViewModel, Rc<ClrPropertyViewModel>, Option<Rc<ClrPropertyViewModel>>],
181 properties: [
182 Name: String { get: ClrPropertyViewModel::name },
183 Group: String { get: ClrPropertyViewModel::group },
184 Priority: String { get: ClrPropertyViewModel::priority },
185 Value: Option<BoxedValue> { get: ClrPropertyViewModel::value, set: ClrPropertyViewModel::set_value },
186 AssignedType: ValueType { get: ClrPropertyViewModel::assigned_type },
187 PropertyType: ValueType { get: ClrPropertyViewModel::property_type },
188 Type: String { get: |this: &Rc<ClrPropertyViewModel>| this.to_property_view_model().type_() },
189 IsReadonly: bool { get: ClrPropertyViewModel::is_readonly },
190 IsAttached: Option<bool> { get: ClrPropertyViewModel::is_attached },
191 IsPinned: bool {
192 get: |this: &Rc<ClrPropertyViewModel>| this.is_pinned(),
193 set: |this: &Rc<ClrPropertyViewModel>, value: bool| this.to_property_view_model().set_is_pinned(value)
194 },
195 FullName: String { get: |this: &Rc<ClrPropertyViewModel>| this.to_property_view_model().full_name() },
196 ],
197 methods: [
198 fn Update() => ClrPropertyViewModel::update,
199 ],
200 notify_property_changed: ClrPropertyViewModel,
201});