ferroui_diagnostics/diagnostics/view_models/
value_frame_view_model.rs1use super::binding_setter_view_model::{binding_kind, binding_of};
4use super::{BindingSetterViewModel, ResourceSetterViewModel, SetterViewModel, SetterViewModelRef, ViewModelBase};
5use ferroui_base::controls::ResourceKey;
6use ferroui_base::data::core::{ValueType, ValueTypes};
7use ferroui_base::data::model::{Event, INotifyPropertyChanged};
8use ferroui_base::diagnostics::{IValueFrameDiagnostic, IValueFrameDiagnosticFrameType};
9use ferroui_base::input::platform::IClipboard;
10use ferroui_base::styling::{ControlTheme, Style, StyleBase};
11use ferroui_base::{ferro_markup_type, BoxedValue, Ref, StyledElement, TypeInfo};
12use ferroui_controls::ItemsSource;
13use ferroui_markup_xaml::markup_extensions::{DynamicResourceExtension, StaticResourceExtension};
14use std::cell::RefCell;
15use std::rc::Rc;
16
17pub struct ValueFrameViewModel {
20 base: ViewModelBase,
21 value_frame: Rc<dyn IValueFrameDiagnostic>,
22 is_active: RefCell<bool>,
23 is_visible: RefCell<bool>,
24 description: Option<String>,
25 setters: Vec<SetterViewModelRef>,
26}
27
28impl PartialEq for ValueFrameViewModel {
29 fn eq(&self, other: &Self) -> bool {
30 std::ptr::eq(self, other)
31 }
32}
33
34impl INotifyPropertyChanged for ValueFrameViewModel {
35 fn property_changed(&self) -> &Event<str> {
36 self.base.property_changed()
37 }
38}
39
40impl ValueFrameViewModel {
41 pub fn new(
42 styled_element: &StyledElement,
43 value_frame: Rc<dyn IValueFrameDiagnostic>,
44 clipboard: Option<Rc<dyn IClipboard>>,
45 ) -> Rc<Self> {
46 let source = Self::source_to_string(value_frame.source());
47 let description = match (value_frame.type_(), source) {
48 (IValueFrameDiagnosticFrameType::Local, source) => format!("Local Values {}", source.unwrap_or_default()),
50 (IValueFrameDiagnosticFrameType::Template, source) => format!("Template {}", source.unwrap_or_default()),
51 (IValueFrameDiagnosticFrameType::Theme, source) => format!("Theme {}", source.unwrap_or_default()),
52 (_, Some(source)) if !source.is_empty() => source,
53 _ => format!("{:?}", value_frame.priority()),
55 };
56
57 let mut setters = Vec::new();
58
59 for entry in value_frame.values() {
60 let setter_property = entry.property;
61 let setter_value = entry.value.and_then(|value| match value.downcast_ref::<Option<BoxedValue>>() {
64 Some(untyped) => untyped.clone(),
65 None => Some(value),
66 });
67
68 let resource_info = Self::get_resource_info(setter_value.as_ref());
69
70 let setter_vm = if let Some((resource_key, is_dynamic)) = resource_info {
71 let resource_value = styled_element.find_resource(&resource_key_of(&resource_key));
72
73 SetterViewModelRef::Resource(ResourceSetterViewModel::new(
74 setter_property,
75 resource_key,
76 resource_value,
77 is_dynamic,
78 clipboard.clone(),
79 ))
80 } else {
81 let is_binding = Self::is_binding(setter_value.as_ref());
82
83 if is_binding {
84 SetterViewModelRef::Binding(BindingSetterViewModel::new(
85 setter_property,
86 setter_value,
87 clipboard.clone(),
88 ))
89 } else {
90 SetterViewModelRef::Setter(SetterViewModel::new(setter_property, setter_value, clipboard.clone()))
91 }
92 };
93 setters.push(setter_vm);
94 }
95
96 let this = Rc::new(Self {
97 base: ViewModelBase::new(),
98 value_frame,
99 is_active: RefCell::new(false),
100 is_visible: RefCell::new(false),
101 description: Some(description),
102 setters,
103 });
104
105 this.set_is_visible(true);
106
107 this.update();
108
109 this
110 }
111
112 pub fn is_active(&self) -> bool {
113 *self.is_active.borrow()
114 }
115
116 pub fn set_is_active(&self, value: bool) {
117 self.base.raise_and_set_if_changed(&self.is_active, value, "IsActive");
118 }
119
120 pub fn is_visible(&self) -> bool {
121 *self.is_visible.borrow()
122 }
123
124 pub fn set_is_visible(&self, value: bool) {
125 self.base.raise_and_set_if_changed(&self.is_visible, value, "IsVisible");
126 }
127
128 pub fn description(&self) -> Option<String> {
129 self.description.clone()
130 }
131
132 pub fn setters(&self) -> &[SetterViewModelRef] {
133 &self.setters
134 }
135
136 pub fn setters_source(&self) -> ItemsSource {
139 ItemsSource::from_items(self.setters.iter().map(|setter| Some(setter.boxed())))
140 }
141
142 pub fn update(&self) {
143 self.set_is_active(self.value_frame.is_active());
144 }
145
146 fn get_resource_info(value: Option<&BoxedValue>) -> Option<(BoxedValue, bool)> {
149 let value = value?;
150
151 if let Some(static_resource) = value.downcast_ref::<Rc<StaticResourceExtension>>() {
152 if let Some(resource_key) = static_resource.resource_key() {
153 return Some((resource_key, false));
154 }
155 }
156
157 if let Some(dynamic_resource) = value.downcast_ref::<Rc<DynamicResourceExtension>>() {
160 if let Some(resource_key) = dynamic_resource.resource_key() {
161 return Some((resource_key, true));
162 }
163 }
164 if let Some(binding) = binding_of(value) {
165 if let Some(dynamic_resource) = binding.as_any().and_then(|any| any.downcast_ref::<DynamicResourceExtension>()) {
166 if let Some(resource_key) = dynamic_resource.resource_key() {
167 return Some((resource_key, true));
168 }
169 }
170 }
171
172 None
173 }
174
175 fn is_binding(value: Option<&BoxedValue>) -> bool {
177 value.is_some_and(|value| binding_kind(value).is_some())
178 }
179
180 fn source_to_string(source: Option<BoxedValue>) -> Option<String> {
181 let source = source?;
182 let object = ValueTypes::as_object(&*source)?;
183
184 if let Some(style) = object.cast::<Style>() {
185 let mut current_style: Option<Ref<StyleBase>> = Some(style.upcast());
186 let mut selectors: Vec<String> = Vec::new();
187
188 while let Some(current) = current_style {
189 if let Some(selector) = current.cast::<Style>().and_then(|style| style.selector()) {
190 selectors.push(selector.to_string());
191 }
192 if let Some(theme) = current.cast::<ControlTheme>() {
193 selectors.push(format!("Theme {}", theme.target_type().map(TypeInfo::name).unwrap_or_default()));
194 }
195
196 current_style = current.parent();
197 }
198
199 selectors.reverse();
202
203 Some(selectors.concat().replace('^', ""))
204 } else if let Some(control_theme) = object.cast::<ControlTheme>() {
205 control_theme.target_type().map(|target_type| target_type.name().to_string())
206 } else if let Some(styled_element) = object.cast::<StyledElement>() {
207 Some(styled_element.style_key().name().to_string())
208 } else {
209 None
210 }
211 }
212}
213
214fn resource_key_of(value: &BoxedValue) -> ResourceKey {
220 if let Some(key) = value.downcast_ref::<ResourceKey>() {
221 return key.clone();
222 }
223 if let Some(text) = value.downcast_ref::<String>() {
224 return ResourceKey::from(text.as_str());
225 }
226 if let Some(text) = value.downcast_ref::<&'static str>() {
227 return ResourceKey::from(*text);
228 }
229 if let Some(type_) = value.downcast_ref::<&'static TypeInfo>() {
230 return ResourceKey::Type(type_);
231 }
232 if let Some(type_) = value.downcast_ref::<ValueType>() {
233 if let Some((class, _)) = TypeInfo::find_by_handle(type_.id()) {
234 return ResourceKey::Type(class);
235 }
236 }
237 ResourceKey::from(ValueTypes::to_display_string(Some(value)))
238}
239
240ferro_markup_type!(class ValueFrameViewModel {
241 this: Rc<ValueFrameViewModel>,
242 handles: [ValueFrameViewModel, Rc<ValueFrameViewModel>, Option<Rc<ValueFrameViewModel>>],
243 properties: [
244 IsActive: bool { get: ValueFrameViewModel::is_active, set: ValueFrameViewModel::set_is_active },
245 IsVisible: bool { get: ValueFrameViewModel::is_visible, set: ValueFrameViewModel::set_is_visible },
246 Description: Option<String> { get: ValueFrameViewModel::description },
247 Setters: ItemsSource { get: ValueFrameViewModel::setters_source },
248 ],
249 methods: [
250 fn Update() => ValueFrameViewModel::update,
251 ],
252 notify_property_changed: ValueFrameViewModel,
253});