How to Implement Custom InputText Behavior with ImGuiInputTextCallback in Dear ImGui
To implement custom InputText behavior in Dear ImGui, pass one or more ImGuiInputTextFlags_Callback* flags to InputText() or InputTextMultiline(), then handle events like resize, character filtering, or completion inside a callback function that manipulates the ImGuiInputTextCallbackData structure.
The ocornut/imgui library provides a powerful callback system that lets you intercept and modify text input at multiple stages of the editing pipeline. By understanding the ImGuiInputTextCallbackData structure and the specific flags available, you can implement dynamic resizing, input validation, auto-completion, and history navigation.
Understanding the ImGuiInputTextCallback System
Dear ImGui's InputText() family functions are thin wrappers around the internal InputTextEx() implementation located in imgui_widgets.cpp. When you pass callback flags, the widget constructs an ImGuiInputTextCallbackData object and invokes your user-supplied function whenever the corresponding event occurs.
The following flags control when your callback executes:
ImGuiInputTextFlags_CallbackResize– Triggered when the buffer needs to grow beyond the initially providedbuf_size. Use this to reallocate backing storage and updatedata->Bufanddata->BufSize.ImGuiInputTextFlags_CallbackEdit– Called after any edit operation (insertion, deletion). Inspect or modifydata->Buf,CursorPos, andSelectionStart/End.ImGuiInputTextFlags_CallbackAlways– Executed every frame while the widget has focus. Ideal for live validation and visual feedback.ImGuiInputTextFlags_CallbackCharFilter– Invoked for each typed character before insertion. Replacedata->EventCharor discard it by returning1.ImGuiInputTextFlags_CallbackCompletion– Fired when the user presses TAB. Implement suggestion logic or insert completion characters here.ImGuiInputTextFlags_CallbackHistory– Triggered on ↑/↓ key presses. Use this to navigate a custom command history.
The callback data structure is defined in imgui.h (around line 2670) and provides helper methods like InsertChars() and DeleteChars() that automatically respect the resize callback.
Implementing the Core Callback Handler
To create a robust implementation, follow this workflow:
- Create a user-data struct to hold your dynamic buffer (e.g.,
std::string) and any additional state or chained callbacks. - Set the appropriate flags when calling
InputText()orInputTextMultiline(). - Switch on
data->EventFlaginside your callback to handle specific events:- For
CallbackResize, reallocate your buffer, updatedata->Bufwith the new pointer, and setdata->BufSize. - For
CallbackCharFilter, inspectdata->EventCharand return1to discard unwanted characters. - For edit or always callbacks, modify
data->Bufdirectly and setdata->BufDirty = trueto signal changes.
- For
- Return 0 to accept the event (except for character filtering where returning
1discards the character).
The widget maintains its internal state in ImGuiInputTextState (a member of ImGuiContext), and the callback is invoked from InputTextEx() after the state processes the event. Prefer using InsertChars() and DeleteChars() over manual buffer manipulation, as these methods handle reallocation boundaries safely.
Complete Example: Dynamic std::string with Character Filtering
Below is a production-ready pattern that combines dynamic resizing with input validation. This example uses a std::string as backing storage and rejects numeric digits.
// User-data structure holding the string and optional chained callback
struct InputTextUserData {
std::string* Str;
ImGuiInputTextCallback UserCallback = nullptr;
void* UserCallbackData = nullptr;
};
// The callback implementation
int CustomInputCallback(ImGuiInputTextCallbackData* data)
{
InputTextUserData* user = (InputTextUserData*)data->UserData;
// Handle buffer resize for dynamic std::string
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
{
std::string& s = *user->Str;
IM_ASSERT(data->Buf == s.c_str()); // Sanity check
s.resize(data->BufTextLen);
data->Buf = (char*)s.c_str();
data->BufSize = s.capacity() + 1;
return 0;
}
// Filter out digits (0-9)
if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter)
{
if (data->EventChar >= '0' && data->EventChar <= '9')
return 1; // Discard digit
}
// Support callback chaining
if (user->UserCallback)
{
data->UserData = user->UserCallbackData;
return user->UserCallback(data);
}
return 0;
}
// Usage in your GUI code
void ShowCustomInput()
{
static std::string text;
InputTextUserData cb_data = { &text, nullptr, nullptr };
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize |
ImGuiInputTextFlags_CallbackCharFilter;
ImGui::InputText("Name (letters only)", &text, flags,
CustomInputCallback, &cb_data);
}
Key implementation details:
- The resize callback ensures the
std::stringgrows automatically as the user types. - The character filter intercepts digits before they reach the buffer.
- The callback uses
data->BufTextLento determine the required size, notstrlen().
Common Callback Patterns
Use these patterns to solve specific interaction requirements:
Auto-Completion (TAB)
Enable ImGuiInputTextFlags_CallbackCompletion and check for data->EventKey == ImGuiKey_Tab. Use data->InsertChars() to append suggestions at data->CursorPos.
History Navigation (↑/↓)
With ImGuiInputTextFlags_CallbackHistory, detect data->EventKey for Up/Down arrows. Maintain a std::vector<std::string> history and replace data->Buf content using DeleteChars() followed by InsertChars().
Live Validation
Combine ImGuiInputTextFlags_CallbackEdit or CallbackAlways with regex or custom logic. If validation fails, modify data->Buf to correct the input and set data->BufDirty = true.
Simple Resize Wrapper
For cases requiring only dynamic resizing without other logic, reference the implementation in misc/cpp/imgui_stdlib.cpp, which demonstrates the minimal resize callback pattern for std::string integration.
Summary
InputText()andInputTextMultiline()forward toInputTextEx()inimgui_widgets.cpp, which manages the callback dispatch loop.- Use
ImGuiInputTextFlags_CallbackResizeto support dynamic buffers likestd::stringby updatingdata->Bufanddata->BufSizeduring reallocation. - Implement
ImGuiInputTextFlags_CallbackCharFilterto sanitize input character-by-character before insertion. - Prefer
InsertChars()andDeleteChars()over raw buffer manipulation to ensure automatic resize handling. - Chain callbacks by storing the original callback pointer in your user-data struct and invoking it after your custom logic.
Frequently Asked Questions
What is the difference between CallbackEdit and CallbackAlways?
ImGuiInputTextFlags_CallbackEdit fires only when the buffer content actually changes (insertions, deletions, or replacements), making it efficient for validation logic that must run after modifications. ImGuiInputTextFlags_CallbackAlways executes every frame while the widget retains focus, which is useful for continuous visual feedback or handling special keys like Enter for auto-indentation, even when no text changes occur.
How do I properly resize a std::string inside the callback?
When handling ImGuiInputTextFlags_CallbackResize, resize your std::string to data->BufTextLen, then update data->Buf with (char*)s.c_str() and data->BufSize with the new capacity plus one. Always verify that data->Buf matches your string's internal pointer before resizing to ensure memory consistency.
Can I chain multiple callbacks together?
Yes. Store the additional callback function pointer and its user data inside your own user-data structure. After processing your custom logic, forward the call by setting data->UserData to the stored user data and returning the result of the chained callback function.
Why is my buffer pointer invalid after resizing?
If you trigger a buffer modification (like InsertChars()) without the ImGuiInputTextFlags_CallbackResize flag set, or if you manually write to data->Buf without updating the pointer after a resize, the internal state may reference freed memory. Always ensure the resize flag is set when using dynamic buffers, and update data->Buf immediately after any reallocation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →