Skip to main content

ferrum_flow/
plugin.rs

1use std::collections::HashMap;
2
3use gpui::{
4    AnyElement, Bounds, Context, Div, KeyDownEvent, KeyUpEvent, MouseDownEvent, MouseMoveEvent,
5    MouseUpEvent, Pixels, Point, ScrollWheelEvent, Size, Styled, Window, div, px, rgb,
6};
7
8use crate::{
9    Edge, EdgeBuilder, EdgeId, FlowCanvas, FlowTheme, Graph, Node, NodeBuilder, NodeId,
10    NodeRenderer, Port, PortId, RendererRegistry, Viewport,
11    canvas::{
12        Command, CommandContext, HistoryProvider, Interaction, InteractionState, PortLayoutCache,
13    },
14    copied_subgraph::CopiedSubgraph,
15};
16
17mod sync;
18mod utils;
19
20pub use sync::SyncPlugin;
21
22pub use utils::{
23    cache_all_node_port_offset, cache_node_port_offset, cache_port_offset_with_edge,
24    cache_port_offset_with_port, is_edge_visible, is_node_visible, port_offset_cached,
25    primary_platform_modifier,
26};
27
28/// Chrome for [`RenderContext::node_card_shell`]. [`NodeCardVariant::Default`] and
29/// [`NodeCardVariant::UndefinedType`] read colors from [`RenderContext::theme`]; plugins may change
30/// them via [`InitPluginContext::theme`] / [`PluginContext::theme`].
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum NodeCardVariant {
33    /// Card from [`FlowTheme::node_card_background`] and border from [`FlowTheme::node_card_border`]
34    /// / [`FlowTheme::node_card_border_selected`] when `selected`.
35    Default,
36    /// Card from [`FlowTheme::undefined_node_background`] and [`FlowTheme::undefined_node_border`]
37    /// (no selection styling).
38    UndefinedType,
39
40    /// Geometry and border width only; set `.bg` / `.border_color` yourself.
41    Custom,
42}
43
44pub trait Plugin {
45    fn name(&self) -> &'static str;
46
47    fn setup(&mut self, _ctx: &mut InitPluginContext) {}
48
49    fn on_event(&mut self, _event: &FlowEvent, _ctx: &mut PluginContext) -> EventResult {
50        EventResult::Continue
51    }
52
53    fn render(&mut self, _ctx: &mut RenderContext) -> Option<AnyElement> {
54        None
55    }
56
57    fn priority(&self) -> i32 {
58        0
59    }
60
61    fn render_layer(&self) -> RenderLayer {
62        RenderLayer::Overlay
63    }
64}
65
66pub struct InitPluginContext<'a, 'b> {
67    pub graph: &'a mut Graph,
68    pub port_offset_cache: &'a mut PortLayoutCache,
69    pub viewport: &'a mut Viewport,
70    pub renderers: &'a mut RendererRegistry,
71    pub gpui_ctx: &'a Context<'b, FlowCanvas>,
72    /// Drawable size from the `window` passed to [`FlowCanvas::builder`] (`Window::viewport_size` when `build()` runs).
73    pub drawable_size: Size<Pixels>,
74    /// Canvas colors and strokes; mutate in [`Plugin::setup`](Plugin::setup) to customize chrome.
75    pub theme: &'a mut FlowTheme,
76    // pub notify: &'a mut dyn FnMut(),
77}
78
79impl<'a, 'b> InitPluginContext<'a, 'b> {
80    pub fn create_node(&self, node_type: &str) -> NodeBuilder {
81        self.graph.create_node(node_type)
82    }
83
84    pub fn create_edge(&self) -> EdgeBuilder {
85        self.graph.create_dege()
86    }
87
88    pub fn next_node_id(&self) -> NodeId {
89        self.graph.next_node_id()
90    }
91
92    pub fn next_port_id(&self) -> PortId {
93        self.graph.next_port_id()
94    }
95
96    pub fn next_edge_id(&self) -> EdgeId {
97        self.graph.next_edge_id()
98    }
99
100    pub fn add_node(&mut self, node: Node) {
101        self.graph.add_node(node);
102    }
103
104    pub fn add_port(&mut self, port: Port) {
105        self.graph.add_port(port);
106    }
107
108    pub fn get_node(&self, id: &NodeId) -> Option<&Node> {
109        self.graph.get_node(id)
110    }
111
112    pub fn get_node_mut(&mut self, id: &NodeId) -> Option<&mut Node> {
113        self.graph.get_node_mut(id)
114    }
115    pub fn remove_node(&mut self, id: &NodeId) {
116        self.graph.remove_node(id);
117    }
118    pub fn nodes(&self) -> &HashMap<NodeId, Node> {
119        self.graph.nodes()
120    }
121    pub fn node_order(&self) -> &Vec<NodeId> {
122        self.graph.node_order()
123    }
124
125    pub fn new_edge(&self) -> Edge {
126        self.graph.new_edge()
127    }
128
129    pub fn add_edge(&mut self, edge: Edge) {
130        self.graph.add_edge(edge);
131    }
132
133    pub fn remove_edge(&mut self, edge_id: EdgeId) {
134        self.graph.remove_edge(edge_id);
135    }
136
137    pub fn add_selected_node(&mut self, id: NodeId, shift: bool) {
138        self.graph.add_selected_node(id, shift);
139    }
140    pub fn clear_selected_node(&mut self) {
141        self.graph.clear_selected_node();
142    }
143    pub fn remove_selected_node(&mut self) -> bool {
144        self.graph.remove_selected_node()
145    }
146
147    pub fn add_selected_edge(&mut self, id: EdgeId, shift: bool) {
148        self.graph.add_selected_edge(id, shift);
149    }
150    pub fn clear_selected_edge(&mut self) {
151        self.graph.clear_selected_edge();
152    }
153    pub fn remove_selected_edge(&mut self) -> bool {
154        self.graph.remove_selected_edge()
155    }
156
157    pub fn selection_bounds(&self) -> Option<Bounds<Pixels>> {
158        self.graph.selection_bounds()
159    }
160
161    pub fn selected_nodes_with_positions(&self) -> HashMap<NodeId, Point<Pixels>> {
162        self.graph.selected_nodes_with_positions()
163    }
164
165    pub fn hit_node(&self, mouse: Point<Pixels>) -> Option<NodeId> {
166        self.graph.hit_node(mouse)
167    }
168
169    pub fn bring_node_to_front(&mut self, node_id: NodeId) {
170        self.graph.bring_node_to_front(node_id);
171    }
172
173    pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
174        self.viewport.world_to_screen(p)
175    }
176
177    pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
178        self.viewport.screen_to_world(p)
179    }
180
181    pub fn is_node_visible(&self, node_id: &NodeId) -> bool {
182        is_node_visible(self.graph, self.viewport, node_id)
183    }
184
185    pub fn is_edge_visible(&self, edge: &Edge) -> bool {
186        is_edge_visible(self.graph, self.viewport, edge)
187    }
188
189    pub fn port_offset_cached(&self, node_id: &NodeId, port_id: &PortId) -> Option<Point<Pixels>> {
190        port_offset_cached(self.port_offset_cache, node_id, port_id)
191    }
192
193    pub fn cache_port_offset_with_node(&mut self, node_ids: &Vec<NodeId>) {
194        for node_id in node_ids {
195            self.cache_node_port_offset(&node_id);
196        }
197    }
198
199    pub fn cache_port_offset_with_edge(&mut self, edge_id: &EdgeId) {
200        cache_port_offset_with_edge(self.graph, self.renderers, self.port_offset_cache, edge_id)
201    }
202
203    pub fn cache_port_offset_with_port(&mut self, port_id: &PortId) {
204        cache_port_offset_with_port(self.graph, self.renderers, self.port_offset_cache, port_id)
205    }
206
207    fn cache_node_port_offset(&mut self, node_id: &NodeId) {
208        cache_node_port_offset(self.graph, self.renderers, self.port_offset_cache, node_id);
209    }
210}
211
212pub struct PluginContext<'a> {
213    pub graph: &'a mut Graph,
214    pub port_offset_cache: &'a mut PortLayoutCache,
215    pub viewport: &'a mut Viewport,
216    pub(crate) interaction: &'a mut InteractionState,
217    pub renderers: &'a mut RendererRegistry,
218
219    sync_plugin: &'a mut Option<Box<dyn SyncPlugin + 'static>>,
220
221    pub history: &'a mut dyn HistoryProvider,
222    /// Shared node subgraph for [`crate::plugins::ClipboardPlugin`] and context menu.
223    pub(crate) clipboard_subgraph: &'a mut Option<CopiedSubgraph>,
224    /// Canvas theme; change during event handling and call [`PluginContext::notify`] to redraw.
225    pub theme: &'a mut FlowTheme,
226    emit: &'a mut dyn FnMut(FlowEvent),
227    notify: &'a mut dyn FnMut(),
228}
229
230pub enum EventResult {
231    Continue,
232    Stop,
233}
234
235impl<'a> PluginContext<'a> {
236    pub fn new(
237        graph: &'a mut Graph,
238        port_offset_cache: &'a mut PortLayoutCache,
239        viewport: &'a mut Viewport,
240        interaction: &'a mut InteractionState,
241        renderers: &'a mut RendererRegistry,
242        sync_plugin: &'a mut Option<Box<dyn SyncPlugin + 'static>>,
243        history: &'a mut dyn HistoryProvider,
244        clipboard_subgraph: &'a mut Option<CopiedSubgraph>,
245        theme: &'a mut FlowTheme,
246        emit: &'a mut dyn FnMut(FlowEvent),
247        notify: &'a mut dyn FnMut(),
248    ) -> Self {
249        Self {
250            graph,
251            port_offset_cache,
252            viewport,
253            interaction,
254            renderers,
255            sync_plugin,
256            history,
257            clipboard_subgraph,
258            theme,
259            emit,
260            notify,
261        }
262    }
263
264    pub fn start_interaction(&mut self, handler: impl Interaction + 'static) {
265        self.interaction.handler = Some(Box::new(handler));
266    }
267
268    pub fn cancel_interaction(&mut self) {
269        self.interaction.handler = None;
270    }
271
272    pub fn has_interaction(&self) -> bool {
273        self.interaction.handler.is_some()
274    }
275
276    /// Tell GPUI that this entity has changed and observers of it should be notified.
277    pub fn notify(&mut self) {
278        (self.notify)();
279    }
280
281    pub fn emit(&mut self, event: FlowEvent) {
282        (self.emit)(event);
283        self.notify();
284    }
285
286    pub fn has_sync_plugin(&self) -> bool {
287        self.sync_plugin.is_some()
288    }
289
290    pub fn execute_command(&mut self, command: impl Command + 'static) {
291        let mut ctx = CommandContext {
292            graph: self.graph,
293            port_offset_cache: self.port_offset_cache,
294            viewport: self.viewport,
295            renderers: self.renderers,
296            notify: self.notify,
297        };
298        if let Some(sync) = &mut self.sync_plugin {
299            let ops = command.to_ops(&mut ctx);
300            for op in ops.into_iter() {
301                sync.process_intent(op);
302            }
303            self.notify();
304        } else {
305            self.history.push(Box::new(command), &mut ctx);
306
307            self.notify();
308        }
309    }
310
311    pub fn undo(&mut self) {
312        if let Some(sync) = &mut self.sync_plugin {
313            sync.undo();
314        } else {
315            let mut ctx = CommandContext {
316                graph: self.graph,
317                port_offset_cache: self.port_offset_cache,
318                viewport: self.viewport,
319                renderers: self.renderers,
320                notify: self.notify,
321            };
322
323            self.history.undo(&mut ctx);
324
325            self.notify();
326        }
327    }
328
329    pub fn redo(&mut self) {
330        if let Some(sync) = &mut self.sync_plugin {
331            sync.redo();
332        } else {
333            let mut ctx = CommandContext {
334                graph: self.graph,
335                port_offset_cache: self.port_offset_cache,
336                viewport: self.viewport,
337                renderers: self.renderers,
338                notify: self.notify,
339            };
340
341            self.history.redo(&mut ctx);
342
343            self.notify();
344        }
345    }
346
347    pub fn create_node(&self, node_type: &str) -> NodeBuilder {
348        self.graph.create_node(node_type)
349    }
350
351    pub fn create_edge(&self) -> EdgeBuilder {
352        self.graph.create_dege()
353    }
354
355    pub fn next_node_id(&self) -> NodeId {
356        self.graph.next_node_id()
357    }
358
359    pub fn next_port_id(&self) -> PortId {
360        self.graph.next_port_id()
361    }
362
363    pub fn next_edge_id(&self) -> EdgeId {
364        self.graph.next_edge_id()
365    }
366
367    pub fn add_node(&mut self, node: Node) {
368        self.graph.add_node(node);
369    }
370
371    pub fn add_port(&mut self, port: Port) {
372        self.graph.add_port(port);
373    }
374
375    pub fn get_node(&self, id: &NodeId) -> Option<&Node> {
376        self.graph.get_node(id)
377    }
378
379    pub fn get_node_render(&self, id: &NodeId) -> Option<&dyn NodeRenderer> {
380        let node = self.get_node(id)?;
381
382        Some(self.renderers.get(&node.node_type))
383    }
384
385    pub fn get_node_mut(&mut self, id: &NodeId) -> Option<&mut Node> {
386        self.graph.get_node_mut(id)
387    }
388    pub fn remove_node(&mut self, id: &NodeId) {
389        self.graph.remove_node(id);
390        self.port_offset_cache.clear_node(id);
391    }
392    pub fn nodes(&self) -> &HashMap<NodeId, Node> {
393        self.graph.nodes()
394    }
395    pub fn node_order(&self) -> &Vec<NodeId> {
396        self.graph.node_order()
397    }
398
399    pub fn new_edge(&self) -> Edge {
400        self.graph.new_edge()
401    }
402
403    pub fn add_edge(&mut self, edge: Edge) {
404        self.graph.add_edge(edge);
405    }
406
407    pub fn remove_edge(&mut self, edge_id: EdgeId) {
408        self.graph.remove_edge(edge_id);
409    }
410
411    pub fn add_selected_node(&mut self, id: NodeId, shift: bool) {
412        self.graph.add_selected_node(id, shift);
413    }
414    pub fn clear_selected_node(&mut self) {
415        self.graph.clear_selected_node();
416    }
417    pub fn remove_selected_node(&mut self) -> bool {
418        self.graph.remove_selected_node()
419    }
420
421    pub fn add_selected_edge(&mut self, id: EdgeId, shift: bool) {
422        self.graph.add_selected_edge(id, shift);
423    }
424    pub fn clear_selected_edge(&mut self) {
425        self.graph.clear_selected_edge();
426    }
427    pub fn remove_selected_edge(&mut self) -> bool {
428        self.graph.remove_selected_edge()
429    }
430
431    pub fn selection_bounds(&self) -> Option<Bounds<Pixels>> {
432        self.graph.selection_bounds()
433    }
434
435    pub fn selected_nodes_with_positions(&self) -> HashMap<NodeId, Point<Pixels>> {
436        self.graph.selected_nodes_with_positions()
437    }
438
439    pub fn hit_node(&self, mouse: Point<Pixels>) -> Option<NodeId> {
440        self.graph.hit_node(mouse)
441    }
442
443    pub fn bring_node_to_front(&mut self, node_id: NodeId) {
444        self.graph.bring_node_to_front(node_id);
445    }
446
447    pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
448        self.viewport.world_to_screen(p)
449    }
450
451    pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
452        self.viewport.screen_to_world(p)
453    }
454
455    pub fn is_node_visible(&self, node_id: &NodeId) -> bool {
456        is_node_visible(self.graph, self.viewport, node_id)
457    }
458
459    pub fn is_edge_visible(&self, edge: &Edge) -> bool {
460        is_edge_visible(self.graph, self.viewport, edge)
461    }
462
463    pub fn port_offset_cached(&self, node_id: &NodeId, port_id: &PortId) -> Option<Point<Pixels>> {
464        port_offset_cached(self.port_offset_cache, node_id, port_id)
465    }
466
467    pub fn cache_all_node_port_offset(&mut self) {
468        cache_all_node_port_offset(self.graph, self.renderers, self.port_offset_cache);
469    }
470
471    pub fn cache_port_offset_with_node(&mut self, node_ids: &Vec<NodeId>) {
472        for node_id in node_ids {
473            self.cache_node_port_offset(&node_id);
474        }
475    }
476
477    pub fn cache_port_offset_with_edge(&mut self, edge_id: &EdgeId) {
478        cache_port_offset_with_edge(self.graph, self.renderers, self.port_offset_cache, edge_id)
479    }
480
481    pub fn cache_port_offset_with_port(&mut self, port_id: &PortId) {
482        cache_port_offset_with_port(self.graph, self.renderers, self.port_offset_cache, port_id)
483    }
484
485    fn cache_node_port_offset(&mut self, node_id: &NodeId) {
486        cache_node_port_offset(self.graph, self.renderers, self.port_offset_cache, node_id);
487    }
488}
489
490pub enum FlowEvent {
491    Input(InputEvent),
492    Custom(Box<dyn std::any::Any + Send>),
493}
494
495impl FlowEvent {
496    pub fn custom<T: 'static + Send>(event: T) -> Self {
497        FlowEvent::Custom(Box::new(event))
498    }
499    pub fn as_custom<T: 'static>(&self) -> Option<&T> {
500        match self {
501            FlowEvent::Custom(e) => e.downcast_ref::<T>(),
502            _ => None,
503        }
504    }
505}
506
507pub enum InputEvent {
508    KeyDown(KeyDownEvent),
509    KeyUp(KeyUpEvent),
510
511    MouseDown(MouseDownEvent),
512    MouseMove(MouseMoveEvent),
513    MouseUp(MouseUpEvent),
514
515    Wheel(ScrollWheelEvent),
516}
517
518pub struct RenderContext<'a> {
519    pub graph: &'a Graph,
520    pub port_offset_cache: &'a mut PortLayoutCache,
521    pub viewport: &'a Viewport,
522    pub renderers: &'a RendererRegistry,
523
524    pub window: &'a Window,
525
526    pub layer: RenderLayer,
527    /// Populated while dragging nodes when alignment matches other nodes.
528    /// Active canvas theme (from [`FlowCanvas::theme`](crate::canvas::FlowCanvas::theme)).
529    pub theme: &'a FlowTheme,
530}
531
532impl<'a> RenderContext<'a> {
533    pub fn new(
534        graph: &'a mut Graph,
535        port_offset_cache: &'a mut PortLayoutCache,
536        viewport: &'a Viewport,
537        renderers: &'a RendererRegistry,
538        window: &'a Window,
539        layer: RenderLayer,
540        theme: &'a FlowTheme,
541    ) -> Self {
542        Self {
543            graph,
544            port_offset_cache,
545            viewport,
546            renderers,
547            window,
548            layer,
549            theme,
550        }
551    }
552
553    pub fn create_node(&self, node_type: &str) -> NodeBuilder {
554        self.graph.create_node(node_type)
555    }
556
557    pub fn create_edge(&self) -> EdgeBuilder {
558        self.graph.create_dege()
559    }
560
561    pub fn next_node_id(&self) -> NodeId {
562        self.graph.next_node_id()
563    }
564
565    pub fn next_port_id(&self) -> PortId {
566        self.graph.next_port_id()
567    }
568
569    pub fn next_edge_id(&self) -> EdgeId {
570        self.graph.next_edge_id()
571    }
572
573    pub fn get_node(&self, id: &NodeId) -> Option<&Node> {
574        self.graph.get_node(id)
575    }
576
577    pub fn get_node_render(&self, id: &NodeId) -> Option<&dyn NodeRenderer> {
578        let node = self.get_node(id)?;
579
580        Some(self.renderers.get(&node.node_type))
581    }
582
583    pub fn nodes(&self) -> &HashMap<NodeId, Node> {
584        self.graph.nodes()
585    }
586    pub fn node_order(&self) -> &Vec<NodeId> {
587        self.graph.node_order()
588    }
589
590    pub fn new_edge(&self) -> Edge {
591        self.graph.new_edge()
592    }
593
594    pub fn selection_bounds(&self) -> Option<Bounds<Pixels>> {
595        self.graph.selection_bounds()
596    }
597
598    pub fn selected_nodes_with_positions(&self) -> HashMap<NodeId, Point<Pixels>> {
599        self.graph.selected_nodes_with_positions()
600    }
601
602    pub fn hit_node(&self, mouse: Point<Pixels>) -> Option<NodeId> {
603        self.graph.hit_node(mouse)
604    }
605
606    pub fn world_to_screen(&self, p: Point<Pixels>) -> Point<Pixels> {
607        self.viewport.world_to_screen(p)
608    }
609
610    /// Absolute-positioned node card shell: screen origin, zoom-scaled size, rounded rect and border.
611    ///
612    /// Chain `.child(...)` for the inner body, then `.into_any()` (see [`gpui::Element`]).
613    pub fn node_card_shell(&self, node: &Node, selected: bool, variant: NodeCardVariant) -> Div {
614        let screen = self.world_to_screen(node.point());
615        let z = self.viewport.zoom;
616        let base = div()
617            .absolute()
618            .left(screen.x)
619            .top(screen.y)
620            .w(node.size.width * z)
621            .h(node.size.height * z)
622            .rounded(px(6.0))
623            .border(px(1.5));
624        let t = self.theme;
625        match variant {
626            NodeCardVariant::Default => {
627                base.bg(rgb(t.node_card_background))
628                    .border_color(rgb(if selected {
629                        t.node_card_border_selected
630                    } else {
631                        t.node_card_border
632                    }))
633            }
634            NodeCardVariant::UndefinedType => base
635                .bg(rgb(t.undefined_node_background))
636                .border_color(rgb(t.undefined_node_border)),
637            NodeCardVariant::Custom => base,
638        }
639    }
640
641    pub fn screen_to_world(&self, p: Point<Pixels>) -> Point<Pixels> {
642        self.viewport.screen_to_world(p)
643    }
644
645    pub fn is_node_visible(&self, node_id: &NodeId) -> bool {
646        is_node_visible(self.graph, self.viewport, node_id)
647    }
648
649    pub fn is_edge_visible(&self, edge: &Edge) -> bool {
650        is_edge_visible(self.graph, self.viewport, edge)
651    }
652
653    pub fn port_offset_cached(&self, node_id: &NodeId, port_id: &PortId) -> Option<Point<Pixels>> {
654        port_offset_cached(self.port_offset_cache, node_id, port_id)
655    }
656
657    pub fn cache_all_node_port_offset(&mut self) {
658        for (node_id, _) in self.graph.nodes() {
659            self.cache_node_port_offset(node_id);
660        }
661    }
662
663    pub fn cache_port_offset_with_nodes(&mut self, node_ids: &Vec<NodeId>) {
664        for node_id in node_ids {
665            self.cache_node_port_offset(node_id);
666        }
667    }
668
669    pub fn cache_port_offset_with_edge(&mut self, edge_id: &EdgeId) {
670        cache_port_offset_with_edge(self.graph, self.renderers, self.port_offset_cache, edge_id)
671    }
672
673    pub fn cache_port_offset_with_port(&mut self, port_id: &PortId) {
674        cache_port_offset_with_port(self.graph, self.renderers, self.port_offset_cache, port_id)
675    }
676
677    fn cache_node_port_offset(&mut self, node_id: &NodeId) {
678        cache_node_port_offset(self.graph, self.renderers, self.port_offset_cache, node_id);
679    }
680}
681
682#[derive(Debug, PartialEq, Eq, Clone, Copy)]
683pub enum RenderLayer {
684    Background,
685    Edges,
686    Nodes,
687    Selection,
688    Interaction,
689    Overlay,
690}
691
692impl RenderLayer {
693    pub const ALL: [RenderLayer; 6] = [
694        RenderLayer::Background,
695        RenderLayer::Edges,
696        RenderLayer::Nodes,
697        RenderLayer::Selection,
698        RenderLayer::Interaction,
699        RenderLayer::Overlay,
700    ];
701    pub fn index(self) -> usize {
702        match self {
703            RenderLayer::Background => 0,
704            RenderLayer::Edges => 1,
705            RenderLayer::Nodes => 2,
706            RenderLayer::Selection => 3,
707            RenderLayer::Interaction => 4,
708            RenderLayer::Overlay => 5,
709        }
710    }
711}
712
713pub struct PluginRegistry {
714    pub plugins: Vec<Box<dyn Plugin>>,
715}
716
717impl PluginRegistry {
718    pub fn new() -> Self {
719        Self { plugins: vec![] }
720    }
721
722    pub fn add(mut self, plugin: impl Plugin + 'static) -> Self {
723        self.plugins.push(Box::new(plugin));
724        self
725    }
726}