ferrum_flow/plugins/node/
mod.rs1mod 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}