1use std::{
2 any::type_name,
3 future::Future,
4 ops::Deref,
5 sync::{Arc, Mutex},
6};
7
8use super::{
9 ComponentId, ComponentState, DeclarativeView, HookId, HookSlotKind, Observable, UiElement,
10 UiId, UiRenderContext, UiScope,
11};
12use crate::{
13 command::{Command, CommandHandle},
14 events::{AsyncEventHandler, Event, EventKey},
15};
16
17pub struct RenderCx<'a, 'ctx> {
18 scope: UiScope,
19 context: &'ctx UiRenderContext<'a>,
20 component_id: ComponentId,
21 hook_index: usize,
22 force_children: bool,
23}
24
25#[derive(Clone)]
26pub struct State<T> {
27 value: Arc<Mutex<T>>,
28 invalidate: Arc<dyn Fn() + Send + Sync + 'static>,
29}
30
31#[derive(Clone)]
32pub struct StateSetter<T> {
33 set: Arc<dyn Fn(T) + Send + Sync + 'static>,
34 state: State<T>,
35}
36
37#[derive(Clone)]
38pub struct UiFocusHandle {
39 target: UiId,
40 updates: Arc<super::UiUpdateQueue>,
41}
42
43#[derive(Clone, Copy)]
44enum HookKind {
45 Stable,
46 State,
47 Effect,
48 Context,
49 Store,
50}
51
52impl<'a, 'ctx> RenderCx<'a, 'ctx> {
53 pub(crate) const fn component_id(&self) -> ComponentId {
54 self.component_id
55 }
56
57 pub fn new(scope: &UiScope, context: &'ctx UiRenderContext<'a>) -> Self {
58 let component_id = context
59 .component_tree()
60 .root(scope.node_id(), "RenderCx root");
61 context.contexts().begin_component(component_id);
62 let force_children = context
63 .component_tree()
64 .begin_component_execution(component_id);
65 Self {
66 scope: scope.clone(),
67 context,
68 component_id,
69 hook_index: 0,
70 force_children,
71 }
72 }
73
74 #[doc(hidden)]
75 pub fn use_stable_id(&mut self) -> UiId {
76 let index = self.next_hook(HookKind::Stable).index();
77 self.scope
78 .id(format!("h.{}.{index}", HookKind::Stable.code()))
79 }
80
81 #[doc(hidden)]
82 pub fn focus_handle(&self, target: UiId) -> UiFocusHandle {
83 UiFocusHandle {
84 target,
85 updates: self.context.hook_updates(),
86 }
87 }
88
89 pub(crate) fn for_component(
90 scope: &UiScope,
91 context: &'ctx UiRenderContext<'a>,
92 component_id: ComponentId,
93 force_children: bool,
94 ) -> Self {
95 Self {
96 scope: scope.clone(),
97 context,
98 component_id,
99 hook_index: 0,
100 force_children,
101 }
102 }
103
104 pub fn node_id(&self) -> UiId {
105 self.scope.node_id()
106 }
107
108 pub fn viewport(&self) -> super::UiRect {
109 self.context.viewport()
110 }
111
112 pub fn application(&mut self) -> crate::application::ApplicationContext {
113 self.use_context::<crate::application::ApplicationContext>()
114 }
115
116 pub fn command<C>(&mut self) -> CommandHandle<C>
117 where
118 C: Command,
119 {
120 self.application().command::<C>()
121 }
122
123 pub fn use_event<E>(
124 &mut self,
125 deps: impl Clone + PartialEq + 'static,
126 handler: impl Fn(E) + Send + Sync + 'static,
127 ) where
128 E: Event,
129 {
130 self.listen_with(EventKey::new(E::NAME), deps, handler);
131 }
132
133 pub fn use_event_once<E>(&mut self, handler: impl Fn(E) + Send + Sync + 'static)
134 where
135 E: Event,
136 {
137 self.use_event::<E>((), handler);
138 }
139
140 pub fn use_event_async<E>(
141 &mut self,
142 deps: impl Clone + PartialEq + 'static,
143 handler: impl AsyncEventHandler<E>,
144 ) where
145 E: Event,
146 {
147 let application = self.application();
148 let handler = Arc::new(handler);
149 let key = EventKey::new(E::NAME);
150 self.use_effect(deps, move || {
151 let task_application = application.clone();
152 let subscription = application.subscribe_keyed(key, move |event| {
153 let context = super::UiAsyncContext::application_only(task_application.clone());
154 let _ = task_application.spawn(handler.call(context, event));
155 });
156 move || drop(subscription)
157 });
158 }
159
160 pub fn use_event_async_once<E>(&mut self, handler: impl AsyncEventHandler<E>)
161 where
162 E: Event,
163 {
164 self.use_event_async::<E>((), handler);
165 }
166
167 pub fn listen<T>(&mut self, key: EventKey<T>, listener: impl Fn(T) + Send + Sync + 'static)
172 where
173 T: Clone + Send + Sync + 'static,
174 {
175 self.listen_with(key, (), listener);
176 }
177
178 pub fn listen_with<T, D>(
181 &mut self,
182 key: EventKey<T>,
183 deps: D,
184 listener: impl Fn(T) + Send + Sync + 'static,
185 ) where
186 T: Clone + Send + Sync + 'static,
187 D: Clone + PartialEq + 'static,
188 {
189 let application = self.application();
190 let effect_deps = (key.clone(), deps);
191 self.use_effect(effect_deps, move || {
192 let subscription = application.subscribe_keyed(key, listener);
193 move || drop(subscription)
194 });
195 }
196
197 pub fn listen_async<T, F, Fut>(&mut self, key: EventKey<T>, listener: F)
200 where
201 T: Clone + Send + Sync + 'static,
202 F: Fn(T) -> Fut + Send + Sync + 'static,
203 Fut: Future<Output = ()> + Send + 'static,
204 {
205 self.listen_async_with(key, (), listener);
206 }
207
208 pub fn listen_async_with<T, D, F, Fut>(&mut self, key: EventKey<T>, deps: D, listener: F)
211 where
212 T: Clone + Send + Sync + 'static,
213 D: Clone + PartialEq + 'static,
214 F: Fn(T) -> Fut + Send + Sync + 'static,
215 Fut: Future<Output = ()> + Send + 'static,
216 {
217 let application = self.application();
218 let effect_deps = (key.clone(), deps);
219 let listener = Arc::new(listener);
220 self.use_effect(effect_deps, move || {
221 let task_application = application.clone();
222 let subscription = application.subscribe_keyed(key, move |payload| {
223 let _ = task_application.spawn(listener(payload));
224 });
225 move || drop(subscription)
226 });
227 }
228
229 pub(crate) fn compile<V>(&self, view: V) -> UiElement
230 where
231 V: DeclarativeView,
232 {
233 view.compile(
234 &self.scope,
235 self.context,
236 self.component_id,
237 self.force_children,
238 )
239 }
240
241 pub fn use_state<T>(&mut self, initial: impl FnOnce() -> T) -> (T, StateSetter<T>)
242 where
243 T: Clone + Send + 'static,
244 {
245 let state = self.create_state_hook(self.scope.node_id(), initial);
246 let value = state.get();
247 (value, StateSetter::new(state))
248 }
249
250 pub fn use_state_eq<T>(&mut self, initial: impl FnOnce() -> T) -> (T, StateSetter<T>)
251 where
252 T: Clone + PartialEq + Send + 'static,
253 {
254 let (value, setter) = self.use_state(initial);
255 (value, setter.with_equality())
256 }
257
258 pub fn state<T>(&mut self, initial: T) -> State<T>
259 where
260 T: Clone + Send + 'static,
261 {
262 self.state_with(|| initial)
263 }
264
265 pub fn state_with<T>(&mut self, initial: impl FnOnce() -> T) -> State<T>
266 where
267 T: Clone + Send + 'static,
268 {
269 self.create_state_hook(self.scope.node_id(), initial)
270 }
271
272 pub fn use_component_state<T, R>(&mut self, update: impl FnOnce(&mut T) -> R) -> R
273 where
274 T: ComponentState + Clone + Default + 'static,
275 {
276 let index = self.next_hook(HookKind::State).index();
277 let id = self
278 .scope
279 .id(format!("h.{}.{index}", HookKind::State.code()));
280 let (result, wants_frame) = self.context.component_state_mut_for_component(
281 &id,
282 self.component_id,
283 self.scope.node_id(),
284 |state: &mut T| {
285 let result = update(state);
286 (result, state.wants_frame())
287 },
288 );
289 if wants_frame {
290 self.context.hook_updates().request_frame();
291 }
292 result
293 }
294
295 fn create_state_hook<T>(&mut self, owner: UiId, initial: impl FnOnce() -> T) -> State<T>
296 where
297 T: Clone + Send + 'static,
298 {
299 let id = self.next_hook(HookKind::State);
300 let value = self
301 .context
302 .hook_state(id, || Arc::new(Mutex::new(initial())));
303 let updates = self.context.hook_updates();
304 let component_id = self.component_id;
305 State {
306 value,
307 invalidate: Arc::new(move || {
308 updates.invalidate(component_id, owner.clone());
309 }),
310 }
311 }
312
313 pub fn use_effect<D, F, R>(&mut self, deps: D, effect: F)
314 where
315 D: Clone + PartialEq + 'static,
316 F: FnOnce() -> R + 'static,
317 R: super::IntoEffectCleanup,
318 {
319 let id = self.next_hook(HookKind::Effect);
320 self.context.effect(id, deps, effect);
321 }
322
323 pub fn use_effect_once<F>(&mut self, effect: F)
324 where
325 F: FnOnce() + 'static,
326 {
327 self.use_effect((), effect);
328 }
329
330 #[cfg(feature = "async")]
331 pub fn use_async_effect<D, F, Fut>(&mut self, deps: D, effect: F)
332 where
333 D: Clone + PartialEq + 'static,
334 F: FnOnce() -> Fut + 'static,
335 Fut: Future<Output = ()> + Send + 'static,
336 {
337 let spawner = self
338 .context
339 .task_spawner()
340 .unwrap_or_else(super::noop_task_spawner);
341 self.use_effect(deps, move || {
342 let (cancel, task) = super::task::cancellable_task(Box::pin(effect()));
343 spawner.spawn(task);
344 move || cancel.cancel()
345 });
346 }
347
348 #[cfg(feature = "async")]
349 pub fn use_async_effect_once<F, Fut>(&mut self, effect: F)
350 where
351 F: FnOnce() -> Fut + 'static,
352 Fut: Future<Output = ()> + Send + 'static,
353 {
354 self.use_async_effect((), effect);
355 }
356
357 pub fn use_mount<F>(&mut self, effect: F)
358 where
359 F: FnOnce() + 'static,
360 {
361 self.use_effect_once(effect);
362 }
363
364 pub fn use_context<T>(&mut self) -> T
365 where
366 T: Clone + 'static,
367 {
368 self.try_use_context::<T>().unwrap_or_else(|| {
369 panic!(
370 "missing context provider for `{}` in component {}",
371 type_name::<T>(),
372 self.component_id
373 )
374 })
375 }
376
377 pub fn try_use_context<T>(&mut self) -> Option<T>
378 where
379 T: Clone + 'static,
380 {
381 self.next_hook(HookKind::Context);
382 self.context.contexts().read(self.component_id)
383 }
384
385 pub fn use_observable<T, S, F>(&mut self, source: Observable<T>, selector: F) -> S
386 where
387 T: Send + 'static,
388 S: Clone + PartialEq + Send + 'static,
389 F: Fn(&T) -> S + Copy + Send + Sync + 'static,
390 {
391 let id = self.next_hook(HookKind::Store);
392 let current = selector(&source.read());
393 let selected = self
394 .context
395 .hook_state(id, || Arc::new(Mutex::new(current.clone())));
396 *selected.lock().expect("store selector state poisoned") = current.clone();
397
398 let owner = self.component_id;
399 let invalidation_id = self.scope.node_id();
400 let updates = self.context.hook_updates();
401 let source_id = source.id();
402 self.use_effect((source_id, std::any::TypeId::of::<F>()), move || {
403 let observed_source = source.clone();
404 let observed_selected = Arc::clone(&selected);
405 let listener = Arc::new(move || {
406 let next = selector(&observed_source.read());
407 let mut current = observed_selected
408 .lock()
409 .expect("store selector state poisoned");
410 if *current == next {
411 return;
412 }
413 *current = next;
414 updates.invalidate(owner, invalidation_id.clone());
415 });
416 let cleanup = source.subscribe(listener);
417 move || cleanup()
418 });
419 current
420 }
421
422 fn next_hook(&mut self, kind: HookKind) -> HookId {
423 let index = self.hook_index;
424 self.hook_index += 1;
425 let kind = kind.slot_kind();
426 self.context
427 .component_tree()
428 .record_hook(self.component_id, kind);
429 HookId::new(self.component_id, index, kind)
430 }
431}
432
433impl HookKind {
434 fn code(self) -> u8 {
435 match self {
436 Self::Stable => 0,
437 Self::State => 1,
438 Self::Effect => 2,
439 Self::Context => 3,
440 Self::Store => 4,
441 }
442 }
443
444 fn slot_kind(self) -> HookSlotKind {
445 match self {
446 Self::Stable => HookSlotKind::Stable,
447 Self::State => HookSlotKind::State,
448 Self::Effect => HookSlotKind::Effect,
449 Self::Context => HookSlotKind::Context,
450 Self::Store => HookSlotKind::Store,
451 }
452 }
453}
454
455impl Drop for RenderCx<'_, '_> {
456 fn drop(&mut self) {
457 if std::thread::panicking() {
458 self.context
459 .component_tree()
460 .abandon_component(self.component_id);
461 } else {
462 self.context
463 .component_tree()
464 .finish_component(self.component_id);
465 }
466 }
467}
468
469impl<T> Deref for StateSetter<T> {
470 type Target = dyn Fn(T) + Send + Sync + 'static;
471
472 fn deref(&self) -> &Self::Target {
473 self.set.as_ref()
474 }
475}
476
477impl<T> State<T>
478where
479 T: Clone + Send + 'static,
480{
481 pub fn get(&self) -> T {
482 self.value.lock().expect("state value poisoned").clone()
483 }
484
485 pub fn set(&self, next: T) {
486 *self.value.lock().expect("state value poisoned") = next;
487 (self.invalidate)();
488 }
489
490 pub fn update(&self, update: impl FnOnce(&mut T)) {
491 update(&mut self.value.lock().expect("state value poisoned"));
492 (self.invalidate)();
493 }
494
495 pub fn try_update(&self, update: impl FnOnce(&mut T) -> bool) -> bool {
496 let changed = update(&mut self.value.lock().expect("state value poisoned"));
497 if changed {
498 (self.invalidate)();
499 }
500 changed
501 }
502}
503
504impl<T> StateSetter<T>
505where
506 T: Clone + Send + 'static,
507{
508 fn new(state: State<T>) -> Self {
509 let set_state = state.clone();
510 Self {
511 set: Arc::new(move |next| set_state.set(next)),
512 state,
513 }
514 }
515
516 pub fn current(&self) -> T {
517 self.state.get()
518 }
519
520 pub fn update(&self, update: impl FnOnce(&mut T) + Send + 'static) {
521 self.state.update(update);
522 }
523
524 pub fn try_update(&self, update: impl FnOnce(&mut T) -> bool) -> bool {
525 self.state.try_update(update)
526 }
527
528 fn with_equality(self) -> Self
529 where
530 T: PartialEq,
531 {
532 let current = self.state.clone();
533 let set = Arc::clone(&self.set);
534 Self {
535 set: Arc::new(move |next| {
536 if current.get() != next {
537 set(next);
538 }
539 }),
540 state: self.state,
541 }
542 }
543}
544
545impl UiFocusHandle {
546 pub fn focus(&self) {
547 self.updates.request_focus(self.target.clone());
548 }
549}
550
551#[cfg(test)]
552#[path = "reactor_test.rs"]
553mod tests;