ferroui_xaml_interactivity/helpers/
command_can_execute_observer.rs1use ferroui_base::input::ICommand;
4use ferroui_base::reactive::IDisposable;
5use ferroui_base::BoxedValue;
6use std::cell::{Cell, RefCell};
7use std::rc::{Rc, Weak};
8
9pub struct CommandCanExecuteObserver {
24 pub(crate) inner: Rc<ObserverState>,
25}
26
27pub(crate) struct ObserverState {
28 set_can_execute: Box<dyn Fn(bool)>,
29 weak_can_execute_changed_handler: Rc<WeakCanExecuteChangedHandler>,
30 command: RefCell<Option<Rc<dyn ICommand>>>,
31 parameter: RefCell<Option<BoxedValue>>,
32 is_parameter_known: Cell<bool>,
33 is_observing: Cell<bool>,
34}
35
36impl CommandCanExecuteObserver {
37 pub fn new(set_can_execute: impl Fn(bool) + 'static) -> Self {
40 let inner = Rc::new_cyclic(|this: &Weak<ObserverState>| ObserverState {
41 set_can_execute: Box::new(set_can_execute),
42 weak_can_execute_changed_handler: Rc::new(WeakCanExecuteChangedHandler::new(this.clone())),
43 command: RefCell::new(None),
44 parameter: RefCell::new(None),
45 is_parameter_known: Cell::new(true),
46 is_observing: Cell::new(false),
47 });
48 Self { inner }
49 }
50
51 pub fn start(&self, command: Option<Rc<dyn ICommand>>, parameter: Option<BoxedValue>) {
54 self.start_with(command, parameter, true);
55 }
56
57 pub fn start_with(&self, command: Option<Rc<dyn ICommand>>, parameter: Option<BoxedValue>, is_parameter_known: bool) {
64 let state = &self.inner;
65 if state.is_observing.get() {
66 self.update_with(command, parameter, is_parameter_known);
67 return;
68 }
69
70 state.is_observing.set(true);
71 *state.command.borrow_mut() = command.clone();
72 *state.parameter.borrow_mut() = parameter;
73 state.is_parameter_known.set(is_parameter_known);
74
75 if let Some(command) = command {
76 state.weak_can_execute_changed_handler.subscribe(&command);
77 }
78
79 state.update_can_execute();
80 }
81
82 pub fn update(&self, command: Option<Rc<dyn ICommand>>, parameter: Option<BoxedValue>) {
85 self.update_with(command, parameter, true);
86 }
87
88 pub fn update_with(&self, command: Option<Rc<dyn ICommand>>, parameter: Option<BoxedValue>, is_parameter_known: bool) {
91 let state = &self.inner;
92 if !state.is_observing.get() {
93 return;
94 }
95
96 let same_command = match (&*state.command.borrow(), &command) {
97 (Some(current), Some(command)) => Rc::ptr_eq(current, command),
98 (None, None) => true,
99 _ => false,
100 };
101 if !same_command {
102 state.weak_can_execute_changed_handler.unsubscribe();
103
104 *state.command.borrow_mut() = command.clone();
105
106 if let Some(command) = command {
107 state.weak_can_execute_changed_handler.subscribe(&command);
108 }
109 }
110
111 *state.parameter.borrow_mut() = parameter;
112 state.is_parameter_known.set(is_parameter_known);
113 state.update_can_execute();
114 }
115
116 pub fn stop(&self) {
118 let state = &self.inner;
119 if !state.is_observing.get() {
120 return;
121 }
122
123 state.weak_can_execute_changed_handler.unsubscribe();
124
125 *state.command.borrow_mut() = None;
126 *state.parameter.borrow_mut() = None;
127 state.is_parameter_known.set(true);
128 state.is_observing.set(false);
129 }
130}
131
132impl IDisposable for CommandCanExecuteObserver {
133 fn dispose(&self) {
135 self.stop();
136 }
137}
138
139impl ObserverState {
140 fn update_can_execute(&self) {
141 self.update_can_execute_core();
144 }
145
146 fn update_can_execute_core(&self) {
147 if !self.is_observing.get() {
148 return;
149 }
150
151 let command = self.command.borrow().clone();
152 let command = match command {
153 Some(command) if self.is_parameter_known.get() => command,
154 _ => {
155 (self.set_can_execute)(true);
156 return;
157 }
158 };
159
160 let parameter = self.parameter.borrow().clone();
161 (self.set_can_execute)(command.can_execute(parameter.as_ref()));
162 }
163}
164
165struct WeakCanExecuteChangedHandler {
168 observer: Weak<ObserverState>,
169 subscription: RefCell<Option<Rc<dyn IDisposable>>>,
170}
171
172impl WeakCanExecuteChangedHandler {
173 fn new(observer: Weak<ObserverState>) -> Self {
174 Self { observer, subscription: RefCell::new(None) }
175 }
176
177 fn subscribe(self: &Rc<Self>, command: &Rc<dyn ICommand>) {
179 let handler = self.clone();
182 let subscription = command.can_execute_changed(Rc::new(move || handler.on_can_execute_changed()));
183 let previous = self.subscription.borrow_mut().replace(subscription);
184 if let Some(previous) = previous {
185 previous.dispose();
186 }
187 }
188
189 fn unsubscribe(&self) {
191 let subscription = self.subscription.borrow_mut().take();
192 if let Some(subscription) = subscription {
193 subscription.dispose();
194 }
195 }
196
197 fn on_can_execute_changed(&self) {
198 if let Some(observer) = self.observer.upgrade() {
199 observer.update_can_execute();
200 return;
201 }
202
203 self.unsubscribe();
204 }
205}