ferrum_flow/plugins/port/
interaction.rs1use 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 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}