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