Styling

first_graph drew bare boxes. The editor’s look is layered, and each layer is independent — reach for the smallest one that does the job: a canvas-wide theme, per-node tints, scoped style var / color brackets for one-off nodes, pin pivots that move the link-attach point onto the node edge, and a background draw list for custom art.

apply_daslang_node_editor_style(g_ed)          // canvas theme (warm-dark + amber)

node(1, (color = float4(0.15, 0.35, 0.18, 0.78))) {   // per-node background tint
    text("Source")
    pin(12, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) { text("out ->") }
}

with_style_var(imgui_node_editor::StyleVar.NodeRounding, 10.0) {        // scoped VAR
    with_style_color(imgui_node_editor::StyleColor.NodeBorder, amber) { // scoped COLOR
        node(3, (color = red)) { ... }
    }
}
with_node_background_drawlist(3) $(var dl) {                            // custom art
    let p = imgui_node_editor::GetNodePosition(3)
    let s = imgui_node_editor::GetNodeSize(3)
    dl |> add_rect_filled(p, float2(p.x + 5.0, p.y + s.y), rgba(255u, 90u, 90u, 220u))
}

Source: examples/tutorial/styling.das.

Walkthrough

  1options gen2
  2options _comment_hygiene = true
  3
  4require imgui/imgui_harness
  5require imgui/imgui_node_editor_boost_v2
  6require imgui/imgui_node_editor_live
  7require imgui/imgui_node_editor_theme_daslang
  8
  9// =============================================================================
 10// TUTORIAL: styling — make nodes look the way you want.
 11//
 12//   apply_daslang_node_editor_style(ed)        -- canvas theme (warm-dark + amber)
 13//   node(id, (color = float4(r,g,b,a)))        -- per-node background tint
 14//   pin(id, (kind=, pivot_alignment=))         -- where the link attaches on the pin
 15//   with_style_var(StyleVar.NodeRounding, 10)  -- scoped style VAR around a node
 16//   with_style_color(StyleColor.NodeBorder, c) -- scoped style COLOR (composes with the var)
 17//   with_node_background_drawlist(id) $(var dl) -- raw drawing behind a node
 18//
 19// first_graph drew bare boxes. The editor's look is layered: a canvas-wide THEME,
 20// per-node TINTS (the `color` arg), SCOPED style var/color brackets for one-off
 21// nodes, pin PIVOTS that move the link-attach point onto the node edge, and a
 22// background DRAW LIST for custom art. Each layer is independent — reach for the
 23// smallest one that does the job.
 24//
 25// STANDALONE: daslang.exe modules/dasImguiNodeEditor/examples/tutorial/styling.das
 26// LIVE:       daslang-live modules/dasImguiNodeEditor/examples/tutorial/styling.das
 27// =============================================================================
 28
 29var g_ed : imgui_node_editor::EditorContext? = null
 30var g_seeded : bool = false
 31
 32def draw_editor() {
 33    node_editor("graph", (editor = g_ed)) {
 34        if (!g_seeded) {
 35            imgui_node_editor::SetNodePosition(1, float2(80.0, 80.0))
 36            imgui_node_editor::SetNodePosition(2, float2(360.0, 120.0))
 37            imgui_node_editor::SetNodePosition(3, float2(660.0, 160.0))
 38            g_seeded = true
 39        }
 40        // (1) A plain tint: the `color` arg fills the node background. Green for a source.
 41        node(1, (color = float4(0.15, 0.35, 0.18, 0.78))) {
 42            text("Source")
 43            pin(12, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
 44                text("out ->")
 45            }
 46        }
 47        // (2) Default look — no tint, square corners, default border. Inputs pivot on the
 48        // left edge (x = 0), outputs on the right (x = 1); with no pivot the link springs
 49        // from the pin label's center. Pin ids are unique editor-wide — same ids merge.
 50        node(2) {
 51            text("Passthrough")
 52            pin(21, (kind = PinKind.Input, pivot_alignment = float2(0.0, 0.5))) {
 53                text("-> in")
 54            }
 55            pin(22, (kind = PinKind.Output, pivot_alignment = float2(1.0, 0.5))) {
 56                text("out ->")
 57            }
 58        }
 59        // (3) The showcase node: a red tint, PLUS scoped style brackets — rounded
 60        // corners (a style VAR) and an amber border (a style COLOR). The two wrappers
 61        // compose; each restores its previous value when the block ends.
 62        with_style_var(imgui_node_editor::StyleVar.NodeRounding, 10.0) {
 63            with_style_color(imgui_node_editor::StyleColor.NodeBorder, float4(0.910, 0.631, 0.227, 1.0)) {
 64                node(3, (color = float4(0.40, 0.18, 0.18, 0.78))) {
 65                    text("Output")
 66                    pin(31, (kind = PinKind.Input, pivot_alignment = float2(0.0, 0.5))) {
 67                        text("-> in")
 68                    }
 69                }
 70            }
 71        }
 72        // A custom accent stripe down node 3's left edge. The node's draw channels only
 73        // exist once it is fully built, so this runs AFTER the node block, reading the
 74        // editor-owned geometry back (GetNodePosition / GetNodeSize).
 75        with_node_background_drawlist(3) $(var dl) {
 76            let p = imgui_node_editor::GetNodePosition(3)
 77            let s = imgui_node_editor::GetNodeSize(3)
 78            dl |> add_rect_filled(p, float2(p.x + 5.0, p.y + s.y), rgba(255u, 90u, 90u, 220u))
 79        }
 80        link(100, 12, 31)
 81    }
 82}
 83
 84[export]
 85def init() {
 86    harness_init("Styling", 1000, 600)
 87    g_ed = create_node_editor()
 88    apply_daslang_node_editor_style(g_ed)   // warm-dark canvas + amber grid to match daslang's ImGui theme
 89}
 90
 91[export]
 92def update() {
 93    if (!harness_begin_frame()) return
 94    harness_new_frame()
 95    let io & = unsafe(GetIO())
 96    SetNextWindowPos(float2(0.0, 0.0), ImGuiCond.Always)
 97    SetNextWindowSize(io.DisplaySize, ImGuiCond.Always)
 98    let flags = (ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoResize |
 99                 ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoScrollbar |
100                 ImGuiWindowFlags.NoScrollWithMouse | ImGuiWindowFlags.NoSavedSettings |
101                 ImGuiWindowFlags.NoBringToFrontOnFocus)
102    window(MAIN_WIN, (text = "Styling", closable = false, flags = flags)) {
103        draw_editor()
104    }
105    harness_end_frame()
106}
107
108[export]
109def shutdown() {
110    destroy_node_editor(g_ed)
111    harness_shutdown()
112}
113
114[export]
115def main() {
116    init()
117    while (!exit_requested()) {
118        update()
119    }
120    shutdown()
121}

