8.18.1.42. Input text widgets

The input_text family is text editing — single-line, multiline, grow-on-overflow, callback-driven, and the inline filter editor. Six widgets, one InputTextState (text_filter uses its own TextFilterState).

input_text(IDENT, (text = "..", flags = ImGuiInputTextFlags....))
input_text_with_hint(IDENT, (text, hint, flags))
input_text_multiline(IDENT, (text, size = float2(w, h), flags))
input_text_growable(IDENT, (text, flags))          // CallbackResize plumbed
input_text_callback(IDENT, text, flags, cb)        // user lambda on flagged events
text_filter(IDENT, (text, width)) + passes_filter(IDENT, line)

Buffer-as-pointer path: state owns array<uint8> buffer + a string mirror. input_text_growable plumbs ImGui’s CallbackResize through a daslang lambda + stack thunk so the buffer expands past state.capacity automatically.

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

8.18.1.42.1. Walkthrough

  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
 18require strings
 19
 20// =============================================================================
 21// TUTORIAL: input_text family — text editing widgets.
 22//
 23//   input_text(IDENT, (text = "..", flags = ImGuiInputTextFlags....))
 24//   input_text_with_hint(IDENT, (text, hint, flags))
 25//   input_text_multiline(IDENT, (text, size = float2(w, h), flags))
 26//   input_text_growable(IDENT, (text, flags))         — auto-grows buffer
 27//   input_text_callback(IDENT, text, flags, cb)       — user callback
 28//   text_filter(IDENT, (text, width)) + passes_filter(IDENT, line)
 29//
 30// Six widgets, six modalities sharing one InputTextState struct (text_filter
 31// uses TextFilterState — different shape, integrates with the family).
 32//
 33// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/input_text.das
 34// LIVE:       daslang-live modules/dasImgui/examples/tutorial/input_text.das
 35//
 36// DRIVE (when running live):
 37//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"IT_WIN/IT_NAME","value":"hello"}}' \
 38//        localhost:9090/command
 39//   curl -X POST -d '{"name":"imgui_force_set","args":{"target":"IT_WIN/IT_BIO","value":"line1\nline2"}}' \
 40//        localhost:9090/command
 41// =============================================================================
 42
 43let private COMMANDS <- ["help", "history", "clear", "classify", "echo", "exit"]
 44
 45let private LOG_LINES <- [
 46    "[info] startup complete",
 47    "[warn] cache miss for key=42",
 48    "[info] connected to api.example.com",
 49    "[error] failed to write tmp/foo: disk full",
 50    "[info] saved settings",
 51    "[debug] frame budget = 16.6ms"
 52]
 53
 54//! Indexed-form text widget requires explicit table declaration at module scope.
 55var private IT_LINE : table<int; NarrativeState>
 56
 57
 58def private completion_cb(var data : ImGuiInputTextCallbackData) : int {
 59    if (data.EventFlag == ImGuiInputTextFlags.CallbackCompletion) {
 60        let prefix = clone_string(data.Buf)
 61        var matches <- [for (c in COMMANDS); c; where c |> starts_with(prefix)]
 62        if (length(matches) == 1) {
 63            data |> DeleteChars(0, data.BufTextLen)
 64            data |> InsertChars(0, matches[0])
 65        }
 66        delete matches
 67    }
 68    return 0
 69}
 70
 71[export]
 72def init() {
 73    live_create_window("dasImgui input_text tutorial", 760, 760)
 74    live_imgui_init(live_window)
 75    let io & = unsafe(GetIO())
 76    GetStyle().FontScaleMain = 1.4
 77}
 78
 79[export]
 80def update() {
 81    if (!live_begin_frame()) return
 82    begin_frame()
 83
 84    ImGui_ImplGlfw_NewFrame()
 85    apply_synth_io_override()
 86    NewFrame()
 87
 88    SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond.Always)
 89    SetNextWindowSize(ImVec2(720.0f, 720.0f), ImGuiCond.Always)
 90    window(IT_WIN, (text = "input_text tutorial", closable = false,
 91                    flags = ImGuiWindowFlags.None)) {
 92
 93        // ---- Stage 1: basic input_text ----
 94        input_text(IT_NAME, (text = "name"))
 95        text("IT_NAME.value = '{IT_NAME.value}'  (capacity={IT_NAME.capacity})")
 96        spacing()
 97
 98        // ---- Stage 2: with_hint — placeholder when empty ----
 99        input_text_with_hint(IT_EMAIL, (text = "email",
100                                        hint = "you@example.com"))
101        text("IT_EMAIL.value = '{IT_EMAIL.value}'")
102        spacing()
103
104        // ---- Stage 3: multiline — CR/LF tolerant, sized box ----
105        input_text_multiline(IT_BIO, (text = "bio",
106                                      size = float2(0.0f, 90.0f)))
107        text("IT_BIO length = {length(IT_BIO.value)} bytes")
108        spacing()
109
110        // ---- Stage 4: growable — buffer expands past `capacity` automatically ----
111        input_text_growable(IT_FREE, (text = "free-form"))
112        text("IT_FREE capacity = {IT_FREE.capacity}, length = {length(IT_FREE.value)}")
113        spacing()
114
115        // ---- Stage 5: callback — TAB completes against COMMANDS dictionary ----
116        text("Type a prefix (h/cla/ex) and press TAB:")
117        if (input_text_callback(IT_CMD, "command",
118                                ImGuiInputTextFlags.CallbackCompletion,
119                                @(var data : ImGuiInputTextCallbackData) =>
120                                    completion_cb(data))) {}
121        spacing()
122        separator()
123
124        // ---- Stage 6: text_filter — incl,-excl token filter ----
125        text("text_filter - type 'info' or '-debug' to filter the log:")
126        text_filter(IT_FILTER)
127        for (i in range(length(LOG_LINES))) {
128            if (passes_filter(IT_FILTER, LOG_LINES[i])) {
129                text(IT_LINE[i], (text = LOG_LINES[i]))
130            }
131        }
132    }
133
134    end_of_frame()
135    Render()
136    var w, h : int
137    live_get_framebuffer_size(w, h)
138    glViewport(0, 0, w, h)
139    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
140    glClear(GL_COLOR_BUFFER_BIT)
141    live_imgui_render()
142
143    live_end_frame()
144}
145
146[export]
147def shutdown() {
148    live_imgui_shutdown()
149    live_destroy_window()
150}
151
152[export]
153def main() {
154    init()
155    while (!exit_requested()) {
156        update()
157    }
158    shutdown()
159}

