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