ferroui_xaml_interactions_custom/actions/
call_method_async_action.rs1use ferroui_base::{
4 ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroProperty, Ref,
5 StyledElementImpl, StyledProperty,
6};
7use ferroui_xaml_interactivity::helpers::member_lookup::{find_methods, type_name_of};
8use ferroui_xaml_interactivity::{StyledElementAction, StyledElementActionImpl};
9use std::rc::Rc;
10
11#[repr(C)]
18pub struct CallMethodAsyncAction {
19 base: StyledElementAction,
20}
21
22ferro_class!(CallMethodAsyncAction: StyledElementAction);
23ferro_class_info!(CallMethodAsyncAction {
24 new: CallMethodAsyncAction::new,
25 markup: {
26 property_attributes: [
27 TargetObject: [ResolveByName],
28 ],
29 },
30});
31ferroui_base::ferro_impl_classes!(CallMethodAsyncAction: FerroObjectImpl, StyledElementImpl);
32
33ferro_properties! {
34 impl CallMethodAsyncAction {
35 pub fn method_name_property() -> StyledProperty<Option<String>> {
37 FerroProperty::register::<CallMethodAsyncAction, _>("MethodName", None)
38 }
39
40 pub fn target_object_property() -> StyledProperty<Option<BoxedValue>> {
42 FerroProperty::register::<CallMethodAsyncAction, _>("TargetObject", None)
43 }
44 }
45}
46
47impl StyledElementActionImpl for CallMethodAsyncAction {
48 fn execute(this: &Self, sender: Option<&BoxedValue>, parameter: Option<&BoxedValue>) -> Option<BoxedValue> {
54 if !this.is_enabled() {
55 return Some(Rc::new(false));
56 }
57
58 let target = if this.get_value(Self::target_object_property()).is_some() {
59 this.target_object()
60 } else {
61 sender.cloned()
62 };
63 let method_name = this.method_name().filter(|name| !name.is_empty());
64 let (Some(target), Some(method_name)) = (target, method_name) else {
65 return Some(Rc::new(false));
66 };
67
68 let method_info = find_methods(&*target, &method_name).into_iter().find(|method| {
69 let parameters = method.parameters;
70 parameters.is_empty() || (parameters.len() == 2 && parameters[0]().is_object())
71 });
72 let Some(method_info) = method_info else {
73 panic!(
74 "Cannot find method named {} on object of type {} that matches the expected signature.",
75 method_name,
76 type_name_of(&target)
77 );
78 };
79
80 let mut arguments = Vec::new();
81 if !method_info.is_static {
82 arguments.push(Some(target.clone()));
83 }
84 if method_info.parameters.len() == 2 {
85 arguments.push(sender.cloned());
86 arguments.push(parameter.cloned());
87 }
88
89 if let Err(error) = (method_info.invoke)(&arguments) {
93 panic!("{error}");
94 }
95
96 Some(Rc::new(true))
97 }
98}
99
100impl CallMethodAsyncAction {
101 pub fn construct() -> Self {
103 Self { base: StyledElementAction::construct() }
104 }
105
106 pub fn new() -> Ref<Self> {
108 instantiate(Self::construct())
109 }
110
111 pub fn method_name(&self) -> Option<String> {
113 self.get_value(Self::method_name_property())
114 }
115
116 pub fn set_method_name(&self, value: Option<String>) {
117 self.set_value(Self::method_name_property(), value)
118 }
119
120 pub fn target_object(&self) -> Option<BoxedValue> {
123 self.get_value(Self::target_object_property())
124 }
125
126 pub fn set_target_object(&self, value: Option<BoxedValue>) {
127 self.set_value(Self::target_object_property(), value)
128 }
129}