egui_reactor/events.rs
1//! Callback props: the fused event closure, [`Handler`] and [`Emitter`].
2//!
3//! `rsx!` turns `<Dialog on_ok={..} on_cancel={..} />` into a single `FnMut(E)`
4//! closure that matches on a generated event enum. Each arm creates its handler
5//! closure and calls it immediately through [`Handler::call`], so the macro does
6//! not need to know whether the user wrote `|| ..` or `|payload| ..`.
7
8use std::cell::RefCell;
9
10/// Marker: the handler ignores the payload (`|| ..`).
11pub struct Arity0;
12
13/// Marker: the handler takes the payload (`|payload| ..`).
14pub struct Arity1;
15
16/// A callback prop that may or may not take the event payload.
17///
18/// `Marker` is `(Arity0 | Arity1, ReturnType)`. It exists only to keep the
19/// zero-arg and one-arg impls from overlapping and to let the handler body
20/// evaluate to something other than `()`; it is inferred from the closure at
21/// every call site, so the macro can always emit a bare
22/// `Handler::call(closure, payload)`.
23pub trait Handler<A, Marker> {
24 /// Call the handler with the payload, dropping it if the handler is nullary.
25 fn call(self, payload: A);
26}
27
28impl<F: FnOnce() -> R, A, R> Handler<A, (Arity0, R)> for F {
29 fn call(self, _payload: A) {
30 self();
31 }
32}
33
34impl<F: FnOnce(A) -> R, A, R> Handler<A, (Arity1, R)> for F {
35 fn call(self, payload: A) {
36 self(payload);
37 }
38}
39
40/// The fused event closure a parent hands to a child, shared by every emitter.
41pub type EventSink<'e, E> = RefCell<&'e mut (dyn FnMut(E) + 'e)>;
42
43/// A child-side handle that fires one event kind.
44///
45/// Several emitters over the same sink can be alive at once, which is what
46/// makes `#[event] on_ok` / `#[event] on_cancel` usable side by side. `A` is
47/// the payload type and `make` is the enum variant constructor, so the child
48/// writes `on_ok.emit(())` instead of naming the generated event enum.
49pub struct Emitter<'a, 'e, E, A> {
50 sink: &'a EventSink<'e, E>,
51 make: fn(A) -> E,
52}
53
54impl<E, A> Clone for Emitter<'_, '_, E, A> {
55 fn clone(&self) -> Self {
56 *self
57 }
58}
59
60impl<E, A> Copy for Emitter<'_, '_, E, A> {}
61
62impl<'a, 'e, E, A> Emitter<'a, 'e, E, A> {
63 /// Build an emitter over a shared event sink.
64 ///
65 /// `make` is the variant constructor `#[component]` generated for this
66 /// event, e.g. `DialogEvent::Ok`.
67 pub fn new(sink: &'a EventSink<'e, E>, make: fn(A) -> E) -> Self {
68 Self { sink, make }
69 }
70
71 /// Fire one event into the parent's fused closure.
72 ///
73 /// # Panics
74 /// Panics if called re-entrantly (an event fired from inside a handler for
75 /// the same child). Direct expansion never does this.
76 pub fn emit(&self, payload: A) {
77 let mut sink = self.sink.try_borrow_mut().expect(
78 "egui-reactor: event emitted re-entrantly; a handler fired another \
79 event on the same component while it was still running",
80 );
81 (*sink)((self.make)(payload));
82 }
83}