feat(editor): highlight drop targets during connection drag
While the operator is dragging from an output port, every
input port now lights up with a tint so they can see at a
glance where the connection can land. The closest port
within the snap distance gets a larger halo + brighter
border so the operator knows which port will accept the
drop if they release now.
Visual states:
- input port, no drag: muted background
- input port, drag in flight: primary @ 35% alpha
- input port, closest target: primary fill + 2.5 px border
+ soft halo glow
The closest-target computation runs once per port per
frame during a drag — O(n) over visible input ports.
Negligible at flow sizes operators care about; revisit if
flows exceed a few dozen steps.
Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
307eaccc3e
commit
fb8730903c
1 changed files with 66 additions and 4 deletions
|
|
@ -483,7 +483,15 @@ class _FlowCanvasState extends State<FlowCanvas> {
|
|||
void Function(Offset cursor)? onDropTarget,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
const size = 16.0;
|
||||
// During a connection drag, highlight every input port
|
||||
// so the operator sees the landing options; the closest
|
||||
// one gets a larger halo so they know which port the
|
||||
// drop will snap to if they release now.
|
||||
final dragging = _draft != null;
|
||||
final isInputDuringDrag = dragging && !isSource;
|
||||
final isClosest =
|
||||
isInputDuringDrag && _isClosestDropTarget(center);
|
||||
final size = isClosest ? 22.0 : 16.0;
|
||||
return Positioned(
|
||||
left: center.dx - size / 2,
|
||||
top: center.dy - size / 2,
|
||||
|
|
@ -517,10 +525,27 @@ class _FlowCanvasState extends State<FlowCanvas> {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isSource
|
||||
color: isClosest
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.surfaceContainerHighest,
|
||||
border: Border.all(color: theme.colorScheme.primary, width: 1.5),
|
||||
: isSource
|
||||
? theme.colorScheme.primary
|
||||
: isInputDuringDrag
|
||||
? theme.colorScheme.primary
|
||||
.withValues(alpha: 0.35)
|
||||
: theme.colorScheme.surfaceContainerHighest,
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.primary,
|
||||
width: isClosest ? 2.5 : 1.5,
|
||||
),
|
||||
boxShadow: isClosest
|
||||
? [
|
||||
BoxShadow(
|
||||
color: theme.colorScheme.primary
|
||||
.withValues(alpha: 0.5),
|
||||
blurRadius: 10,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -528,6 +553,43 @@ class _FlowCanvasState extends State<FlowCanvas> {
|
|||
);
|
||||
}
|
||||
|
||||
/// True if this input port is the nearest valid drop
|
||||
/// target to the current draft cursor (within snap
|
||||
/// distance). Used to paint the highlight halo so the
|
||||
/// operator sees which port will accept the connection.
|
||||
bool _isClosestDropTarget(Offset portCenter) {
|
||||
final draft = _draft;
|
||||
if (draft == null) return false;
|
||||
final graph = widget.controller.graph;
|
||||
final layout = widget.controller.layout;
|
||||
final outputsX = _outputsX(graph, layout);
|
||||
const maxDist = 32.0;
|
||||
double bestDist = double.infinity;
|
||||
Offset? best;
|
||||
for (final step in graph.steps) {
|
||||
final keys = step.with_.keys.toList();
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
final p = _inputPortPosition(step.id, i, layout, outputsX);
|
||||
final d = (draft.cursor - p).distance;
|
||||
if (d < bestDist && d <= maxDist) {
|
||||
bestDist = d;
|
||||
best = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
final outs = graph.outputs.keys.toList();
|
||||
for (var i = 0; i < outs.length; i++) {
|
||||
final p = _inputPortPosition('__outputs__', i, layout, outputsX);
|
||||
final d = (draft.cursor - p).distance;
|
||||
if (d < bestDist && d <= maxDist) {
|
||||
bestDist = d;
|
||||
best = p;
|
||||
}
|
||||
}
|
||||
if (best == null) return false;
|
||||
return (best - portCenter).distance < 0.5;
|
||||
}
|
||||
|
||||
void Function(Offset) _dropTargetFor(
|
||||
String targetId,
|
||||
String targetField,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue