dasImguiNodeEditor documentation Logo
2.0

Contents

  • dasImguiNodeEditor tutorials
    • First graph
      • Walkthrough
        • EditorContext
        • Nodes and pins
        • Links
    • Connecting by drag
      • Walkthrough
        • The create scope
        • Driving it from a test
    • Deleting & selection
      • Walkthrough
        • The delete scope
        • The cascade
        • Driving the delete
    • Context menus
      • Walkthrough
        • The suspend island
        • Which menu fired
        • Driving it from a test
    • Create by drag
      • Walkthrough
        • The create scope
        • The auto-connect
        • Driving it from a test
    • Clipboard
      • Walkthrough
        • The app owns the clipboard
        • Flag now, act later
        • Driving it from a test
    • Styling
      • Walkthrough
        • Canvas theme
        • Per-node tint
        • Scoped style var + color
        • Pin pivots
        • Background draw list
    • Groups
      • Walkthrough
        • A group is a node
        • Membership is spatial
        • Editor-owned geometry
        • The zoomed-out label
    • Navigation
      • Walkthrough
        • Two view ops and one node move
        • Placement: inside vs outside the block
        • Driving it from a test
  • dasImguiNodeEditor v2.0
    • 1. Boost v2 — the node-editor DSL
      • 1.1. Boost v2 — the node-editor DSL: graph entities, link/delete queues, selection, context menus, shortcuts, styling
        • 1.1.1. Type aliases
        • 1.1.2. Call macros
        • 1.1.3. Editor lifecycle
        • 1.1.4. Node geometry & view
        • 1.1.5. Selection
        • 1.1.6. Link creation
        • 1.1.7. Item deletion
        • 1.1.8. Context menus
        • 1.1.9. Clipboard & shortcuts
        • 1.1.10. Styling
        • 1.1.11. Drawing & hints
        • 1.1.12. Flow animation
    • 2. Canvas theme
      • 2.1. Canvas theme — daslang-styled node-editor colors and style vars
        • 2.1.1. Canvas theme
    • 3. Testing harness
      • 3.1. App harness — with_node_editor_app + with_node_editor_recording_app, thin node-editor delegates over imgui_playwright’s with_imgui_app / with_recording_app
        • 3.1.1. App harness
      • 3.2. Editor playwright — node-editor-aware test layer over imgui_playwright (EditorSession + ne_* helpers)
        • 3.2.1. Structures
        • 3.2.2. Session
        • 3.2.3. Actions
        • 3.2.4. Snapshots & queries
        • 3.2.5. Polling / await
    • 4. Live commands
    • 5. Consumer lint
    • 6. External types
      • 6.1. Node / pin / link ids
      • 6.2. imgui_node_editor::EditorContext
      • 6.3. imgui_node_editor::PinKind
      • 6.4. imgui_node_editor::FlowDirection
      • 6.5. imgui_node_editor::StyleColor
      • 6.6. imgui_node_editor::StyleVar
      • 6.7. imgui::ImDrawList
      • 6.8. imgui_playwright::ImguiApp
dasImguiNodeEditor documentation
  • dasImguiNodeEditor tutorials
  • Navigation
  • Edit on GitHub

Navigation

The editor separates the graph (node positions, in canvas space) from the view (the pan + zoom that maps canvas to screen). Two of these ops change only the view — fit the whole graph, or frame the selection. The third, center_node_on_screen, is a footgun: despite the name it moves the node to the view center.

if (button(FIT_ALL, (text = "Fit All"))) {
    g_fit = true                                  // serviced inside the editor block
}
if (button(FRAME_SEL, (text = "Frame Selection"))) {
    navigate_to_selection(g_ed, false, -1.0)      // bracketed wrapper → safe out here
}
if (button(CENTER_FIRST, (text = "Center #1"))) {
    center_node_on_screen(g_ed, 1)
}

node_editor("graph", (editor = g_ed)) {
    ...
    if (g_fit) {
        imgui_node_editor::NavigateToContent(0.0)  // must run with the editor current
        g_fit = false
    }
}

Source: examples/tutorial/navigation.das.

Walkthrough

