Connecting by drag

The signature node-editor gesture: drag from an output pin to an input pin and a link is created. The interaction lives in a begin_create scope — query_new_link reports the pinned pair while the drag hovers a target, and accept_new_item commits it on release.

begin_create(g_ed) {
    var a = 0
    var b = 0
    if (query_new_link(g_ed, a, b)) {       // a pin-drag is over a target pin
        if (a != 0 && b != 0 && a != b && accept_new_item(g_ed)) {
            // released on a compatible pin — commit the link
            g_connected = true
        }
    }
}

The model here is intentionally trivial — one output pin, one input pin, and a single bool. A real graph validates pin kinds, dedupes, rejects cycles, and stores a list of links; this just teaches the gesture and the create scope.

Source: examples/tutorial/connect_by_drag.das.

Walkthrough

The recording is voiced and self-verifying: a real synthetic pin-drag commits the link, the recording asserts it committed (a no-op aborts at teardown), then pulses the new link with flow() to show data running output pin 11 -> input pin 21.

  1options gen2
  2options _comment_hygiene = true
  3
  4require imgui/imgui_harness
  5require imgui/imgui_node_editor_boost_v2
  6require imgui/imgui_node_editor_live
  7
  8// =============================================================================
  9// TUTORIAL: connect_by_drag — the signature node-editor gesture. Drag from an
 10// output pin to an input pin and a link is created.
 11//
 12//   begin_create(ed) {                    -- open the create-interaction scope
 13//       if (query_new_link(ed, a, b)) {   -- a pin-drag is hovering a target pin
 14//           if (accept_new_item(ed)) {    -- the user released: commit the link
 15//               ... add the link ...
 16//           }
 17//       }
 18//   }
 19//
 20// The model here is intentionally trivial — one output pin, one input pin, and a
 21// single bool tracking whether they are joined. A real graph would validate kinds,
 22// dedupe, and store a list; this just teaches the gesture and the create scope.
 23//
 24// STANDALONE: daslang.exe modules/dasImguiNodeEditor/examples/tutorial/connect_by_drag.das
 25// LIVE:       daslang-live modules/dasImguiNodeEditor/examples/tutorial/connect_by_drag.das
 26// =============================================================================
 27
 28var g_ed : imgui_node_editor::EditorContext? = null
 29var g_seeded : bool = false
 30var g_connected : bool = false
 31
 32let OUT_PIN = 11
 33let IN_PIN  = 21
 34let LINK_ID = 100
 35
 36[export]
 37def init() {
 38    harness_init("Connect by drag", 1000, 600)
 39    g_ed = create_node_editor()
 40}
 41
 42def draw_editor() {
 43    node_editor("graph", (editor = g_ed)) {
 44        // Seed two nodes side by side, once.
 45        if (!g_seeded) {
 46            imgui_node_editor::SetNodePosition(1, float2(120.0, 160.0))
 47            imgui_node_editor::SetNodePosition(2, float2(560.0, 160.0))
 48            g_seeded = true
 49        }
 50        node(1) {
 51            text("Source")
 52            pin(OUT_PIN, PinKind.Output) {
 53                text("value ->")
 54            }
 55        }
 56        node(2) {
 57            text("Sink")
 58            pin(IN_PIN, PinKind.Input) {
 59                text("-> value")
 60            }
 61        }
 62        // The one link, drawn once it has been made.
 63        if (g_connected) {
 64            link(LINK_ID, OUT_PIN, IN_PIN)
 65        }
 66        // The hero gesture: a pin-drag released on a compatible pin commits a link.
 67        // With exactly one output + one input, any cross-pin drag IS the connection.
 68        begin_create(g_ed) {
 69            var a = 0
 70            var b = 0
 71            if (query_new_link(g_ed, a, b)) {
 72                if (a != 0 && b != 0 && a != b && accept_new_item(g_ed)) {
 73                    g_connected = true
 74                }
 75            }
 76        }
 77    }
 78}
 79
 80[export]
 81def update() {
 82    if (!harness_begin_frame()) return
 83    harness_new_frame()
 84    let io & = unsafe(GetIO())
 85    SetNextWindowPos(float2(0.0, 0.0), ImGuiCond.Always)
 86    SetNextWindowSize(io.DisplaySize, ImGuiCond.Always)
 87    let flags = (ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize |
 88                 ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar |
 89                 ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings |
 90                 ImGuiWindowFlags.NoBringToFrontOnFocus)
 91    window(MAIN_WIN, (text = "Connect by drag", closable = false, flags = flags)) {
 92        draw_editor()
 93    }
 94    harness_end_frame()
 95}
 96
 97[export]
 98def shutdown() {
 99    destroy_node_editor(g_ed)
100    harness_shutdown()
101}
102
103[export]
104def main() {
105    init()
106    while (!exit_requested()) {
107        update()
108    }
109    shutdown()
110}

The create scope

begin_create(ed) { ... } opens the editor’s create-interaction scope for the frame. Inside it:

  • query_new_link(ed, a, b) returns true while a pin-drag is hovering a candidate target pin, writing the two pin ids into a (the drag source) and b (the hovered target). Pins come in drag order, so a real handler normalizes them to output/input and validates the pair every frame.

  • accept_new_item(ed) returns true on the frame the user releases over a valid target — that is where the link is committed. reject_new_item is its counterpart for an invalid pair (it shows the reject cursor).

Driving it from a test

The recording above is produced by a synthetic mouse drag (see tests/integration/record_connect_by_drag.das); the headless regression test_connect_drag.das drives the same gesture and asserts the link commits. Grabbing a small pin needs the press to land exactly on it, so the synthetic timeline parks the cursor on the source pin through the press, travels the full distance with the button held, then parks on the target pin through the release.

For programmatic link creation that bypasses the mouse entirely (validity-rule tests), the harness also exposes ne_add_link, which queues a link the same create handler validates.