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)returnstruewhile a pin-drag is hovering a candidate target pin, writing the two pin ids intoa(the drag source) andb(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)returnstrueon the frame the user releases over a valid target — that is where the link is committed.reject_new_itemis 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.