Skip to main content

ferrum_flow/plugins/node/
mod.rs

1mod command;
2mod interaction;
3mod renderer;
4
5pub use renderer::{NodeRenderer, RendererRegistry};
6
7use gpui::{Element, ParentElement, Styled as _, div, px, rgb};
8pub use interaction::NodeInteractionPlugin;
9
10use crate::{NodeId, RenderContext, plugin::Plugin, plugins::port::port_screen_position};
11
12pub struct NodePlugin {
13    renderers: RendererRegistry,
14}
15
16impl NodePlugin {
17    pub fn new() -> Self {
18        Self {
19            renderers: RendererRegistry::new(),
20        }
21    }
22    pub fn register_node<R>(mut self, name: impl Into<String>, renderer: R) -> Self
23    where
24        R: NodeRenderer + 'static,
25    {
26        self.renderers.register(name, renderer);
27        self
28    }
29
30    fn render_ports(&self, node_id: &NodeId, ctx: &RenderContext) -> Option<gpui::AnyElement> {
31        let ports: Vec<_> = ctx
32            .graph
33            .ports
34            .iter()
35            .filter(|(_, port)| port.node_id == *node_id)
36            .filter_map(|(id, _)| {
37                let position = port_screen_position(*id, &ctx)?;
38
39                Some(
40                    div()
41                        .absolute()
42                        .left(position.x - px(6.0 * ctx.viewport.zoom))
43                        .top(position.y - px(6.0 * ctx.viewport.zoom))
44                        .w(px(12.0 * ctx.viewport.zoom))
45                        .h(px(12.0 * ctx.viewport.zoom))
46                        .rounded_full()
47                        .bg(rgb(0x1A192B)),
48                )
49            })
50            .collect();
51
52        Some(div().children(ports).into_any())
53    }
54}
55
56impl Plugin for NodePlugin {
57    fn name(&self) -> &'static str {
58        "node"
59    }
60    fn setup(&mut self, _ctx: &mut crate::plugin::InitPluginContext) {}
61    fn on_event(
62        &mut self,
63        _event: &crate::plugin::FlowEvent,
64        _context: &mut crate::plugin::PluginContext,
65    ) -> crate::plugin::EventResult {
66        crate::plugin::EventResult::Continue
67    }
68    fn priority(&self) -> i32 {
69        60
70    }
71    fn render_layer(&self) -> crate::plugin::RenderLayer {
72        crate::plugin::RenderLayer::Nodes
73    }
74    fn render(&mut self, ctx: &mut RenderContext) -> Option<gpui::AnyElement> {
75        let list = ctx
76            .graph
77            .node_order()
78            .iter()
79            .filter(|node_id| {
80                let Some(node) = ctx.graph.get_node(node_id) else {
81                    return false;
82                };
83                let Some(window_bounds) = ctx.viewport.window_bounds else {
84                    return false;
85                };
86                let screen = ctx.viewport.world_to_screen(node.point());
87
88                screen.x + node.size.width * ctx.viewport.zoom > px(0.0)
89                    && screen.x < window_bounds.size.width
90                    && screen.y + node.size.height * ctx.viewport.zoom > px(0.0)
91                    && screen.y < window_bounds.size.height
92            })
93            .filter_map(|node_id| {
94                let node = ctx.graph.nodes().get(node_id)?;
95                let render = self.renderers.get(&node.node_type);
96
97                match self.render_ports(node_id, &ctx) {
98                    Some(ports) => Some(
99                        div()
100                            .child(render.render(node, ctx))
101                            .child(ports)
102                            .into_any(),
103                    ),
104                    None => Some(render.render(node, ctx)),
105                }
106            });
107
108        Some(div().children(list).into_any())
109    }
110}