Your browser doesn't support HTML5 video. Download the recording.
  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: navigation — the view ops, and the one that fools you.
 10//
 11//   NavigateToContent(duration)            -- fit the whole graph to the viewport (VIEW)
 12//   navigate_to_selection(ed, zoomIn, dur) -- frame just the selected nodes        (VIEW)
 13//   center_node_on_screen(ed, nodeId)      -- MOVES the node to the view center    (GRAPH!)
 14//
 15// The editor separates the GRAPH (node positions, in canvas space) from the VIEW
 16// (the pan + zoom that maps canvas to screen). Fit All and Frame Selection change only
 17// the view — no node moves. center_node_on_screen is the footgun: despite the name,
 18// upstream CenterNodeOnScreen MOVES the node to the view center, it does not pan.
 19// Placement also matters: NavigateToContent must run INSIDE the node_editor block with
 20// the editor current, so the app raises a flag and services it there;
 21// navigate_to_selection / center_node_on_screen are bracketed wrappers that set the
 22// editor current themselves, so the toolbar buttons call them directly.
 23//
 24// STANDALONE: daslang.exe modules/dasImguiNodeEditor/examples/tutorial/navigation.das
 25// LIVE:       daslang-live modules/dasImguiNodeEditor/examples/tutorial/navigation.das
 26// =============================================================================
 27
 28var g_ed : imgui_node_editor::EditorContext? = null
 29var g_seeded : bool = false
 30var g_fit : bool = false   // "Fit All" raises this; serviced inside the editor block
 31
 32// Spread wide so a fit visibly zooms out and recenters.
 33struct Nd {
 34    id  : int
 35    pos : float2
 36}
 37let NODES = [
 38    Nd(id = 1, pos = float2(40.0,   40.0)),
 39    Nd(id = 2, pos = float2(560.0,  300.0)),
 40    Nd(id = 3, pos = float2(1180.0, 560.0))
 41]
 42
 43def toolbar() {
 44    if (button(FIT_ALL, (text = "Fit All"))) {
 45        g_fit = true   // NavigateToContent runs inside the block (editor must be current)
 46    }
 47    same_line()
 48    if (button(FRAME_SEL, (text = "Frame Selection"))) {
 49        navigate_to_selection(g_ed, false, -1.0)   // bracketed wrapper → safe outside the block
 50    }
 51    same_line()
 52    if (button(CENTER_FIRST, (text = "Center #1"))) {
 53        center_node_on_screen(g_ed, 1)   // NB: MOVES node 1 to the view center - not a view op
 54    }
 55}
 56
 57def draw_editor() {
 58    node_editor("graph", (editor = g_ed)) {
 59        if (!g_seeded) {
 60            for (n in NODES) {
 61                imgui_node_editor::SetNodePosition(n.id, n.pos)
 62            }
 63            g_seeded = true
 64        }
 65        for (n in NODES) {
 66            node(n.id) {
 67                text("Node {n.id}")
 68                pin(n.id * 10 + 1, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
 69                    text("out ->")
 70                }
 71            }
 72        }
 73        // Service "Fit All" here, with the editor current. NavigateToContent(0) snaps
 74        // instantly; pass a duration (seconds) for an animated fly-to instead.
 75        if (g_fit) {
 76            imgui_node_editor::NavigateToContent(0.0)
 77            g_fit = false
 78        }
 79    }
 80}
 81
 82[export]
 83def init() {
 84    harness_init("Navigation", 1000, 600)
 85    g_ed = create_node_editor()
 86}
 87
 88[export]
 89def update() {
 90    if (!harness_begin_frame()) return
 91    harness_new_frame()
 92    let io & = unsafe(GetIO())
 93    SetNextWindowPos(float2(0.0, 0.0), ImGuiCond.Always)
 94    SetNextWindowSize(io.DisplaySize, ImGuiCond.Always)
 95    let flags = (ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize |
 96                 ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar |
 97                 ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings |
 98                 ImGuiWindowFlags.NoBringToFrontOnFocus)
 99    window(MAIN_WIN, (text = "Navigation", closable = false, flags = flags)) {
100        toolbar()
101        draw_editor()
102    }
103    harness_end_frame()
104}
105
106[export]
107def shutdown() {
108    destroy_node_editor(g_ed)
109    harness_shutdown()
110}
111
112[export]
113def main() {
114    init()
115    while (!exit_requested()) {
116        update()
117    }
118    shutdown()
119}

Two view ops and one node move

  • NavigateToContent(duration) — view: fit the whole graph to the viewport. 0.0 snaps instantly; a positive duration (seconds) animates a fly-to.

  • navigate_to_selection(ed, zoomIn, duration) — view: frame just the selected nodes.

  • center_node_on_screen(ed, nodeId) — moves the node, not the view. Despite the name, upstream CenterNodeOnScreen translates the node’s bounds to the view center (and marks a user position change). Reach for it to recall a stray node, not to navigate.

Placement: inside vs outside the block

NavigateToContent must run with the editor current — i.e. inside the node_editor block. So the “Fit All” button raises a flag and the block services it, the same flag-then-act pattern the context menus use for deferred work. navigate_to_selection and center_node_on_screen are bracketed wrappers that set the editor current themselves, so the toolbar buttons — which run outside the block — call them directly.

Driving it from a test

A view op shifts a node’s screen-space bbox while its canvas position holds; center_node_on_screen is the mirror image — it leaves the view alone and changes the canvas position. The recording (record_navigation.das) and the headless regression (test_navigation.das) exploit both: they assert Fit All and Frame Selection move the screen bbox but leave the canvas bbox put (record_check_changed + record_check_unchanged), then assert Center #1 moves the canvas bbox — proof it relocates the node, not the view. The recording app holds set_user_control(false) so the real OS cursor can’t race the synth and eat a click.

Previous Next

© Copyright 2026-2026, Gaijin Entertainment.

Built with Sphinx using a theme provided by Read the Docs.