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
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: groups — a comment box that corrals the nodes dragged onto it.
10//
11// node_group(id, (size = float2(w, h))) { -- a pin-less node; its body is the label
12// text(...)
13// }
14// group_hint(id) $(var fg; var bg) { -- off-screen label when zoomed out
15// fg |> add_text(GetGroupMin(), color, title)
16// }
17//
18// A GROUP is just a node with no pins and an explicit content `size`. Drag nodes
19// over it and they move WITH the group — the editor handles that for free. Draw
20// groups FIRST so regular nodes paint on top of them. The group is editor-owned
21// geometry after its first frame, exactly like a node position: seed `size` once,
22// then `set_group_size` to resize it later (mutating your own struct won't).
23//
24// group_hint draws a floating label when the canvas is zoomed out far enough that
25// the group's own title is too small to read; it self-gates (a no-op at normal
26// zoom), so the loop is unconditional.
27//
28// STANDALONE: daslang.exe modules/dasImguiNodeEditor/examples/tutorial/groups.das
29// LIVE: daslang-live modules/dasImguiNodeEditor/examples/tutorial/groups.das
30// =============================================================================
31
32var g_ed : imgui_node_editor::EditorContext? = null
33var g_seeded : bool = false
34
35// node_group(id) and node(id) live in the SAME id space — keep them disjoint.
36let GROUP_ID = 1
37let NODE_A = 10
38let NODE_B = 20
39
40def draw_editor() {
41 node_editor("graph", (editor = g_ed)) {
42 if (!g_seeded) {
43 imgui_node_editor::SetNodePosition(GROUP_ID, float2(60.0, 80.0))
44 imgui_node_editor::SetNodePosition(NODE_A, float2(130.0, 170.0)) // INSIDE the group
45 imgui_node_editor::SetNodePosition(NODE_B, float2(560.0, 200.0)) // OUTSIDE the group
46 g_seeded = true
47 }
48 // The group, drawn first so the two nodes sit on top of it. `size` is the
49 // INITIAL content box — once placed, the editor owns it.
50 node_group(GROUP_ID, (size = float2(300.0, 280.0))) {
51 text(GROUP_TITLE, (text = "Inputs"))
52 }
53 // Zoomed-out label: floats the title above the group bounds in the hint draw
54 // list. GetGroupMin is the group's top-left (screen space inside a hint). The
55 // hint lists swap R/B for asymmetric colors, so a near-grey label is safest.
56 group_hint(GROUP_ID) $(var fg; var _bg) {
57 let mn = imgui_node_editor::GetGroupMin()
58 fg |> add_text(float2(mn.x + 4.0, mn.y - 18.0), rgba(232u, 226u, 210u, 255u), "Inputs")
59 }
60 // Two ordinary nodes: Texture starts INSIDE the group, Tint OUTSIDE. Membership is
61 // spatial - dragging the group carries whatever sits within its bounds at that moment
62 // (the editor's FindNodesInRect), so drag Tint in and it joins, drag it out and it leaves.
63 node(NODE_A) {
64 text("Texture")
65 pin(11, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
66 text("RGBA ->")
67 }
68 }
69 node(NODE_B) {
70 text("Tint")
71 pin(21, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
72 text("Color ->")
73 }
74 }
75 }
76}
77
78[export]
79def init() {
80 harness_init("Groups", 1000, 600)
81 g_ed = create_node_editor()
82}
83
84[export]
85def update() {
86 if (!harness_begin_frame()) return
87 harness_new_frame()
88 let io & = unsafe(GetIO())
89 SetNextWindowPos(float2(0.0, 0.0), ImGuiCond.Always)
90 SetNextWindowSize(io.DisplaySize, ImGuiCond.Always)
91 let flags = (ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize |
92 ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar |
93 ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings |
94 ImGuiWindowFlags.NoBringToFrontOnFocus)
95 window(MAIN_WIN, (text = "Groups", closable = false, flags = flags)) {
96 draw_editor()
97 }
98 harness_end_frame()
99}
100
101[export]
102def shutdown() {
103 destroy_node_editor(g_ed)
104 harness_shutdown()
105}
106
107[export]
108def main() {
109 init()
110 while (!exit_requested()) {
111 update()
112 }
113 shutdown()
114}
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.