Canvas theme

apply_daslang_node_editor_style(ed) (from imgui/imgui_node_editor_theme_daslang) sets the canvas background, grid, and link colors to the warm-dark + amber palette that matches daslang’s ImGui theme. It is one call at init and affects the whole editor — the broadest styling layer.

Per-node tint

The color tuple arg on node(id, (color = float4(r, g, b, a))) fills that one node’s background. Node 1 is green (a source), node 3 red (an output); node 2 passes no color and keeps the editor default. This is the cheapest per-node knob — no scope, no restore.

Scoped style var + color

For properties that aren’t a node arg — corner rounding, border color — wrap the node in a scoped bracket. with_style_var(StyleVar.NodeRounding, 10.0) rounds the corners; with_style_color(StyleColor.NodeBorder, amber) recolors the border. They compose (nest one inside the other), and each restores the previous value when its block ends, so only the node inside is affected.

Pin pivots

pivot_alignment on a pin moves the point where links attach. float2(0.0, 0.5) is the pin’s left edge (use it for inputs), float2(1.0, 0.5) the right edge (outputs). Without a pivot the link springs from the pin label’s center, which looks untidy once a node has several pins. (pivot_size and pivot_scale are the other two knobs — see shader_graph.das.)

Background draw list

with_node_background_drawlist(id) $(var dl) { ... } hands you a draw list behind the node for custom art — here a red accent stripe down node 3’s left edge. The node’s draw channels only exist once it is fully built, so this runs after the node block, reading the editor-owned geometry back with GetNodePosition / GetNodeSize.