santui_core/event.rs
1use std::collections::VecDeque;
2
3use crate::theme::Theme;
4
5/// Events that can be published through the application's [`EventBus`].
6#[derive(Clone, Debug, PartialEq)]
7pub enum Event {
8 /// The active theme changed (plugins should refresh their colours).
9 ThemeChanged(Theme),
10 /// Plugin-to-plugin message. `from` and `to` are plugin ids.
11 PluginMessage {
12 from: String,
13 to: String,
14 action: String,
15 data: String,
16 },
17}
18
19/// A read-only observer registered via [`EventBus::subscribe`].
20pub type EventSubscriber = Box<dyn FnMut(&Event) + Send>;
21
22/// A simple in-app event bus for decoupling components.
23///
24/// Components emit events via [`EventBus::emit`] and the main loop drains the
25/// pending queue via [`EventBus::drain`] once per frame, forwarding them to
26/// [`PluginManager::process_events`](crate::app::plugin_manager::PluginManager).
27///
28/// External code can register read-only observers with [`EventBus::subscribe`]
29/// to react to events without consuming them (e.g. event logging, metrics).
30///
31/// The pending queue is capped at [`MAX_PENDING`] entries. If full, the oldest
32/// event is dropped to make room for the newest, ensuring the bus never grows
33/// without bound.
34pub struct EventBus {
35 pending: VecDeque<Event>,
36 subscribers: Vec<EventSubscriber>,
37}
38
39const MAX_PENDING: usize = 1024;
40
41impl EventBus {
42 pub fn new() -> Self {
43 Self {
44 pending: VecDeque::new(),
45 subscribers: Vec::new(),
46 }
47 }
48
49 /// Register a read-only observer that is called for every emitted event.
50 ///
51 /// Subscribers are invoked synchronously inside [`EventBus::emit`] after the
52 /// event is pushed to the pending queue. They receive a shared reference and
53 /// cannot modify or consume the event.
54 pub fn subscribe(&mut self, f: EventSubscriber) {
55 self.subscribers.push(f);
56 }
57
58 /// Push an event onto the pending queue and notify all subscribers.
59 ///
60 /// If the queue is at capacity the oldest event is dropped.
61 pub fn emit(&mut self, event: Event) {
62 for sub in &mut self.subscribers {
63 sub(&event);
64 }
65 if self.pending.len() >= MAX_PENDING {
66 self.pending.pop_front();
67 }
68 self.pending.push_back(event);
69 }
70
71 /// Drain all pending events.
72 pub fn drain(&mut self) -> Vec<Event> {
73 std::mem::take(&mut self.pending).into_iter().collect()
74 }
75}
76
77impl std::fmt::Debug for EventBus {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("EventBus")
80 .field("pending", &self.pending)
81 .field(
82 "subscribers",
83 &format_args!("{} subscribers", self.subscribers.len()),
84 )
85 .finish()
86 }
87}
88
89impl Default for EventBus {
90 fn default() -> Self {
91 Self::new()
92 }
93}