ferroui_reactiveui/binding/command_binding/
creates_command_binding_via_event.rs1use crate::binding::bindings::BindingAffinity;
10use crate::binding::interfaces::{CommandEventSubscription, CommandParameter, ICreatesCommandBinding};
11use crate::reactive_command::ICommand;
12use ferroui_reactive::disposables::{CompositeDisposable, Disposable};
13use ferroui_reactive::ObservableExt;
14use ferroui_system::events::Event;
15use ferroui_system::{IDisposable, IObservable};
16use std::any::{Any, TypeId};
17use std::cell::RefCell;
18use std::fmt;
19use std::rc::Rc;
20
21pub const DEFAULT_COMMAND_EVENTS: [&str; 3] = ["Click", "TouchUpInside", "MouseUp"];
24
25pub type ControlEventSubscription<C> = Rc<dyn Fn(&Rc<C>, Rc<dyn Fn()>) -> Rc<dyn IDisposable>>;
27
28pub fn control_event<C: 'static, A: ?Sized + 'static>(event: fn(&C) -> &Event<A>) -> ControlEventSubscription<C> {
30 Rc::new(move |control: &Rc<C>, handler: Rc<dyn Fn()>| {
31 let token = event(control).add(Rc::new(move |_: &A| handler()));
32 let control = control.clone();
33 Disposable::create(move || {
34 event(&control).remove(token);
35 })
36 })
37}
38
39pub fn bind_command_to_event(
43 command: Rc<dyn ICommand>,
44 command_parameter: &Rc<dyn IObservable<CommandParameter>>,
45 subscribe: impl FnOnce(Rc<dyn Fn()>) -> Rc<dyn IDisposable>,
46) -> Rc<dyn IDisposable> {
47 let latest: Rc<RefCell<CommandParameter>> = Rc::new(RefCell::new(None));
48 let latest_for_parameter = latest.clone();
49 let parameters = command_parameter.subscribe_next(move |parameter| *latest_for_parameter.borrow_mut() = parameter);
50 let handler = subscribe(Rc::new(move || {
51 let parameter = latest.borrow().clone();
52 if !command.can_execute(parameter.as_deref()) {
53 return;
54 }
55 command.execute(parameter.as_deref());
56 }));
57 CompositeDisposable::from_disposables([handler, parameters])
58}
59
60pub struct CreatesCommandBindingViaEvent<C> {
71 events: Vec<(String, ControlEventSubscription<C>)>,
72}
73
74impl<C> fmt::Debug for CreatesCommandBindingViaEvent<C> {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("CreatesCommandBindingViaEvent").field("events", &self.events.iter().map(|(name, _)| name).collect::<Vec<_>>()).finish()
77 }
78}
79
80impl<C: 'static> Default for CreatesCommandBindingViaEvent<C> {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86impl<C: 'static> CreatesCommandBindingViaEvent<C> {
87 pub fn new() -> Self {
89 Self { events: Vec::new() }
90 }
91
92 pub fn with_event<A: ?Sized + 'static>(mut self, name: impl Into<String>, event: fn(&C) -> &Event<A>) -> Self {
94 self.events.push((name.into(), control_event(event)));
95 self
96 }
97
98 pub fn with_event_subscription(mut self, name: impl Into<String>, subscription: ControlEventSubscription<C>) -> Self {
100 self.events.push((name.into(), subscription));
101 self
102 }
103
104 fn event(&self, name: &str) -> Option<&ControlEventSubscription<C>> {
106 self.events.iter().find(|(event_name, _)| event_name == name).map(|(_, event)| event)
107 }
108
109 fn default_event(&self) -> Option<&ControlEventSubscription<C>> {
111 DEFAULT_COMMAND_EVENTS.iter().find_map(|name| self.event(name)).or_else(|| self.events.first().map(|(_, event)| event))
112 }
113
114 fn bind(
116 command: Option<Rc<dyn ICommand>>,
117 target: Option<Rc<dyn Any>>,
118 command_parameter: Rc<dyn IObservable<CommandParameter>>,
119 event: &ControlEventSubscription<C>,
120 ) -> Option<Rc<dyn IDisposable>> {
121 let command = command?;
122 let control = target?.downcast::<C>().ok()?;
123 let event = event.clone();
124 Some(bind_command_to_event(command, &command_parameter, move |handler| event(&control, handler)))
125 }
126}
127
128impl<C: 'static> ICreatesCommandBinding for CreatesCommandBindingViaEvent<C> {
129 fn get_affinity_for_object(&self, target_type: TypeId, has_event_target: bool) -> i32 {
130 if target_type != TypeId::of::<C>() {
131 return 0;
132 }
133 if has_event_target {
134 return if self.events.is_empty() { 0 } else { BindingAffinity::EXPLICIT };
135 }
136 if self.default_event().is_some() {
137 BindingAffinity::DEFAULT_EVENT
138 } else {
139 0
140 }
141 }
142
143 fn bind_command_to_object(
144 &self,
145 command: Option<Rc<dyn ICommand>>,
146 target: Option<Rc<dyn Any>>,
147 command_parameter: Rc<dyn IObservable<CommandParameter>>,
148 ) -> Option<Rc<dyn IDisposable>> {
149 let event = self.default_event()?;
150 Self::bind(command, target, command_parameter, event)
151 }
152
153 fn bind_command_to_object_event(
154 &self,
155 command: Option<Rc<dyn ICommand>>,
156 target: Option<Rc<dyn Any>>,
157 command_parameter: Rc<dyn IObservable<CommandParameter>>,
158 event_name: &str,
159 ) -> Option<Rc<dyn IDisposable>> {
160 let event = self.event(event_name)?;
161 Self::bind(command, target, command_parameter, event)
162 }
163
164 fn bind_command_to_object_with_handlers(
165 &self,
166 command: Option<Rc<dyn ICommand>>,
167 target: Option<Rc<dyn Any>>,
168 command_parameter: Rc<dyn IObservable<CommandParameter>>,
169 subscription: CommandEventSubscription,
170 ) -> Option<Rc<dyn IDisposable>> {
171 let command = command?;
172 target?;
173 Some(bind_command_to_event(command, &command_parameter, move |handler| subscription(handler)))
174 }
175}