egui/
plugin.rs

1use crate::{Context, FullOutput, RawInput};
2use ahash::HashMap;
3use epaint::mutex::{Mutex, MutexGuard};
4use std::sync::Arc;
5
6/// A plugin to extend egui.
7///
8/// Add plugins via [`Context::add_plugin`].
9///
10/// Plugins should not hold a reference to the [`Context`], since this would create a cycle
11/// (which would prevent the [`Context`] from being dropped).
12#[expect(unused_variables)]
13pub trait Plugin: Send + Sync + std::any::Any + 'static {
14    /// Plugin name.
15    ///
16    /// Used when profiling.
17    fn debug_name(&self) -> &'static str;
18
19    /// Called once, when the plugin is registered.
20    ///
21    /// Useful to e.g. register image loaders.
22    fn setup(&mut self, ctx: &Context) {}
23
24    /// Called at the start of each pass.
25    ///
26    /// Can be used to show ui, e.g. a [`crate::Window`] or [`crate::SidePanel`].
27    fn on_begin_pass(&mut self, ctx: &Context) {}
28
29    /// Called at the end of each pass.
30    ///
31    /// Can be used to show ui, e.g. a [`crate::Window`].
32    fn on_end_pass(&mut self, ctx: &Context) {}
33
34    /// Called just before the input is processed.
35    ///
36    /// Useful to inspect or modify the input.
37    /// Since this is called outside a pass, don't show ui here.
38    fn input_hook(&mut self, input: &mut RawInput) {}
39
40    /// Called just before the output is passed to the backend.
41    ///
42    /// Useful to inspect or modify the output.
43    /// Since this is called outside a pass, don't show ui here.
44    fn output_hook(&mut self, output: &mut FullOutput) {}
45}
46
47pub(crate) struct PluginHandle {
48    plugin: Box<dyn Plugin>,
49}
50
51pub struct TypedPluginHandle<P: Plugin> {
52    handle: Arc<Mutex<PluginHandle>>,
53    _type: std::marker::PhantomData<P>,
54}
55
56impl<P: Plugin> TypedPluginHandle<P> {
57    pub(crate) fn new(handle: Arc<Mutex<PluginHandle>>) -> Self {
58        Self {
59            handle,
60            _type: std::marker::PhantomData,
61        }
62    }
63
64    pub fn lock(&self) -> TypedPluginGuard<'_, P> {
65        TypedPluginGuard {
66            guard: self.handle.lock(),
67            _type: std::marker::PhantomData,
68        }
69    }
70}
71
72pub struct TypedPluginGuard<'a, P: Plugin> {
73    guard: MutexGuard<'a, PluginHandle>,
74    _type: std::marker::PhantomData<P>,
75}
76
77impl<P: Plugin> TypedPluginGuard<'_, P> {}
78
79impl<P: Plugin> std::ops::Deref for TypedPluginGuard<'_, P> {
80    type Target = P;
81
82    fn deref(&self) -> &Self::Target {
83        self.guard.typed_plugin()
84    }
85}
86
87impl<P: Plugin> std::ops::DerefMut for TypedPluginGuard<'_, P> {
88    fn deref_mut(&mut self) -> &mut Self::Target {
89        self.guard.typed_plugin_mut()
90    }
91}
92
93impl PluginHandle {
94    pub fn new<P: Plugin>(plugin: P) -> Arc<Mutex<Self>> {
95        Arc::new(Mutex::new(Self {
96            plugin: Box::new(plugin),
97        }))
98    }
99
100    fn plugin_type_id(&self) -> std::any::TypeId {
101        (*self.plugin).type_id()
102    }
103
104    pub fn dyn_plugin_mut(&mut self) -> &mut dyn Plugin {
105        &mut *self.plugin
106    }
107
108    fn typed_plugin<P: Plugin + 'static>(&self) -> &P {
109        (&*self.plugin as &dyn std::any::Any)
110            .downcast_ref::<P>()
111            .expect("PluginHandle: plugin is not of the expected type")
112    }
113
114    pub fn typed_plugin_mut<P: Plugin + 'static>(&mut self) -> &mut P {
115        (&mut *self.plugin as &mut dyn std::any::Any)
116            .downcast_mut::<P>()
117            .expect("PluginHandle: plugin is not of the expected type")
118    }
119}
120
121/// User-registered plugins.
122#[derive(Clone, Default)]
123pub(crate) struct Plugins {
124    plugins: HashMap<std::any::TypeId, Arc<Mutex<PluginHandle>>>,
125    plugins_ordered: PluginsOrdered,
126}
127
128#[derive(Clone, Default)]
129pub(crate) struct PluginsOrdered(Vec<Arc<Mutex<PluginHandle>>>);
130
131impl PluginsOrdered {
132    fn for_each_dyn<F>(&self, mut f: F)
133    where
134        F: FnMut(&mut dyn Plugin),
135    {
136        for plugin in &self.0 {
137            let mut plugin = plugin.lock();
138            profiling::scope!("plugin", plugin.dyn_plugin_mut().debug_name());
139            f(plugin.dyn_plugin_mut());
140        }
141    }
142
143    pub fn on_begin_pass(&self, ctx: &Context) {
144        profiling::scope!("plugins", "on_begin_pass");
145        self.for_each_dyn(|p| {
146            p.on_begin_pass(ctx);
147        });
148    }
149
150    pub fn on_end_pass(&self, ctx: &Context) {
151        profiling::scope!("plugins", "on_end_pass");
152        self.for_each_dyn(|p| {
153            p.on_end_pass(ctx);
154        });
155    }
156
157    pub fn on_input(&self, input: &mut RawInput) {
158        profiling::scope!("plugins", "on_input");
159        self.for_each_dyn(|plugin| {
160            plugin.input_hook(input);
161        });
162    }
163
164    pub fn on_output(&self, output: &mut FullOutput) {
165        profiling::scope!("plugins", "on_output");
166        self.for_each_dyn(|plugin| {
167            plugin.output_hook(output);
168        });
169    }
170}
171
172impl Plugins {
173    pub fn ordered_plugins(&self) -> PluginsOrdered {
174        self.plugins_ordered.clone()
175    }
176
177    /// Remember to call [`Plugin::setup`] on the plugin after adding it.
178    ///
179    /// Will not add the plugin if a plugin of the same type already exists.
180    /// Returns `false` if the plugin was not added, `true` if it was added.
181    pub fn add(&mut self, handle: Arc<Mutex<PluginHandle>>) -> bool {
182        profiling::scope!("plugins", "add");
183
184        let type_id = handle.lock().plugin_type_id();
185
186        if self.plugins.contains_key(&type_id) {
187            return false;
188        }
189
190        self.plugins.insert(type_id, handle.clone());
191        self.plugins_ordered.0.push(handle);
192
193        true
194    }
195
196    pub fn get(&self, type_id: std::any::TypeId) -> Option<Arc<Mutex<PluginHandle>>> {
197        self.plugins.get(&type_id).cloned()
198    }
199}
200
201/// Generic event callback.
202pub type ContextCallback = Arc<dyn Fn(&Context) + Send + Sync>;
203
204#[derive(Default)]
205pub(crate) struct CallbackPlugin {
206    pub on_begin_plugins: Vec<(&'static str, ContextCallback)>,
207    pub on_end_plugins: Vec<(&'static str, ContextCallback)>,
208}
209
210impl Plugin for CallbackPlugin {
211    fn debug_name(&self) -> &'static str {
212        "CallbackPlugins"
213    }
214
215    fn on_begin_pass(&mut self, ctx: &Context) {
216        profiling::function_scope!();
217
218        for (_debug_name, cb) in &self.on_begin_plugins {
219            profiling::scope!(*_debug_name);
220            (cb)(ctx);
221        }
222    }
223
224    fn on_end_pass(&mut self, ctx: &Context) {
225        profiling::function_scope!();
226
227        for (_debug_name, cb) in &self.on_end_plugins {
228            profiling::scope!(*_debug_name);
229            (cb)(ctx);
230        }
231    }
232}