Skip to main content

ferrum_flow/plugins/port/
interaction.rs

1use gpui::{Element, Pixels, Point, canvas, rgb};
2
3use crate::{
4    NodeId, PortId,
5    canvas::Interaction,
6    plugin::{FlowEvent, InputEvent, Plugin},
7    plugins::port::{edge_bezier, port_screen_bounds, port_screen_position},
8};
9
10use super::command::CreateEdge;
11
12pub struct PortInteractionPlugin;
13
14impl PortInteractionPlugin {
15    pub fn new() -> Self {
16        Self {}
17    }
18}
19
20impl Plugin for PortInteractionPlugin {
21    fn name(&self) -> &'static str {
22        "port_interaction"
23    }
24    fn setup(&mut self, _ctx: &mut crate::plugin::InitPluginContext) {}
25    fn on_event(
26        &mut self,
27        event: &FlowEvent,
28        ctx: &mut crate::plugin::PluginContext,
29    ) -> crate::plugin::EventResult {
30        if let FlowEvent::Input(InputEvent::MouseDown(ev)) = event {
31            let mouse_world = ctx.viewport.screen_to_world(ev.position);
32            if let Some((node_id, port_id)) = ctx
33                .graph
34                .ports
35                .iter()
36                .find(|(id, _)| match port_screen_bounds(**id, ctx) {
37                    Some(b) => b.contains(&mouse_world),
38                    None => false,
39                })
40                .map(|(_, p)| (p.node_id, p.id))
41            {
42                ctx.start_interaction(PortConnecting {
43                    node_id,
44                    port_id,
45                    mouse: mouse_world,
46                    moving: false,
47                });
48                return crate::plugin::EventResult::Stop;
49            }
50        }
51
52        crate::plugin::EventResult::Continue
53    }
54    fn priority(&self) -> i32 {
55        125
56    }
57    fn render(&mut self, _context: &mut crate::RenderContext) -> Option<gpui::AnyElement> {
58        None
59    }
60    fn render_layer(&self) -> crate::plugin::RenderLayer {
61        crate::plugin::RenderLayer::Interaction
62    }
63}
64
65struct PortConnecting {
66    node_id: NodeId,
67    port_id: PortId,
68    moving: bool,
69    mouse: Point<Pixels>,
70}
71
72impl PortConnecting {}
73
74impl Interaction for PortConnecting {
75    fn on_mouse_move(
76        &mut self,
77        event: &gpui::MouseMoveEvent,
78        ctx: &mut crate::plugin::PluginContext,
79    ) -> crate::canvas::InteractionResult {
80        // let mouse_world = ctx.viewport.world_to_screen(event.position);
81        self.mouse = event.position;
82        self.moving = true;
83        ctx.notify();
84        crate::canvas::InteractionResult::Continue
85    }
86    fn on_mouse_up(
87        &mut self,
88        ev: &gpui::MouseUpEvent,
89        ctx: &mut crate::plugin::PluginContext,
90    ) -> crate::canvas::InteractionResult {
91        let mouse_world = ctx.viewport.screen_to_world(ev.position);
92        if let Some((node_id, port_id)) = ctx
93            .graph
94            .ports
95            .iter()
96            .find(|(id, _)| match port_screen_bounds(**id, ctx) {
97                Some(b) => b.contains(&mouse_world),
98                None => false,
99            })
100            .map(|(_, p)| (p.node_id, p.id))
101        {
102            if node_id == self.node_id {
103                ctx.cancel_interaction();
104                ctx.notify();
105                return crate::canvas::InteractionResult::End;
106            }
107            let connecting_port = &ctx.graph.ports[&self.port_id];
108            let target_port = &ctx.graph.ports[&port_id];
109            if connecting_port.kind == target_port.kind {
110                ctx.cancel_interaction();
111                ctx.notify();
112                return crate::canvas::InteractionResult::End;
113            }
114            let edge = ctx
115                .graph
116                .new_edge()
117                .source(self.port_id.clone())
118                .target(port_id);
119
120            ctx.execute_command(CreateEdge::new(edge));
121        }
122        ctx.cancel_interaction();
123        crate::canvas::InteractionResult::End
124    }
125    fn render(&self, ctx: &mut crate::RenderContext) -> Option<gpui::AnyElement> {
126        if !self.moving {
127            return None;
128        }
129
130        let mouse: Point<Pixels> = self.mouse;
131        let start = port_screen_position(self.port_id, &ctx)?;
132        Some(
133            canvas(
134                |_, _, _| {},
135                move |_, _, win, _| {
136                    if let Ok(line) = edge_bezier(start, mouse) {
137                        win.paint_path(line, rgb(0xb1b1b8));
138                    }
139                },
140            )
141            .into_any(),
142        )
143    }
144}