1use crate::{Context, FullOutput, RawInput, Ui};
2use ahash::HashMap;
3use alloc::sync::Arc;
4use epaint::mutex::{Mutex, MutexGuard};
5
6#[expect(unused_variables)]
13pub trait Plugin: Send + Sync + core::any::Any + 'static {
14 fn debug_name(&self) -> &'static str;
18
19 fn setup(&mut self, ctx: &Context) {}
23
24 fn on_exit(&mut self, ctx: &Context) {}
29
30 fn on_begin_pass(&mut self, ui: &mut Ui) {}
34
35 fn on_end_pass(&mut self, ui: &mut Ui) {}
39
40 fn input_hook(&mut self, ctx: &Context, input: &mut RawInput) {}
45
46 fn output_hook(&mut self, ctx: &Context, output: &mut FullOutput) {}
51
52 #[cfg(debug_assertions)]
57 fn on_widget_under_pointer(&mut self, ctx: &Context, widget: &crate::WidgetRect) {}
58}
59
60pub(crate) struct PluginHandle {
61 plugin: Box<dyn Plugin>,
62}
63
64pub struct TypedPluginHandle<P: Plugin> {
68 handle: Arc<Mutex<PluginHandle>>,
69 _type: core::marker::PhantomData<P>,
70}
71
72impl<P: Plugin> TypedPluginHandle<P> {
73 pub(crate) fn new(handle: Arc<Mutex<PluginHandle>>) -> Self {
74 Self {
75 handle,
76 _type: core::marker::PhantomData,
77 }
78 }
79
80 pub fn lock(&self) -> TypedPluginGuard<'_, P> {
84 TypedPluginGuard {
85 guard: self.handle.lock(),
86 _type: core::marker::PhantomData,
87 }
88 }
89}
90
91pub struct TypedPluginGuard<'a, P: Plugin> {
93 guard: MutexGuard<'a, PluginHandle>,
94 _type: core::marker::PhantomData<P>,
95}
96
97impl<P: Plugin> TypedPluginGuard<'_, P> {}
98
99impl<P: Plugin> core::ops::Deref for TypedPluginGuard<'_, P> {
100 type Target = P;
101
102 fn deref(&self) -> &Self::Target {
103 self.guard.typed_plugin()
104 }
105}
106
107impl<P: Plugin> core::ops::DerefMut for TypedPluginGuard<'_, P> {
108 fn deref_mut(&mut self) -> &mut Self::Target {
109 self.guard.typed_plugin_mut()
110 }
111}
112
113impl PluginHandle {
114 pub fn new<P: Plugin>(plugin: P) -> Arc<Mutex<Self>> {
115 Arc::new(Mutex::new(Self {
116 plugin: Box::new(plugin),
117 }))
118 }
119
120 fn plugin_type_id(&self) -> core::any::TypeId {
121 (*self.plugin).type_id()
122 }
123
124 pub fn dyn_plugin_mut(&mut self) -> &mut dyn Plugin {
125 &mut *self.plugin
126 }
127
128 fn typed_plugin<P: Plugin + 'static>(&self) -> &P {
129 (self.plugin.as_ref() as &dyn core::any::Any)
130 .downcast_ref::<P>()
131 .expect("PluginHandle: plugin is not of the expected type")
132 }
133
134 pub fn typed_plugin_mut<P: Plugin + 'static>(&mut self) -> &mut P {
135 (self.plugin.as_mut() as &mut dyn core::any::Any)
136 .downcast_mut::<P>()
137 .expect("PluginHandle: plugin is not of the expected type")
138 }
139}
140
141#[derive(Clone, Default)]
143pub(crate) struct Plugins {
144 plugins: HashMap<core::any::TypeId, Arc<Mutex<PluginHandle>>>,
145 plugins_ordered: PluginsOrdered,
146}
147
148#[derive(Clone, Default)]
149pub(crate) struct PluginsOrdered(Vec<Arc<Mutex<PluginHandle>>>);
150
151impl PluginsOrdered {
152 fn for_each_dyn<F>(&self, mut f: F)
153 where
154 F: FnMut(&mut dyn Plugin),
155 {
156 for plugin in &self.0 {
157 let mut plugin = plugin.lock();
158 profiling::scope!("plugin", plugin.dyn_plugin_mut().debug_name());
159 f(plugin.dyn_plugin_mut());
160 }
161 }
162
163 pub fn on_begin_pass(&self, ui: &mut Ui) {
164 profiling::scope!("plugins", "on_begin_pass");
165 self.for_each_dyn(|p| {
166 p.on_begin_pass(ui);
167 });
168 }
169
170 pub fn on_end_pass(&self, ui: &mut Ui) {
171 profiling::scope!("plugins", "on_end_pass");
172 self.for_each_dyn(|p| {
173 p.on_end_pass(ui);
174 });
175 }
176
177 pub fn on_input(&self, ctx: &Context, input: &mut RawInput) {
178 profiling::scope!("plugins", "on_input");
179 self.for_each_dyn(|plugin| {
180 plugin.input_hook(ctx, input);
181 });
182 }
183
184 pub fn on_output(&self, ctx: &Context, output: &mut FullOutput) {
185 profiling::scope!("plugins", "on_output");
186 self.for_each_dyn(|plugin| {
187 plugin.output_hook(ctx, output);
188 });
189 }
190
191 pub fn on_exit(&self, ctx: &Context) {
192 profiling::scope!("plugins", "on_exit");
193 self.for_each_dyn(|plugin| {
194 plugin.on_exit(ctx);
195 });
196 }
197
198 #[cfg(debug_assertions)]
199 pub fn on_widget_under_pointer(&self, ctx: &Context, widget: &crate::WidgetRect) {
200 profiling::scope!("plugins", "on_widget_under_pointer");
201 self.for_each_dyn(|plugin| {
202 plugin.on_widget_under_pointer(ctx, widget);
203 });
204 }
205}
206
207impl Plugins {
208 pub fn ordered_plugins(&self) -> PluginsOrdered {
209 self.plugins_ordered.clone()
210 }
211
212 pub fn add(&mut self, handle: Arc<Mutex<PluginHandle>>) -> bool {
217 profiling::scope!("plugins", "add");
218
219 let type_id = handle.lock().plugin_type_id();
220
221 if self.plugins.contains_key(&type_id) {
222 return false;
223 }
224
225 self.plugins.insert(type_id, Arc::clone(&handle));
226 self.plugins_ordered.0.push(handle);
227
228 true
229 }
230
231 pub fn get(&self, type_id: core::any::TypeId) -> Option<Arc<Mutex<PluginHandle>>> {
232 self.plugins.get(&type_id).cloned()
233 }
234}
235
236pub type ContextCallback = Arc<dyn Fn(&mut Ui) + Send + Sync>;
238
239#[derive(Default)]
240pub(crate) struct CallbackPlugin {
241 pub on_begin_plugins: Vec<(&'static str, ContextCallback)>,
242 pub on_end_plugins: Vec<(&'static str, ContextCallback)>,
243}
244
245impl Plugin for CallbackPlugin {
246 fn debug_name(&self) -> &'static str {
247 "CallbackPlugins"
248 }
249
250 fn on_begin_pass(&mut self, ui: &mut Ui) {
251 profiling::function_scope!();
252
253 for (_debug_name, cb) in &self.on_begin_plugins {
254 profiling::scope!("on_begin_pass", *_debug_name);
255 (cb)(ui);
256 }
257 }
258
259 fn on_end_pass(&mut self, ui: &mut Ui) {
260 profiling::function_scope!();
261
262 for (_debug_name, cb) in &self.on_end_plugins {
263 profiling::scope!("on_end_pass", *_debug_name);
264 (cb)(ui);
265 }
266 }
267}