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: examples/tutorial/narrative_widgets.das.

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

Requires

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

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.

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).

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.

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.

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").

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: examples/tutorial/narrative_widgets.das

Integration tests: tests/integration/test_narrative_text.das, tests/integration/test_narrative_bullet.das, tests/integration/test_narrative_separator.das.

Boost macros — the macro layer.