Skip to content

Edge in-progress cancels when released over a node frame #57

@mitchmindtree

Description

@mitchmindtree

Problem

When dragging an edge from a socket and releasing it over the target socket, the edge cancels instead of connecting if the pointer is over a node frame at the time of release. This makes edge creation frustrating - you have to release the pointer over the exposed half of the socket that extends beyond the frame, rather than being able to release anywhere near the target socket including over the node frame.

Cause

The closest_socket calculation (lib.rs:358) is gated on ui.response().hover_pos(), which internally relies on scene_response.hovered(). Since node frames are sublayers with click_and_drag sense, they win egui's hit test over the scene layer.

When the pointer is over a node frame, the scene's hovered() returns false, hover_pos() returns None, and closest_socket is set to None.

On the release frame, node.rs:449 sees socket_press_released is Some but gmem.closest_socket is None, and fires EdgeEvent::Cancelled:

// node.rs ~line 449
} else if edge_event.is_none() {
    if self.id == r.node {
        edge_event = Some(EdgeEvent::Cancelled);
    }
}

Why contains_pointer() is not a simple fix

The naive fix of replacing hovered() with contains_pointer() is too permissive. contains_pointer() only filters widgets that are fully hidden behind another widget on a different layer (hit_test.rs:145).

Since the scene rect spans the entire graph, no small floating window can fully contain it - so contains_pointer() would return true even when a floating window covers the pointer position, regressing the existing correct behaviour where hovering a window above the graph does not trigger graph interactions.

Proposed approach

Rather than checking only scene_response.hovered(), check whether the union of the scene response and all node frame responses is hovered.

If the pointer is over a node frame (which is a child of the graph), that should count as being "over the graph" for the purposes of closest_socket detection and ptr_on_graph.

This requires restructuring the interaction flow slightly, since closest_socket and ptr_on_graph are currently computed before nodes are drawn. Options include:

  1. Defer closest_socket to after node drawing - compute it in a second pass once node frame responses are available.
  2. Use previous-frame node rects - gmem.node_sizes and layout positions are available before drawing. Check whether ptr_graph falls within any known node rect as a proxy for "a node frame is hovered". This avoids restructuring but relies on one-frame-old data.
  3. Collect node frame hovered() states during drawing - accumulate a flag like any_node_hovered as nodes are drawn, then use it in a deferred closest_socket computation.

Option 1 is likely cleanest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions