1use crate::metadata::PseudoClassesAttribute;
2use crate::primitives::TemplatedControlImpl;
3use crate::templates::IDataTemplate;
4use crate::{ContentControl, ContentControlImpl, Control, ControlImpl};
5use ferroui_base::data::{
6 AggregateError, AggregateException, BindingChainException, BindingError, DataValidationException,
7};
8use ferroui_base::input::InputElementImpl;
9use ferroui_base::interactivity::InteractiveImpl;
10use ferroui_base::layout::LayoutableImpl;
11use ferroui_base::{
12 ferro_class, ferro_impl_classes, ferro_property, instantiate, AttachedProperty, BoxedValue, DirectProperty,
13 ElementRef, FerroObject, FerroObjectImpl, FerroProperty, FerroPropertyChangedEventArgs, Ref,
14 StyledElement, StyledElementImpl, StyledProperty, VisualImpl,
15};
16use std::cell::RefCell;
17use std::fmt;
18use std::rc::Rc;
19
20#[derive(Clone)]
25pub struct ErrorConverter(Rc<dyn Fn(&BoxedValue) -> Option<BoxedValue>>);
26
27impl ErrorConverter {
28 pub fn new(convert: impl Fn(&BoxedValue) -> Option<BoxedValue> + 'static) -> Self {
29 Self(Rc::new(convert))
30 }
31
32 pub fn convert(&self, error: &BoxedValue) -> Option<BoxedValue> {
34 (self.0)(error)
35 }
36}
37
38impl PartialEq for ErrorConverter {
39 fn eq(&self, other: &Self) -> bool {
40 Rc::ptr_eq(&self.0, &other.0)
41 }
42}
43
44impl fmt::Debug for ErrorConverter {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 f.write_str("ErrorConverter")
47 }
48}
49
50#[repr(C)]
60pub struct DataValidationErrors {
61 base: ContentControl,
62 owner: RefCell<Option<ElementRef<Control>>>,
63}
64
65ferro_class!(DataValidationErrors: ContentControl);
66ferroui_base::ferro_class_info!(DataValidationErrors { new: DataValidationErrors::new });
67ferro_impl_classes!(
68 DataValidationErrors: StyledElementImpl,
69 VisualImpl,
70 LayoutableImpl,
71 InteractiveImpl,
72 InputElementImpl,
73 ControlImpl,
74 TemplatedControlImpl,
75 ContentControlImpl
76);
77
78ferroui_base::ferro_impl_classes!(DataValidationErrors: FerroObjectImpl);
79
80impl DataValidationErrors {
81 pub const PSEUDO_CLASSES: PseudoClassesAttribute = PseudoClassesAttribute::new(&[":error"]);
83}
84
85ferroui_base::ferro_properties! { impl DataValidationErrors {
86 ferro_property!(
87 pub fn errors_property() -> AttachedProperty<Option<Vec<BoxedValue>>> {
89 FerroProperty::register_attached::<DataValidationErrors, Control, _>("Errors", None)
90 }
91 );
92
93 ferro_property!(
94 pub fn has_errors_property() -> AttachedProperty<bool> {
96 FerroProperty::register_attached::<DataValidationErrors, Control, _>("HasErrors", false)
97 }
98 );
99
100 ferro_property!(
101 pub fn error_converter_property() -> AttachedProperty<Option<ErrorConverter>> {
104 FerroProperty::register_attached::<DataValidationErrors, Control, _>("ErrorConverter", None)
105 }
106 );
107
108 ferro_property!(
109 pub fn error_template_property() -> StyledProperty<Option<Rc<dyn IDataTemplate>>> {
111 FerroProperty::register::<DataValidationErrors, _>("ErrorTemplate", None)
112 }
113 );
114
115 ferro_property!(
116 fn original_errors_property() -> AttachedProperty<Option<Vec<BoxedValue>>> {
118 FerroProperty::register_attached::<DataValidationErrors, Control, _>("OriginalErrors", None)
119 }
120 );
121
122 ferro_property!(
123 fn overriding_errors_internally_property() -> AttachedProperty<bool> {
126 FerroProperty::register_attached::<DataValidationErrors, Control, _>("OverridingErrorsInternally", false)
127 }
128 );
129
130 ferro_property!(
131 pub fn owner_property() -> DirectProperty<DataValidationErrors, Option<ElementRef<Control>>> {
140 ferroui_base::data::core::ValueTypes::register_element_ref::<Control>();
141 FerroProperty::register_direct::<DataValidationErrors, _>(
142 "Owner",
143 |o| o.owner.borrow().clone(),
144 Some(|o, v| o.set_owner(ElementRef::resolve(&v))),
145 None,
146 )
147 }
148 );
149} }
150
151impl DataValidationErrors {
152 fn static_constructor() {
154 Self::errors_property().changed().subscribe(Self::errors_changed);
155 Self::has_errors_property().changed().subscribe(Self::has_errors_changed);
156 StyledElement::templated_parent_property()
157 .changed()
158 .add_class_handler::<DataValidationErrors>(|x, e| x.on_templated_parent_change(e));
159 Self::error_converter_property().changed().subscribe(Self::on_error_converter_changed);
160 }
161
162 pub fn construct() -> Self {
164 Self { base: ContentControl::construct(), owner: RefCell::new(None) }
165 }
166
167 pub fn new() -> Ref<Self> {
169 instantiate(Self::construct())
170 }
171
172 pub fn owner(&self) -> Option<Ref<Control>> {
174 ElementRef::resolve(&self.owner.borrow())
175 }
176
177 pub fn set_owner(&self, value: Option<Ref<Control>>) {
178 self.set_and_raise(Self::owner_property(), &self.owner, ElementRef::from_nullable(value));
179 }
180
181 fn sender_control(e: &FerroPropertyChangedEventArgs<'_>) -> Ref<Control> {
188 match e.sender().downcast_ref::<Control>() {
189 Some(control) => control.to_ref(),
190 None => panic!(
191 "Unable to cast object of type '{}' to type 'Control'.",
192 e.sender().get_type().name()
193 ),
194 }
195 }
196
197 fn on_error_converter_changed(e: &FerroPropertyChangedEventArgs<'_>) {
198 Self::on_errors_or_converter_changed(&Self::sender_control(e));
199 }
200
201 fn on_templated_parent_change(&self, e: &FerroPropertyChangedEventArgs<'_>) {
202 if self.owner().is_none() {
203 let new_value = e.get_new_value::<Option<Ref<FerroObject>>>();
204 self.set_owner(new_value.and_then(|value| value.cast::<Control>()));
205 }
206 }
207
208 pub fn error_template(&self) -> Option<Rc<dyn IDataTemplate>> {
210 self.get_value(Self::error_template_property())
211 }
212
213 pub fn set_error_template(&self, value: Option<Rc<dyn IDataTemplate>>) {
214 self.set_value(Self::error_template_property(), value)
215 }
216
217 fn errors_changed(e: &FerroPropertyChangedEventArgs<'_>) {
218 let control = Self::sender_control(e);
219
220 if control.get_value(Self::overriding_errors_internally_property()) {
221 return;
222 }
223
224 let errors = e.get_new_value::<Option<Vec<BoxedValue>>>();
225
226 control.set_value(Self::original_errors_property(), errors);
228
229 Self::on_errors_or_converter_changed(&control);
230 }
231
232 fn has_errors_changed(e: &FerroPropertyChangedEventArgs<'_>) {
233 let control = Self::sender_control(e);
234 control.pseudo_classes().set(":error", e.get_new_value::<bool>());
235 }
236
237 pub fn get_errors(control: &Control) -> Option<Vec<BoxedValue>> {
239 control.get_value(Self::errors_property())
240 }
241
242 pub fn set_errors(control: &Control, errors: Option<Vec<BoxedValue>>) {
244 control.set_value(Self::errors_property(), errors);
245 }
246
247 pub fn set_error(control: &Control, error: Option<&BindingError>) {
249 Self::set_errors(
250 control,
251 Self::unpack_exception(error)
252 .map(|errors| errors.iter().filter_map(Self::unpack_data_validation_exception).collect()),
253 );
254 }
255
256 fn on_errors_or_converter_changed(control: &Control) {
257 let converter = Self::get_error_converter(control);
258 let original_errors = control.get_value(Self::original_errors_property());
259 let new_errors: Option<Vec<BoxedValue>> = match converter {
260 None => original_errors,
261 Some(converter) => {
262 original_errors.map(|errors| errors.iter().filter_map(|error| converter.convert(error)).collect())
263 }
264 };
265
266 let has_errors = new_errors.as_ref().is_some_and(|errors| !errors.is_empty());
267
268 control.set_current_value(Self::overriding_errors_internally_property(), true);
269
270 {
271 struct Restore<'a>(&'a Control);
273
274 impl Drop for Restore<'_> {
275 fn drop(&mut self) {
276 self.0.set_current_value(DataValidationErrors::overriding_errors_internally_property(), false);
277 }
278 }
279
280 let _restore = Restore(control);
281
282 control.set_current_value(Self::errors_property(), new_errors);
283 }
284
285 control.set_value(Self::has_errors_property(), has_errors);
286 }
287
288 pub fn clear_errors(control: &Control) {
290 Self::set_errors(control, None);
291 }
292
293 pub fn get_has_errors(control: &Control) -> bool {
295 control.get_value(Self::has_errors_property())
296 }
297
298 pub fn get_error_converter(control: &Control) -> Option<ErrorConverter> {
300 control.get_value(Self::error_converter_property())
301 }
302
303 pub fn set_error_converter(control: &Control, converter: Option<ErrorConverter>) {
305 control.set_value(Self::error_converter_property(), converter);
306 }
307
308 fn unpack_exception(exception: Option<&BindingError>) -> Option<Vec<BindingError>> {
311 let exception = exception?;
312 let inner = exception.inner();
313
314 let exceptions: Vec<BindingError> = if let Some(aggregate) = inner.downcast_ref::<AggregateError>() {
315 aggregate.inner_errors().to_vec()
316 } else if let Some(aggregate) = inner.downcast_ref::<AggregateException>() {
317 aggregate.inner_exceptions().iter().cloned().map(BindingError::new).collect()
318 } else {
319 vec![exception.clone()]
320 };
321
322 Some(exceptions.into_iter().filter(|x| !x.inner().is::<BindingChainException>()).collect())
323 }
324
325 fn unpack_data_validation_exception(exception: &BindingError) -> Option<BoxedValue> {
328 if let Some(data_validation_exception) = exception.inner().downcast_ref::<DataValidationException>() {
329 return data_validation_exception.error_data().cloned();
330 }
331
332 let error: BoxedValue = Rc::new(exception.clone());
333 Some(error)
334 }
335}