Skip to main content

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}