8.18.1.5. Narrative widgets

The narrative family is dasImgui’s set of read-only display widgets — text emission with optional color, emphasis, bullets, or label/value framing. All share a NarrativeState (or LabelTextState) payload that echoes the call-site string back into the snapshot, so playwright tests can assert “the label says what I expect” without any layout state.

Every rail accepts an optional leading ident. Pass one — text(IDENT, (text = "...")) — and the widget registers at NARRATIVE_WIN/IDENT so a snapshot can target it by name; omit it — text("...") — and it registers under an auto-generated source-line key instead. This tutorial passes an ident on every call so each line is assertable.

The nine rails:

  • text(IDENT, (text = "...")) — one line of plain text. Implemented via TextUnformatted (no printf-style format expansion).

  • text_unformatted(IDENT, (text = "...")) — explicit alias for text; identical implementation. Exists so call sites can signal intent.

  • text_wrapped(IDENT, (text = "...")) — reflows long strings to the window’s content edge.

  • text_colored(IDENT, (color = float4(...), text = ...)) — colored variant.

  • text_disabled(IDENT, (text = "...")) — greyed for non-actionable hints.

  • bullet(IDENT) — bullet glyph alone.

  • bullet_text(IDENT, (text = "...")) — bullet glyph + text bundled.

  • label_text(IDENT, (key = ..., value = ...)) — two-string display, both echoed into the payload.

  • separator_text(IDENT, (text = "...")) — horizontal rule with a centered label.

Source: modules/dasImgui/examples/tutorial/narrative_widgets.das.

8.18.1.5.1. Walkthrough

These widgets take no input, so the recording is a voiced, self-verifying tour: each beat narrates a group while the cursor points at it and asserts the widget rendered and echoed its call-site string into the snapshot — the “the label says what I expect” claim made concrete. A missing or wrong value aborts the recording at teardown instead of shipping.

  1options gen2
  2options _comment_hygiene = true
  3
  4require imgui
  5require imgui_app
  6require opengl/opengl_boost
  7require live/glfw_live
  8require live/live_api
  9require live/live_commands
 10require live/live_vars
 11require live_host
 12require imgui/imgui_live
 13require imgui/imgui_boost_runtime
 14require imgui/imgui_boost_v2
 15require imgui/imgui_widgets_builtin
 16require imgui/imgui_containers_builtin
 17require imgui/imgui_visual_aids
 18
 19// =============================================================================
 20// TUTORIAL: narrative_widgets — boost v2 read-only display widgets.
 21//
 22// All eight share a NarrativeState (or LabelTextState) payload: the call-site
 23// string echoes back into the snapshot so playwright tests can assert "the
 24// label says what I expect". No focus, no click, no telemetry beyond `value`.
 25//
 26// Covers: text / text_unformatted / text_wrapped / text_colored / text_disabled
 27//         / bullet / bullet_text / label_text / separator_text
 28//
 29// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/narrative_widgets.das
 30// LIVE:       daslang-live modules/dasImgui/examples/tutorial/narrative_widgets.das
 31//
 32// DRIVE (when running live):
 33//   curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command \
 34//        | jq '.globals."NARRATIVE_WIN/BULLET_MARK".payload.value'
 35// =============================================================================
 36
 37[export]
 38def init() {
 39    live_create_window("dasImgui narrative widgets", 760, 600)
 40    live_imgui_init(live_window)
 41    let io & = unsafe(GetIO())
 42    GetStyle().FontScaleMain = 1.4
 43}
 44
 45[export]
 46def update() {
 47    if (!live_begin_frame()) return
 48    begin_frame()
 49
 50    ImGui_ImplGlfw_NewFrame()
 51    apply_synth_io_override()
 52    NewFrame()
 53
 54    SetNextWindowPos(ImVec2(60.0, 60.0), ImGuiCond.Always)
 55    SetNextWindowSize(ImVec2(640.0, 480.0), ImGuiCond.Always)
 56    window(NARRATIVE_WIN, (text = "Narrative widgets", closable = false,
 57                           flags = ImGuiWindowFlags.None)) {
 58        // Each call passes an explicit ident so the widget registers at
 59        // NARRATIVE_WIN/<ident> and a snapshot can assert "the label says what
 60        // I expect". NarrativeState.value mirrors the call-site string.
 61        text(PLAIN_LINE, (text = "text() - one line, telemetry-visible."))
 62
 63        // text_wrapped reflows to the window's content edge.
 64        text_wrapped(WRAPPED_LINE, (text = "text_wrapped() reflows long strings to the window's right edge. Resize the window and the layout follows; the snapshot still carries the full source string regardless of wrapping."))
 65
 66        separator_text(SEP_COLOR, (text = "Color and emphasis"))
 67
 68        // Colored / disabled - visual variants, same payload shape.
 69        text_colored(COLORED_LINE, (color = float4(0.7f, 0.9f, 0.4f, 1.0f),
 70                                    text = "text_colored() with an inline float4 color."))
 71        text_disabled(DISABLED_LINE, (text = "text_disabled() - greyed for non-actionable hints."))
 72
 73        separator_text(SEP_BULLET, (text = "Bulleted lines"))
 74
 75        // bullet() emits the marker glyph; bullet_text() bundles marker + text.
 76        bullet(BULLET_MARK)
 77        bullet_text(BULLET_FIRST, (text = "First point of interest."))
 78        bullet_text(BULLET_SECOND, (text = "Second point - bullet glyph plus text in one call."))
 79
 80        separator_text(SEP_LABEL, (text = "Label/value pair"))
 81
 82        // label_text - two-string display, both strings echo into the payload.
 83        label_text(VERSION_LABEL, (key = "Version", value = "v2.0-detour"))
 84    }
 85
 86    end_of_frame()
 87    Render()
 88    var w, h : int
 89    live_get_framebuffer_size(w, h)
 90    glViewport(0, 0, w, h)
 91    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
 92    glClear(GL_COLOR_BUFFER_BIT)
 93    live_imgui_render()
 94
 95    live_end_frame()
 96}
 97
 98[export]
 99def shutdown() {
100    live_imgui_shutdown()
101    live_destroy_window()
102}
103
104[export]
105def main() {
106    init()
107    while (!exit_requested()) {
108        update()
109    }
110    shutdown()
111}