8.18.1.42.1.1. Requires

Already in the baseline boost layer:

  • imgui/imgui_widgets_builtin — every input_text* rail + text_filter + passes_filter.

  • imgui/imgui_boost_runtimeInputTextState (buffer + mirror) and TextFilterState (bound ImGuiTextFilter inline).

  • strings — for starts_with used in the callback completion stage.

8.18.1.42.1.2. Basic and with_hint

input_text is the single-line editor backed by a fixed-size buffer (state.capacity, default 256 bytes). input_text_with_hint adds a placeholder rendered when the buffer is empty:

input_text(NAME, (text = "name"))
input_text_with_hint(EMAIL, (text = "email",
                             hint = "you@example.com"))

Both share InputTextState. state.value is a clone of the buffer content updated each frame.

8.18.1.42.1.3. Multiline

input_text_multiline accepts CR / LF and exposes a sized text box. size = float2(0, 0) lets ImGui pick a default rect — wide and short. Pass an explicit (w, h) for editor-style panels:

input_text_multiline(BIO, (text = "bio",
                           size = float2(0.0f, 90.0f)))  // 90px tall

8.18.1.42.1.4. Growable

input_text_growable is the same as input_text but the buffer resizes itself when the user types past state.capacity. ImGui’s CallbackResize event is plumbed through a daslang lambda; the thunk holding (Context*, LineInfo*, lambda) lives on the C call stack for the duration of the ImGui::InputText call:

input_text_growable(FREE, (text = "free-form"))
// state.capacity grows as needed; state.buffer follows

Use this when the input is genuinely free-form (notes, code, long URLs); use the fixed-size form when 256 bytes is enough (names, emails, identifiers).

8.18.1.42.1.5. Callback

input_text_callback takes an extra cb arg — a lambda fired by ImGui on flagged events. Wire the events you want via flags:

  • CallbackCompletion — TAB pressed

  • CallbackHistory — Up/Down arrows

  • CallbackAlways — every frame the field is active

  • CallbackCharFilter — per-character; return non-zero to reject

  • CallbackEdit — buffer modified

  • CallbackResize — buffer overflow (use input_text_growable for the standard impl)

input_text_callback(CMD, "command",
                    ImGuiInputTextFlags.CallbackCompletion,
                    @(var data : ImGuiInputTextCallbackData) =>
                        completion_cb(data))

The data : ImGuiInputTextCallbackData pointer is valid only inside the call. data.Buf is the current buffer; DeleteChars / InsertChars are the in-place edit helpers. Cloning data.Buf to a daslang string + doing the matching in pure das is the common pattern.

8.18.1.42.1.6. text_filter

text_filter renders an inline InputText editor whose buffer is parsed as comma-separated tokens:

  • foo,bar — pass lines containing foo OR bar

  • -debug — exclude lines containing debug

  • info,-debug — combine

Pair with passes_filter(STATE, line) : bool to gate output:

text_filter(FILTER)
for (i in range(length(LOG_LINES))) {
    if (passes_filter(FILTER, LOG_LINES[i])) {
        text(LOG_LINE[i], (text = LOG_LINES[i]))
    }
}

While the filter expression is empty, passes_filter returns true for every line — the filter is silently disabled until the user types. Use is_active(STATE) to branch on filter-empty vs filter-non-empty when you want a different code path (e.g. clipper-cull vs sequential scan).

8.18.1.42.1.7. Indexed-form note

The text(IDENT[i], (text = ...)) form requires you to declare the table at module scope:

var private LOG_LINE : table<int; NarrativeState>

The single-state form (text(IDENT, (text = ...))) auto-emits the state global from the macro. Indexed form does not — the table key type needs the user’s hand.

8.18.1.42.1.8. Driving from outside

The walkthrough above types into every field with real synthetic key events (a real click to focus, then imgui_key_type), so it exercises exactly what a user would: the completion callback fires on Tab, the filter narrows the log as you type, and one \n in the multiline buffer inserts exactly one line break. For scripted setup that skips the keystrokes, imgui_force_set writes state.pending_value and the next frame overwrites the buffer:

curl -X POST -d '{"name":"imgui_force_set","args":{"target":"IT_WIN/IT_NAME","value":"hello"}}' \
     localhost:9090/command
# Multiline content — embed \n in the JSON string:
curl -X POST -d '{"name":"imgui_force_set","args":{"target":"IT_WIN/IT_BIO","value":"line 1\nline 2"}}' \
     localhost:9090/command

For input_text_growable, sending a payload longer than current state.capacity triggers CallbackResize on the same frame the buffer is consumed.

text_filter is currently read-only via telemetry — there’s no imgui_force_set path that writes the filter expression. Click the filter field and type directly to change it.

See also

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

Features-side demos: modules/dasImgui/examples/features/inputs_text.das (all five non-filter forms) and modules/dasImgui/examples/features/input_text_callback.das (canonical TAB-completion).

Sibling tutorial: Input numeric widgets.

Boost macros — the macro layer.