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