Dear ImGui is a bloat-free graphical user interface library for C++. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer-agnostic, and self-contained (no external dependencies). Dear ImGui is designed to enable fast iterations and to empower programmers to create content creation tools, visualization/debug tools, and any UI for games or real-time applications.
Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any application where operating-system features are non-standard.
1. Quick Start
What Is Dear ImGui?
Dear ImGui (short for "Dear Immediate Mode Graphical User Interface") is a C++ library that makes it trivially easy to add debug UIs, tools, and overlays to any application that already has a rendering pipeline. Unlike retained-mode GUI frameworks (Qt, wxWidgets, GTK), Dear ImGui redraws the entire UI every frame from your code — there is no hidden widget state to synchronize. You describe the UI, Dear ImGui draws it, and returns interaction results immediately.
Key Features
- Immediate mode — no widget objects, no callbacks, no synchronization. UI code lives alongside your logic.
- Renderer agnostic — outputs vertex buffers (ImDrawList) that you feed to any GPU API: OpenGL, DirectX 9/10/11/12, Vulkan, Metal, WebGPU, SDL_GPU, or your own.
- Self-contained — no external dependencies. STB-style single-header libraries for font rasterization and text editing are bundled.
- Keyboard & gamepad navigation — full navigation support without a mouse, including tabbing, directional movement, and activation.
- Tables — feature-rich table widget with sorting, reordering, hiding columns, horizontal scrolling, and freeze rows/columns.
- Multi-select — beta API for multi-selection in lists, tables, and tree nodes with range-select and Ctrl+click.
- Drag and drop — built-in drag-and-drop system with typed payloads between any widgets.
- Extensive widget library — text inputs, sliders, drags, color pickers, trees, tabs, menus, popups, modals, plots, images, and more.
Prerequisites
| Requirement | Minimum | Recommended |
|---|---|---|
| C++ standard | C++11 | C++14 or later |
| Operating system | Any (Windows, macOS, Linux, iOS, Android, Emscripten) | — |
| Graphics API | Any (or software rasterizer) | OpenGL 3+, DirectX 11, Vulkan |
| Build system | Any (add .cpp files to your project) |
— |
Minimal Code Example
#include "imgui.h"
#include "imgui_impl_allegro5.h"
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
int main()
{
// Platform/renderer setup (Allegro5 example)
al_init();
al_init_primitives_addon();
ALLEGRO_DISPLAY* display = al_create_display(1280, 720);
// Initialize Dear ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
// Setup platform/renderer backends
ImGui_ImplAllegro5_Init(display);
// Style
ImGui::StyleColorsDark();
bool running = true;
while (running)
{
// Poll events...
ImGui_ImplAllegro5_NewFrame();
ImGui::NewFrame();
// Your UI code — this is the entire API pattern:
ImGui::Begin("My Tool");
ImGui::Text("Hello, Dear ImGui!");
static float value = 0.0f;
ImGui::SliderFloat("Value", &value, 0.0f, 1.0f);
if (ImGui::Button("Press Me"))
{
// React to button press
}
ImGui::End();
// Render
ImGui::Render();
al_clear_to_color(al_map_rgb(0, 0, 0));
ImGui_ImplAllegro5_RenderDrawData(ImGui::GetDrawData());
al_flip_display();
}
// Cleanup
ImGui_ImplAllegro5_Shutdown();
ImGui::DestroyContext();
al_destroy_display(display);
return 0;
}
What to Try Next
- ShowDemoWindow() — call
ImGui::ShowDemoWindow()anywhere in your frame loop to see every widget in action with source code. - Examples — the
examples/folder contains 26 ready-to-build projects for every major platform/renderer combination. - Wiki — the Dear ImGui Wiki covers integration, third-party bindings, and community resources.
Tip
ImGui::ShowDemoWindow()is the single best learning resource for Dear ImGui. Every widget, layout technique, and API pattern is demonstrated with inline code. You can Ctrl+click on any demo code in the Demo window to jump to the corresponding source inimgui_demo.cpp. Treat it as a living, interactive reference manual.
2. Installation
Dear ImGui is a source-level library — there is no package manager install, no pre-built binaries, and no cmake install target. You add the source files directly to your project.
System Requirements
| Component | Requirement |
|---|---|
| Language | C++11 or later |
| OS | Windows, macOS, Linux, iOS, Android, Emscripten, consoles |
| GPU API | Any — Dear ImGui outputs vertex buffers; you provide the renderer |
| Dependencies | None — all dependencies are bundled |
| Build system | Any — Visual Studio, Make, CMake, Meson, Premake, Xcode, etc. |
Step 1: Add Core Files
Copy these files into your project (or add them as includes from the imgui directory):
| File | Purpose |
|---|---|
imgui.cpp |
Core library (context, windows, widgets, navigation) |
imgui_draw.cpp |
Drawing, font atlas, ImDrawList |
imgui_tables.cpp |
Table widget |
imgui_widgets.cpp |
Standard widgets (buttons, sliders, inputs, trees, etc.) |
imgui.h |
Main header — include this in your code |
imgui_internal.h |
Internal header (included by imgui.cpp; rarely needed by users) |
imconfig.h |
Compile-time configuration (user-editable) |
imstb_rectpack.h |
STB rect packing for font atlas |
imstb_textedit.h |
STB text editing for InputText |
imstb_truetype.h |
STB TrueType rasterizer (unless using FreeType) |
Step 2: Pick a Platform + Renderer Backend
Backends live in the backends/ folder. You need one platform backend (handles window creation, input, clipboard) and one renderer backend (handles drawing via GPU API).
Backend Combinations
| Platform ↓ / Renderer → | OpenGL2 | OpenGL3 | DirectX9 | DirectX10 | DirectX11 | DirectX12 | Vulkan | Metal | WebGPU | SDL_GPU | SDL_Renderer | Allegro5 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| GLFW | Yes | Yes | — | — | — | — | Yes | Yes | Yes | — | — | — |
| SDL2 | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | — | — | Yes (2) | — |
| SDL3 | — | Yes | — | — | — | — | Yes | Yes | Yes | Yes | Yes (3) | — |
| Win32 | — | — | Yes | Yes | Yes | Yes | Yes | — | — | — | — | — |
| GLUT | — | Yes | — | — | — | — | — | — | — | — | — | — |
| OSX (Cocoa) | — | Yes | — | — | — | — | — | Metal | — | — | — | — |
| Android | — | Yes | — | — | — | — | — | — | — | — | — | — |
| Allegro5 | — | — | — | — | — | — | — | — | — | — | — | Yes |
Backend files follow the naming convention imgui_impl_<name>.h and imgui_impl_<name>.cpp. For example, to use GLFW + OpenGL3:
// Add these files to your build:
// backends/imgui_impl_glfw.cpp
// backends/imgui_impl_glfw.h
// backends/imgui_impl_opengl3.cpp
// backends/imgui_impl_opengl3.h
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
Step 3: Review Example Applications
The examples/ folder contains ready-to-build projects for every supported backend combination:
| Example | Platform | Renderer |
|---|---|---|
example_allegro5/ |
Allegro5 | Allegro5 |
example_android_opengl3/ |
Android | OpenGL ES 3 |
example_apple_metal/ |
OSX (Cocoa) | Metal |
example_apple_opengl2/ |
OSX (Cocoa) | OpenGL 2 |
example_emscripten_wgpu/ |
Emscripten + GLFW | WebGPU |
example_glfw_metal/ |
GLFW | Metal |
example_glfw_opengl2/ |
GLFW | OpenGL 2 |
example_glfw_opengl3/ |
GLFW | OpenGL 3 |
example_glfw_vulkan/ |
GLFW | Vulkan |
example_glfw_wgpu/ |
GLFW | WebGPU |
example_glut_opengl2/ |
GLUT | OpenGL 2 |
example_null/ |
Null | Null (no rendering) |
example_sdl2_directx11/ |
SDL2 | DirectX 11 |
example_sdl2_metal/ |
SDL2 | Metal |
example_sdl2_opengl2/ |
SDL2 | OpenGL 2 |
example_sdl2_opengl3/ |
SDL2 | OpenGL 3 |
example_sdl2_sdlrenderer2/ |
SDL2 | SDL_Renderer (SDL2) |
example_sdl2_vulkan/ |
SDL2 | Vulkan |
example_sdl3_opengl3/ |
SDL3 | OpenGL 3 |
example_sdl3_sdlgpu3/ |
SDL3 | SDL_GPU |
example_sdl3_sdlrenderer3/ |
SDL3 | SDL_Renderer (SDL3) |
example_sdl3_vulkan/ |
SDL3 | Vulkan |
example_win32_directx9/ |
Win32 | DirectX 9 |
example_win32_directx10/ |
Win32 | DirectX 10 |
example_win32_directx11/ |
Win32 | DirectX 11 |
example_win32_directx12/ |
Win32 | DirectX 12 |
example_win32_opengl3/ |
Win32 | OpenGL 3 |
example_win32_vulkan/ |
Win32 | Vulkan |
Each example directory contains a main.cpp and platform-specific project files (.vcxproj, Makefile, build_win32.bat, etc.).
Step 4: Build
Add the .cpp files to your existing build system. No special build process is required — no code generation, no pre-processing, no configure step.
Visual Studio: Add the .cpp files to your project. Add the imgui/ and imgui/backends/ directories to your include paths.
CMake:
add_executable(my_app
main.cpp
imgui/imgui.cpp
imgui/imgui_draw.cpp
imgui/imgui_tables.cpp
imgui/imgui_widgets.cpp
imgui/backends/imgui_impl_glfw.cpp
imgui/backends/imgui_impl_opengl3.cpp
)
target_include_directories(my_app PRIVATE imgui imgui/backends)
Makefile:
IMGUI_SRCS = imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp
BACKEND_SRCS = imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp
Step 5: Verify
// In your initialization code:
IMGUI_CHECKVERSION(); // Verify header/library version match
ImGui::CreateContext();
ImGui::ShowDemoWindow(); // Should display the demo window
Important
imconfig.his a compile-time configuration file. If you modify it, you must recompile ALL translation units that includeimgui.h. Inconsistentimconfig.hacross translation units causes subtle memory corruption and crashes. If Dear ImGui is compiled as a static library or DLL, the sameimconfig.hmust be used for both the library build and your application.
Optional: FreeType Font Rasterizer
For higher-quality font rendering, enable FreeType:
#define IMGUI_ENABLE_FREETYPEinimconfig.h- Add
misc/freetype/imgui_freetype.cppto your build - Link against FreeType library
For SVG-in-font support (color emoji, icon fonts), additionally define IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG and link the corresponding library.
Optional: std::string Support
// Add to your build:
// misc/cpp/imgui_stdlib.cpp
// misc/cpp/imgui_stdlib.h
#include "misc/cpp/imgui_stdlib.h"
std::string my_text = "Hello";
ImGui::InputText("Input", &my_text); // Works with std::string directly
Optional: Debugger Helpers
| File | Debugger | Purpose |
|---|---|---|
misc/debuggers/imgui.natvis |
Visual Studio | Pretty-print ImVector, ImGuiID, ImGuiCol, etc. |
misc/debuggers/imgui.gdb |
GDB | Pretty-print ImVector |
misc/debuggers/imgui.natstepfilter |
Visual Studio | Skip internal functions when stepping |
misc/debuggers/imgui.py |
LLDB | Pretty-print for LLDB |
Optional: Single-File Build
For projects that prefer a single compilation unit:
// imgui_single_file.h is provided in misc/single_file/
#include "misc/single_file/imgui_single_file.h"
This #includes all four core .cpp files into a single translation unit.
3. Configuration
Compile-Time Configuration (imconfig.h)
These #defines must be set in imconfig.h before compiling. They cannot be changed at runtime.
| Define | Default | Purpose |
|---|---|---|
IM_ASSERT(EXPR) |
assert(EXPR) |
Override assertion macro. Define to your own crash/log handler. |
IMGUI_API |
(empty) | Decorate public symbols for DLL export/import (__declspec(dllexport), etc.) |
IMGUI_DISABLE_OBSOLETE_FUNCTIONS |
Not set | Remove deprecated functions to catch outdated usage at compile time |
IMGUI_DISABLE_OBSOLETE_KEYIO |
Not set | Remove legacy key mapping (pre-1.87 backends) |
IMGUI_DISABLE |
Not set | Stub out all functions (for conditional compilation) |
IMGUI_DISABLE_DEMO_WINDOWS |
Not set | Remove ShowDemoWindow() and imgui_demo.cpp code |
IMGUI_DISABLE_DEBUG_TOOLS |
Not set | Remove debug/metrics tools |
IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS |
Not set | Disable Win32 clipboard handling |
IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS |
Not set | Disable Win32 IME handling |
IMGUI_DISABLE_WIN32_FUNCTIONS |
Not set | Disable all Win32-specific functions |
IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS |
Not set | Disable default ImGui::PlatformOpenInShellFn |
IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS |
Not set | Disable default ImFileOpen/ImFileClose/ImFileRead/ImFileWrite |
IMGUI_DISABLE_DEFAULT_ALLOCATORS |
Not set | Disable default malloc/free-based allocators |
IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS |
Not set | Disable bundled ImFormatString/ImFormatStringV |
IMGUI_ENABLE_TEST_ENGINE |
Not set | Enable hooks for Dear ImGui Test Engine |
IMGUI_USE_BGRA_PACKED_COLOR |
Not set | Use BGRA color packing (for some Direct3D backends) |
IMGUI_USE_WCHAR32 |
Not set | Use 32-bit ImWchar for full Unicode support beyond BMP |
IMGUI_ENABLE_FREETYPE |
Not set | Use FreeType for font rasterization instead of stb_truetype |
IMGUI_ENABLE_FREETYPE_PLUTOSVG |
Not set | Enable PlutoSVG for SVG-in-font support (requires FreeType) |
IMGUI_ENABLE_FREETYPE_LUNASVG |
Not set | Enable LunaSVG for SVG-in-font support (requires FreeType) |
IMGUI_DEFINE_MATH_OPERATORS |
Not set | Enable +, -, *, / operators on ImVec2/ImVec4 |
ImDrawIdx |
unsigned short |
Set to unsigned int for 32-bit draw indices (meshes >64K vertices) |
IM_VEC2_CLASS_EXTRA |
Not set | Inject methods/operators into ImVec2 (e.g., conversion to your math type) |
IM_VEC4_CLASS_EXTRA |
Not set | Inject methods/operators into ImVec4 |
IMGUI_STB_TRUETYPE_FILENAME |
"imstb_truetype.h" |
Override bundled stb_truetype |
IMGUI_STB_RECT_PACK_FILENAME |
"imstb_rectpack.h" |
Override bundled stb_rectpack |
IMGUI_STB_SPRINTF_FILENAME |
Not set | Use stb_sprintf instead of system snprintf |
Math type interop example:
// In imconfig.h — let ImVec2 convert to/from your engine's Vector2:
#define IM_VEC2_CLASS_EXTRA \
constexpr ImVec2(const MyEngine::Vector2& v) : x(v.x), y(v.y) {} \
operator MyEngine::Vector2() const { return MyEngine::Vector2(x, y); }
Runtime Configuration (ImGuiIO)
After CreateContext(), configure behavior through ImGui::GetIO():
ImGuiIO& io = ImGui::GetIO();
// Navigation
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Keyboard navigation
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Gamepad navigation
// Behavior
io.ConfigInputTextCursorBlink = true; // Blinking cursor in text inputs
io.ConfigWindowsResizeFromEdges = true; // Resize from window edges (not just corner)
io.ConfigWindowsMoveFromTitleBarOnly = false; // If true, only title bar drags windows
io.ConfigScrollbarScrollByPage = true; // Page-scroll when clicking scrollbar track
// Persistence
io.IniFilename = "imgui.ini"; // Set to NULL to disable auto-save/load
// Display
io.FontGlobalScale = 1.0f; // Global font scale multiplier
Key ImGuiConfigFlags:
| Flag | Purpose |
|---|---|
ImGuiConfigFlags_NavEnableKeyboard |
Enable keyboard navigation |
ImGuiConfigFlags_NavEnableGamepad |
Enable gamepad navigation |
ImGuiConfigFlags_NoMouseCursorChange |
Prevent Dear ImGui from changing the OS cursor shape |
ImGuiConfigFlags_NoMouse |
Disable mouse input |
ImGuiConfigFlags_NavNoCaptureKeyboard |
Do not set io.WantCaptureKeyboard during navigation |
Built-In Styles
ImGui::StyleColorsDark(); // Dark theme (default) — best for most tools
ImGui::StyleColorsLight(); // Light theme
ImGui::StyleColorsClassic(); // Classic ImGui theme (gray with blue highlights)
Tip
Call
ImGui::ShowStyleEditor()anywhere in your frame loop to open an interactive style editor. You can tweak every color, size, and rounding value in real time and export the result as code. This is the fastest way to create a custom theme.
For a complete reference of all configuration options, style variables, and color IDs, see the Admin Guide.
4. Core Usage
Dear ImGui follows a consistent pattern: every frame, you call functions that describe your UI, and Dear ImGui handles layout, drawing, and interaction. This section covers every widget category organized by what you want to accomplish.
4.1 Creating Windows
Every piece of Dear ImGui content lives inside a window created with Begin()/End():
ImGui::Begin("My Window");
// ... widgets go here ...
ImGui::End();
Begin() returns false when the window is collapsed, but you must always call End() regardless. For optimization, skip widget code when collapsed:
if (ImGui::Begin("My Window"))
{
ImGui::Text("Visible content");
}
ImGui::End(); // Always required
Window Flags
Pass flags as the third parameter to Begin():
ImGui::Begin("My Window", &show_window, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoResize);
| Flag | Effect |
|---|---|
ImGuiWindowFlags_NoTitleBar |
No title bar |
ImGuiWindowFlags_NoResize |
User cannot resize |
ImGuiWindowFlags_NoMove |
User cannot move |
ImGuiWindowFlags_NoScrollbar |
No scrollbar (content clips) |
ImGuiWindowFlags_NoScrollWithMouse |
Disable mouse wheel scrolling |
ImGuiWindowFlags_NoCollapse |
No collapse button |
ImGuiWindowFlags_AlwaysAutoResize |
Resize to fit content every frame |
ImGuiWindowFlags_NoBackground |
No background fill |
ImGuiWindowFlags_NoSavedSettings |
Never save/load position/size in .ini |
ImGuiWindowFlags_MenuBar |
Has a menu bar (use with BeginMenuBar()) |
ImGuiWindowFlags_HorizontalScrollbar |
Enable horizontal scrollbar |
ImGuiWindowFlags_NoFocusOnAppearing |
Don't take focus when appearing |
ImGuiWindowFlags_NoBringToFrontOnFocus |
Don't raise to front when focused |
ImGuiWindowFlags_AlwaysVerticalScrollbar |
Always show vertical scrollbar |
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
Always show horizontal scrollbar |
ImGuiWindowFlags_NoNavInputs |
No navigation within this window |
ImGuiWindowFlags_NoNavFocus |
No focusing via navigation |
ImGuiWindowFlags_NoNav |
Shorthand for NoNavInputs + NoNavFocus |
ImGuiWindowFlags_NoDecoration |
Shorthand for NoTitleBar + NoResize + NoScrollbar + NoCollapse |
ImGuiWindowFlags_NoInputs |
Shorthand for NoMouseInputs + NoNavInputs + NoNavFocus |
Window Manipulation
Set properties for the next Begin() call:
ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowCollapsed(false, ImGuiCond_Once);
ImGui::SetNextWindowFocus();
ImGui::SetNextWindowBgAlpha(0.8f);
Condition flags (ImGuiCond_):
| Flag | When applied |
|---|---|
ImGuiCond_Always |
Every frame |
ImGuiCond_Once |
First time only (per session) |
ImGuiCond_FirstUseEver |
First time ever (across .ini sessions) |
ImGuiCond_Appearing |
Each time window appears after being hidden |
Child Windows
Create scrollable sub-regions within a window:
// With explicit size
ImGui::BeginChild("ScrollRegion", ImVec2(0, 300), ImGuiChildFlags_Borders);
for (int i = 0; i < 100; i++)
ImGui::Text("Item %d", i);
ImGui::EndChild();
// Auto-sizing child
ImGui::BeginChild("AutoChild", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY);
ImGui::Text("This child sizes to its content vertically");
ImGui::EndChild();
Window Queries
bool focused = ImGui::IsWindowFocused();
bool hovered = ImGui::IsWindowHovered();
ImVec2 pos = ImGui::GetWindowPos();
ImVec2 size = ImGui::GetWindowSize();
4.2 Displaying Text and Basic Widgets
Text Display
ImGui::Text("Simple text");
ImGui::Text("Formatted: %d items, %.2f seconds", count, elapsed);
ImGui::TextColored(ImVec4(1,0,0,1), "Red warning text");
ImGui::TextDisabled("Grayed out hint");
ImGui::TextWrapped("This long text will wrap to the available width "
"of the current window or column.");
ImGui::BulletText("Bulleted item");
ImGui::SeparatorText("Section Header");
Buttons
if (ImGui::Button("Click Me"))
{
// Action happens on the frame the button is clicked
}
if (ImGui::Button("Sized Button", ImVec2(200, 40)))
{
// Button with explicit size
}
ImGui::SmallButton("Compact"); // Small, no frame padding
ImGui::ArrowButton("##left", ImGuiDir_Left); // Directional arrow
// Invisible button — a clickable region with no visual
if (ImGui::InvisibleButton("zone", ImVec2(100, 100)))
{
// Clicked in the invisible region
}
// Hyperlinks
ImGui::TextLink("Open Documentation"); // Returns true when clicked
ImGui::TextLinkOpenURL("Visit GitHub", "https://github.com/ocornut/imgui");
Checkboxes and Radio Buttons
static bool enabled = true;
ImGui::Checkbox("Enable Feature", &enabled);
static int mode = 0;
ImGui::RadioButton("Mode A", &mode, 0); ImGui::SameLine();
ImGui::RadioButton("Mode B", &mode, 1); ImGui::SameLine();
ImGui::RadioButton("Mode C", &mode, 2);
Progress Bar
ImGui::ProgressBar(0.65f, ImVec2(0, 0), "65%");
4.3 Input Widgets
Text Input
static char buf[256] = "";
if (ImGui::InputText("Name", buf, sizeof(buf)))
{
// Text changed this frame
}
static char multiline[1024] = "";
ImGui::InputTextMultiline("Description", multiline, sizeof(multiline),
ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 8));
static char search[128] = "";
ImGui::InputTextWithHint("##search", "Search...", search, sizeof(search));
Key ImGuiInputTextFlags:
| Flag | Effect |
|---|---|
ImGuiInputTextFlags_CharsDecimal |
Allow 0123456789.+-*/ |
ImGuiInputTextFlags_CharsHexadecimal |
Allow 0123456789ABCDEFabcdef |
ImGuiInputTextFlags_CharsScientific |
Allow 0123456789.+-*/eE |
ImGuiInputTextFlags_CharsUppercase |
Force uppercase |
ImGuiInputTextFlags_CharsNoBlank |
Filter spaces and tabs |
ImGuiInputTextFlags_EnterReturnsTrue |
Return true on Enter instead of on every edit |
ImGuiInputTextFlags_CallbackCompletion |
Tab key triggers callback |
ImGuiInputTextFlags_CallbackHistory |
Up/Down triggers callback |
ImGuiInputTextFlags_CallbackResize |
Callback when buffer needs to grow (for std::string) |
ImGuiInputTextFlags_Password |
Mask display with * |
ImGuiInputTextFlags_ReadOnly |
Display only, no editing |
ImGuiInputTextFlags_AutoSelectAll |
Select all on focus |
ImGuiInputTextFlags_NoHorizontalScroll |
Disable horizontal scrolling |
ImGuiInputTextFlags_EscapeClearsAll |
Escape clears the field |
Numeric Input
static float f = 0.5f;
static int i = 42;
// Drag widgets — click and drag to adjust
ImGui::DragFloat("Speed", &f, 0.01f, 0.0f, 1.0f, "%.3f");
ImGui::DragInt("Count", &i, 1, 0, 100);
// Slider widgets — slide within a fixed range
ImGui::SliderFloat("Volume", &f, 0.0f, 1.0f, "%.2f");
ImGui::SliderInt("Quality", &i, 0, 10);
ImGui::SliderAngle("Rotation", &f); // Displays in degrees, stores radians
// Direct numeric input
ImGui::InputFloat("Value", &f, 0.1f, 1.0f, "%.3f");
ImGui::InputInt("Count", &i, 1, 10);
ImGui::InputDouble("Precision", &d, 0.001, 0.01, "%.6f");
// Multi-component
static float vec3[3] = {0, 0, 0};
ImGui::DragFloat3("Position", vec3, 0.1f);
ImGui::SliderFloat3("Color", vec3, 0.0f, 1.0f);
ImGui::InputFloat3("Coords", vec3);
Note
Ctrl+Click on any
SliderFloat,SliderInt,DragFloat, orDragIntwidget to convert it into a direct text input box. This lets users type exact values when precision matters. The behavior is automatic and requires no extra code.
Color Editing
static float col3[3] = {1.0f, 0.5f, 0.0f};
static float col4[4] = {1.0f, 0.5f, 0.0f, 1.0f};
ImGui::ColorEdit3("Color", col3); // RGB
ImGui::ColorEdit4("Color+Alpha", col4); // RGBA
ImGui::ColorPicker3("Pick Color", col3); // Full picker widget
ImGui::ColorPicker4("Pick Color+A", col4); // Full picker with alpha
// Color button (shows a color swatch, opens picker on click)
ImGui::ColorButton("Preview", ImVec4(col4[0], col4[1], col4[2], col4[3]));
std::string Support
#include "misc/cpp/imgui_stdlib.h"
std::string name = "Default";
ImGui::InputText("Name", &name); // Automatically resizes the string
4.4 Layout and Positioning
Cursor and Content Region
// The two most important layout functions:
ImVec2 pos = ImGui::GetCursorScreenPos(); // Where the next widget will be drawn (screen coords)
ImVec2 avail = ImGui::GetContentRegionAvail(); // Remaining space in current region
Horizontal Layout
ImGui::Button("A"); ImGui::SameLine();
ImGui::Button("B"); ImGui::SameLine();
ImGui::Button("C");
// Result: [A] [B] [C] on one line
ImGui::SameLine(200); // Position next item at x=200 within the window
ImGui::SameLine(0, 20); // Default position, but with 20px spacing
Spacing and Separators
ImGui::NewLine(); // Empty line
ImGui::Spacing(); // Small vertical gap
ImGui::Dummy(ImVec2(0, 20)); // Explicit spacer of given size
ImGui::Separator(); // Horizontal line
ImGui::SeparatorText("Section"); // Line with centered text
Indentation
ImGui::Text("Root");
ImGui::Indent();
ImGui::Text("Indented");
ImGui::Indent();
ImGui::Text("More indented");
ImGui::Unindent(2 * ImGui::GetTreeNodeToLabelSpacing()); // Reset
Groups
Groups let you treat multiple widgets as a single item for layout purposes:
ImGui::BeginGroup();
ImGui::Text("Label:");
ImGui::Text("Value:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text("Name");
ImGui::Text("42");
ImGui::EndGroup();
Item Width
// Set width for the next widget
ImGui::SetNextItemWidth(200);
ImGui::InputFloat("##val", &val);
// Push width for multiple widgets
ImGui::PushItemWidth(200);
ImGui::InputFloat("A", &a);
ImGui::InputFloat("B", &b);
ImGui::PopItemWidth();
// Negative width: relative to right edge
ImGui::PushItemWidth(-100); // Leave 100px on the right for labels
Text Wrapping
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 300); // Wrap at 300px from cursor
ImGui::TextWrapped("This text wraps within the specified width...");
ImGui::PopTextWrapPos();
// Wrap to window edge:
ImGui::PushTextWrapPos(0.0f);
ImGui::TextWrapped("Wraps to the window edge.");
ImGui::PopTextWrapPos();
4.5 Trees and Hierarchies
if (ImGui::TreeNode("Root Node"))
{
ImGui::Text("Leaf content");
if (ImGui::TreeNode("Child Node"))
{
ImGui::Text("Nested content");
ImGui::TreePop();
}
ImGui::TreePop(); // Required for every TreeNode that returns true
}
TreeNodeEx with Flags
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow
| ImGuiTreeNodeFlags_OpenOnDoubleClick
| ImGuiTreeNodeFlags_SpanAvailWidth;
// Leaf node (no arrow, not expandable)
ImGuiTreeNodeFlags leaf_flags = flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
if (ImGui::TreeNodeEx("Parent", flags))
{
ImGui::TreeNodeEx("Leaf Item", leaf_flags);
ImGui::TreePop();
}
CollapsingHeader
Unlike TreeNode, CollapsingHeader does not require TreePop():
if (ImGui::CollapsingHeader("Settings"))
{
ImGui::SliderFloat("Volume", &volume, 0.0f, 1.0f);
ImGui::Checkbox("Mute", &mute);
}
// With close button:
static bool show_header = true;
if (ImGui::CollapsingHeader("Closeable", &show_header))
{
ImGui::Text("Content");
}
Controlling Open State
ImGui::SetNextItemOpen(true, ImGuiCond_Once); // Open by default
if (ImGui::TreeNode("Open by default"))
{
// ...
ImGui::TreePop();
}
4.6 Tables
The table widget is one of Dear ImGui's most powerful features, supporting sorting, reordering, column hiding, freezing, and more.
// Basic table
if (ImGui::BeginTable("my_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
{
// Setup columns (optional but recommended)
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
// Rows
for (int row = 0; row < 5; row++)
{
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Item %d", row);
ImGui::TableSetColumnIndex(1);
ImGui::Text("Type A");
ImGui::TableSetColumnIndex(2);
ImGui::Text("%.2f", row * 1.5f);
}
ImGui::EndTable();
}
Alternative row traversal using TableNextColumn():
if (ImGui::BeginTable("table2", 3))
{
for (int row = 0; row < 5; row++)
{
ImGui::TableNextColumn(); ImGui::Text("Cell %d,0", row);
ImGui::TableNextColumn(); ImGui::Text("Cell %d,1", row);
ImGui::TableNextColumn(); ImGui::Text("Cell %d,2", row);
}
ImGui::EndTable();
}
Table Flags
| Flag | Effect |
|---|---|
ImGuiTableFlags_Resizable |
Columns can be resized |
ImGuiTableFlags_Reorderable |
Columns can be reordered by dragging headers |
ImGuiTableFlags_Hideable |
Columns can be hidden via context menu |
ImGuiTableFlags_Sortable |
Enable sorting (click headers) |
ImGuiTableFlags_SortMulti |
Allow sorting on multiple columns (Shift+click) |
ImGuiTableFlags_RowBg |
Alternate row background colors |
ImGuiTableFlags_Borders |
All borders (alias for BordersInner + BordersOuter) |
ImGuiTableFlags_BordersV |
Vertical borders |
ImGuiTableFlags_BordersH |
Horizontal borders |
ImGuiTableFlags_NoBordersInBody |
No borders in body (headers only) |
ImGuiTableFlags_ScrollX |
Enable horizontal scrolling |
ImGuiTableFlags_ScrollY |
Enable vertical scrolling (requires outer size) |
ImGuiTableFlags_SizingFixedFit |
Columns default to fixed width |
ImGuiTableFlags_SizingStretchSame |
Columns stretch with equal weight |
ImGuiTableFlags_SizingStretchProp |
Columns stretch proportionally |
Sorting
if (ImGui::BeginTable("sortable", 3, ImGuiTableFlags_Sortable))
{
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Score");
ImGui::TableHeadersRow();
if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
{
if (sort_specs->SpecsDirty)
{
// Re-sort your data based on sort_specs->Specs[0].ColumnIndex
// and sort_specs->Specs[0].SortDirection
sort_specs->SpecsDirty = false;
}
}
// ... render sorted rows ...
ImGui::EndTable();
}
Freeze Rows/Columns
if (ImGui::BeginTable("frozen", 3, ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
ImVec2(0, 300)))
{
ImGui::TableSetupScrollFreeze(1, 1); // Freeze 1 column, 1 row
ImGui::TableSetupColumn("Frozen Col");
ImGui::TableSetupColumn("Col 2");
ImGui::TableSetupColumn("Col 3");
ImGui::TableHeadersRow();
// ...
ImGui::EndTable();
}
Warning
The legacy
Columns()API is deprecated. Use theBeginTable()/EndTable()API instead. The table widget offers vastly superior features including sorting, column reordering, column hiding, scrolling, and freeze rows/columns. The oldColumns()API is retained only for backward compatibility and will be removed in a future version.
4.7 Tab Bars
if (ImGui::BeginTabBar("Tabs"))
{
if (ImGui::BeginTabItem("General"))
{
ImGui::Text("General settings");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Graphics"))
{
ImGui::Text("Graphics settings");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Audio"))
{
ImGui::Text("Audio settings");
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
Closeable tabs:
static bool show_tab = true;
if (ImGui::BeginTabItem("Closeable", &show_tab))
{
ImGui::Text("This tab can be closed");
ImGui::EndTabItem();
}
Tab bar utilities:
ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing); // Add-tab button
ImGui::SetTabItemClosed("Tab Name"); // Programmatically close
4.8 Menus
Window Menu Bar
ImGui::Begin("Editor", nullptr, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("New", "Ctrl+N")) { /* action */ }
if (ImGui::MenuItem("Open", "Ctrl+O")) { /* action */ }
ImGui::Separator();
if (ImGui::MenuItem("Quit", "Alt+F4")) { /* action */ }
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Edit"))
{
if (ImGui::MenuItem("Undo", "Ctrl+Z", false, has_undo)) { /* action */ }
if (ImGui::MenuItem("Redo", "Ctrl+Y", false, has_redo)) { /* action */ }
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
ImGui::End();
Main Menu Bar (top of screen)
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File")) { /* ... */ ImGui::EndMenu(); }
if (ImGui::BeginMenu("Edit")) { /* ... */ ImGui::EndMenu(); }
ImGui::EndMainMenuBar();
}
Submenus and Toggle Items
if (ImGui::BeginMenu("View"))
{
ImGui::MenuItem("Show Grid", nullptr, &show_grid); // Toggle
ImGui::MenuItem("Show FPS", nullptr, &show_fps); // Toggle
if (ImGui::BeginMenu("Layout")) // Submenu
{
ImGui::MenuItem("Horizontal");
ImGui::MenuItem("Vertical");
ImGui::EndMenu();
}
ImGui::EndMenu();
}
4.9 Popups and Modals
Basic Popup
if (ImGui::Button("Open Popup"))
ImGui::OpenPopup("my_popup");
if (ImGui::BeginPopup("my_popup"))
{
ImGui::Text("Popup content");
if (ImGui::Button("Close"))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
Modal Dialog
if (ImGui::Button("Delete"))
ImGui::OpenPopup("Confirm Delete");
if (ImGui::BeginPopupModal("Confirm Delete", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("Are you sure you want to delete this item?");
ImGui::Separator();
if (ImGui::Button("OK", ImVec2(120, 0)))
{
// Perform delete
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
Context Menus
// Context menu on the last item
ImGui::Text("Right-click me");
if (ImGui::BeginPopupContextItem("item_context"))
{
if (ImGui::MenuItem("Copy")) { /* ... */ }
if (ImGui::MenuItem("Paste")) { /* ... */ }
ImGui::EndPopup();
}
// Context menu on window background
if (ImGui::BeginPopupContextWindow())
{
if (ImGui::MenuItem("Add Item")) { /* ... */ }
ImGui::EndPopup();
}
// Context menu on void (no window hovered)
if (ImGui::BeginPopupContextVoid())
{
if (ImGui::MenuItem("Settings")) { /* ... */ }
ImGui::EndPopup();
}
Warning
In Dear ImGui 1.92.6, the default popup trigger changed to right-click (
ImGuiPopupFlags_MouseButtonRight). If you relied on left-click to open popups viaOpenPopupOnItemClick(), you must now explicitly passImGuiPopupFlags_MouseButtonLeft. TheBeginPopupContextItem/Window/Voidhelpers already default to right-click and are unaffected.
4.10 Tooltips
// Simple tooltip on hover
ImGui::Button("Hover me");
if (ImGui::IsItemHovered())
ImGui::SetTooltip("This is a tooltip");
// Rich tooltip with multiple widgets
ImGui::Button("Rich tooltip");
if (ImGui::BeginItemTooltip())
{
ImGui::Text("Detailed information:");
ImGui::BulletText("Point 1");
ImGui::BulletText("Point 2");
ImGui::ProgressBar(0.7f);
ImGui::EndTooltip();
}
// Shorthand for text-only item tooltip
ImGui::Button("Short");
ImGui::SetItemTooltip("Tooltip text for the preceding item");
4.11 Images
Dear ImGui can render application textures via Image() and ImageButton(). You must create the texture using your graphics API and pass its handle.
// ImTextureRef wraps a texture identifier + size information (new in 1.92)
ImTextureRef tex = ...; // Backend-specific: e.g., (ImTextureID)(intptr_t)opengl_texture_id
ImGui::Image(tex, ImVec2(256, 256)); // Display at 256x256
ImGui::Image(tex, ImVec2(128, 128), ImVec2(0,0), ImVec2(1,1)); // With UV coords
// Image with background color (for transparent images)
ImGui::ImageWithBg(tex, ImVec2(256, 256), ImVec2(0,0), ImVec2(1,1),
ImVec4(0,0,0,1)); // Black background
// Image button
if (ImGui::ImageButton("btn_id", tex, ImVec2(32, 32)))
{
// Clicked
}
The exact type of ImTextureID depends on your renderer backend:
| Backend | ImTextureID value |
|---|---|
| OpenGL | (ImTextureID)(intptr_t)gl_texture |
| DirectX9 | (ImTextureID)d3d9_texture_ptr |
| DirectX11 | (ImTextureID)srv_ptr |
| DirectX12 | (ImTextureID)gpu_descriptor_handle.ptr |
| Vulkan | (ImTextureID)vk_descriptor_set |
| Metal | (ImTextureID)mtl_texture |
4.12 Selectables and Multi-Select
Selectable
static int selected = -1;
const char* items[] = { "Apple", "Banana", "Cherry", "Date" };
for (int i = 0; i < 4; i++)
{
if (ImGui::Selectable(items[i], selected == i))
selected = i;
}
Multi-Select (Beta)
static ImGuiSelectionBasicStorage selection;
ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, item_count);
selection.ApplyRequests(ms_io);
for (int i = 0; i < item_count; i++)
{
ImGui::SetNextItemSelectionUserData(i);
bool is_selected = selection.Contains((ImGuiID)i);
ImGui::Selectable(items[i], is_selected);
}
ms_io = ImGui::EndMultiSelect();
selection.ApplyRequests(ms_io);
4.13 List Boxes and Combo Boxes
Combo Box
static int current_item = 0;
const char* items[] = { "Option A", "Option B", "Option C" };
// Simple version
ImGui::Combo("Combo", ¤t_item, items, IM_ARRAYSIZE(items));
// Begin/End version (custom content)
if (ImGui::BeginCombo("Custom Combo", items[current_item]))
{
for (int i = 0; i < IM_ARRAYSIZE(items); i++)
{
bool is_selected = (current_item == i);
if (ImGui::Selectable(items[i], is_selected))
current_item = i;
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
List Box
static int current = 0;
if (ImGui::BeginListBox("Items", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
{
for (int i = 0; i < item_count; i++)
{
bool is_selected = (current == i);
if (ImGui::Selectable(items[i], is_selected))
current = i;
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndListBox();
}
4.14 Data Plotting
static float values[100];
// ... fill values ...
ImGui::PlotLines("Signal", values, IM_ARRAYSIZE(values), 0, "overlay text",
-1.0f, 1.0f, ImVec2(0, 80));
ImGui::PlotHistogram("Distribution", values, IM_ARRAYSIZE(values), 0, nullptr,
0.0f, 1.0f, ImVec2(0, 80));
For advanced plotting (axes, legends, multiple series, heatmaps), use the companion library ImPlot.
4.15 Drag and Drop
// Source: the widget you drag FROM
if (ImGui::Button("Drag me"))
{
}
if (ImGui::BeginDragDropSource())
{
int payload_data = 42;
ImGui::SetDragDropPayload("MY_TYPE", &payload_data, sizeof(int));
ImGui::Text("Dragging value: %d", payload_data); // Preview tooltip
ImGui::EndDragDropSource();
}
// Target: the region you drop INTO
ImGui::Button("Drop here");
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("MY_TYPE"))
{
int received = *(const int*)payload->Data;
// Handle the drop
}
ImGui::EndDragDropTarget();
}
The payload type string ("MY_TYPE") acts as a type filter — only matching payloads are accepted.
4.16 Keyboard and Gamepad Navigation
Enabling Navigation
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Arrow keys, Tab, Enter, Escape
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // D-pad, face buttons, triggers
Keyboard Shortcuts
// Global shortcut (works when Dear ImGui has focus)
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S))
{
// Save
}
// Shortcut bound to a menu item
if (ImGui::MenuItem("Save", "Ctrl+S"))
{
// Save action
}
// Shortcut assigned to a specific widget
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Z);
if (ImGui::Button("Undo"))
{
// Undo action — also triggered by Ctrl+Z
}
Modifier keys: ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiMod_Alt, ImGuiMod_Super.
4.17 Fonts
Default Fonts
ImGuiIO& io = ImGui::GetIO();
// Vector-based default font (scalable, new approach)
io.Fonts->AddFontDefaultVector();
// Bitmap-based default font (ProggyClean 13px)
io.Fonts->AddFontDefaultBitmap();
Loading Custom Fonts
// From file
ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 16.0f);
// From memory (e.g., embedded font data)
ImFont* font2 = io.Fonts->AddFontFromMemoryTTF(font_data, font_data_size, 16.0f);
// With specific glyph ranges
ImFont* font_jp = io.Fonts->AddFontFromFileTTF("fonts/NotoSansJP.ttf", 18.0f,
nullptr, io.Fonts->GetGlyphRangesJapanese());
Dynamic Font Sizing (New in 1.92)
ImFont* font = io.Fonts->AddFontFromFileTTF("Roboto.ttf", 0); // Base size 0 = vector font
// In your frame loop:
ImGui::PushFont(font, 24.0f); // Render at 24px
ImGui::Text("Large text");
ImGui::PopFont();
ImGui::PushFont(font, 12.0f); // Render at 12px
ImGui::Text("Small text");
ImGui::PopFont();
Font Merging (Icon Fonts)
// Load primary font
io.Fonts->AddFontFromFileTTF("Roboto.ttf", 16.0f);
// Merge icon font into primary font
ImFontConfig config;
config.MergeMode = true;
config.GlyphMinAdvanceX = 16.0f;
static const ImWchar icon_ranges[] = { 0xf000, 0xf3ff, 0 };
io.Fonts->AddFontFromFileTTF("FontAwesome.ttf", 16.0f, &config, icon_ranges);
Available Glyph Ranges
| Method | Coverage |
|---|---|
GetGlyphRangesDefault() |
Basic Latin + supplement (U+0020–U+00FF) |
GetGlyphRangesGreek() |
Greek and Coptic |
GetGlyphRangesCyrillic() |
Cyrillic |
GetGlyphRangesKorean() |
Korean (Hangul) |
GetGlyphRangesJapanese() |
Hiragana, Katakana, CJK Unified |
GetGlyphRangesChineseFull() |
Full Chinese character set |
GetGlyphRangesChineseSimplifiedCommon() |
~2500 most common simplified Chinese |
GetGlyphRangesThai() |
Thai |
GetGlyphRangesVietnamese() |
Vietnamese |
4.18 Drawing API (ImDrawList)
The low-level drawing API lets you render arbitrary shapes. Every window has a draw list, and there are global background and foreground draw lists.
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 p = ImGui::GetCursorScreenPos(); // Use as origin
ImU32 col = IM_COL32(255, 255, 0, 255); // Yellow, fully opaque
// Lines
draw_list->AddLine(ImVec2(p.x, p.y), ImVec2(p.x + 100, p.y + 50), col, 2.0f);
// Rectangles
draw_list->AddRect(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col);
draw_list->AddRectFilled(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col);
draw_list->AddRectFilled(ImVec2(p.x, p.y), ImVec2(p.x + 80, p.y + 40), col, 10.0f); // Rounded
// Circles
draw_list->AddCircle(ImVec2(p.x + 40, p.y + 40), 30.0f, col, 0, 2.0f);
draw_list->AddCircleFilled(ImVec2(p.x + 40, p.y + 40), 30.0f, col);
// Triangles
draw_list->AddTriangle(ImVec2(p.x, p.y + 60), ImVec2(p.x + 60, p.y + 60),
ImVec2(p.x + 30, p.y), col, 2.0f);
draw_list->AddTriangleFilled(ImVec2(p.x, p.y + 60), ImVec2(p.x + 60, p.y + 60),
ImVec2(p.x + 30, p.y), col);
// Text
draw_list->AddText(ImVec2(p.x, p.y), col, "Hello");
// Bezier curves
draw_list->AddBezierCubic(ImVec2(p.x, p.y), ImVec2(p.x+50, p.y-50),
ImVec2(p.x+100, p.y+50), ImVec2(p.x+150, p.y), col, 2.0f);
draw_list->AddBezierQuadratic(ImVec2(p.x, p.y), ImVec2(p.x+75, p.y-50),
ImVec2(p.x+150, p.y), col, 2.0f);
// N-gons (regular polygons)
draw_list->AddNgon(ImVec2(p.x+40, p.y+40), 30.0f, col, 6, 2.0f); // Hexagon outline
draw_list->AddNgonFilled(ImVec2(p.x+40, p.y+40), 30.0f, col, 6); // Filled hexagon
// Ellipses
draw_list->AddEllipse(ImVec2(p.x+50, p.y+30), ImVec2(50, 30), col, 0.0f, 0, 2.0f);
draw_list->AddEllipseFilled(ImVec2(p.x+50, p.y+30), ImVec2(50, 30), col);
// Images
draw_list->AddImage(tex_id, ImVec2(p.x, p.y), ImVec2(p.x + 100, p.y + 100));
// Polyline
ImVec2 points[] = { {p.x,p.y}, {p.x+30,p.y-20}, {p.x+60,p.y+10}, {p.x+90,p.y-5} };
draw_list->AddPolyline(points, 4, col, ImDrawFlags_None, 2.0f);
// Convex filled polygon
draw_list->AddConvexPolyFilled(points, 4, col);
Draw List Layers
ImDrawList* bg = ImGui::GetBackgroundDrawList(); // Behind all windows
ImDrawList* fg = ImGui::GetForegroundDrawList(); // In front of all windows
ImDrawList* wl = ImGui::GetWindowDrawList(); // Current window's draw list
4.19 ID System
Dear ImGui uses IDs to track widget state (open/closed, active, edited, scroll position). Understanding IDs is essential to avoid bugs.
How IDs Work
Every widget needs a unique ID within its parent window/tree scope. By default, the ID is derived from the label string:
ImGui::Button("OK"); // ID = hash of "OK" in current scope
ImGui::Button("OK"); // BUG: duplicate ID — second button won't work
Solving ID Conflicts
Method 1: ## separator — text after ## is part of the ID but not displayed:
ImGui::Button("OK##save"); // Displays "OK", ID includes "save"
ImGui::Button("OK##cancel"); // Displays "OK", different ID
Method 2: ### separator — only text after ### forms the ID:
// Useful when label changes but you want stable widget state
char buf[64];
sprintf(buf, "Frame %d###AnimatingTitle", frame_count);
ImGui::Begin(buf); // Window ID stays the same even as title changes
ImGui::End();
Method 3: PushID()/PopID() — create a unique ID scope:
for (int i = 0; i < items.size(); i++)
{
ImGui::PushID(i); // Push integer as ID scope
ImGui::Button("Delete"); // "Delete" is now unique within this scope
ImGui::SameLine();
ImGui::Button("Edit");
ImGui::PopID();
}
PushID() accepts int, const char*, const void*, or a string range.
4.20 Disabling Widgets
bool can_edit = user_has_permission();
ImGui::BeginDisabled(!can_edit);
ImGui::InputText("Name", buf, sizeof(buf));
ImGui::Button("Save");
ImGui::EndDisabled();
// When disabled: widgets are grayed out and do not respond to input
4.21 Clipping
ImGui::PushClipRect(ImVec2(100, 100), ImVec2(300, 300), true);
// Only content within the rectangle is visible
ImGui::Text("This text may be clipped");
ImGui::PopClipRect();
4.22 Logging and Capture
Dear ImGui can capture UI output as text — useful for copying widget content to the clipboard or a file.
ImGui::LogToClipboard(); // Start capturing to clipboard
ImGui::LogToFile(-1, "output.txt"); // Capture to file
ImGui::LogToTTY(); // Capture to stdout
// All subsequent widget output is captured as text...
ImGui::LogFinish(); // Stop capturing
// Convenience: show buttons for all log targets
ImGui::LogButtons();
4.23 Settings Persistence
Dear ImGui automatically saves and loads window positions, sizes, collapsed states, and table column settings to an .ini file.
// Default behavior: saves to "imgui.ini" on exit
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = "my_app_layout.ini"; // Custom filename
io.IniFilename = nullptr; // Disable auto-save/load
// Manual control
ImGui::LoadIniSettingsFromDisk("layout.ini");
ImGui::SaveIniSettingsToDisk("layout.ini");
// In-memory (useful for custom storage)
const char* ini_data = ImGui::SaveIniSettingsToMemory();
ImGui::LoadIniSettingsFromMemory(ini_data);
// Detect when settings need saving (useful for custom persistence)
if (io.WantSaveIniSettings)
{
const char* data = ImGui::SaveIniSettingsToMemory();
// Save 'data' to your custom storage
io.WantSaveIniSettings = false;
}
4.24 Item Queries
After any widget call, you can query its state:
ImGui::InputFloat("Value", &val);
// Hover and focus
bool hovered = ImGui::IsItemHovered(); // Mouse is over it
bool focused = ImGui::IsItemFocused(); // Has keyboard focus
bool active = ImGui::IsItemActive(); // Being interacted with (held, editing)
// Interaction events
bool clicked = ImGui::IsItemClicked(); // Was clicked this frame
bool edited = ImGui::IsItemEdited(); // Value was changed this frame
bool activated = ImGui::IsItemActivated(); // Became active this frame
bool deacted = ImGui::IsItemDeactivated(); // Stopped being active this frame
bool deact_edit = ImGui::IsItemDeactivatedAfterEdit(); // Stopped being active AND value changed
// Useful for commit-on-deactivate patterns (undo/redo):
ImGui::DragFloat("Position", &pos);
if (ImGui::IsItemActivated())
undo_stack.push(pos); // Save old value when editing starts
if (ImGui::IsItemDeactivatedAfterEdit())
redo_stack.clear(); // Commit when editing ends with a change
Input Dispatch
// Check if Dear ImGui wants to capture input (critical for game/app integration):
ImGuiIO& io = ImGui::GetIO();
if (io.WantCaptureMouse)
{
// Dear ImGui is using the mouse — do NOT pass mouse events to your game
}
if (io.WantCaptureKeyboard)
{
// Dear ImGui is using the keyboard — do NOT pass key events to your game
}
5. Common Tasks
5.1 Build a Property Editor
Goal: Display and edit properties of a selected object in a two-column layout.
Steps:
void DrawPropertyEditor(GameObject* obj)
{
if (!obj) return;
ImGui::Text("Editing: %s", obj->name.c_str());
ImGui::Separator();
if (ImGui::BeginTable("properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV))
{
ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150.0f);
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
// Transform section
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("Position");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::DragFloat3("##pos", &obj->position.x, 0.1f);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("Rotation");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::DragFloat3("##rot", &obj->rotation.x, 1.0f);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("Scale");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::DragFloat3("##scale", &obj->scale.x, 0.01f, 0.01f, 100.0f);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("Visible");
ImGui::TableSetColumnIndex(1);
ImGui::Checkbox("##visible", &obj->visible);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("Color");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::ColorEdit4("##color", &obj->color.x);
ImGui::EndTable();
}
}
Verify: Run the property editor, change values, confirm they apply to the object in real time.
If it fails: Check that PushID/PopID scopes are correct if multiple objects share property names. The ## prefix on labels avoids duplicate IDs within the table.
5.2 Create a Dockable Tool Window with Menu Bar
Goal: A standalone tool window with its own menu bar.
static bool show_tool = true;
void DrawToolWindow()
{
if (!show_tool) return;
ImGui::Begin("Asset Browser", &show_tool, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("View"))
{
static bool show_thumbnails = true;
ImGui::MenuItem("Thumbnails", nullptr, &show_thumbnails);
static bool show_details = false;
ImGui::MenuItem("Details", nullptr, &show_details);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Sort"))
{
if (ImGui::MenuItem("By Name")) { /* sort */ }
if (ImGui::MenuItem("By Date")) { /* sort */ }
if (ImGui::MenuItem("By Size")) { /* sort */ }
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
// Content
static char filter[128] = "";
ImGui::InputTextWithHint("##filter", "Filter assets...", filter, sizeof(filter));
ImGui::BeginChild("AssetList", ImVec2(0, 0), ImGuiChildFlags_Borders);
for (int i = 0; i < asset_count; i++)
{
if (filter[0] && !strstr(assets[i].name, filter))
continue;
ImGui::Selectable(assets[i].name);
}
ImGui::EndChild();
ImGui::End();
}
5.3 Add a File Browser Dialog
Goal: Let the user pick a file path using Dear ImGui widgets.
struct FileBrowser
{
char current_path[512] = ".";
char selected_file[256] = "";
bool is_open = false;
void Draw()
{
if (!is_open) return;
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
if (ImGui::BeginPopupModal("Select File", &is_open))
{
ImGui::InputText("Path", current_path, sizeof(current_path));
ImGui::Separator();
ImGui::BeginChild("FileList", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()));
// List directory contents here using your OS API
// For each entry:
// if (ImGui::Selectable(entry_name, is_selected))
// strncpy(selected_file, entry_name, sizeof(selected_file));
ImGui::EndChild();
if (ImGui::Button("Open", ImVec2(120, 0)))
{
// Process selected_file
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
selected_file[0] = '\0';
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void Open() { is_open = true; ImGui::OpenPopup("Select File"); }
};
5.4 Implement Undo/Redo with IsItemDeactivatedAfterEdit
Goal: Track value changes for undo/redo with proper commit semantics.
struct UndoEntry { float old_value; float new_value; };
std::vector<UndoEntry> undo_stack;
int undo_index = -1;
static float value = 0.0f;
static float value_before_edit = 0.0f;
ImGui::DragFloat("Value", &value, 0.01f);
if (ImGui::IsItemActivated())
{
value_before_edit = value; // Snapshot before editing begins
}
if (ImGui::IsItemDeactivatedAfterEdit())
{
// Editing ended and value changed — commit to undo stack
undo_stack.resize(undo_index + 1); // Truncate any redo entries
undo_stack.push_back({ value_before_edit, value });
undo_index = (int)undo_stack.size() - 1;
}
// Undo
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z) && undo_index >= 0)
{
value = undo_stack[undo_index].old_value;
undo_index--;
}
// Redo
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y) && undo_index < (int)undo_stack.size() - 1)
{
undo_index++;
value = undo_stack[undo_index].new_value;
}
5.5 Create a Log/Console Window with Filtering
Goal: Scrollable, filterable log display with auto-scroll.
struct AppLog
{
ImGuiTextBuffer buf;
ImGuiTextFilter filter;
ImVector<int> line_offsets;
bool auto_scroll = true;
void Clear() { buf.clear(); line_offsets.clear(); line_offsets.push_back(0); }
void AddLog(const char* fmt, ...)
{
int old_size = buf.size();
va_list args;
va_start(args, fmt);
buf.appendfv(fmt, args);
va_end(args);
for (int i = old_size; i < buf.size(); i++)
if (buf[i] == '\n')
line_offsets.push_back(i + 1);
}
void Draw(const char* title, bool* p_open = nullptr)
{
ImGui::Begin(title, p_open);
// Options
ImGui::Checkbox("Auto-scroll", &auto_scroll);
ImGui::SameLine();
if (ImGui::Button("Clear")) Clear();
ImGui::SameLine();
filter.Draw("Filter", -100.0f);
ImGui::Separator();
// Log content
ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None,
ImGuiWindowFlags_HorizontalScrollbar);
const char* buf_begin = buf.begin();
const char* buf_end = buf.end();
if (filter.IsActive())
{
for (int i = 0; i < line_offsets.Size - 1; i++)
{
const char* line_start = buf_begin + line_offsets[i];
const char* line_end = (i + 1 < line_offsets.Size)
? buf_begin + line_offsets[i + 1] - 1 : buf_end;
if (filter.PassFilter(line_start, line_end))
ImGui::TextUnformatted(line_start, line_end);
}
}
else
{
// Use clipper for large logs
ImGuiListClipper clipper;
clipper.Begin(line_offsets.Size - 1);
while (clipper.Step())
{
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
const char* line_start = buf_begin + line_offsets[i];
const char* line_end = (i + 1 < line_offsets.Size)
? buf_begin + line_offsets[i + 1] - 1 : buf_end;
ImGui::TextUnformatted(line_start, line_end);
}
}
clipper.End();
}
if (auto_scroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
ImGui::End();
}
};
5.6 Build a Color Theme Editor
Goal: Interactive theme editor that exports code.
void DrawThemeEditor()
{
ImGuiStyle& style = ImGui::GetStyle();
// Preset buttons
if (ImGui::Button("Dark")) ImGui::StyleColorsDark();
ImGui::SameLine();
if (ImGui::Button("Light")) ImGui::StyleColorsLight();
ImGui::SameLine();
if (ImGui::Button("Classic")) ImGui::StyleColorsClassic();
ImGui::Separator();
// Sizes
ImGui::SliderFloat("Window Rounding", &style.WindowRounding, 0.0f, 12.0f);
ImGui::SliderFloat("Frame Rounding", &style.FrameRounding, 0.0f, 12.0f);
ImGui::SliderFloat2("Window Padding", (float*)&style.WindowPadding, 0.0f, 20.0f);
ImGui::SliderFloat2("Frame Padding", (float*)&style.FramePadding, 0.0f, 20.0f);
ImGui::Separator();
// Colors
if (ImGui::BeginChild("Colors", ImVec2(0, 0), ImGuiChildFlags_Borders))
{
for (int i = 0; i < ImGuiCol_COUNT; i++)
{
const char* name = ImGui::GetStyleColorName(i);
ImGui::PushID(i);
ImGui::ColorEdit4("##color", (float*)&style.Colors[i],
ImGuiColorEditFlags_AlphaBar);
ImGui::SameLine();
ImGui::TextUnformatted(name);
ImGui::PopID();
}
}
ImGui::EndChild();
// Export button
if (ImGui::Button("Export to Clipboard"))
{
ImGui::LogToClipboard();
ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;\n");
for (int i = 0; i < ImGuiCol_COUNT; i++)
{
const ImVec4& c = style.Colors[i];
ImGui::LogText("colors[ImGuiCol_%s] = ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\n",
ImGui::GetStyleColorName(i), c.x, c.y, c.z, c.w);
}
ImGui::LogFinish();
}
}
5.7 Display an Image/Texture from Your Application
Goal: Load and display a texture (OpenGL example).
// Step 1: Create texture using your graphics API
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Step 2: Display in Dear ImGui
ImGui::Begin("Image Viewer");
ImGui::Image((ImTextureID)(intptr_t)texture, ImVec2((float)width, (float)height));
// With UV coordinates (display a sub-region):
ImGui::Image((ImTextureID)(intptr_t)texture,
ImVec2(128, 128), // display size
ImVec2(0.0f, 0.0f), // UV top-left
ImVec2(0.5f, 0.5f)); // UV bottom-right
ImGui::End();
5.8 Create a Sortable Data Table
Goal: Display tabular data with clickable column headers for sorting.
struct Employee { int id; const char* name; float salary; };
static Employee employees[] = {
{1, "Alice", 75000}, {2, "Bob", 82000}, {3, "Carol", 91000},
{4, "Dave", 68000}, {5, "Eve", 95000}
};
void DrawEmployeeTable()
{
ImGuiTableFlags flags = ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable
| ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders
| ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("employees", 3, flags, ImVec2(0, 300)))
{
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Salary", ImGuiTableColumnFlags_PreferSortDescending);
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();
if (ImGuiTableSortSpecs* specs = ImGui::TableGetSortSpecs())
{
if (specs->SpecsDirty)
{
auto& s = specs->Specs[0];
std::sort(std::begin(employees), std::end(employees),
[&](const Employee& a, const Employee& b) {
int cmp = 0;
switch (s.ColumnIndex) {
case 0: cmp = a.id - b.id; break;
case 1: cmp = strcmp(a.name, b.name); break;
case 2: cmp = (a.salary > b.salary) ? 1 : -1; break;
}
return (s.SortDirection == ImGuiSortDirection_Ascending) ? cmp < 0 : cmp > 0;
});
specs->SpecsDirty = false;
}
}
for (auto& e : employees)
{
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::Text("%d", e.id);
ImGui::TableSetColumnIndex(1); ImGui::Text("%s", e.name);
ImGui::TableSetColumnIndex(2); ImGui::Text("$%.0f", e.salary);
}
ImGui::EndTable();
}
}
5.9 Implement Keyboard Shortcuts
Goal: Add application-wide and widget-specific keyboard shortcuts.
// Global shortcuts (evaluated every frame)
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_N)) NewDocument();
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_O)) OpenDocument();
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S)) SaveDocument();
// Widget-bound shortcut (activates the button via keyboard)
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Z);
if (ImGui::Button("Undo")) Undo();
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_Y);
if (ImGui::Button("Redo")) Redo();
// Menu items with shortcut display
if (ImGui::BeginMainMenuBar())
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("New", "Ctrl+N")) NewDocument();
if (ImGui::MenuItem("Open", "Ctrl+O")) OpenDocument();
if (ImGui::MenuItem("Save", "Ctrl+S")) SaveDocument();
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
5.10 Create a Search/Filter Widget
Goal: Filterable list using ImGuiTextFilter.
static ImGuiTextFilter filter;
filter.Draw("Search", -100.0f);
ImGui::Separator();
const char* items[] = { "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig" };
for (int i = 0; i < IM_ARRAYSIZE(items); i++)
{
if (filter.PassFilter(items[i]))
ImGui::BulletText("%s", items[i]);
}
ImGuiTextFilter supports comma-separated include terms and - prefix for exclusion (e.g., apple,banana,-rotten).
5.11 Build a Tree View with Drag-and-Drop
Goal: Hierarchical tree where items can be rearranged via drag-and-drop.
struct TreeNode { const char* name; int id; std::vector<TreeNode> children; };
void DrawTreeNode(TreeNode& node)
{
ImGui::PushID(node.id);
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow
| ImGuiTreeNodeFlags_SpanAvailWidth;
if (node.children.empty())
flags |= ImGuiTreeNodeFlags_Leaf;
bool open = ImGui::TreeNodeEx(node.name, flags);
// Drag source
if (ImGui::BeginDragDropSource())
{
ImGui::SetDragDropPayload("TREE_NODE", &node.id, sizeof(int));
ImGui::Text("Move: %s", node.name);
ImGui::EndDragDropSource();
}
// Drop target
if (ImGui::BeginDragDropTarget())
{
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("TREE_NODE"))
{
int dropped_id = *(const int*)payload->Data;
// Reparent the node with dropped_id under this node
}
ImGui::EndDragDropTarget();
}
if (open)
{
for (auto& child : node.children)
DrawTreeNode(child);
ImGui::TreePop();
}
ImGui::PopID();
}
5.12 Create a Right-Click Context Menu
Goal: Show a context menu when right-clicking items in a list.
for (int i = 0; i < item_count; i++)
{
ImGui::PushID(i);
ImGui::Selectable(items[i], selected == i);
if (ImGui::BeginPopupContextItem())
{
ImGui::Text("Actions for: %s", items[i]);
ImGui::Separator();
if (ImGui::MenuItem("Rename")) { /* start rename */ }
if (ImGui::MenuItem("Duplicate")) { /* duplicate item */ }
ImGui::Separator();
if (ImGui::MenuItem("Delete")) { /* delete item */ }
ImGui::EndPopup();
}
ImGui::PopID();
}
5.13 Modal Confirmation Dialog
Goal: Block all other interaction until the user confirms or cancels.
// Trigger
static bool pending_delete = false;
if (ImGui::Button("Delete All"))
{
pending_delete = true;
ImGui::OpenPopup("Confirm Deletion");
}
// Modal
if (ImGui::BeginPopupModal("Confirm Deletion", nullptr,
ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("This will permanently delete all items.");
ImGui::Text("This action cannot be undone.");
ImGui::Separator();
if (ImGui::Button("Delete", ImVec2(120, 0)))
{
// Perform deletion
pending_delete = false;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
pending_delete = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
5.14 Build a Node Editor Canvas (Using ImDrawList)
Goal: Draggable nodes connected by curves on a scrollable canvas.
struct Node { int id; ImVec2 pos; const char* name; };
void DrawNodeEditor(std::vector<Node>& nodes)
{
ImGui::Begin("Node Editor");
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
ImVec2 canvas_size = ImGui::GetContentRegionAvail();
// Canvas background
draw_list->AddRectFilled(canvas_pos,
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
IM_COL32(40, 40, 40, 255));
// Grid
float grid_step = 32.0f;
for (float x = 0; x < canvas_size.x; x += grid_step)
draw_list->AddLine(ImVec2(canvas_pos.x + x, canvas_pos.y),
ImVec2(canvas_pos.x + x, canvas_pos.y + canvas_size.y),
IM_COL32(200, 200, 200, 40));
for (float y = 0; y < canvas_size.y; y += grid_step)
draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + y),
ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + y),
IM_COL32(200, 200, 200, 40));
// Draw nodes
for (auto& node : nodes)
{
ImGui::PushID(node.id);
ImVec2 node_pos = ImVec2(canvas_pos.x + node.pos.x, canvas_pos.y + node.pos.y);
ImVec2 node_size = ImVec2(120, 60);
// Node box
draw_list->AddRectFilled(node_pos,
ImVec2(node_pos.x + node_size.x, node_pos.y + node_size.y),
IM_COL32(60, 60, 60, 255), 4.0f);
draw_list->AddRect(node_pos,
ImVec2(node_pos.x + node_size.x, node_pos.y + node_size.y),
IM_COL32(100, 100, 100, 255), 4.0f);
draw_list->AddText(ImVec2(node_pos.x + 8, node_pos.y + 8),
IM_COL32(255, 255, 255, 255), node.name);
// Invisible button for dragging
ImGui::SetCursorScreenPos(node_pos);
ImGui::InvisibleButton("node", node_size);
if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0))
{
node.pos.x += ImGui::GetIO().MouseDelta.x;
node.pos.y += ImGui::GetIO().MouseDelta.y;
}
ImGui::PopID();
}
// Draw connections (Bezier curves between nodes)
// draw_list->AddBezierCubic(start, cp1, cp2, end, color, thickness);
ImGui::End();
}
5.15 Create a Multi-Select List
Goal: List with Ctrl+click, Shift+click, and range selection.
static ImGuiSelectionBasicStorage selection;
const char* items[] = { "Item A", "Item B", "Item C", "Item D", "Item E",
"Item F", "Item G", "Item H", "Item I", "Item J" };
int item_count = IM_ARRAYSIZE(items);
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape
| ImGuiMultiSelectFlags_BoxSelect1d;
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, item_count);
selection.ApplyRequests(ms_io);
for (int i = 0; i < item_count; i++)
{
ImGui::SetNextItemSelectionUserData(i);
bool is_selected = selection.Contains((ImGuiID)i);
ImGui::Selectable(items[i], is_selected);
}
ms_io = ImGui::EndMultiSelect();
selection.ApplyRequests(ms_io);
ImGui::Text("Selected: %d items", selection.Size);
5.16 Load Custom Fonts with Specific Glyph Ranges
Goal: Display Chinese and icon glyphs alongside Latin text.
ImGuiIO& io = ImGui::GetIO();
// Primary font (Latin)
ImFont* font_main = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 16.0f);
// Chinese font merged into primary
ImFontConfig cfg;
cfg.MergeMode = true;
io.Fonts->AddFontFromFileTTF("fonts/NotoSansSC-Regular.ttf", 16.0f,
&cfg, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
// Icon font merged into primary
cfg.GlyphMinAdvanceX = 16.0f;
static const ImWchar icon_ranges[] = { 0xf000, 0xf3ff, 0 };
io.Fonts->AddFontFromFileTTF("fonts/FontAwesome5.ttf", 16.0f, &cfg, icon_ranges);
// Build atlas
io.Fonts->Build();
// Upload atlas texture to GPU (backend-specific)
5.17 Create a Toolbar with Image Buttons
Goal: Horizontal toolbar with icon buttons.
void DrawToolbar(ImTextureID icons_texture)
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 4));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));
ImVec2 btn_size(24, 24);
float icon_w = 1.0f / 8.0f; // 8 icons in texture atlas
// Each button uses a different UV region of the icon atlas
if (ImGui::ImageButton("##new", icons_texture, btn_size, ImVec2(0,0), ImVec2(icon_w,1)))
NewFile();
ImGui::SameLine();
if (ImGui::ImageButton("##open", icons_texture, btn_size, ImVec2(icon_w,0), ImVec2(icon_w*2,1)))
OpenFile();
ImGui::SameLine();
if (ImGui::ImageButton("##save", icons_texture, btn_size, ImVec2(icon_w*2,0), ImVec2(icon_w*3,1)))
SaveFile();
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
if (ImGui::ImageButton("##undo", icons_texture, btn_size, ImVec2(icon_w*3,0), ImVec2(icon_w*4,1)))
Undo();
ImGui::SameLine();
if (ImGui::ImageButton("##redo", icons_texture, btn_size, ImVec2(icon_w*4,0), ImVec2(icon_w*5,1)))
Redo();
ImGui::PopStyleVar(2);
}
5.18 Build a Status Bar
Goal: Fixed-height bar at the bottom of a window showing status information.
void DrawStatusBar()
{
// Reserve space at the bottom
float status_height = ImGui::GetFrameHeightWithSpacing();
ImVec2 window_size = ImGui::GetWindowSize();
ImGui::SetCursorPosY(window_size.y - status_height - ImGui::GetStyle().WindowPadding.y);
ImGui::Separator();
ImGui::Text("Ready");
ImGui::SameLine(200);
ImGui::Text("Line: %d, Col: %d", cursor_line, cursor_col);
ImGui::SameLine(400);
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
}
5.19 Implement Value Editing with DragFloat
Goal: Precise value editing with variable-speed dragging.
static float position[3] = {0, 0, 0};
static float rotation = 0.0f;
static float scale = 1.0f;
// Speed parameter controls sensitivity
ImGui::DragFloat3("Position", position, 0.1f); // 0.1 units per pixel
ImGui::DragFloat("Rotation", &rotation, 1.0f, 0, 360, "%.1f deg"); // 1 degree per pixel
ImGui::DragFloat("Scale", &scale, 0.01f, 0.01f, 100.0f, "%.2fx"); // Clamped range
// Format string controls display
ImGui::DragFloat("Speed", &speed, 0.1f, 0.0f, FLT_MAX, "%.1f m/s");
ImGui::DragFloat("Opacity", &opacity, 0.005f, 0.0f, 1.0f, "%.0f%%",
ImGuiSliderFlags_AlwaysClamp);
// Integer drag
static int count = 10;
ImGui::DragInt("Count", &count, 1, 0, 1000);
// Range drag (two values)
static float range[2] = {20.0f, 80.0f};
ImGui::DragFloatRange2("Range", &range[0], &range[1], 0.5f, 0.0f, 100.0f);
5.20 Create Animated Widgets Using GetTime()
Goal: Widgets that animate smoothly using Dear ImGui's built-in time.
// Pulsing button
float pulse = 0.5f + 0.5f * sinf((float)ImGui::GetTime() * 3.0f);
ImGui::PushStyleColor(ImGuiCol_Button,
ImVec4(pulse * 0.8f, 0.2f, 0.2f, 1.0f));
ImGui::Button("Alert!", ImVec2(100, 40));
ImGui::PopStyleColor();
// Animated progress bar
float progress = fmodf((float)ImGui::GetTime() * 0.3f, 1.0f);
ImGui::ProgressBar(progress, ImVec2(0, 0), "Loading...");
// Spinning indicator using ImDrawList
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 center = ImGui::GetCursorScreenPos();
center.x += 20; center.y += 20;
float radius = 15.0f;
float t = (float)ImGui::GetTime();
int segments = 12;
for (int i = 0; i < segments; i++)
{
float a = (float)i / segments * IM_PI * 2.0f + t * 4.0f;
float alpha = (float)i / segments;
draw_list->AddCircleFilled(
ImVec2(center.x + cosf(a) * radius, center.y + sinf(a) * radius),
3.0f, IM_COL32(255, 255, 255, (int)(alpha * 255)));
}
ImGui::Dummy(ImVec2(40, 40)); // Reserve space
// Smooth scrolling
static float target_scroll = 0.0f;
static float current_scroll = 0.0f;
current_scroll += (target_scroll - current_scroll) * ImGui::GetIO().DeltaTime * 10.0f;
5.21 Custom Widget Background Colors
Goal: Highlight table rows or widget backgrounds conditionally.
if (ImGui::BeginTable("colored_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders))
{
for (int row = 0; row < 10; row++)
{
ImGui::TableNextRow();
// Highlight rows conditionally
if (data[row].has_error)
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(255, 0, 0, 50));
else if (data[row].is_selected)
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, IM_COL32(0, 120, 255, 50));
ImGui::TableSetColumnIndex(0); ImGui::Text("%d", row);
ImGui::TableSetColumnIndex(1); ImGui::Text("%s", data[row].name);
ImGui::TableSetColumnIndex(2); ImGui::Text("%.2f", data[row].value);
}
ImGui::EndTable();
}
5.22 Using ImGuiListClipper for Large Lists
Goal: Efficiently render lists with thousands of items.
ImGui::BeginChild("LargeList", ImVec2(0, 400));
ImGuiListClipper clipper;
clipper.Begin(100000); // 100,000 items
while (clipper.Step())
{
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
ImGui::Text("Item %d: data here", i);
}
}
clipper.End();
ImGui::EndChild();
The clipper calculates which items are visible and only calls your code for those items, making even million-item lists performant.
6. Troubleshooting
Error: Assertion failed: thickness > 0.0f
Cause: A draw call (e.g., AddLine, AddRect, AddCircle) was given a zero or negative thickness value.
Solution:
- Search your code for draw calls passing
0.0fas the thickness parameter. - Ensure thickness variables are initialized to a positive value.
- If thickness comes from user input, clamp it:
ImMax(thickness, 1.0f).
Prevention: Always validate draw parameters before passing them. Use a minimum thickness of 1.0f for visible lines.
Error: Assertion failed: g.CurrentWindow != NULL
Cause: You called a widget function (e.g., ImGui::Text(), ImGui::Button()) outside of a Begin()/End() block, or after calling End() without a matching Begin().
Solution:
- Ensure every widget call is between a
Begin()/End()pair. - Check for early returns that skip the
End()call. - Never call widget functions before
NewFrame()or afterRender().
Prevention: Structure your code so End() is always called — even when Begin() returns false:
if (ImGui::Begin("Window")) { /* widgets */ }
ImGui::End(); // Always called
Error: Assertion failed: (flags & ImGuiWindowFlags_NoInputs) == 0
Cause: Attempted to focus or interact with a window that has ImGuiWindowFlags_NoInputs set.
Solution:
- Remove
ImGuiWindowFlags_NoInputsif the window needs interaction. - If using
NoInputsintentionally (overlay windows), ensure no code tries to focus or interact with it.
Prevention: Only use NoInputs for purely visual overlay windows.
Error: Duplicate widget IDs causing interaction failures
Cause: Two or more widgets in the same scope share the same label, producing the same ID. Only the first widget receives interactions.
Solution:
- Add unique suffixes with
##:Button("Delete##item1"),Button("Delete##item2"). - Use
PushID(i)/PopID()in loops. - Use the ID Stack Debugger:
ImGui::ShowIDStackToolWindow()to inspect active IDs.
Prevention: Use ## or PushID whenever creating widgets in a loop or with repeated labels. This is the most common beginner mistake.
Error: WantCaptureMouse not preventing game input
Cause: The application is not checking io.WantCaptureMouse / io.WantCaptureKeyboard before dispatching input to the game layer.
Solution:
- After
ImGui::NewFrame(), check the flags before processing input:
if (!io.WantCaptureMouse)
{
// Process mouse for game
}
if (!io.WantCaptureKeyboard)
{
// Process keyboard for game
}
- Ensure you are checking the flags after
NewFrame(), not before.
Prevention: Always gate your application's input handling behind these flags.
Error: Font atlas texture fails to build
Cause: The combined size of all fonts exceeds the maximum texture dimensions, or a font file path is incorrect.
Solution:
- Verify font file paths exist and are readable.
- Reduce the number of glyph ranges or fonts.
- Increase atlas size:
io.Fonts->TexDesiredWidth = 4096; - Check return value of
AddFontFromFileTTF()— it returnsnullptron failure.
Prevention: Build the font atlas early in initialization and check the return values. Use GetGlyphRangesChineseSimplifiedCommon() instead of GetGlyphRangesChineseFull() when full CJK is not needed.
Error: Linker errors — undefined reference to ImGui::*
Cause: The imgui .cpp files are not compiled and linked into your application.
Solution:
- Ensure all four core files are in your build:
imgui.cpp,imgui_draw.cpp,imgui_tables.cpp,imgui_widgets.cpp. - Ensure backend
.cppfiles are also compiled:imgui_impl_<platform>.cppandimgui_impl_<renderer>.cpp. - If using a static library, ensure it was built with the same
imconfig.h.
Prevention: Add all required .cpp files to your build system at setup time.
Error: Assertion failed: g.FrameCount > 0 or NewFrame not called
Cause: You called Render() or widget functions without first calling NewFrame(), or called NewFrame() twice without an intervening Render().
Solution:
- Ensure every frame follows the pattern:
NewFrame()→ widgets →Render(). - Check that backend
_NewFrame()calls happen beforeImGui::NewFrame().
Prevention: Use the standard frame structure from the example applications.
Error: Window positions reset every launch
Cause: The .ini file is not being saved or loaded. Common causes: IniFilename set to nullptr, working directory changes between runs, file permissions.
Solution:
- Check
io.IniFilenameis set (default is"imgui.ini"). - Verify the working directory is consistent.
- Use an absolute path:
io.IniFilename = "C:/myapp/imgui.ini"; - If using manual persistence, call
SaveIniSettingsToDisk()before destroying the context.
Prevention: Set IniFilename to an absolute path in a known-writable directory.
Error: DLL boundary crashes when using Dear ImGui across DLL boundaries
Cause: Each DLL gets its own copy of Dear ImGui's global state (GImGui). Widgets called from a DLL use a different context than the main application.
Solution:
- Use
ImGui::SetCurrentContext()at the start of DLL functions to set the context created by the host application. - Pass the context pointer from the host to the DLL via an initialization function.
- Alternatively, configure
IMGUI_APIto export/import symbols so all code shares one copy.
Prevention: Design your architecture so all Dear ImGui calls share a single context. Pass the ImGuiContext* across DLL boundaries.
Error: Text renders as rectangles or question marks
Cause: The requested glyphs are not in the font atlas. The default font only covers Basic Latin (U+0020–U+00FF).
Solution:
- Load a font with the required glyph ranges:
AddFontFromFileTTF("font.ttf", size, nullptr, io.Fonts->GetGlyphRangesJapanese()). - For custom ranges, build an
ImWchararray:{ 0x4e00, 0x9fff, 0 }. - Merge icon fonts using
ImFontConfig::MergeMode = true.
Prevention: Plan your glyph requirements at initialization time. Test with the target language immediately.
Error: Colors look wrong (swapped red and blue channels)
Cause: Your renderer expects BGRA color packing but Dear ImGui outputs RGBA by default (or vice versa).
Solution:
- Define
IMGUI_USE_BGRA_PACKED_COLORinimconfig.hif your renderer expects BGRA. - Alternatively, swap channels in your pixel shader.
Prevention: Check your renderer's expected color format and configure imconfig.h accordingly.
Error: Assertion failed: draw_list->CmdBuffer.Size > 0 or empty draw data
Cause: Render() was called without any Begin()/End() pairs, or the Dear ImGui context is not properly initialized.
Solution:
- Ensure at least one window exists (even an empty one) before rendering.
- Check that
CreateContext()was called before any other Dear ImGui function. - Verify the backend initialization order: platform init → renderer init → main loop.
Prevention: Follow the initialization order from the example applications.
Error: ImGui::TableNextRow() called without calling ImGui::TableNextColumn()
Cause: Incorrect table traversal pattern — you must call TableNextColumn() or TableSetColumnIndex() after TableNextRow() before emitting content.
Solution:
// Correct pattern:
ImGui::TableNextRow();
ImGui::TableNextColumn(); // Move to column 0
ImGui::Text("Content");
Prevention: Use either the TableNextRow() + TableSetColumnIndex() pattern or the simpler TableNextColumn() flow consistently.
Error: Scrollbar appears but content is not scrollable
Cause: The BeginChild() region has no height limit, so the child auto-sizes to fit content and never needs scrolling.
Solution:
- Specify an explicit height:
BeginChild("list", ImVec2(0, 300)). - Or use
ImGui::BeginChild("list", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()))to fill remaining space minus a footer.
Prevention: Always specify a height for scrollable child regions.
Error: Assertion failed: user_callback != NULL in ImDrawCmd
Cause: A draw command has a user callback set but the function pointer is null or invalid. This can happen with corrupted draw lists.
Solution:
- If using custom draw callbacks, ensure the callback pointer is valid for the lifetime of the frame.
- Check for memory corruption in your draw list manipulation code.
Prevention: Only use AddCallback() when necessary, and ensure callbacks survive until Render() completes.
Error: Mouse cursor is offset or in the wrong position
Cause: Display scaling (HiDPI), multiple monitors with different DPIs, or incorrect io.DisplayFramebufferScale.
Solution:
- Ensure
io.DisplaySizematches your window's client area in screen coordinates. - Set
io.DisplayFramebufferScaleto the ratio of framebuffer pixels to screen coordinates (typically 2.0 on Retina displays). - Use the backend's built-in handling (most backends handle this automatically).
Prevention: Use the platform backend's DPI handling. Check ImGui_ImplXxx_NewFrame() in your backend for DPI-related code.
Error: Widgets stop responding after a popup is closed unexpectedly
Cause: A popup was opened but never properly closed (e.g., the code path that calls EndPopup() was skipped).
Solution:
- Ensure
EndPopup()is always called whenBeginPopup()/BeginPopupModal()returns true. - Check for early returns between
BeginPopup()andEndPopup().
Prevention: Structure popup code as:
if (ImGui::BeginPopup("name"))
{
// All code here
ImGui::EndPopup();
}
Never place returns or conditionals between BeginPopup and EndPopup.
Error: Table column widths reset unexpectedly
Cause: The table ID changed between frames (dynamic label, different scope), or .ini settings are not being persisted.
Solution:
- Use a stable string ID for
BeginTable(). - If the table is in a loop, use
PushID()to create a stable scope. - Check that
io.IniFilenameis set and writable.
Prevention: Use fixed table identifiers and ensure .ini persistence is working.
Error: ImGui::Shortcut() does not trigger
Cause: Another window or widget has focus and is consuming the key. Or ImGuiConfigFlags_NavEnableKeyboard is not set.
Solution:
- Enable keyboard navigation:
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard. - Use
ImGuiInputFlags_RouteGlobalif you want the shortcut to fire regardless of focus:ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_RouteGlobal).
Prevention: Plan your shortcut routing strategy. Use route flags to control priority.
Error: Assertion failed: "Mismatched Begin/End calls" or stack mismatch
Cause: A Begin* call (e.g., BeginChild, BeginGroup, BeginTable, BeginMenu, PushStyleColor) does not have a matching End* / Pop* call, or vice versa.
Solution:
- Use the Metrics/Debug window (
ShowMetricsWindow()) — it displays the active stack and highlights mismatches. - Search your code for all
Begin*/Push*calls and verify matchingEnd*/Pop*calls. - Pay attention to conditional logic — if
BeginChild()is inside anif,EndChild()must be in the sameifblock.
Prevention: Keep Begin*/End* pairs at the same indentation level and in the same scope. Never conditionally skip an End* call.
Error: IMGUI_CHECKVERSION() assertion fails
Cause: The Dear ImGui version compiled into your application does not match the version compiled into a library or DLL you are linking against. This typically means mismatched headers and .cpp files.
Solution:
- Ensure all translation units use the same version of Dear ImGui headers.
- If using a precompiled library, rebuild it with the same
imgui.has your application.
Prevention: Always compile Dear ImGui from source as part of your project rather than using prebuilt binaries.
Error: AddFontFromFileTTF returns nullptr
Cause: The font file path is invalid, the file cannot be read, or memory allocation failed.
Solution:
- Verify the file exists at the specified path.
- Use an absolute path or verify the working directory.
- On mobile platforms, ensure the font is bundled in the app's assets.
- Check that
ImFileOpenworks for your platform (override viaIMGUI_DISABLE_DEFAULT_FILE_FUNCTIONSif needed).
Prevention: Check the return value of AddFontFromFileTTF(). Log the attempted path if it fails.
Error: Draw list generates too many vertices (overflow with 16-bit indices)
Cause: A single draw call generated more than 65,535 vertices, which exceeds the range of ImDrawIdx (default unsigned short).
Solution:
- Define
ImDrawIdxasunsigned intinimconfig.hfor 32-bit indices. - Or reduce the complexity of your drawing (fewer segments in circles, fewer items per draw list).
Prevention: For complex applications with lots of drawing, use 32-bit indices from the start: #define ImDrawIdx unsigned int in imconfig.h.
7. FAQ
General
Q: Which version should I use — master or docking branch?
The master branch is the stable, mainline version. The docking branch adds multi-window docking and multi-viewport support but is experimental. For new projects, start with master. If you specifically need window docking (tabbing windows together like an IDE), use the docking branch and be prepared for occasional API changes.
Q: How do I integrate Dear ImGui into my existing application?
Add the source files to your project, pick a platform+renderer backend from backends/, and call the initialization functions once. In your main loop, add the three-phase pattern: NewFrame() → widget calls → Render() + backend draw. Dear ImGui does not take over your main loop or event handling — you keep full control. See the examples/ folder for complete working integrations.
Q: Is Dear ImGui thread-safe?
No. All Dear ImGui calls must happen on the same thread. The library uses global state (GImGui) that is not protected by locks. If you need to update the UI from another thread, collect the data on that thread and read it on the main thread during UI rendering.
Q: Can I use Dear ImGui with language X?
Dear ImGui is a C++ library, but there are third-party bindings for many languages: Python (pyimgui, imgui-bundle), C# (ImGui.NET), Rust (imgui-rs), Go, Java, Lua, and others. The C binding (cimgui) enables integration with most languages that support C FFI. See the Wiki for a list of bindings.
Q: How do I handle high-DPI / Retina displays?
Load fonts at the appropriate size for the display (e.g., 2x the normal size for a 2x Retina display), and set io.FontGlobalScale if you want to uniformly scale all text. Most backends handle io.DisplayFramebufferScale automatically. With the new dynamic font sizing in 1.92, you can load a single vector font and render it at any size, which simplifies DPI handling.
Q: Does Dear ImGui support Unicode?
Yes. Dear ImGui uses UTF-8 for all text. The default font covers Basic Latin. For other scripts (CJK, Cyrillic, Thai, etc.), load a font with the appropriate glyph ranges. Define IMGUI_USE_WCHAR32 for characters outside the Basic Multilingual Plane (above U+FFFF).
Q: What is the performance overhead of Dear ImGui?
Minimal. Dear ImGui generates vertex buffers and draw commands, which are very fast to produce. The GPU cost is typically negligible compared to your application's own rendering. The main cost is the Render() call, which flattens the draw lists. For large UIs with thousands of items, use ImGuiListClipper to cull off-screen items.
Windows
Q: How do I make a window that fills the entire screen?
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::Begin("Fullscreen", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoBringToFrontOnFocus);
// Content...
ImGui::End();
Q: How do I create a window that cannot be closed?
Do not pass a p_open pointer (second parameter) to Begin(). Without it, no close button is shown.
Q: How do I prevent a window from being moved or resized?
Use ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize.
Q: How can I have a window with no title bar?
Use ImGuiWindowFlags_NoTitleBar. The window still needs a unique string name for identification — this name is used as the ID, not displayed.
Widgets
Q: Why is my widget not reacting when I click on it?
Most likely a duplicate ID conflict. Two widgets with the same label in the same scope will share an ID, and only the first one receives interactions. Use ##unique_suffix to differentiate or PushID() in loops. Use ShowIDStackToolWindow() to debug.
Q: How can I have widgets with empty labels?
Use ## with a unique ID: ImGui::Button("##hidden_button"). The label is empty but the ID is derived from "hidden_button". You can also use "" as a label, but then you must ensure uniqueness via PushID().
Q: How can I have multiple widgets with the same visible label?
Use the ## separator: ImGui::Button("OK##dialog1") and ImGui::Button("OK##dialog2") both display "OK" but have different IDs.
Q: How do I make a button that fills the available width?
ImGui::SetNextItemWidth(-FLT_MIN);
// or for buttons specifically:
if (ImGui::Button("Full Width", ImVec2(-FLT_MIN, 0))) { }
Q: How do I change a widget's color temporarily?
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.1f, 0.1f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.2f, 0.2f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.1f, 0.1f, 1.0f));
ImGui::Button("Red Button");
ImGui::PopStyleColor(3);
Q: How do I get the text height for layout calculations?
Use ImGui::GetTextLineHeight() for a single line or ImGui::GetTextLineHeightWithSpacing() for a line including the spacing between lines.
Input
Q: How do I dispatch inputs between Dear ImGui and my application?
Check io.WantCaptureMouse and io.WantCaptureKeyboard after NewFrame(). When they are true, Dear ImGui needs the input — do not pass it to your application. When false, Dear ImGui does not need it.
Q: How do I handle text input (character input) correctly?
Use io.AddInputCharacter() or io.AddInputCharactersUTF8() for text input. Do not convert key presses to characters yourself — that misses dead keys, IME, and system-specific text input. Most backends handle this automatically.
Q: How does navigation (keyboard/gamepad) work?
Enable it with io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard. Then Arrow keys move between widgets, Enter activates, Escape cancels/closes. Tab cycles through input fields. Ctrl+Tab switches between windows. The system works automatically with all standard widgets.
Q: Can I detect when a slider value has been committed (not just changed)?
Yes. Use IsItemDeactivatedAfterEdit() — it returns true on the frame the user releases the slider (or confirms input), and only if the value changed during the interaction.
Fonts
Q: How do I load a custom font?
ImGuiIO& io = ImGui::GetIO();
ImFont* font = io.Fonts->AddFontFromFileTTF("my_font.ttf", 18.0f);
// In your frame loop, to use it:
ImGui::PushFont(font, 18.0f);
ImGui::Text("Custom font text");
ImGui::PopFont();
Q: How do I display an icon font alongside text?
Load the icon font with MergeMode = true so it merges into the previous font. Then use the icon's Unicode codepoint inline: ImGui::Text("Save " ICON_FA_SAVE) (where ICON_FA_SAVE is a define for the Unicode codepoint string like "\xef\x83\x87").
Q: Why is my font blurry?
Bitmap fonts look best at their native size. If you need crisp text at various sizes, use the new vector font support (AddFontDefaultVector() or PushFont(font, size)) or enable FreeType with IMGUI_ENABLE_FREETYPE. Also ensure io.FontGlobalScale is 1.0 and that you are loading fonts at the correct size for your display DPI.
Q: How do I use the new dynamic font sizing (1.92)?
Load a font without specifying a pixel size (or with 0), then use PushFont(font, desired_size) and PopFont() at render time. This allows a single font to be drawn at different sizes without reloading.
Performance
Q: How do I render a large scrollable list efficiently?
Use ImGuiListClipper to skip off-screen items:
ImGuiListClipper clipper;
clipper.Begin(10000);
while (clipper.Step())
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
ImGui::Text("Item %d", i);
Without the clipper, Dear ImGui would process all 10,000 items even if only 20 are visible.
Q: Is there a limit to how many widgets I can have per frame?
No hard limit. Practically, thousands of visible widgets per frame is fine. For tens of thousands of items, use ImGuiListClipper or virtual scrolling. The CPU cost is proportional to the number of visible widgets, not the total data size.
Q: How do I reduce Dear ImGui's draw call count?
Dear ImGui automatically batches draw calls by texture. To minimize draw calls: avoid switching fonts/textures frequently, merge icon fonts into your text font, and use a single texture atlas. The Metrics window (ShowMetricsWindow()) shows draw call counts per window.
Integration
Q: How do I display a texture/image from my application?
Create the texture using your graphics API, then pass its identifier as ImTextureID to ImGui::Image(). The exact cast depends on your backend (see section 4.11). The texture must remain valid until Render() completes.
Q: How do I add Dear ImGui to a project that uses CMake?
Add the source files directly — there is no CMakeLists.txt provided by Dear ImGui (by design). Create your own add_library(imgui STATIC ...) or add the files to your executable's source list. Add the imgui/ and imgui/backends/ directories to your include path.
Q: How do I render Dear ImGui on top of my 3D scene?
Dear ImGui's render output (ImGui::GetDrawData()) is just a list of draw commands with vertex buffers. Render it after your 3D scene in your main render pass. Most backends handle this automatically — you just call ImGui_ImplXxx_RenderDrawData(ImGui::GetDrawData()) at the end of your frame.
Q: Can I use Dear ImGui without a window/display (headless)?
Yes. Use the Null backend (example_null/). This is useful for testing, generating screenshots from a CI pipeline, or running the test engine.
Building
Q: Do I need CMake to build Dear ImGui?
No. Dear ImGui has no build system of its own. Add the .cpp files to whatever build system you use. It works with Visual Studio, Make, CMake, Meson, Premake, Xcode, Ninja, or any build tool that can compile C++.
Q: I get linker errors about undefined ImGui symbols. What am I missing?
You need to compile and link all four core files: imgui.cpp, imgui_draw.cpp, imgui_tables.cpp, imgui_widgets.cpp. Also compile the backend files you are using. If you added the headers but not the source files, you will get linker errors.
Q: How do I compile Dear ImGui as a DLL/shared library?
Define IMGUI_API as __declspec(dllexport) when building the DLL and __declspec(dllimport) when consuming it. Use the same imconfig.h for both. Pass ImGuiContext* across the DLL boundary using ImGui::SetCurrentContext().
Q: What compiler warnings should I expect?
Dear ImGui compiles cleanly with most compilers at high warning levels. If you use -Wall -Wextra (GCC/Clang) or /W4 (MSVC), you may see a few warnings in the STB headers — these are safe to suppress with pragmas or by excluding them from high-warning-level builds.