oxide_mvu/emitter.rs
1//! Event emitter for embedding callbacks in Props.
2
3use async_channel::Sender;
4
5use crate::Event as EventTrait;
6
7/// Event emitter that can be embedded in Props.
8///
9/// Clone this handle to create callbacks in your Props that can trigger
10/// events when invoked (e.g., by user interaction).
11///
12/// `Emitter` wraps a lock-free MPMC channel sender, making it cheap to clone
13/// and thread-safe without any locking overhead.
14///
15/// # Example
16///
17/// ```rust
18/// use oxide_mvu::{Emitter, MvuLogic, Effect};
19///
20/// #[derive(Clone)]
21/// enum Event { Click }
22///
23/// #[derive(Clone)]
24/// struct Model { clicks: u32 }
25///
26/// struct Props {
27/// clicks: u32,
28/// on_click: Box<dyn Fn()>,
29/// }
30///
31/// struct MyApp;
32///
33/// impl MvuLogic<Event, Model, Props> for MyApp {
34/// fn init(&self, model: Model) -> (Model, Effect<Event>) {
35/// (model, Effect::none())
36/// }
37///
38/// fn update(&self, event: Event, model: &Model) -> (Model, Effect<Event>) {
39/// match event {
40/// Event::Click => {
41/// let new_model = Model {
42/// clicks: model.clicks + 1,
43/// ..model.clone()
44/// };
45/// (new_model, Effect::none())
46/// }
47/// }
48/// }
49///
50/// fn view(&self, model: &Model, emitter: &Emitter<Event>) -> Props {
51/// let emitter = emitter.clone();
52/// Props {
53/// clicks: model.clicks,
54/// on_click: Box::new(move || {
55/// emitter.try_emit(Event::Click);
56/// }),
57/// }
58/// }
59/// }
60/// ```
61pub struct Emitter<Event: EventTrait>(pub(crate) Sender<Event>);
62
63impl<Event: EventTrait> Clone for Emitter<Event> {
64 fn clone(&self) -> Self {
65 Self(self.0.clone())
66 }
67}
68
69impl<Event: EventTrait> Emitter<Event> {
70 /// Create a new emitter from a channel sender.
71 pub(crate) fn new(sender: Sender<Event>) -> Self {
72 Self(sender)
73 }
74
75 /// Emit an event without blocking.
76 ///
77 /// This attempts to queue the event for processing by the runtime. If the
78 /// event queue is full, the event will be dropped and `false` is returned.
79 ///
80 /// Multiple threads can safely call this method concurrently via the lock-free channel.
81 pub fn try_emit(&self, event: Event) -> bool {
82 self.0.try_send(event).is_ok()
83 }
84
85 /// Emit an event, waiting until space is available.
86 ///
87 /// This queues the event for processing by the runtime. If the queue is full,
88 /// this method will await until space becomes available.
89 ///
90 /// Multiple threads can safely call this method concurrently via the lock-free channel.
91 pub async fn emit(&self, event: Event) {
92 self.0.send(event).await.ok();
93 }
94}