Skip to content

Commit ada8e87

Browse files
committed
feat: refactor context menu callbacks to use ScreenPosition, enhance port interaction APIs, and simplify node constructor parameters
1 parent a471b4b commit ada8e87

File tree

16 files changed

+172
-168
lines changed

16 files changed

+172
-168
lines changed

packages/demo/lib/examples/basics/callbacks.dart

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,33 +267,33 @@ class _CallbacksExampleState extends State<CallbacksExample> {
267267

268268
// ===== PORT EVENTS =====
269269
port: PortEvents(
270-
onTap: (node, port, isOutput) {
270+
onTap: (node, port) {
271271
_addEvent(
272-
'Tapped ${isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
272+
'Tapped ${port.isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
273273
EventType.interaction,
274274
);
275275
},
276-
onDoubleTap: (node, port, isOutput) {
276+
onDoubleTap: (node, port) {
277277
_addEvent(
278-
'Double-tapped ${isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
278+
'Double-tapped ${port.isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
279279
EventType.interaction,
280280
);
281281
},
282-
onMouseEnter: (node, port, isOutput) {
282+
onMouseEnter: (node, port) {
283283
_addEvent(
284-
'Mouse entered ${isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
284+
'Mouse entered ${port.isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
285285
EventType.interaction,
286286
);
287287
},
288-
onMouseLeave: (node, port, isOutput) {
288+
onMouseLeave: (node, port) {
289289
_addEvent(
290-
'Mouse left ${isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
290+
'Mouse left ${port.isOutput ? 'output' : 'input'} port ${port.id} on ${node.id}',
291291
EventType.interaction,
292292
);
293293
},
294-
onContextMenu: (node, port, isOutput, position) {
294+
onContextMenu: (node, port, position) {
295295
_addEvent(
296-
'Context menu on ${isOutput ? 'output' : 'input'} port ${port.id} at ${position.dx.toInt()},${position.dy.toInt()}',
296+
'Context menu on ${port.isOutput ? 'output' : 'input'} port ${port.id} at ${position.dx.toInt()},${position.dy.toInt()}',
297297
EventType.interaction,
298298
);
299299
},
@@ -351,15 +351,18 @@ class _CallbacksExampleState extends State<CallbacksExample> {
351351
EventType.interaction,
352352
);
353353
},
354-
onConnectStart: (nodeId, portId, isOutput) {
354+
onConnectStart: (sourceNode, sourcePort) {
355355
_addEvent(
356-
'Started connecting from $nodeId:$portId',
356+
'Started connecting from ${sourceNode.id}:${sourcePort.id}',
357357
EventType.connection,
358358
);
359359
},
360-
onConnectEnd: (success) {
360+
onConnectEnd: (targetNode, targetPort) {
361+
final success = targetNode != null && targetPort != null;
361362
_addEvent(
362-
success ? 'Connection completed' : 'Connection cancelled',
363+
success
364+
? 'Connection completed to ${targetNode.id}:${targetPort.id}'
365+
: 'Connection cancelled',
363366
EventType.connection,
364367
);
365368
},

packages/vyuh_node_flow/lib/graph/api/connection_api.dart

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -747,15 +747,19 @@ extension ConnectionApi<T> on NodeFlowController<T> {
747747
return validationResult;
748748
}
749749

750+
// Get node and port for callbacks
751+
final node = getNode(nodeId);
752+
final port = node?.allPorts.where((p) => p.id == portId).firstOrNull;
753+
750754
// Fire connection start event
751-
events.connection?.onConnectStart?.call(nodeId, portId, isOutput);
755+
if (node != null && port != null) {
756+
events.connection?.onConnectStart?.call(node, port);
757+
}
752758

753759
// Check if we need to remove existing connections
754760
// (for ports that don't allow multiple connections)
755-
final node = getNode(nodeId);
756-
if (node != null) {
757-
final port = node.allPorts.where((p) => p.id == portId).firstOrNull;
758-
if (port != null && !port.multiConnections) {
761+
if (node != null && port != null) {
762+
if (!port.multiConnections) {
759763
// Remove existing connections from this port
760764
final connectionsToRemove = _connections
761765
.where(
@@ -1097,7 +1101,7 @@ extension ConnectionApi<T> on NodeFlowController<T> {
10971101
}) {
10981102
final temp = interaction.temporaryConnection.value;
10991103
if (temp == null) {
1100-
events.connection?.onConnectEnd?.call(false);
1104+
events.connection?.onConnectEnd?.call(null, null);
11011105
return null;
11021106
}
11031107

@@ -1204,7 +1208,12 @@ extension ConnectionApi<T> on NodeFlowController<T> {
12041208
return connection;
12051209
});
12061210

1207-
events.connection?.onConnectEnd?.call(true);
1211+
// Fire connection end event with the target node and port that the user dropped on
1212+
final droppedOnNode = _nodes[targetNodeId];
1213+
final droppedOnPort = droppedOnNode?.allPorts
1214+
.where((p) => p.id == targetPortId)
1215+
.firstOrNull;
1216+
events.connection?.onConnectEnd?.call(droppedOnNode, droppedOnPort);
12081217

12091218
return createdConnection;
12101219
}
@@ -1237,6 +1246,6 @@ extension ConnectionApi<T> on NodeFlowController<T> {
12371246
interaction.panEnabled.value = true;
12381247
});
12391248

1240-
events.connection?.onConnectEnd?.call(false);
1249+
events.connection?.onConnectEnd?.call(null, null);
12411250
}
12421251
}

packages/vyuh_node_flow/lib/graph/layers/nodes_layer.dart

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import '../../nodes/node.dart';
66
import '../../nodes/node_widget.dart';
77
import '../../ports/port_widget.dart';
88
import '../../shared/unbounded_widgets.dart';
9+
import '../coordinates.dart';
910
import '../node_flow_controller.dart';
1011

1112
/// Nodes layer widget that renders all nodes with optimized reactivity.
@@ -57,15 +58,11 @@ class NodesLayer<T> extends StatelessWidget {
5758
PortBuilder<T>? portBuilder,
5859
void Function(Node<T> node)? onNodeTap,
5960
void Function(Node<T> node)? onNodeDoubleTap,
60-
void Function(Node<T> node, Offset globalPosition)? onNodeContextMenu,
61+
void Function(Node<T> node, ScreenPosition screenPosition)?
62+
onNodeContextMenu,
6163
void Function(Node<T> node)? onNodeMouseEnter,
6264
void Function(Node<T> node)? onNodeMouseLeave,
63-
void Function(
64-
String nodeId,
65-
String portId,
66-
bool isOutput,
67-
Offset globalPosition,
68-
)?
65+
void Function(String nodeId, String portId, ScreenPosition screenPosition)?
6966
onPortContextMenu,
7067
double portSnapDistance = 8.0,
7168
}) {
@@ -99,15 +96,11 @@ class NodesLayer<T> extends StatelessWidget {
9996
PortBuilder<T>? portBuilder,
10097
void Function(Node<T> node)? onNodeTap,
10198
void Function(Node<T> node)? onNodeDoubleTap,
102-
void Function(Node<T> node, Offset globalPosition)? onNodeContextMenu,
99+
void Function(Node<T> node, ScreenPosition screenPosition)?
100+
onNodeContextMenu,
103101
void Function(Node<T> node)? onNodeMouseEnter,
104102
void Function(Node<T> node)? onNodeMouseLeave,
105-
void Function(
106-
String nodeId,
107-
String portId,
108-
bool isOutput,
109-
Offset globalPosition,
110-
)?
103+
void Function(String nodeId, String portId, ScreenPosition screenPosition)?
111104
onPortContextMenu,
112105
double portSnapDistance = 8.0,
113106
}) {
@@ -141,15 +134,11 @@ class NodesLayer<T> extends StatelessWidget {
141134
PortBuilder<T>? portBuilder,
142135
void Function(Node<T> node)? onNodeTap,
143136
void Function(Node<T> node)? onNodeDoubleTap,
144-
void Function(Node<T> node, Offset globalPosition)? onNodeContextMenu,
137+
void Function(Node<T> node, ScreenPosition screenPosition)?
138+
onNodeContextMenu,
145139
void Function(Node<T> node)? onNodeMouseEnter,
146140
void Function(Node<T> node)? onNodeMouseLeave,
147-
void Function(
148-
String nodeId,
149-
String portId,
150-
bool isOutput,
151-
Offset globalPosition,
152-
)?
141+
void Function(String nodeId, String portId, ScreenPosition screenPosition)?
153142
onPortContextMenu,
154143
double portSnapDistance = 8.0,
155144
}) {
@@ -200,7 +189,9 @@ class NodesLayer<T> extends StatelessWidget {
200189
final void Function(Node<T> node)? onNodeDoubleTap;
201190

202191
/// Callback invoked when a node is right-clicked (context menu).
203-
final void Function(Node<T> node, Offset globalPosition)? onNodeContextMenu;
192+
/// The [screenPosition] is in screen/global coordinates for menu positioning.
193+
final void Function(Node<T> node, ScreenPosition screenPosition)?
194+
onNodeContextMenu;
204195

205196
/// Callback invoked when mouse enters a node.
206197
final void Function(Node<T> node)? onNodeMouseEnter;
@@ -209,11 +200,11 @@ class NodesLayer<T> extends StatelessWidget {
209200
final void Function(Node<T> node)? onNodeMouseLeave;
210201

211202
/// Callback invoked when a port is right-clicked (context menu).
203+
/// The [screenPosition] is in screen/global coordinates for menu positioning.
212204
final void Function(
213205
String nodeId,
214206
String portId,
215-
bool isOutput,
216-
Offset globalPosition,
207+
ScreenPosition screenPosition,
217208
)?
218209
onPortContextMenu;
219210

packages/vyuh_node_flow/lib/graph/node_flow_editor_hit_testing.dart

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
201201
// positioning with showMenu or similar popup APIs
202202
widget.controller.events.connection?.onContextMenu?.call(
203203
connection,
204-
event.position,
204+
ScreenPosition(event.position),
205205
);
206206
}
207207
break;
@@ -211,7 +211,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
211211
ScreenPosition(event.localPosition),
212212
);
213213
widget.controller.events.viewport?.onCanvasContextMenu?.call(
214-
graphPosition.offset,
214+
graphPosition,
215215
);
216216
break;
217217
}
@@ -273,11 +273,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
273273
if (node != null) {
274274
final port = _findPort(node, hitResult.portId!);
275275
if (port != null) {
276-
widget.controller.events.port?.onTap?.call(
277-
node,
278-
port,
279-
hitResult.isOutput ?? false,
280-
);
276+
widget.controller.events.port?.onTap?.call(node, port);
281277
}
282278
}
283279
break;
@@ -300,9 +296,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
300296
final graphPosition = widget.controller.viewport.toGraph(
301297
ScreenPosition(position),
302298
);
303-
widget.controller.events.viewport?.onCanvasTap?.call(
304-
graphPosition.offset,
305-
);
299+
widget.controller.events.viewport?.onCanvasTap?.call(graphPosition);
306300
}
307301
break;
308302
}
@@ -322,11 +316,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
322316
if (node != null) {
323317
final port = _findPort(node, hitResult.portId!);
324318
if (port != null) {
325-
widget.controller.events.port?.onDoubleTap?.call(
326-
node,
327-
port,
328-
hitResult.isOutput ?? false,
329-
);
319+
widget.controller.events.port?.onDoubleTap?.call(node, port);
330320
}
331321
}
332322
break;
@@ -351,7 +341,7 @@ extension _HitTestingExtension<T> on _NodeFlowEditorState<T> {
351341
ScreenPosition(position),
352342
);
353343
widget.controller.events.viewport?.onCanvasDoubleTap?.call(
354-
graphPosition.offset,
344+
graphPosition,
355345
);
356346
break;
357347
}

packages/vyuh_node_flow/lib/graph/node_flow_editor_widget_handlers.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ extension _WidgetGestureHandlers<T> on _NodeFlowEditorState<T> {
5151
///
5252
/// The [screenPosition] is in screen/global coordinates, passed directly
5353
/// to the callback for use with [showMenu] or similar popup APIs.
54-
void _handleNodeContextMenu(Node<T> node, Offset screenPosition) {
54+
void _handleNodeContextMenu(Node<T> node, ScreenPosition screenPosition) {
5555
widget.controller.events.node?.onContextMenu?.call(node, screenPosition);
5656
}
5757

@@ -78,8 +78,7 @@ extension _WidgetGestureHandlers<T> on _NodeFlowEditorState<T> {
7878
void _handlePortContextMenu(
7979
String nodeId,
8080
String portId,
81-
bool isOutput,
82-
Offset screenPosition,
81+
ScreenPosition screenPosition,
8382
) {
8483
final node = widget.controller.getNode(nodeId);
8584
if (node == null) return;
@@ -94,7 +93,6 @@ extension _WidgetGestureHandlers<T> on _NodeFlowEditorState<T> {
9493
widget.controller.events.port?.onContextMenu?.call(
9594
node,
9695
port,
97-
isOutput,
9896
screenPosition,
9997
);
10098
}

0 commit comments

Comments
 (0)