ferroui_xaml_interactions/core/
call_method_action.rs1use ferroui_base::{
4 ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroObjectImplExt,
5 FerroProperty, FerroPropertyChangedEventArgs, Ref, StyledElementImpl, StyledProperty,
6};
7use ferroui_xaml_interactivity::helpers::member_lookup::type_name_of;
8use ferroui_xaml_interactivity::helpers::MethodHelper;
9use ferroui_xaml_interactivity::{StyledElementAction, StyledElementActionImpl};
10use std::rc::Rc;
11
12#[repr(C)]
18pub struct CallMethodAction {
19 base: StyledElementAction,
20 method_helper: MethodHelper,
21}
22
23ferro_class!(CallMethodAction: StyledElementAction);
24ferro_class_info!(CallMethodAction {
25 new: CallMethodAction::new,
26 markup: {
27 property_attributes: [
28 TargetObject: [ResolveByName],
29 ],
30 },
31});
32ferroui_base::ferro_impl_classes!(CallMethodAction: StyledElementImpl);
33
34ferro_properties! {
35 impl CallMethodAction {
36 pub fn method_name_property() -> StyledProperty<Option<String>> {
38 FerroProperty::register::<CallMethodAction, _>("MethodName", None)
39 }
40
41 pub fn target_object_property() -> StyledProperty<Option<BoxedValue>> {
43 FerroProperty::register::<CallMethodAction, _>("TargetObject", None)
44 }
45 }
46}
47
48ferroui_base::ferro_overrides! {
49 impl FerroObjectImpl for CallMethodAction {
50 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
51 Self::parent_on_property_changed(this, change);
52
53 if change.property() == Self::method_name_property().as_property() {
54 let method_name = change.get_new_value::<Option<String>>();
55 this.method_helper.update_method_descriptors(method_name.as_deref());
56 }
57
58 if change.property() == Self::target_object_property().as_property() {
59 let new_value = change.get_new_value::<Option<BoxedValue>>();
60 if let Some(new_value) = new_value {
61 this.method_helper.update_target_type(&new_value, this.method_name().as_deref());
62 }
63 }
64 }
65 }
66}
67
68impl StyledElementActionImpl for CallMethodAction {
69 fn execute(this: &Self, sender: Option<&BoxedValue>, parameter: Option<&BoxedValue>) -> Option<BoxedValue> {
81 if !this.is_enabled() {
82 return Some(Rc::new(false));
83 }
84
85 let Some(method_name) = this.method_name().filter(|name| !name.is_empty()) else {
86 return Some(Rc::new(false));
87 };
88
89 let target = match this.target_object() {
90 Some(target_object) => Some(target_object),
91 None => sender.cloned(),
92 };
93 let Some(target) = target else {
94 return Some(Rc::new(false));
95 };
96
97 this.method_helper.update_target_type(&target, Some(&method_name));
98
99 let Some(method_descriptor) = this.method_helper.find_best_method(parameter) else {
100 if this.target_object().is_some() {
101 panic!(
102 "Cannot find method named {} on object of type {} that matches the expected signature.",
103 method_name,
104 type_name_of(&target)
105 );
106 }
107
108 return Some(Rc::new(false));
109 };
110
111 let method = method_descriptor.method_info();
112 let result = match method_descriptor.parameter_count() {
113 0 => (method.invoke)(&[Some(target.clone())]),
114 2 => (method.invoke)(&[Some(target.clone()), Some(target.clone()), parameter.cloned()]),
115 _ => return Some(Rc::new(false)),
116 };
117
118 if let Err(error) = result {
120 panic!("{error}");
121 }
122
123 Some(Rc::new(true))
124 }
125}
126
127impl CallMethodAction {
128 pub fn construct() -> Self {
130 Self { base: StyledElementAction::construct(), method_helper: MethodHelper::new() }
131 }
132
133 pub fn new() -> Ref<Self> {
135 instantiate(Self::construct())
136 }
137
138 pub fn method_name(&self) -> Option<String> {
140 self.get_value(Self::method_name_property())
141 }
142
143 pub fn set_method_name(&self, value: Option<String>) {
144 self.set_value(Self::method_name_property(), value)
145 }
146
147 pub fn target_object(&self) -> Option<BoxedValue> {
150 self.get_value(Self::target_object_property())
151 }
152
153 pub fn set_target_object(&self, value: Option<BoxedValue>) {
154 self.set_value(Self::target_object_property(), value)
155 }
156}