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

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.