ferroui_xaml_interactions_custom/button/
button_click_event_trigger_behavior.rs1use ferroui_base::input::{InputElement, KeyEventArgs, KeyModifiers};
4use ferroui_base::interactivity::{
5 IRoutedEventArgs, Interactive, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
6};
7use ferroui_base::{
8 ferro_class, ferro_class_info, ferro_properties, instantiate, BoxedValue, FerroObjectImpl, FerroProperty, Ref,
9 StaticType, StyledElementImpl, StyledProperty,
10};
11use ferroui_controls::Button;
12use ferroui_xaml_interactivity::{Interaction, StyledElementBehaviorImpl, StyledElementTriggerOfT};
13use std::cell::Cell;
14use std::rc::Rc;
15
16#[derive(Clone, Copy)]
18struct Handlers {
19 click: RoutedEventHandlerToken,
20 key_down: RoutedEventHandlerToken,
21 key_up: RoutedEventHandlerToken,
22}
23
24#[repr(C)]
29pub struct ButtonClickEventTriggerBehavior {
30 base: StyledElementTriggerOfT,
31 saved_key_modifiers: Cell<KeyModifiers>,
32 handlers: Cell<Option<Handlers>>,
35}
36
37ferro_class!(ButtonClickEventTriggerBehavior: StyledElementTriggerOfT);
38ferro_class_info!(ButtonClickEventTriggerBehavior { new: ButtonClickEventTriggerBehavior::new });
39ferroui_base::ferro_impl_classes!(ButtonClickEventTriggerBehavior: FerroObjectImpl, StyledElementImpl);
40
41ferro_properties! {
42 impl ButtonClickEventTriggerBehavior {
43 pub fn key_modifiers_property() -> StyledProperty<KeyModifiers> {
45 FerroProperty::register::<ButtonClickEventTriggerBehavior, _>("KeyModifiers", KeyModifiers::NONE)
46 }
47 }
48}
49
50ferroui_base::ferro_overrides! {
51 impl StyledElementBehaviorImpl for ButtonClickEventTriggerBehavior {
52 fn on_attached_to_visual_tree(this: &Self) {
53 let Some(associated_object) = this.associated_object_as::<Button>() else {
54 return;
55 };
56
57 this.remove_handlers(&associated_object);
58
59 let trigger = this.to_ref().downgrade();
60 let click = associated_object.click(move |_sender: &Interactive, e: &RoutedEventArgs| {
61 if let Some(trigger) = trigger.upgrade() {
62 trigger.associated_object_on_click(e);
63 }
64 });
65
66 let trigger = this.to_ref().downgrade();
67 let key_down = associated_object.add_handler_with(
68 InputElement::key_down_event(),
69 move |_sender: &Interactive, e: &KeyEventArgs| {
70 if let Some(trigger) = trigger.upgrade() {
71 trigger.button_on_key_down(e);
72 }
73 },
74 RoutingStrategies::TUNNEL,
75 false,
76 );
77
78 let trigger = this.to_ref().downgrade();
79 let key_up = associated_object.add_handler_with(
80 InputElement::key_up_event(),
81 move |_sender: &Interactive, e: &KeyEventArgs| {
82 if let Some(trigger) = trigger.upgrade() {
83 trigger.button_on_key_up(e);
84 }
85 },
86 RoutingStrategies::TUNNEL,
87 false,
88 );
89
90 this.handlers.set(Some(Handlers { click, key_down, key_up }));
91 }
92
93 fn on_detached_from_visual_tree(this: &Self) {
94 if let Some(associated_object) = this.associated_object_as::<Button>() {
95 this.remove_handlers(&associated_object);
96 }
97 }
98 }
99}
100
101impl ButtonClickEventTriggerBehavior {
102 pub fn construct() -> Self {
104 Self {
105 base: StyledElementTriggerOfT::construct(<Button as StaticType>::TYPE),
106 saved_key_modifiers: Cell::new(KeyModifiers::NONE),
107 handlers: Cell::new(None),
108 }
109 }
110
111 pub fn new() -> Ref<Self> {
113 instantiate(Self::construct())
114 }
115
116 pub fn key_modifiers(&self) -> KeyModifiers {
119 self.get_value(Self::key_modifiers_property())
120 }
121
122 pub fn set_key_modifiers(&self, value: KeyModifiers) {
123 self.set_value(Self::key_modifiers_property(), value)
124 }
125
126 fn remove_handlers(&self, button: &Button) {
127 if let Some(handlers) = self.handlers.take() {
128 button.remove_handler(Button::click_event(), handlers.click);
129 button.remove_handler(InputElement::key_down_event(), handlers.key_down);
130 button.remove_handler(InputElement::key_up_event(), handlers.key_up);
131 }
132 }
133
134 fn associated_object_on_click(&self, e: &RoutedEventArgs) {
135 let parameter: BoxedValue = Rc::new(IRoutedEventArgs::share(e));
137 self.execute(Some(¶meter));
138 }
139
140 fn execute(&self, parameter: Option<&BoxedValue>) {
141 if !self.is_enabled() {
142 return;
143 }
144
145 if let Some(associated_object) = self.associated_object() {
146 if self.key_modifiers() == self.saved_key_modifiers.get() {
147 let sender: BoxedValue = Rc::new(associated_object);
148 Interaction::execute_actions(Some(&sender), self.actions().as_ref(), parameter);
149 }
150 }
151 }
152
153 fn button_on_key_down(&self, e: &KeyEventArgs) {
154 self.saved_key_modifiers.set(e.key_modifiers);
155 }
156
157 fn button_on_key_up(&self, _e: &KeyEventArgs) {
158 self.saved_key_modifiers.set(KeyModifiers::NONE);
159 }
160}