ferroui_diagnostics/diagnostics/view_models/
property_view_model.rs1use super::{ClrPropertyViewModel, FerroPropertyViewModel, ViewModelBase};
12use crate::diagnostics::TypeExtesnions;
13use ferroui_base::data::core::{ValueType, ValueTypes};
14use ferroui_base::data::model::{Event, INotifyPropertyChanged};
15use ferroui_base::metadata::MarkupType;
16use ferroui_base::{BoxedValue, FerroProperty};
17use std::cell::RefCell;
18use std::ops::Deref;
19use std::rc::Rc;
20
21#[derive(Clone, Debug, PartialEq, Eq, Hash)]
24pub enum PropertyViewModelKey {
25 Property(&'static FerroProperty),
26 Name(String),
27}
28
29pub struct PropertyViewModel {
32 base: ViewModelBase,
33 is_pinned: RefCell<bool>,
34}
35
36impl INotifyPropertyChanged for PropertyViewModel {
37 fn property_changed(&self) -> &Event<str> {
38 self.base.property_changed()
39 }
40}
41
42impl PropertyViewModel {
43 pub fn construct() -> Self {
45 Self { base: ViewModelBase::new(), is_pinned: RefCell::new(false) }
46 }
47
48 pub fn view_model_base(&self) -> &ViewModelBase {
50 &self.base
51 }
52
53 pub fn is_pinned(&self) -> bool {
54 *self.is_pinned.borrow()
55 }
56
57 pub(crate) fn store_is_pinned(&self, value: bool) -> bool {
61 if *self.is_pinned.borrow() == value {
62 return false;
63 }
64 *self.is_pinned.borrow_mut() = value;
65 true
66 }
67
68 pub fn raise_property_changed(&self, property_name: &str) {
70 self.base.raise_property_changed(property_name);
71 }
72}
73
74pub(crate) fn type_name(type_: ValueType) -> String {
76 type_.get_type_name()
77}
78
79pub(crate) fn type_full_name(type_: ValueType) -> String {
82 if let Some(class) = ValueTypes::class_of(type_) {
83 return class.full_name();
84 }
85 if let Some(markup) = MarkupType::find_by_handle(type_.id()) {
86 return markup.full_name();
87 }
88 ValueTypes::type_full_name(type_)
89}
90
91pub(crate) fn runtime_type_of(value: Option<&BoxedValue>) -> Option<ValueType> {
94 let value = ValueTypes::normalize(value?.clone())?;
95
96 if let Some(object) = ValueTypes::as_object(&*value) {
97 let class = object.get_type();
98 if let Some(handle) = class.handle() {
99 return Some(ValueType::new(handle, class.name()));
100 }
101 }
102
103 Some(ValueType::of_value(&*value))
104}
105
106#[derive(Clone)]
109pub enum PropertyViewModelRef {
110 Ferro(Rc<FerroPropertyViewModel>),
111 Clr(Rc<ClrPropertyViewModel>),
112}
113
114impl std::fmt::Debug for PropertyViewModelRef {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 let kind = match self {
118 Self::Ferro(_) => "Ferro",
119 Self::Clr(_) => "Clr",
120 };
121 f.debug_tuple(kind).field(&self.name()).finish()
122 }
123}
124
125impl PartialEq for PropertyViewModelRef {
127 fn eq(&self, other: &Self) -> bool {
128 std::ptr::eq::<PropertyViewModel>(&**self, &**other)
129 }
130}
131
132impl Deref for PropertyViewModelRef {
133 type Target = PropertyViewModel;
134
135 fn deref(&self) -> &PropertyViewModel {
136 match self {
137 Self::Ferro(property) => property,
138 Self::Clr(property) => property,
139 }
140 }
141}
142
143impl INotifyPropertyChanged for PropertyViewModelRef {
144 fn property_changed(&self) -> &Event<str> {
145 (**self).property_changed()
146 }
147}
148
149impl PropertyViewModelRef {
150 pub fn key(&self) -> PropertyViewModelKey {
151 match self {
152 Self::Ferro(property) => property.key(),
153 Self::Clr(property) => property.key(),
154 }
155 }
156
157 pub fn name(&self) -> String {
158 match self {
159 Self::Ferro(property) => property.name(),
160 Self::Clr(property) => property.name(),
161 }
162 }
163
164 pub fn group(&self) -> String {
165 match self {
166 Self::Ferro(property) => property.group(),
167 Self::Clr(property) => property.group(),
168 }
169 }
170
171 pub fn assigned_type(&self) -> ValueType {
172 match self {
173 Self::Ferro(property) => property.assigned_type(),
174 Self::Clr(property) => property.assigned_type(),
175 }
176 }
177
178 pub fn declaring_type(&self) -> Option<ValueType> {
179 match self {
180 Self::Ferro(property) => property.declaring_type(),
181 Self::Clr(property) => property.declaring_type(),
182 }
183 }
184
185 pub fn value(&self) -> Option<BoxedValue> {
186 match self {
187 Self::Ferro(property) => property.value(),
188 Self::Clr(property) => property.value(),
189 }
190 }
191
192 pub fn set_value(&self, value: Option<BoxedValue>) {
193 match self {
194 Self::Ferro(property) => property.set_value(value),
195 Self::Clr(property) => property.set_value(value),
196 }
197 }
198
199 pub fn priority(&self) -> String {
200 match self {
201 Self::Ferro(property) => property.priority(),
202 Self::Clr(property) => property.priority(),
203 }
204 }
205
206 pub fn is_attached(&self) -> Option<bool> {
207 match self {
208 Self::Ferro(property) => property.is_attached(),
209 Self::Clr(property) => property.is_attached(),
210 }
211 }
212
213 pub fn update(&self) {
214 match self {
215 Self::Ferro(property) => property.update(),
216 Self::Clr(property) => property.update(),
217 }
218 }
219
220 pub fn property_type(&self) -> ValueType {
221 match self {
222 Self::Ferro(property) => property.property_type(),
223 Self::Clr(property) => property.property_type(),
224 }
225 }
226
227 pub fn is_readonly(&self) -> bool {
228 match self {
229 Self::Ferro(property) => property.is_readonly(),
230 Self::Clr(property) => property.is_readonly(),
231 }
232 }
233
234 pub fn type_(&self) -> String {
237 let property_type = self.property_type();
238 let assigned_type = self.assigned_type();
239
240 if property_type == assigned_type {
241 type_name(property_type)
242 } else {
243 format!("{} {{{}}}", type_name(property_type), type_name(assigned_type))
244 }
245 }
246
247 pub fn set_is_pinned(&self, value: bool) {
248 if self.store_is_pinned(value) {
249 self.on_property_changed("IsPinned");
251 self.raise_property_changed("IsPinned");
252 }
253 }
254
255 pub(crate) fn on_property_changed(&self, property_name: &str) {
257 match self {
258 Self::Ferro(property) => property.on_property_changed(property_name),
259 Self::Clr(property) => property.on_property_changed(property_name),
260 }
261 }
262
263 pub fn full_name(&self) -> String {
266 let class_name = match self {
268 Self::Ferro(_) => "FerroPropertyViewModel",
269 Self::Clr(_) => "ClrPropertyViewModel",
270 };
271 let declaring_type = self.declaring_type().map(type_full_name).unwrap_or_default();
272
273 format!("{}:{}.{}", class_name.replace("PropertyViewModel", ""), declaring_type, self.name())
274 }
275
276 pub fn boxed(&self) -> BoxedValue {
278 match self {
279 Self::Ferro(property) => property.clone(),
280 Self::Clr(property) => property.clone(),
281 }
282 }
283
284 pub fn from_boxed(value: &BoxedValue) -> Option<Self> {
287 if let Some(property) = value.downcast_ref::<PropertyViewModelRef>() {
288 return Some(property.clone());
289 }
290 if let Some(property) = value.downcast_ref::<Rc<FerroPropertyViewModel>>() {
291 return Some(Self::Ferro(property.clone()));
292 }
293 if let Some(property) = value.downcast_ref::<Rc<ClrPropertyViewModel>>() {
294 return Some(Self::Clr(property.clone()));
295 }
296 let any: Rc<dyn std::any::Any> = value.clone();
298 let any = match any.downcast::<FerroPropertyViewModel>() {
299 Ok(property) => return Some(Self::Ferro(property)),
300 Err(any) => any,
301 };
302 any.downcast::<ClrPropertyViewModel>().ok().map(Self::Clr)
303 }
304
305 pub fn as_ferro(&self) -> Option<&Rc<FerroPropertyViewModel>> {
307 match self {
308 Self::Ferro(property) => Some(property),
309 Self::Clr(_) => None,
310 }
311 }
312
313 pub fn as_clr(&self) -> Option<&Rc<ClrPropertyViewModel>> {
315 match self {
316 Self::Ferro(_) => None,
317 Self::Clr(property) => Some(property),
318 }
319 }
320}