ferroui_diagnostics/diagnostics/view_models/
binding_setter_view_model.rs1use super::SetterViewModel;
4use ferroui_base::data::model::{Event, INotifyPropertyChanged};
5use ferroui_base::data::{BindingBase, CompiledBinding, ReflectionBinding, TemplateBinding};
6use ferroui_base::input::platform::IClipboard;
7use ferroui_base::media::{Brushes, IBrush};
8use ferroui_base::{ferro_markup_type, BoxedValue, FerroProperty};
9use ferroui_markup::data::Binding;
10use ferroui_markup_xaml::markup_extensions::CompiledBindingExtension;
11use std::ops::Deref;
12use std::rc::Rc;
13
14pub struct BindingSetterViewModel {
17 base: SetterViewModel,
18 tint: Rc<dyn IBrush>,
19 value_type_tooltip: String,
20 path: String,
21}
22
23impl PartialEq for BindingSetterViewModel {
24 fn eq(&self, other: &Self) -> bool {
25 std::ptr::eq(self, other)
26 }
27}
28
29impl Deref for BindingSetterViewModel {
30 type Target = SetterViewModel;
31
32 fn deref(&self) -> &SetterViewModel {
33 &self.base
34 }
35}
36
37impl INotifyPropertyChanged for BindingSetterViewModel {
38 fn property_changed(&self) -> &Event<str> {
39 self.base.property_changed()
40 }
41}
42
43pub(crate) fn binding_of(value: &BoxedValue) -> Option<Rc<dyn BindingBase>> {
46 if let Some(binding) = value.downcast_ref::<Rc<dyn BindingBase>>() {
47 return Some(binding.clone());
48 }
49 if let Some(binding) = value.downcast_ref::<Rc<Binding>>() {
50 return Some(binding.clone());
51 }
52 if let Some(binding) = value.downcast_ref::<Rc<ReflectionBinding>>() {
53 return Some(binding.clone());
54 }
55 if let Some(binding) = value.downcast_ref::<Rc<CompiledBindingExtension>>() {
56 return Some(binding.clone());
57 }
58 if let Some(binding) = value.downcast_ref::<Rc<CompiledBinding>>() {
59 return Some(binding.clone());
60 }
61 if let Some(binding) = value.downcast_ref::<Rc<TemplateBinding>>() {
62 return Some(binding.clone());
63 }
64 None
65}
66
67pub(crate) enum BindingKind {
70 Reflection { path: String },
72 Compiled { path: String },
74 Template { property: Option<&'static FerroProperty> },
76}
77
78pub(crate) fn binding_kind(value: &BoxedValue) -> Option<BindingKind> {
81 let binding = binding_of(value)?;
82 let any = binding.as_any()?;
83
84 if let Some(binding) = any.downcast_ref::<Binding>() {
85 return Some(BindingKind::Reflection { path: binding.path() });
86 }
87 if let Some(binding) = any.downcast_ref::<ReflectionBinding>() {
90 return Some(BindingKind::Reflection { path: binding.path() });
91 }
92 if let Some(binding) = any.downcast_ref::<CompiledBindingExtension>() {
93 return Some(BindingKind::Compiled { path: binding.path().map(|path| path.to_string()).unwrap_or_default() });
94 }
95 if let Some(binding) = any.downcast_ref::<CompiledBinding>() {
98 return Some(BindingKind::Compiled { path: binding.path().map(|path| path.to_string()).unwrap_or_default() });
99 }
100 if let Some(binding) = any.downcast_ref::<TemplateBinding>() {
101 return Some(BindingKind::Template { property: binding.property() });
102 }
103
104 None
105}
106
107impl BindingSetterViewModel {
108 pub fn new(
112 property: &'static FerroProperty,
113 value: Option<BoxedValue>,
114 clipboard: Option<Rc<dyn IClipboard>>,
115 ) -> Rc<Self> {
116 let kind = value.as_ref().and_then(binding_kind);
117
118 let (path, tint, value_type_tooltip): (String, Rc<dyn IBrush>, &str) = match kind {
119 Some(BindingKind::Reflection { path }) => (path, Brushes::cornflower_blue(), "Reflection Binding"),
120 Some(BindingKind::Compiled { path }) => (path, Brushes::dark_green(), "Compiled Binding"),
121 Some(BindingKind::Template { property }) => {
122 let path = match property {
123 Some(template_property) => {
124 format!("{}.{}", template_property.owner_type().name(), template_property.name())
125 }
126 None => "Unassigned".to_string(),
127 };
128
129 (path, Brushes::orange_red(), "Template Binding")
130 }
131 None => panic!("Invalid binding type (Parameter 'value')"),
132 };
133
134 Rc::new(Self {
135 base: SetterViewModel::construct(property, value, clipboard),
136 tint,
137 value_type_tooltip: value_type_tooltip.to_string(),
138 path,
139 })
140 }
141
142 pub fn tint(&self) -> Rc<dyn IBrush> {
143 self.tint.clone()
144 }
145
146 pub fn value_type_tooltip(&self) -> String {
147 self.value_type_tooltip.clone()
148 }
149
150 pub fn path(&self) -> String {
151 self.path.clone()
152 }
153
154 pub fn copy_value(&self) {
156 self.base.copy_to_clipboard(&self.path);
157 }
158}
159
160ferro_markup_type!(class BindingSetterViewModel {
161 this: Rc<BindingSetterViewModel>,
162 handles: [BindingSetterViewModel, Rc<BindingSetterViewModel>, Option<Rc<BindingSetterViewModel>>],
163 base: SetterViewModel,
164 properties: [
165 Property: &'static FerroProperty { get: |this: &Rc<BindingSetterViewModel>| this.property() },
166 Name: String { get: |this: &Rc<BindingSetterViewModel>| this.name() },
167 Value: Option<BoxedValue> { get: |this: &Rc<BindingSetterViewModel>| this.value() },
168 IsActive: bool {
169 get: |this: &Rc<BindingSetterViewModel>| this.is_active(),
170 set: |this: &Rc<BindingSetterViewModel>, value: bool| this.set_is_active(value)
171 },
172 IsVisible: bool {
173 get: |this: &Rc<BindingSetterViewModel>| this.is_visible(),
174 set: |this: &Rc<BindingSetterViewModel>, value: bool| this.set_is_visible(value)
175 },
176 Tint: Rc<dyn IBrush> { get: BindingSetterViewModel::tint },
177 ValueTypeTooltip: String { get: BindingSetterViewModel::value_type_tooltip },
178 Path: String { get: BindingSetterViewModel::path },
179 ],
180 methods: [
181 fn CopyValue() => BindingSetterViewModel::copy_value,
182 fn CopyPropertyName() => |this: &Rc<BindingSetterViewModel>| this.copy_property_name(),
183 ],
184 notify_property_changed: BindingSetterViewModel,
185});