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)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.