8.18.1.5.1.1. Requires

Baseline boost layer (imgui/imgui_boost_v2 re-exports imgui/imgui_widgets_builtin). No extra modules.

8.18.1.5.1.2. text vs text_unformatted vs text_wrapped

  • text(s) — emits s through ImGui’s TextUnformatted(s). No printf-style format expansion; the string is rendered verbatim.

  • text_unformatted(s) — identical to text(s) (same TextUnformatted call). Exists as an explicit-intent alias for call sites where unformatted reads better, or where you want to underscore the no-printf-expansion contract for readers.

  • text_wrapped(s) — emits through TextWrapped(s) so the text reflows at the window’s content edge. The wrap position respects with_text_wrap_pos if you’ve pushed one.

The payload shape is identical for all three; the alias-pair (text / text_unformatted) renders identically, while text_wrapped is the one that actually changes rendering behavior.

8.18.1.5.1.3. Colored vs disabled

text_colored((color, text)) takes an inline float4 and renders the text in that color. Useful for status callouts, error indicators, or section accents. The color is per-call — no scope push needed.

text_disabled(s) renders in ImGui’s StyleColor.TextDisabled — the greyed tone used for inert menu items and hints. The most common use is the (?) help marker glyph (see Flat tooltips).

8.18.1.5.1.4. Bullets

bullet(IDENT) emits the marker glyph alone — useful when you want custom content (a colored swatch, an icon) immediately after the bullet.

bullet_text(IDENT, (text = "...")) is the bundled form — glyph + text in one call. Use this 95% of the time; reach for bullet(IDENT) + same_line + custom-glyph only when you genuinely need the manual layout.

8.18.1.5.1.5. label_text

label_text((key, value)) maps to ImGui’s LabelText(key, value). ImGui draws the value text on the left and the key as a label to its right — so (key = "Version", value = "v2.0-detour") renders v2.0-detour on the left with Version to its right. Both strings round-trip through the snapshot payload, so snapshot-driven property tests get both halves for assertion.

8.18.1.5.1.6. separator_text

separator_text("Section name") is a horizontal rule with the label centered above it. Use it to break a long window into named sections; much easier to scan than separator() followed by text("Section name").

8.18.1.5.1.7. Snapshot shape

With an ident, a narrative widget registers at NARRATIVE_WIN/IDENT — a stable path you can pull a field from. Without one it still surfaces a path, but keyed by source line (NARRATIVE_WIN/:61:8), which shifts when you edit the file; pass an ident whenever a test or driver needs to target the line.

curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command \
    | jq '.globals."NARRATIVE_WIN/VERSION_LABEL".payload.value'

See also

Full source: modules/dasImgui/examples/tutorial/narrative_widgets.das

Integration tests: modules/dasImgui/tests/test_narrative_text.das, modules/dasImgui/tests/test_narrative_bullet.das, modules/dasImgui/tests/test_narrative_separator.das.

Boost macros — the macro layer.