Groups
A group (or comment box) is a node with no pins and an explicit content size — the editor’s tool for visually organizing a large graph. Membership is purely spatial: drag a node inside the group’s bounds and it travels with the group from then on; drag it back out and the group leaves it behind. There is no “add to group” call.
node_group(GROUP_ID, (size = float2(300.0, 280.0))) { // pin-less node; body = label
text(GROUP_TITLE, (text = "Inputs"))
}
group_hint(GROUP_ID) $(var fg; var _bg) { // off-screen label when zoomed out
let mn = imgui_node_editor::GetGroupMin()
fg |> add_text(float2(mn.x + 4.0, mn.y - 18.0), rgba(232u, 226u, 210u, 255u), "Inputs")
}
Source: examples/tutorial/groups.das.
Walkthrough
The recording is voiced and self-verifying: Texture starts inside the group, Tint outside. It drags Tint in (a group move then carries both nodes), then drags Tint back out (a group move then leaves it behind) — asserting at each step that the right nodes moved and, crucially, that the outside node stayed put (a no-op aborts at teardown).
1options gen2
2
3require imgui/imgui_harness
4require imgui/imgui_node_editor_boost_v2
5require imgui/imgui_node_editor_live
6
7// =============================================================================
8// TUTORIAL: groups — a comment box that corrals the nodes dragged onto it.
9//
10// node_group(id, (size = float2(w, h))) { -- a pin-less node; its body is the label
11// text(...)
12// }
13// group_hint(id) $(var fg; var bg) { -- off-screen label when zoomed out
14// fg |> add_text(GetGroupMin(), color, title)
15// }
16//
17// A GROUP is just a node with no pins and an explicit content `size`. Drag nodes
18// over it and they move WITH the group — the editor handles that for free. Draw
19// groups FIRST so regular nodes paint on top of them. The group is editor-owned
20// geometry after its first frame, exactly like a node position: seed `size` once,
21// then `set_group_size` to resize it later (mutating your own struct won't).
22//
23// group_hint draws a floating label when the canvas is zoomed out far enough that
24// the group's own title is too small to read; it self-gates (a no-op at normal
25// zoom), so the loop is unconditional.
26//
27// STANDALONE: daslang.exe modules/dasImguiNodeEditor/examples/tutorial/groups.das
28// LIVE: daslang-live modules/dasImguiNodeEditor/examples/tutorial/groups.das
29// =============================================================================
30
31var g_ed : imgui_node_editor::EditorContext? = null
32var g_seeded : bool = false
33
34// node_group(id) and node(id) live in the SAME id space — keep them disjoint.
35let GROUP_ID = 1
36let NODE_A = 10
37let NODE_B = 20
38
39def draw_editor() {
40 node_editor("graph", (editor = g_ed)) {
41 if (!g_seeded) {
42 imgui_node_editor::SetNodePosition(GROUP_ID, float2(60.0, 80.0))
43 imgui_node_editor::SetNodePosition(NODE_A, float2(130.0, 170.0)) // INSIDE the group
44 imgui_node_editor::SetNodePosition(NODE_B, float2(560.0, 200.0)) // OUTSIDE the group
45 g_seeded = true
46 }
47 // The group, drawn first so the two nodes sit on top of it. `size` is the
48 // INITIAL content box — once placed, the editor owns it.
49 node_group(GROUP_ID, (size = float2(300.0, 280.0))) {
50 text(GROUP_TITLE, (text = "Inputs"))
51 }
52 // Zoomed-out label: floats the title above the group bounds in the hint draw
53 // list. GetGroupMin is the group's top-left (screen space inside a hint). The
54 // hint lists swap R/B for asymmetric colors, so a near-grey label is safest.
55 group_hint(GROUP_ID) $(var fg; var _bg) {
56 let mn = imgui_node_editor::GetGroupMin()
57 fg |> add_text(float2(mn.x + 4.0, mn.y - 18.0), rgba(232u, 226u, 210u, 255u), "Inputs")
58 }
59 // Two ordinary nodes: Texture starts INSIDE the group, Tint OUTSIDE. Membership is
60 // spatial - dragging the group carries whatever sits within its bounds at that moment
61 // (the editor's FindNodesInRect), so drag Tint in and it joins, drag it out and it leaves.
62 node(NODE_A) {
63 text("Texture")
64 pin(11, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
65 text("RGBA ->")
66 }
67 }
68 node(NODE_B) {
69 text("Tint")
70 pin(21, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
71 text("Color ->")
72 }
73 }
74 }
75}
76
77[export]
78def init() {
79 harness_init("Groups", 1000, 600)
80 g_ed = create_node_editor()
81}
82
83[export]
84def update() {
85 if (!harness_begin_frame()) return
86 harness_new_frame()
87 let io & = unsafe(GetIO())
88 SetNextWindowPos(float2(0.0, 0.0), ImGuiCond.Always)
89 SetNextWindowSize(io.DisplaySize, ImGuiCond.Always)
90 let flags = (ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize |
91 ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar |
92 ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings |
93 ImGuiWindowFlags.NoBringToFrontOnFocus)
94 window(MAIN_WIN, (text = "Groups", closable = false, flags = flags)) {
95 draw_editor()
96 }
97 harness_end_frame()
98}
99
100[export]
101def shutdown() {
102 destroy_node_editor(g_ed)
103 harness_shutdown()
104}
105
106[export]
107def main() {
108 init()
109 while (!exit_requested()) {
110 update()
111 }
112 shutdown()
113}
A group is a node
node_group(id, (size = float2(w, h))) { ... } is BeginNode + Group(size): a
pin-less node whose body is its label. It shares the same id space as node(id) —
keep the ids disjoint (the tutorial uses 1 for the group, 10 / 20 for the nodes). Draw
groups first so the regular nodes paint on top of them.
Membership is spatial
The editor decides what a group carries at the moment you drag it: it moves every node
whose bounds fall inside the group’s content box (FindNodesInRect over m_GroupBounds
in the C++). There is no membership list and no join API — drop a node inside the bounds and
the next group move takes it along; drag it back outside and the next move leaves it.
To move the group itself, grab its header — the title strip above the content box. Pressing the content box instead starts a rubber-band selection (it moves nothing), which is why the recording aims its group-drag at the title.
Editor-owned geometry
size is the initial content box only. Once the group is placed, the editor owns its
bounds — exactly like a node position. SetNodePosition seeds the group’s canvas position
once; to resize it later call set_group_size (mutating your own struct’s size field
won’t move it). Dragging a node onto the group, or dragging the group itself, is all handled
by the editor — your draw code never changes.
The zoomed-out label
When the canvas is zoomed out far enough that the group’s own title is too small to read,
group_hint(id) $(var fg; var bg) { ... } draws a floating label instead. It self-gates
— a no-op at normal zoom — so the loop over groups is unconditional. GetGroupMin returns
the group’s top-left (screen space inside a hint); fg is the foreground hint draw list,
bg the background one. The hint lists swap red/blue for asymmetric colors, so a near-grey
label is the safe choice. See navigation for zooming the view
out far enough to trigger it.